From 1a2b1cec66115385f1c6107747ef9a6f95c23d34 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Thu, 28 Mar 2019 15:40:33 +0200 Subject: [PATCH 001/734] magento/magento2#22010: Updates AbstractExtensibleObject and AbstractExtensibleModel annotations --- lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php | 2 ++ .../Magento/Framework/Model/AbstractExtensibleModel.php | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index 97c24167d47e1..bf2967ba564ff 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -12,6 +12,8 @@ * * @SuppressWarnings(PHPMD.NumberOfChildren) * @api + * @deprecated + * @see \Magento\Framework\Model\AbstractExtensibleModel */ abstract class AbstractExtensibleObject extends AbstractSimpleObject implements CustomAttributesDataInterface { diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index 1cffba2543b0b..1beacf1cc6bdd 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -15,6 +15,7 @@ * This class defines basic data structure of how custom attributes are stored in an ExtensibleModel. * Implementations may choose to process custom attributes as their persistence requires them to. * @SuppressWarnings(PHPMD.NumberOfChildren) + * @api */ abstract class AbstractExtensibleModel extends AbstractModel implements \Magento\Framework\Api\CustomAttributesDataInterface From 9e92ab8ed2be34121c26bf4c87c463e94c7b9232 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi Date: Fri, 29 Mar 2019 17:24:46 +0200 Subject: [PATCH 002/734] Convert UpdateCmsPageEntityTest to MFTF --- .../CmsNewPagePageBasicFieldsSection.xml | 1 + .../Test/AdminUpdateCmsPageEntityTest.xml | 65 +++++++++++++++++++ .../Test/TestCase/UpdateCmsPageEntityTest.xml | 2 + 3 files changed, 68 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 7288e5d455d52..92170c447fa11 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -12,6 +12,7 @@ + diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml new file mode 100644 index 0000000000000..dc42d9b0918d3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml @@ -0,0 +1,65 @@ + + + + + + + + + <description value="Admin should be able to update a CMS Page"/> + <group value="backend"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Update page, deactivate--> + <!--Navigate to Page in Admin--> + <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <!--Fill data using _duplicatedCMSPage--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> + <!--Deactivate page--> + <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> + <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageNotActive"/> + <!--Save page--> + <actionGroup ref="saveCmsPage" stepKey="saveDeactivatedPage"/> + <!--Check that page is not found on frontend--> + <amOnPage url="{{StorefrontHomePage.url}}/{{_duplicatedCMSPage.identifier}}" stepKey="amOnDeactivatedPageOnFrontend"/> + <waitForPageLoad stepKey="waitForDeactivatedPageLoadOnFrontend"/> + <see userInput="Whoops, our bad..." stepKey="seePageError"/> + <!--Check page data is updated properly--> + <!--Navigate to Page in Admin--> + <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToUpdatedCMSPageInAdmin"> + <argument name="CMSPage" value="_duplicatedCMSPage"/> + </actionGroup> + <!--Verify data in admin--> + <actionGroup ref="AssertCMSPageContent" stepKey="verifyPageDataInAdmin"/> + <!--Activate page--> + <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('0')}}" stepKey="seePageIsDisabled" /> + <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageActive"/> + <actionGroup ref="saveCmsPage" stepKey="saveActivatedPage"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Verify data on frontend--> + <amOnPage url="{{StorefrontHomePage.url}}/{{_duplicatedCMSPage.identifier}}" stepKey="amOnPageTestPage"/> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="verifyPageDataOnFrontend"> + <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> + <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> + <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml index 4bee113add071..92f12c9468393 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml @@ -12,6 +12,7 @@ <data name="cms/data/title" xsi:type="string">CmsPageEdited%isolation%</data> <data name="cms/data/is_active" xsi:type="string">No</data> <data name="cms/data/content/content" xsi:type="string">cms_page_text_content_after_edit</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageSuccessSaveMessage" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageDisabledOnFrontend" /> </variation> @@ -21,6 +22,7 @@ <data name="cms/data/identifier" xsi:type="string">cms_page_url_edited_%isolation%</data> <data name="cms/data/content_heading" xsi:type="string">Content Heading TextEdited</data> <data name="cms/data/content/content" xsi:type="string">cms_page_text_content_after_edit</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageSuccessSaveMessage" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageForm" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagePreview" /> From c18d9a0c404572ce3dd742a969d507524c67387e Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 10 Apr 2019 20:00:26 +0300 Subject: [PATCH 003/734] 622 Convert CreateCmsBlockEntityTest to MFTF --- .../AdminAddCmsBlockToCategoryActionGroup.xml | 39 +++++++++++++++ ...gateToCreateNewCMSBlockPageActionGroup.xml | 14 ++++++ .../AdminSetCMSBlockDisabledActionGroup.xml | 14 ++++++ .../AdminSetCMSBlockEnabledActionGroup.xml | 13 +++++ .../AssertCMSBlockInGridActionGroup.xml | 25 ++++++++++ ...rtNoTextOnCategoryFrontPageActionGroup.xml | 21 +++++++++ ...ssertTextOnCategoryFronPageActionGroup.xml | 21 +++++++++ .../CmsNewBlockBlockBasicFieldsSection.xml | 1 + ...dCmsBlockEntityAndAssignToCategoryTest.xml | 47 +++++++++++++++++++ ...dCmsBlockEntityAndAssignToCategoryTest.xml | 46 ++++++++++++++++++ .../TestCase/CreateCmsBlockEntityTest.xml | 4 +- 11 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml new file mode 100644 index 0000000000000..ddc21169614a0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml @@ -0,0 +1,39 @@ +<?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="AdminAddCmsBlockToCategory"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="block" defaultValue="_defaultBlock"/> + </arguments> + + <!--Go to Catalog > Categories (choose category)--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoadAddProducts" after="onCategoryIndexPage"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll" after="waitForCategoryPageLoadAddProducts"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category.name)}}" stepKey="clickCategoryLink"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + + <!--Content > Add CMS Block: name of saved block--> + <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForContentLoad"/> + + <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" stepKey="selectBlock" userInput="{{block.title}}"/> + + <!--Display Settings > Display Mode: Static block only--> + <waitForElementVisible selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" stepKey="waitForDisplaySettingsSection"/> + <conditionalClick selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" dependentSelector="{{AdminCategoryDisplaySettingsSection.displayMode}}" visible="false" stepKey="openDisplaySettingsSection"/> + <waitForPageLoad stepKey="waitForDisplaySettingsLoad"/> + <selectOption stepKey="selectStaticBlockOnlyOption" userInput="Static block only" selector="{{AdminCategoryDisplaySettingsSection.displayMode}}"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> + <waitForPageLoad stepKey="waitForCategorySaved"/> + <see userInput="You saved the category." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml new file mode 100644 index 0000000000000..2906b84328e50 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminSetCMSBlockEnabled"> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockIsDisabled" /> + <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockEnabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml new file mode 100644 index 0000000000000..d638b340465ce --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminSetCMSBlockDisabled"> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('1')}}" stepKey="seeBlockIsEnabled" /> + <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockDisabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml new file mode 100644 index 0000000000000..62e13fe945f8c --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml @@ -0,0 +1,13 @@ +<?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="AdminNavigateToCreateNewCMSBlockPage"> + <amOnPage url="{{CmsNewBlock.url}}" stepKey="navigateToCreateNewCMSBlockPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml new file mode 100644 index 0000000000000..5e9651a9fe22b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertCMSBlockInGrid"> + <arguments> + <argument name="CMSBlockPage" defaultValue=""/> + </arguments> + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish" /> + <!-- Conditional Click again in case it goes from default state to ascending on first click --> + <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish" /> + <click selector="{{BlockPageActionsSection.select(CMSBlockPage.identifier)}}" stepKey="clickSelectCreatedCMSBlock" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml new file mode 100644 index 0000000000000..b7915d8d15a5f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertNoTextOnCategoryFrontPage"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="text" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{category.name_lwr}}" stepKey="assertCategoryOnStorefront"/> + <seeInTitle userInput="{{category.name}}" stepKey="seeCategoryNameInTitle"/> + <dontSee selector="{{StorefrontCMSPageSection.mainContent}}" userInput="{{text}}" stepKey="seeAssertTextInMainContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml new file mode 100644 index 0000000000000..6176843df568f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertTextOnCategoryFrontPage"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="text" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{category.name_lwr}}" stepKey="assertCategoryOnStorefront"/> + <seeInTitle userInput="{{category.name}}" stepKey="seeCategoryNameInTitle"/> + <see selector="{{StorefrontCMSPageSection.mainContent}}" userInput="{{text}}" stepKey="seeAssertTextInMainContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml index 79fc3bac0fb25..0b20edf558f9e 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml @@ -19,5 +19,6 @@ <element name="identifier" type="input" selector="input[name=identifier]"/> <element name="storeView" type="multiselect" selector="select[name=store_id]"/> <element name="duplicatedIdentifier" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> + <element name="isActiveLabel" type="button" selector="div[data-index=is_active] .admin__actions-switch-label"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml new file mode 100644 index 0000000000000..7f49385989342 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -0,0 +1,47 @@ +<?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="AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS block via the Admin, disable, add to category, verify on frontend"/> + <title value="Create disabled CMS block entity and assign to category"/> + <severity value="MAJOR"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> + <argument name="Block" value="_defaultBlock"/> + </actionGroup> + <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateToCreateNewCMSBlockPage" stepKey="navigateToCreateNewCMSBlockPage"/> + <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> + <actionGroup ref="AdminSetCMSBlockDisabled" stepKey="disableBlock"/> + <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> + <actionGroup ref="AssertCMSBlockInGrid" stepKey="assertCMSBlockInGrid"> + <argument name="CMSBlockPage" value="_defaultBlock"/> + </actionGroup> + <actionGroup ref="AdminAddCmsBlockToCategory" stepKey="addCmsBlockToCategory"> + <argument name="category" value="$$newDefaultCategory$$"/> + </actionGroup> + <actionGroup ref="AssertNoTextOnCategoryFrontPage" stepKey="assertBlockOnCategoryFrontPage"> + <argument name="category" value="$$newDefaultCategory$$"/> + <argument name="text" value="{{_defaultBlock.content}}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml new file mode 100644 index 0000000000000..eb15f65614707 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -0,0 +1,46 @@ +<?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="AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS block via the Admin, add to category, verify on frontend"/> + <title value="Create CMS block entity and assign to category"/> + <severity value="MAJOR"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> + <argument name="Block" value="_defaultBlock"/> + </actionGroup> + <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateToCreateNewCMSBlockPage" stepKey="navigateToCreateNewCMSBlockPage"/> + <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> + <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> + <actionGroup ref="AssertCMSBlockInGrid" stepKey="assertCMSBlockInGrid"> + <argument name="CMSBlockPage" value="_defaultBlock"/> + </actionGroup> + <actionGroup ref="AdminAddCmsBlockToCategory" stepKey="addCmsBlockToCategory"> + <argument name="category" value="$$newDefaultCategory$$"/> + </actionGroup> + <actionGroup ref="AssertTextOnCategoryFrontPage" stepKey="assertBlockOnCategoryFrontPage"> + <argument name="category" value="$$newDefaultCategory$$"/> + <argument name="text" value="{{_defaultBlock.content}}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml index 5f537baf9c979..bd9db3f34bdd7 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\CreateCmsBlockEntityTest" summary="Create CMS Block" ticketId="MAGETWO-25578"> <variation name="CreateCmsBlockEntityTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes</data> <data name="cmsBlock/data/title" xsi:type="string">block_%isolation%</data> <data name="cmsBlock/data/identifier" xsi:type="string">identifier_%isolation%</data> <data name="cmsBlock/data/stores/dataset/option_0" xsi:type="string">All Store Views</data> @@ -19,7 +19,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsBlockOnCategoryPage" /> </variation> <variation name="CreateCmsBlockEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="cmsBlock/data/title" xsi:type="string">block_%isolation%</data> <data name="cmsBlock/data/identifier" xsi:type="string">identifier_%isolation%</data> <data name="cmsBlock/data/stores/dataset/option_0" xsi:type="string">default</data> From 2421220be90bf59f6cc45d8bac2f0993c101baaa Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Fri, 12 Apr 2019 14:08:01 +0300 Subject: [PATCH 004/734] refactored according best practices --- .../AdminCMSPageSetDisabledActionGroup.xml | 14 ++++++ ...ssertCMSPageNotFoundOnFrontActionGroup.xml | 18 ++++++++ ...efrontCMSPageNavigateToPageActionGroup.xml | 16 +++++++ .../Test/AdminCmsPageUpdateAndDisableTest.xml | 43 +++++++++++++++++++ ...ityTest.xml => AdminCmsPageUpdateTest.xml} | 35 +++++---------- .../Test/TestCase/UpdateCmsPageEntityTest.xml | 6 +-- 6 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml rename app/code/Magento/Cms/Test/Mftf/Test/{AdminUpdateCmsPageEntityTest.xml => AdminCmsPageUpdateTest.xml} (54%) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml new file mode 100644 index 0000000000000..76189d589bd04 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminCMSPageSetDisabled"> + <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> + <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageDisabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml new file mode 100644 index 0000000000000..245eab49eb73d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AssertCMSPageNotFoundOnFront"> + <arguments> + <argument name="identifier" type="string"/> + </arguments> + <amOnPage url="{{StorefrontHomePage.url}}/{{identifier}}" stepKey="amOnPageOnFrontend"/> + <waitForPageLoad stepKey="waitForPageLoadOnFrontend"/> + <see userInput="Whoops, our bad..." stepKey="seePageErrorForFirstPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml new file mode 100644 index 0000000000000..c27fe2f316ec9 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml @@ -0,0 +1,16 @@ +<?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="StorefrontCMSPageNavigateToPage"> + <arguments> + <argument name="identifier" type="string"/> + </arguments> + <amOnPage url="{{StorefrontHomePage.url}}/{{identifier}}" stepKey="amOnCmsPageOnStorefront"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml new file mode 100644 index 0000000000000..cbc8089f0e36a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + *CreateNewPage 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="AdminCmsPageUpdateAndDisableTest"> + <annotations> + <features value="Cms"/> + <title value="Update CMS Page via the Admin, disable"/> + <description value="Admin should be able to update a CMS Page"/> + <group value="backend"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> + </before> + <after> + <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Navigate to Page in Admin--> + <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$existingCMSPage$$"/> + </actionGroup> + <!--Fill data using _duplicatedCMSPage--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> + <!--Deactivate page--> + <actionGroup ref="AdminCMSPageSetDisabled" stepKey="disablePage"/> + <!--Save page--> + <actionGroup ref="saveCmsPage" stepKey="saveDeactivatedPage"/> + <!--Check that page is not found on frontend--> + <actionGroup ref="AssertCMSPageNotFoundOnFront" stepKey="checkPageIsDisabledOnStorefront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml similarity index 54% rename from app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml rename to app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml index dc42d9b0918d3..6277f8f8178f5 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminUpdateCmsPageEntityTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUpdateCmsPageEntityTest"> + <test name="AdminCmsPageUpdateTest"> <annotations> <features value="Cms"/> <title value="Update CMS Page via the Admin"/> @@ -19,43 +19,28 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> + <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> </before> <after> - <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage" /> + <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> <actionGroup ref="logout" stepKey="logout"/> </after> - <!--Update page, deactivate--> <!--Navigate to Page in Admin--> <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> - <argument name="CMSPage" value="$$createCMSPage$$"/> + <argument name="CMSPage" value="$$existingCMSPage$$"/> </actionGroup> <!--Fill data using _duplicatedCMSPage--> <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> - <!--Deactivate page--> - <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> - <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageNotActive"/> - <!--Save page--> - <actionGroup ref="saveCmsPage" stepKey="saveDeactivatedPage"/> - <!--Check that page is not found on frontend--> - <amOnPage url="{{StorefrontHomePage.url}}/{{_duplicatedCMSPage.identifier}}" stepKey="amOnDeactivatedPageOnFrontend"/> - <waitForPageLoad stepKey="waitForDeactivatedPageLoadOnFrontend"/> - <see userInput="Whoops, our bad..." stepKey="seePageError"/> - <!--Check page data is updated properly--> - <!--Navigate to Page in Admin--> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToUpdatedCMSPageInAdmin"> + <actionGroup ref="saveCmsPage" stepKey="saveActivatedPage"/> + <!--Verify data in admin--> + <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToUpdatedCMSPage"> <argument name="CMSPage" value="_duplicatedCMSPage"/> </actionGroup> - <!--Verify data in admin--> <actionGroup ref="AssertCMSPageContent" stepKey="verifyPageDataInAdmin"/> - <!--Activate page--> - <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('0')}}" stepKey="seePageIsDisabled" /> - <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageActive"/> - <actionGroup ref="saveCmsPage" stepKey="saveActivatedPage"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Verify data on frontend--> - <amOnPage url="{{StorefrontHomePage.url}}/{{_duplicatedCMSPage.identifier}}" stepKey="amOnPageTestPage"/> + <actionGroup ref="StorefrontCMSPageNavigateToPage" stepKey="navigateToPageOnStorefront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> <actionGroup ref="AssertStoreFrontCMSPage" stepKey="verifyPageDataOnFrontend"> <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml index 92f12c9468393..61ca945adcad9 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml @@ -8,21 +8,19 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\UpdateCmsPageEntityTest" summary="Update Cms Page" ticketId="MAGETWO-25186"> <variation name="UpdateCmsPageEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="cms/data/title" xsi:type="string">CmsPageEdited%isolation%</data> <data name="cms/data/is_active" xsi:type="string">No</data> <data name="cms/data/content/content" xsi:type="string">cms_page_text_content_after_edit</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageSuccessSaveMessage" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageDisabledOnFrontend" /> </variation> <variation name="UpdateCmsPageEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="cms/data/title" xsi:type="string">CmsPageEdited%isolation%</data> <data name="cms/data/identifier" xsi:type="string">cms_page_url_edited_%isolation%</data> <data name="cms/data/content_heading" xsi:type="string">Content Heading TextEdited</data> <data name="cms/data/content/content" xsi:type="string">cms_page_text_content_after_edit</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageSuccessSaveMessage" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageForm" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagePreview" /> From f45e7f6364c85b7cfe4499eeb0b6ef38761b9ad6 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 1 May 2019 18:15:48 +0300 Subject: [PATCH 005/734] refactoring --- .../AdminAddCmsBlockToCategoryActionGroup.xml | 2 +- ...gateToCreateNewCMSBlockPageActionGroup.xml | 5 ++-- .../AdminOpenCMSBlocksGridActionGroup.xml | 14 +++++++++++ ...AdminSelectCMSBlockFromGridActionGroup.xml | 16 ++++++++++++ .../AdminSetCMSBlockDisabledActionGroup.xml | 2 +- .../AdminSetCMSBlockEnabledActionGroup.xml | 5 ++-- .../AssertCMSBlockInGridActionGroup.xml | 25 ------------------- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 18 ++++++++----- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 16 ++++++++---- 9 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml index ddc21169614a0..728a42e83277c 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAddCmsBlockToCategory"> + <actionGroup name="AdminAddCmsBlockToCategoryActionGroup"> <arguments> <argument name="category" defaultValue="_defaultCategory"/> <argument name="block" defaultValue="_defaultBlock"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml index 2906b84328e50..2bb0d6dc58764 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml @@ -7,8 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSetCMSBlockEnabled"> - <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockIsDisabled" /> - <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockEnabled"/> + <actionGroup name="AdminNavigateToCreateNewCMSBlockPageActionGroup"> + <amOnPage url="{{CmsNewBlock.url}}" stepKey="navigateToCreateNewCMSBlockPage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml new file mode 100644 index 0000000000000..2aad658fa1ae9 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminOpenCMSBlocksGridActionGroup"> + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml new file mode 100644 index 0000000000000..46a624430e2f4 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminSelectCMSBlockFromGridActionGroup"> + <arguments> + <argument name="identifier" defaultValue=""/> + </arguments> + <click selector="{{BlockPageActionsSection.select(identifier)}}" stepKey="clickSelectCreatedCMSBlock" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml index d638b340465ce..5c02822bedd4a 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSetCMSBlockDisabled"> + <actionGroup name="AdminSetCMSBlockDisabledActionGroup"> <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('1')}}" stepKey="seeBlockIsEnabled" /> <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockDisabled"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml index 62e13fe945f8c..4ba17decb82ab 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml @@ -7,7 +7,8 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminNavigateToCreateNewCMSBlockPage"> - <amOnPage url="{{CmsNewBlock.url}}" stepKey="navigateToCreateNewCMSBlockPage"/> + <actionGroup name="AdminSetCMSBlockEnabledActionGroup"> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockIsDisabled" /> + <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockEnabled"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml deleted file mode 100644 index 5e9651a9fe22b..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockInGridActionGroup.xml +++ /dev/null @@ -1,25 +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="AssertCMSBlockInGrid"> - <arguments> - <argument name="CMSBlockPage" defaultValue=""/> - </arguments> - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish" /> - <!-- Conditional Click again in case it goes from default state to ascending on first click --> - <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish" /> - <click selector="{{BlockPageActionsSection.select(CMSBlockPage.identifier)}}" stepKey="clickSelectCreatedCMSBlock" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index 7f49385989342..e0a9378903f87 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -29,17 +29,23 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="AdminNavigateToCreateNewCMSBlockPage" stepKey="navigateToCreateNewCMSBlockPage"/> + <actionGroup ref="AdminNavigateToCreateNewCMSBlockPageActionGroup" stepKey="navigateToCreateNewCMSBlockPage"/> <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> - <actionGroup ref="AdminSetCMSBlockDisabled" stepKey="disableBlock"/> + <actionGroup ref="AdminSetCMSBlockDisabledActionGroup" stepKey="disableBlock"/> <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> - <actionGroup ref="AssertCMSBlockInGrid" stepKey="assertCMSBlockInGrid"> - <argument name="CMSBlockPage" value="_defaultBlock"/> + + <actionGroup ref="AdminOpenCMSBlocksGridActionGroup" stepKey="openCMSBlocksGrid"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilters"/> + <actionGroup ref="SortByIdDescendingActionGroup" stepKey="sortGridByIdDescending"/> + <actionGroup ref="AdminSelectCMSBlockFromGridActionGroup" stepKey="verifyBlockInGrid"> + <argument name="identifier" value="_defaultBlock.identifier"/> </actionGroup> - <actionGroup ref="AdminAddCmsBlockToCategory" stepKey="addCmsBlockToCategory"> + + <actionGroup ref="AdminAddCmsBlockToCategoryActionGroup" stepKey="addCmsBlockToCategory"> <argument name="category" value="$$newDefaultCategory$$"/> </actionGroup> - <actionGroup ref="AssertNoTextOnCategoryFrontPage" stepKey="assertBlockOnCategoryFrontPage"> + + <actionGroup ref="AssertNoTextOnCategoryFrontPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml index eb15f65614707..d4e15c1456712 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -29,16 +29,22 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="AdminNavigateToCreateNewCMSBlockPage" stepKey="navigateToCreateNewCMSBlockPage"/> + <actionGroup ref="AdminNavigateToCreateNewCMSBlockPageActionGroup" stepKey="navigateToCreateNewCMSBlockPage"/> <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> - <actionGroup ref="AssertCMSBlockInGrid" stepKey="assertCMSBlockInGrid"> - <argument name="CMSBlockPage" value="_defaultBlock"/> + + <actionGroup ref="AdminOpenCMSBlocksGridActionGroup" stepKey="openCMSBlocksGrid"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilters"/> + <actionGroup ref="SortByIdDescendingActionGroup" stepKey="sortGridByIdDescending"/> + <actionGroup ref="AdminSelectCMSBlockFromGridActionGroup" stepKey="verifyBlockInGrid"> + <argument name="identifier" value="_defaultBlock.identifier"/> </actionGroup> - <actionGroup ref="AdminAddCmsBlockToCategory" stepKey="addCmsBlockToCategory"> + + <actionGroup ref="AdminAddCmsBlockToCategoryActionGroup" stepKey="addCmsBlockToCategory"> <argument name="category" value="$$newDefaultCategory$$"/> </actionGroup> - <actionGroup ref="AssertTextOnCategoryFrontPage" stepKey="assertBlockOnCategoryFrontPage"> + + <actionGroup ref="AssertTextOnCategoryFronPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> From b0fbf58a860825bc875fd53bdb00872c720ac81d Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 1 May 2019 18:20:27 +0300 Subject: [PATCH 006/734] rename AssertTextOnCategoryFronPageActionGroup AssertNoTextOnCategoryFrontPageActionGroup action groups --- .../ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml | 2 +- .../ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml | 2 +- ...AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml index b7915d8d15a5f..e5994d70e1227 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertNoTextOnCategoryFrontPage"> + <actionGroup name="AssertNoTextOnCategoryFrontPageActionGroup"> <arguments> <argument name="category" defaultValue="_defaultCategory"/> <argument name="text" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml index 6176843df568f..98c25eebfa928 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertTextOnCategoryFrontPage"> + <actionGroup name="AssertTextOnCategoryFronPageActionGroup"> <arguments> <argument name="category" defaultValue="_defaultCategory"/> <argument name="text" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index e0a9378903f87..38d5bec9667b9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -44,7 +44,7 @@ <actionGroup ref="AdminAddCmsBlockToCategoryActionGroup" stepKey="addCmsBlockToCategory"> <argument name="category" value="$$newDefaultCategory$$"/> </actionGroup> - + <actionGroup ref="AssertNoTextOnCategoryFrontPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> <argument name="text" value="{{_defaultBlock.content}}"/> From 1a433b47f63101a5f6ba6266662f76d15b7919d8 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 1 May 2019 18:36:03 +0300 Subject: [PATCH 007/734] refactoring --- .../ActionGroup/AdminDisableCMSPageActionGroup.xml | 14 ++++++++++++++ ...sertCMSPageNotFoundOnStorefrontActionGroup.xml} | 11 +++-------- ...up.xml => StorefrontGoToCMSPageActionGroup.xml} | 3 ++- .../Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml | 9 +++++---- .../Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml | 2 +- 5 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{AssertCMSPageNotFoundOnFrontActionGroup.xml => AssertCMSPageNotFoundOnStorefrontActionGroup.xml} (53%) rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{StorefrontCMSPageNavigateToPageActionGroup.xml => StorefrontGoToCMSPageActionGroup.xml} (79%) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml new file mode 100644 index 0000000000000..6617c8eea8155 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminDisableCMSPageActionGroup"> + <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> + <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageDisabled"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml similarity index 53% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml index 245eab49eb73d..c0e0a1b599482 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnFrontActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml @@ -7,12 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertCMSPageNotFoundOnFront"> - <arguments> - <argument name="identifier" type="string"/> - </arguments> - <amOnPage url="{{StorefrontHomePage.url}}/{{identifier}}" stepKey="amOnPageOnFrontend"/> - <waitForPageLoad stepKey="waitForPageLoadOnFrontend"/> - <see userInput="Whoops, our bad..." stepKey="seePageErrorForFirstPage"/> + <actionGroup name="AssertCMSPageNotFoundOnStorefrontActionGroup"> + <see userInput="Whoops, our bad..." stepKey="seePageErrorNotFound"/> </actionGroup> -</actionGroups> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml similarity index 79% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml index c27fe2f316ec9..ac27ac89bba14 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontCMSPageNavigateToPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml @@ -7,10 +7,11 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCMSPageNavigateToPage"> + <actionGroup name="StorefrontGoToCMSPageActionGroup"> <arguments> <argument name="identifier" type="string"/> </arguments> <amOnPage url="{{StorefrontHomePage.url}}/{{identifier}}" stepKey="amOnCmsPageOnStorefront"/> + <waitForPageLoad stepKey="waitForPageLoadOnStorefront"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml index cbc8089f0e36a..a6dc52a9b744d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml @@ -32,12 +32,13 @@ <!--Fill data using _duplicatedCMSPage--> <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> <!--Deactivate page--> - <actionGroup ref="AdminCMSPageSetDisabled" stepKey="disablePage"/> + <actionGroup ref="AdminDisableCMSPageActionGroup" stepKey="setPageDisabled"/> <!--Save page--> - <actionGroup ref="saveCmsPage" stepKey="saveDeactivatedPage"/> - <!--Check that page is not found on frontend--> - <actionGroup ref="AssertCMSPageNotFoundOnFront" stepKey="checkPageIsDisabledOnStorefront"> + <actionGroup ref="saveCmsPage" stepKey="saveDisabledPage"/> + <!--Check that page is not found on storefront--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToCMSPageOnStorefront"> <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> </actionGroup> + <actionGroup ref="AssertCMSPageNotFoundOnStorefrontActionGroup" stepKey="seeNotFoundError"/> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml index 6277f8f8178f5..8271d4c66b18b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml @@ -38,7 +38,7 @@ </actionGroup> <actionGroup ref="AssertCMSPageContent" stepKey="verifyPageDataInAdmin"/> <!--Verify data on frontend--> - <actionGroup ref="StorefrontCMSPageNavigateToPage" stepKey="navigateToPageOnStorefront"> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStorefront"> <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> </actionGroup> <actionGroup ref="AssertStoreFrontCMSPage" stepKey="verifyPageDataOnFrontend"> From fa1f103d6f7d8a6d05abd28a965af58b740cbb62 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz <avs@integer-net.de> Date: Sat, 11 May 2019 20:59:40 +0200 Subject: [PATCH 008/734] Create new database table "expires at" --- app/code/Magento/User/etc/db_schema.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/User/etc/db_schema.xml b/app/code/Magento/User/etc/db_schema.xml index c3356a96b94a7..ddec6dd36eb84 100644 --- a/app/code/Magento/User/etc/db_schema.xml +++ b/app/code/Magento/User/etc/db_schema.xml @@ -36,7 +36,9 @@ default="0" comment="Failure Number"/> <column xsi:type="timestamp" name="first_failure" on_update="false" nullable="true" comment="First Failure"/> <column xsi:type="timestamp" name="lock_expires" on_update="false" nullable="true" - comment="Expiration Lock Dates"/> + comment="Expiration Lock Date"/> + <column xsi:type="timestamp" name="expires_at" on_update="false" nullable="true" + comment="User Expiration Date"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="user_id"/> </constraint> From 451fae8970e40ecfa45ef40de25c6f0328dd9702 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz <avs@integer-net.de> Date: Sat, 11 May 2019 20:59:40 +0200 Subject: [PATCH 009/734] 22833 Create new database table "expires at" --- app/code/Magento/User/etc/db_schema.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/User/etc/db_schema.xml b/app/code/Magento/User/etc/db_schema.xml index c3356a96b94a7..ddec6dd36eb84 100644 --- a/app/code/Magento/User/etc/db_schema.xml +++ b/app/code/Magento/User/etc/db_schema.xml @@ -36,7 +36,9 @@ default="0" comment="Failure Number"/> <column xsi:type="timestamp" name="first_failure" on_update="false" nullable="true" comment="First Failure"/> <column xsi:type="timestamp" name="lock_expires" on_update="false" nullable="true" - comment="Expiration Lock Dates"/> + comment="Expiration Lock Date"/> + <column xsi:type="timestamp" name="expires_at" on_update="false" nullable="true" + comment="User Expiration Date"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="user_id"/> </constraint> From b17c54d7c5745edb4fe43587c6c342305a900720 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 11 May 2019 12:12:39 -0700 Subject: [PATCH 010/734] add whitelist for expires_at on user table (#22833) --- app/code/Magento/User/etc/db_schema_whitelist.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/User/etc/db_schema_whitelist.json b/app/code/Magento/User/etc/db_schema_whitelist.json index 2af77c0d8455b..3da364b803c5a 100644 --- a/app/code/Magento/User/etc/db_schema_whitelist.json +++ b/app/code/Magento/User/etc/db_schema_whitelist.json @@ -19,7 +19,8 @@ "interface_locale": true, "failures_num": true, "first_failure": true, - "lock_expires": true + "lock_expires": true, + "expires_at": true }, "constraint": { "PRIMARY": true, From c0c6825487004e67e1b4ad750e54d151c80c2cae Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 11 May 2019 13:41:30 -0700 Subject: [PATCH 011/734] add getters/setters for expires_at (#22833) --- app/code/Magento/User/Api/Data/UserInterface.php | 15 +++++++++++++++ app/code/Magento/User/Model/User.php | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/code/Magento/User/Api/Data/UserInterface.php b/app/code/Magento/User/Api/Data/UserInterface.php index 07bc190db5433..3faec1d10226f 100644 --- a/app/code/Magento/User/Api/Data/UserInterface.php +++ b/app/code/Magento/User/Api/Data/UserInterface.php @@ -162,4 +162,19 @@ public function getInterfaceLocale(); * @return $this */ public function setInterfaceLocale($interfaceLocale); + + /** + * Get user expiration date. + * + * @return string + */ + public function getExpiresAt(); + + /** + * Set user expiration date. + * + * @param string $expiresAt + * @return $this + */ + public function setExpiresAt($expiresAt); } diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index d8040b0bbaaac..68bd922d989bc 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -917,6 +917,22 @@ public function setInterfaceLocale($interfaceLocale) return $this->setData('interface_locale', $interfaceLocale); } + /** + * @inheritDoc + */ + public function getExpiresAt() + { + return $this->_getData('expires_at'); + } + + /** + * @inheritDoc + */ + public function setExpiresAt($expiresAt) + { + return $this->setData('expires_at', $expiresAt); + } + /** * Security check for admin user * From 6e2043ebcfde04a7184ef35dfeb4c3d976edabbf Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 11 May 2019 14:48:09 -0700 Subject: [PATCH 012/734] adding validation rules (#22833) --- .../Magento/User/Block/User/Edit/Tab/Main.php | 28 +++++++++++++++++++ app/code/Magento/User/Model/User.php | 4 +++ .../User/Model/UserValidationRules.php | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index 27e00483733d0..b69e50b7a6de7 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -170,6 +170,34 @@ protected function _prepareForm() ); } + $baseFieldset->addField( + 'expires_at', + 'date', + [ + 'name' => 'expires_at', + 'label' => __('Expiration Date'), + 'title' => __('Expiration Date'), + 'date_format' => 'yyyy-MM-dd', + 'time_format' => 'hh:mm:ss', + 'class' => 'validate-date', + ] + ) +// ->setAfterElementHtml("<script type=\"text/javascript\"> +// //<![CDATA[ +// require([ +// 'jquery', +// 'mage/calendar' +// ], function($){ +// $('#expires_at').calendar({ +// hideIfNoPrevNext: true, +// minDate: new Date(), +// showOn: 'button', +// dateFormat: 'yyyy-MM-dd' +// }); }); +// //]]> +// </script>") + ; + $baseFieldset->addField('user_roles', 'hidden', ['name' => 'user_roles', 'id' => '_user_roles']); $currentUserVerificationFieldset = $form->addFieldset( diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 68bd922d989bc..3fdc35f8396a2 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -322,6 +322,10 @@ protected function _getValidationRulesBeforeSave() $this->validationRules->addPasswordConfirmationRule($validator, $this->getPasswordConfirmation()); } } + + if ($this->hasExpiresAt()) { + $this->validationRules->addExpiresAtRule($validator); + } return $validator; } diff --git a/app/code/Magento/User/Model/UserValidationRules.php b/app/code/Magento/User/Model/UserValidationRules.php index e40c785749e03..8539f78dce29d 100644 --- a/app/code/Magento/User/Model/UserValidationRules.php +++ b/app/code/Magento/User/Model/UserValidationRules.php @@ -125,4 +125,32 @@ public function addPasswordConfirmationRule( $validator->addRule($passwordConfirmation, 'password'); return $validator; } + + /** + * Adds validation rule for expiration date. + * @param \Magento\Framework\Validator\DataObject $validator + * @return \Magento\Framework\Validator\DataObject + */ + public function addExpiresAtRule(\Magento\Framework\Validator\DataObject $validator) + { + $expiresValidator = new \Zend_Validate_Date( + [ + 'format' => \Magento\Framework\Stdlib\DateTime::DATETIME_INTERNAL_FORMAT, + ] + ); + $expiresValidator->setMessage( + __('"Expiration date" invalid type entered.'), + \Zend_Validate_Date::INVALID + ); + $expiresValidator->setMessage( + __('"Expiration date" is not a valid date.'), + \Zend_Validate_Date::INVALID_DATE + ); + $expiresValidator->setMessage( + __('"Expiration date" does not fit the required date format.'), + \Zend_Validate_Date::FALSEFORMAT + ); + $validator->addRule($expiresValidator, 'expires_at'); + return $validator; + } } From 9136564ba2b72bdd2fa9c633a80be1edf43312a3 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 12 May 2019 11:18:48 -0700 Subject: [PATCH 013/734] finish validation; add tests (#22833) --- app/code/Magento/User/Model/User.php | 2 +- .../User/Model/UserValidationRules.php | 29 ++++++++++-- .../User/Model/Validator/ExpiresAt.php | 36 +++++++++++++++ .../Unit/Model/UserValidationRulesTest.php | 6 +++ .../Unit/Model/Validator/ExpiresAtTest.php | 46 +++++++++++++++++++ 5 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/User/Model/Validator/ExpiresAt.php create mode 100644 app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 3fdc35f8396a2..79f5ef7b3e3d3 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -323,7 +323,7 @@ protected function _getValidationRulesBeforeSave() } } - if ($this->hasExpiresAt()) { + if (!empty($this->getExpiresAt())) { $this->validationRules->addExpiresAtRule($validator); } return $validator; diff --git a/app/code/Magento/User/Model/UserValidationRules.php b/app/code/Magento/User/Model/UserValidationRules.php index 8539f78dce29d..7f48ae5dfd4f1 100644 --- a/app/code/Magento/User/Model/UserValidationRules.php +++ b/app/code/Magento/User/Model/UserValidationRules.php @@ -6,10 +6,12 @@ namespace Magento\User\Model; +use Magento\User\Model\Validator\ExpiresAt; use Magento\Framework\Validator\EmailAddress; use Magento\Framework\Validator\NotEmpty; use Magento\Framework\Validator\Regex; use Magento\Framework\Validator\StringLength; +use Magento\Framework\App\ObjectManager; /** * Class for adding validation rules to an Admin user @@ -23,6 +25,20 @@ class UserValidationRules * Minimum length of admin password */ const MIN_PASSWORD_LENGTH = 7; + /** + * @var Validator\ExpiresAt|null + */ + private $expiresValiator; + + /** + * UserValidationRules constructor. + * @param Validator\ExpiresAt|null $expiresValiator + */ + public function __construct(?ExpiresAt $expiresValiator = null) + { + $this->expiresValiator = $expiresValiator + ?: ObjectManager::getInstance()->get(ExpiresAt::class); + } /** * Adds validation rule for user first name, last name, username and email @@ -130,27 +146,30 @@ public function addPasswordConfirmationRule( * Adds validation rule for expiration date. * @param \Magento\Framework\Validator\DataObject $validator * @return \Magento\Framework\Validator\DataObject + * @throws \Zend_Validate_Exception */ public function addExpiresAtRule(\Magento\Framework\Validator\DataObject $validator) { - $expiresValidator = new \Zend_Validate_Date( + $dateValidator = new \Zend_Validate_Date( [ 'format' => \Magento\Framework\Stdlib\DateTime::DATETIME_INTERNAL_FORMAT, ] ); - $expiresValidator->setMessage( + $dateValidator->setMessage( __('"Expiration date" invalid type entered.'), \Zend_Validate_Date::INVALID ); - $expiresValidator->setMessage( + $dateValidator->setMessage( __('"Expiration date" is not a valid date.'), \Zend_Validate_Date::INVALID_DATE ); - $expiresValidator->setMessage( + $dateValidator->setMessage( __('"Expiration date" does not fit the required date format.'), \Zend_Validate_Date::FALSEFORMAT ); - $validator->addRule($expiresValidator, 'expires_at'); + $validator->addRule($dateValidator, 'expires_at'); + $validator->addRule($this->expiresValiator, 'expires_at'); + return $validator; } } diff --git a/app/code/Magento/User/Model/Validator/ExpiresAt.php b/app/code/Magento/User/Model/Validator/ExpiresAt.php new file mode 100644 index 0000000000000..531d5b2cc3607 --- /dev/null +++ b/app/code/Magento/User/Model/Validator/ExpiresAt.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Model\Validator; + +use Magento\Framework\Validator\AbstractValidator; + +/** + * Class ExpiresAt + * @package Magento\User\Model\Validator + */ +class ExpiresAt extends AbstractValidator +{ + + /** + * Ensure that the given date is later than the current date. + * @param String $value + * @return bool + * @throws \Exception + */ + public function isValid($value) + { + $currentTime = new \DateTime(); + $expiresAt = new \DateTime($value); + + if ($expiresAt < $currentTime) { + $message = __('The expiration date must be later than the current date.'); + $this->_addMessages([$message]); + } + + return !$this->hasMessages(); + } +} diff --git a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php index 5777e3b573890..9eb3ee203f0aa 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php @@ -42,4 +42,10 @@ public function testAddPasswordConfirmationRule() $this->validator->expects($this->once())->method('addRule')->willReturn($this->validator); $this->assertSame($this->validator, $this->rules->addPasswordConfirmationRule($this->validator, '')); } + + public function testAddExpiresAtRule() + { + $this->validator->expects($this->once())->method('addRule')->willReturn($this->validator); + $this->assertSame($this->validator, $this->rules->addExpiresAtRule($this->validator)); + } } diff --git a/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php b/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php new file mode 100644 index 0000000000000..0df6317a38cb3 --- /dev/null +++ b/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\Unit\Model\Validator; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Class ExpiresAtValidatorTest + * @package Magento\User\Test\Unit\Model + */ +class ExpiresAtTest extends \PHPUnit\Framework\TestCase +{ + + /** @var \Magento\User\Model\Validator\ExpiresAt */ + protected $validator; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->validator = $objectManager->getObject( + \Magento\User\Model\Validator\ExpiresAt::class + ); + } + + public function testIsValidWhenInvalid() + { + static::assertFalse($this->validator->isValid('2018-01-01 00:00:00')); + static::assertContains( + 'The expiration date must be later than the current date.', + $this->validator->getMessages() + ); + } + + public function testIsValidWhenValid() + { + $futureDate = new \DateTime(); + $futureDate->modify('+1 days'); + static::assertTrue($this->validator->isValid($futureDate->format('Y-m-d H:i:s'))); + static::assertEquals([], $this->validator->getMessages()); + } +} From 37c9d1200635c0bb6f4dd8c339cdc0bf11ee719a Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 12 May 2019 14:01:17 -0700 Subject: [PATCH 014/734] add cronjob, finish tests (#22833) --- .../Magento/User/Cron/DisableExpiredUsers.php | 45 +++++++++++++++ .../Model/ResourceModel/User/Collection.php | 19 ++++++- .../Unit/Model/UserValidationRulesTest.php | 2 +- app/code/Magento/User/etc/crontab.xml | 11 ++++ .../User/Cron/DisableExpiredUsersTest.php | 32 +++++++++++ .../ResourceModel/User/CollectionTest.php | 43 ++++++++++++++ .../testsuite/Magento/User/Model/UserTest.php | 11 ++++ .../Magento/User/_files/expired_users.php | 57 +++++++++++++++++++ 8 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/User/Cron/DisableExpiredUsers.php create mode 100644 app/code/Magento/User/etc/crontab.xml create mode 100644 dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php create mode 100644 dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/User/_files/expired_users.php diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php new file mode 100644 index 0000000000000..08b35bd45433a --- /dev/null +++ b/app/code/Magento/User/Cron/DisableExpiredUsers.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Cron; + +/** + * Disable expired users. + */ +class DisableExpiredUsers +{ + + /** + * @var \Magento\User\Model\ResourceModel\User\CollectionFactory + */ + private $collectionFactory; + + /** + * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + */ + public function __construct( + \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * Disable all expired user accounts. + */ + public function execute() + { + $users = $this->collectionFactory->create() + ->addExpiresAtFilter() + ->addFieldToFilter('is_active', 1) + ; + /** @var \Magento\User\Model\User $user */ + foreach ($users as $user) { + $user->setIsActive(0) + ->setExpiresAt(null) + ->save(); + } + } +} diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index 7683adae84365..133c253c28574 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -13,9 +13,9 @@ */ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection { + /** * Define resource model - * * @return void */ protected function _construct() @@ -41,4 +41,21 @@ protected function _initSelect() ['role_name'] ); } + + /** + * Filter users by expires_at. + * @param string|null $now + * @return $this + */ + public function addExpiresAtFilter($now = null) + { + if ($now === null) { + $now = new \DateTime(); + $now->format('Y-m-d H:i:s'); + } + + $this->addFieldToFilter('expires_at', ['lt' => $now]); + + return $this; + } } diff --git a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php index 9eb3ee203f0aa..be23fde541eb2 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php @@ -45,7 +45,7 @@ public function testAddPasswordConfirmationRule() public function testAddExpiresAtRule() { - $this->validator->expects($this->once())->method('addRule')->willReturn($this->validator); + $this->validator->expects($this->atMost(2))->method('addRule')->willReturn($this->validator); $this->assertSame($this->validator, $this->rules->addExpiresAtRule($this->validator)); } } diff --git a/app/code/Magento/User/etc/crontab.xml b/app/code/Magento/User/etc/crontab.xml new file mode 100644 index 0000000000000..a58379f00e952 --- /dev/null +++ b/app/code/Magento/User/etc/crontab.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd"> + + <group id="default"> + <job name="user_expire_users" instance="Magento\User\Cron\DisableExpiredUsers" method="execute"> + <schedule>0 * * * *</schedule> + </job> + </group> + +</config> diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php new file mode 100644 index 0000000000000..2adac918ae29c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Cron; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoAppArea adminhtml + */ +class DisableExpiredUsersTest extends \PHPUnit\Framework\TestCase +{ + /** + * @magentoDataFixture Magento/User/_files/expired_users.php + */ + public function testExecute() + { + /** @var \Magento\User\Cron\DisableExpiredUsers $job */ + $job = Bootstrap::getObjectManager()->create(\Magento\User\Cron\DisableExpiredUsers::class); + $job->execute(); + + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername('adminUser3'); + static::assertEquals(0, $user->getIsActive()); + static::assertNull($user->getExpiresAt()); + } + +} diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php new file mode 100644 index 0000000000000..0a81814c982a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Model\ResourceModel\User; + +/** + * User collection test + * @magentoAppArea adminhtml + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\User\Model\ResourceModel\User\Collection + */ + protected $collection; + + protected function setUp() + { + $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\User\Model\ResourceModel\User\Collection::class + ); + } + + public function testSelectQueryInitialized() + { + static::assertContains( + 'main_table.user_id = user_role.user_id AND user_role.parent_id != 0', + $this->collection->getSelect()->__toString() + ); + } + + /** + * @magentoDataFixture Magento/User/_files/expired_users.php + */ + public function testExpiresAtFilter() + { + $this->collection->addExpiresAtFilter(); + static::assertCount(1, $this->collection); + } +} diff --git a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php index 8b85339afd789..eb5a3a0d11f53 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php @@ -387,6 +387,17 @@ public function testBeforeSavePasswordTooShort() $this->_model->save(); } + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage The expiration date must be later than the current date. + * @magentoDbIsolation enabled + */ + public function testBeforeSaveExpireDateBeforeNow() + { + $this->_model->setExpiresAt('2010-01-01 00:00:00'); + $this->_model->save(); + } + /** * @dataProvider beforeSavePasswordInsecureDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException diff --git a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php new file mode 100644 index 0000000000000..f6c562e4e03a5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** + * Create an admin user with expired access date + */ +$userIds = []; + +/** @var $model \Magento\User\Model\User */ +$model = $objectManager->create(\Magento\User\Model\User::class); +$model->setFirstname("John") + ->setLastname("Doe") + ->setUsername('adminUser3') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUser3@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow'); +$model->save(); +$userIds[] = $model->getDataByKey('user_id'); + +/** @var $model \Magento\User\Model\User */ +$futureDate = new \DateTime(); +$futureDate->modify('+10 days'); +$model = $objectManager->create(\Magento\User\Model\User::class); +$model->setFirstname("John") + ->setLastname("Doe") + ->setUsername('adminUser4') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUser4@example.com') + ->setExpiresAt($futureDate->format('Y-m-d H:i:s')) + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow'); +$model->save(); +$userIds[] = $model->getDataByKey('user_id'); + +// need to bypass model validation to set expired date +$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); +$conn = $resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION); +$tableName = $resource->getTableName('admin_user'); +$sql = "UPDATE " . $tableName . " SET expires_at = '2010-01-01 00:00:00' WHERE user_id=" . + $userIds[0] . ";"; +$result = $conn->query($sql); + + From 8b5bec6685ffeeffeafaa0c097ca88209948b255 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 18 May 2019 14:12:58 -0400 Subject: [PATCH 015/734] update tests to check for active session (#22833) --- .../Magento/User/Cron/DisableExpiredUsers.php | 2 + .../User/Cron/DisableExpiredUsersTest.php | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php index 08b35bd45433a..8e38814c565f8 100644 --- a/app/code/Magento/User/Cron/DisableExpiredUsers.php +++ b/app/code/Magento/User/Cron/DisableExpiredUsers.php @@ -28,6 +28,8 @@ public function __construct( /** * Disable all expired user accounts. + * TODO: add plugin to authentication to disable since not everyone + * has cron running (see \Magento\Security\Model\AdminSessionsManager::processLogin?) */ public function execute() { diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php index 2adac918ae29c..90afe70cf3307 100644 --- a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php @@ -13,20 +13,66 @@ */ class DisableExpiredUsersTest extends \PHPUnit\Framework\TestCase { + /** * @magentoDataFixture Magento/User/_files/expired_users.php */ - public function testExecute() + public function testExecuteWithExpiredUser() { + $adminUserNameFromFixture = 'adminUser3'; + + $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); + $tokenService->createAdminAccessToken( + $adminUserNameFromFixture, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + /** @var \Magento\User\Cron\DisableExpiredUsers $job */ $job = Bootstrap::getObjectManager()->create(\Magento\User\Cron\DisableExpiredUsers::class); $job->execute(); /** @var \Magento\User\Model\User $user */ $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername('adminUser3'); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ + $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); + $token = $tokenModel->loadByAdminId($user->getId()); + static::assertEquals(0, $user->getIsActive()); static::assertNull($user->getExpiresAt()); + static::assertEquals(null, $token->getId()); + } + + /** + * @magentoDataFixture Magento/User/_files/expired_users.php + */ + public function testExecuteWithNonExpiredUser() + { + $adminUserNameFromFixture = 'adminUser4'; + + $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); + $tokenService->createAdminAccessToken( + $adminUserNameFromFixture, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + /** @var \Magento\User\Cron\DisableExpiredUsers $job */ + $job = Bootstrap::getObjectManager()->create(\Magento\User\Cron\DisableExpiredUsers::class); + $job->execute(); + + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ + $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); + $token = $tokenModel->loadByAdminId($user->getId()); + + static::assertEquals(1, $user->getIsActive()); + static::assertNotNull($user->getExpiresAt()); + static::assertNotNull($token->getId()); + } } From 0b78cbb272391b1d0e58cfef90eb0a4da3192daf Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 19 May 2019 16:49:49 -0400 Subject: [PATCH 016/734] Invalidate user sessions and handle expired users on login (#22833) --- .../Security/Model/AdminSessionsManager.php | 39 +++++++++++++- .../Magento/User/Cron/DisableExpiredUsers.php | 52 +++++++++++++------ .../Model/ResourceModel/User/Collection.php | 10 ++-- .../User/Cron/DisableExpiredUsersTest.php | 1 + .../ResourceModel/User/CollectionTest.php | 6 +-- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 7503fe04ba480..6dcc4226f5ac8 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -27,6 +27,11 @@ class AdminSessionsManager */ const LOGOUT_REASON_USER_LOCKED = 10; + /** + * User has been logged out due to an expired user account + */ + const LOGOUT_REASON_USER_EXPIRED = 11; + /** * @var ConfigInterface * @since 100.1.0 @@ -100,7 +105,7 @@ public function __construct( } /** - * Handle all others active sessions according Sharing Account Setting + * Handle all others active sessions according Sharing Account Setting and expired users. * * @return $this * @since 100.1.0 @@ -122,6 +127,11 @@ public function processLogin() } } + if ($this->authSession->getUser()->getExpiresAt()) + { + $this->revokeExpiredAdminUser(); + } + return $this; } @@ -144,6 +154,11 @@ public function processProlong() $this->getCurrentSession()->save(); } + if ($this->authSession->getUser()->getExpiresAt()) + { + $this->revokeExpiredAdminUser(); + } + return $this; } @@ -209,6 +224,11 @@ public function getLogoutReasonMessageByStatus($statusCode) 'Your account is temporarily disabled. Please try again later.' ); break; + case self::LOGOUT_REASON_USER_EXPIRED: + $reasonMessage = __( + 'Your account has expired.' + ); + break; default: $reasonMessage = __('Your current session has been expired.'); break; @@ -353,4 +373,21 @@ private function getIntervalBetweenConsecutiveProlongs() ) ); } + + /** + * Check if the current user is expired and, if so, revoke their admin token. + */ + private function revokeExpiredAdminUser() + { + $expiresAt = $this->dateTime->gmtTimestamp($this->authSession->getUser()->getExpiresAt()); + if ($expiresAt < $this->dateTime->gmtTimestamp()) { + $currentSessions = $this->getSessionsForCurrentUser(); + $currentSessions->setDataToAll('status', self::LOGOUT_REASON_USER_EXPIRED) + ->save(); + $this->authSession->getUser() + ->setIsActive(0) + ->setExpiresAt(null) + ->save(); + } + } } diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php index 8e38814c565f8..312054dacd744 100644 --- a/app/code/Magento/User/Cron/DisableExpiredUsers.php +++ b/app/code/Magento/User/Cron/DisableExpiredUsers.php @@ -6,6 +6,8 @@ namespace Magento\User\Cron; +use Magento\Security\Model\AdminSessionsManager; + /** * Disable expired users. */ @@ -15,33 +17,53 @@ class DisableExpiredUsers /** * @var \Magento\User\Model\ResourceModel\User\CollectionFactory */ - private $collectionFactory; + private $userCollectionFactory; + /** + * @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory + */ + private $adminSessionCollectionFactory; + /** + * @var \Magento\Security\Model\ConfigInterface + */ + private $securityConfig; /** - * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory + * @param \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory + * @param \Magento\Security\Model\ConfigInterface $securityConfig */ public function __construct( - \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory, + \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory, + \Magento\Security\Model\ConfigInterface $securityConfig ) { - $this->collectionFactory = $collectionFactory; + $this->userCollectionFactory = $userCollectionFactory; + $this->adminSessionCollectionFactory = $adminSessionCollectionFactory; + $this->securityConfig = $securityConfig; } /** - * Disable all expired user accounts. - * TODO: add plugin to authentication to disable since not everyone - * has cron running (see \Magento\Security\Model\AdminSessionsManager::processLogin?) + * Disable all expired user accounts and invalidate their sessions. */ public function execute() { - $users = $this->collectionFactory->create() - ->addExpiresAtFilter() - ->addFieldToFilter('is_active', 1) - ; - /** @var \Magento\User\Model\User $user */ - foreach ($users as $user) { - $user->setIsActive(0) - ->setExpiresAt(null) + /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ + $users = $this->userCollectionFactory->create() + ->addActiveExpiredUsersFilter(); + + if ($users->getSize() > 0) + { + /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ + $currentSessions = $this->adminSessionCollectionFactory->create() + ->addFieldToFilter('user_id', ['in' => $users->getAllIds()]) + ->addFieldToFilter('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN) + ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); + $currentSessions->setDataToAll('status', AdminSessionsManager::LOGOUT_REASON_USER_EXPIRED) ->save(); } + + $users->setDataToAll('expires_at', null) + ->setDataToAll('is_active', 0) + ->save(); } } diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index 133c253c28574..6b258aa8000bd 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -43,18 +43,18 @@ protected function _initSelect() } /** - * Filter users by expires_at. - * @param string|null $now + * Filter for expired, active users. + * @param null $now * @return $this */ - public function addExpiresAtFilter($now = null) + public function addActiveExpiredUsersFilter($now = null) { if ($now === null) { $now = new \DateTime(); $now->format('Y-m-d H:i:s'); } - - $this->addFieldToFilter('expires_at', ['lt' => $now]); + $this->addFieldToFilter('expires_at', ['lt' => $now]) + ->addFieldToFilter('is_active', 1); return $this; } diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php index 90afe70cf3307..16909086c081f 100644 --- a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php @@ -9,6 +9,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** + * TODO: test logging out sessions * @magentoAppArea adminhtml */ class DisableExpiredUsersTest extends \PHPUnit\Framework\TestCase diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php index 0a81814c982a8..262468b01fdaf 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php @@ -35,9 +35,9 @@ public function testSelectQueryInitialized() /** * @magentoDataFixture Magento/User/_files/expired_users.php */ - public function testExpiresAtFilter() + public function testExpiredActiveUsersFilter() { - $this->collection->addExpiresAtFilter(); - static::assertCount(1, $this->collection); + $this->collection->addActiveExpiredUsersFilter(); + static::assertGreaterThan(1, $this->collection->getSize()); } } From 976076fc6e37e0ee09d8ae00725c8a04f4c1823b Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 19 May 2019 17:02:27 -0400 Subject: [PATCH 017/734] Fix test for AdminSessionsManager calling getUser (#22833) --- .../Security/Test/Unit/Model/AdminSessionsManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index be0bdaecf8de3..d510ded0a2c39 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -151,7 +151,7 @@ public function testProcessLogin() ->method('getSessionId') ->willReturn($sessionId); - $this->authSessionMock->expects($this->once()) + $this->authSessionMock->expects($this->any()) ->method('getUser') ->willReturn($this->userMock); $this->userMock->expects($this->once()) From db95a0d0655939c7a26714e9540e8d6c361bd0ef Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 19 May 2019 18:04:40 -0400 Subject: [PATCH 018/734] handle calling getUser in test (#22833) --- app/code/Magento/Security/Model/AdminSessionsManager.php | 1 + .../Security/Test/Unit/Model/AdminSessionsManagerTest.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 6dcc4226f5ac8..500121802917d 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -154,6 +154,7 @@ public function processProlong() $this->getCurrentSession()->save(); } + // todo: don't necessarily have a user here if ($this->authSession->getUser()->getExpiresAt()) { $this->revokeExpiredAdminUser(); diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index d510ded0a2c39..6a3a0d9f2632d 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -253,6 +253,10 @@ public function testProcessProlong() ->method('save') ->willReturnSelf(); + $this->authSessionMock->expects($this->once()) + ->method('getUser') + ->willReturn($this->userMock); + $this->model->processProlong(); } From cf6633868766d246fac9552e45452f84d1832db5 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 19 May 2019 20:23:44 -0400 Subject: [PATCH 019/734] handle possibility of 1 or more in user collection test (#22833) --- .../Magento/User/Model/ResourceModel/User/CollectionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php index 262468b01fdaf..0c68027a36834 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php @@ -38,6 +38,6 @@ public function testSelectQueryInitialized() public function testExpiredActiveUsersFilter() { $this->collection->addActiveExpiredUsersFilter(); - static::assertGreaterThan(1, $this->collection->getSize()); + static::assertGreaterThanOrEqual(1, $this->collection->getSize()); } } From d1cfc9662504842ec7cb0a0d225525642b8d16f1 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Tue, 21 May 2019 09:56:21 +0300 Subject: [PATCH 020/734] #580 Convert DeleteSystemCustomerGroupTest to MFTF --- ...stomerGroupEditPageFromGridActionGroup.xml | 18 +++++++ ...nOpenCustomerGroupsGridPageActionGroup.xml | 15 ++++++ ...eCustomerGroupButtonMissingActionGroup.xml | 14 ++++++ .../Section/AdminEditCustomerGroupSection.xml | 14 ++++++ ...CustomersDeleteSystemCustomerGroupTest.xml | 47 +++++++++++++++++++ .../DeleteSystemCustomerGroupTest.xml | 2 + 6 files changed, 110 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml new file mode 100644 index 0000000000000..7b14bd8f84e50 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AdminOpenCustomerGroupEditPageFromGridActionGroup"> + <arguments> + <argument name="groupName" type="string"/> + </arguments> + <conditionalClick selector="{{AdminCustomerGroupMainSection.selectFirstRow}}" dependentSelector="{{AdminCustomerGroupMainSection.selectFirstRow}}" visible="true" stepKey="clickSelectButton"/> + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(groupName)}}" stepKey="clickOnEditCustomerGroup" /> + <waitForPageLoad stepKey="waitForCustomerGroupEditPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml new file mode 100644 index 0000000000000..d6fd64426e3cf --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.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="AdminOpenCustomerGroupsGridPageActionGroup"> + <amOnPage url="{{AdminCustomerGroupsIndexPage.url}}" stepKey="goToAdminCustomerGroupIndexPage"/> + <waitForPageLoad time="30" stepKey="waitForCustomerGroupIndexPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml new file mode 100644 index 0000000000000..e45490e6330e5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AssertDeleteCustomerGroupButtonMissingActionGroup"> + <dontSeeElement selector="AdminEditCustomerGroupSection.deleteButton" stepKey="dontSeeDeleteButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml new file mode 100644 index 0000000000000..be2490602200f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminEditCustomerGroupSection"> + <element name="deleteButton" type="button" selector="#delete"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml new file mode 100644 index 0000000000000..944207a81ff25 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml @@ -0,0 +1,47 @@ +<?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="AdminCustomersDeleteSystemCustomerGroupTest"> + <annotations> + <features value="Customer"/> + <title value="System Customer Groups"/> + <description value="Admin should not be able to delete system customer groups"/> + <group value="customers"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Verify Not Logged In customer group--> + <!--Go to Customer Group grid page--> + <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckNotLoggedInGroup"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByNotLoggedInGroupName"> + <argument name="customerGroupName" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openNotLoggedInCustomerGroupEditPage"> + <argument name="groupName" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForNotLoggedInGroup"/> + + <!--Verify General customer group--> + <!--Go to Customer Group grid page--> + <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckGeneralGroup"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByGeneralGroupName"> + <argument name="customerGroupName" value="{{GeneralCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openGeneralCustomerGroupEditPage"> + <argument name="groupName" value="{{GeneralCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForGeneralGroup"/> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml index c8cf44a674b28..822cbcfbcb7ee 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml @@ -10,10 +10,12 @@ <variation name="DeleteSystemCustomerGroup1" summary="Checks that NOT LOGGED IN Customer Group cannot be deleted"> <data name="customerGroup/dataset" xsi:type="string">NOT_LOGGED_IN</data> <constraint name="Magento\Customer\Test\Constraint\AssertNoDeleteForSystemCustomerGroup" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="DeleteSystemCustomerGroup2" summary="Checks that General Customer Group cannot be deleted"> <data name="customerGroup/dataset" xsi:type="string">General</data> <constraint name="Magento\Customer\Test\Constraint\AssertNoDeleteForSystemCustomerGroup" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From c552e5fe0f4e46d1d4ff0097ced8fcff46974b60 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Wed, 22 May 2019 18:48:45 -0400 Subject: [PATCH 021/734] clean up code style issues (#22833) --- app/code/Magento/Security/Model/AdminSessionsManager.php | 6 ++---- .../Security/Test/Unit/Model/AdminSessionsManagerTest.php | 6 ++++-- .../Magento/User/Model/ResourceModel/User/Collection.php | 4 +++- app/code/Magento/User/Model/UserValidationRules.php | 1 + app/code/Magento/User/Model/Validator/ExpiresAt.php | 1 + app/code/Magento/User/etc/crontab.xml | 6 ++++++ .../testsuite/Magento/User/Cron/DisableExpiredUsersTest.php | 2 -- .../testsuite/Magento/User/_files/expired_users.php | 2 -- 8 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 500121802917d..dcf6b7c2365f3 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -127,8 +127,7 @@ public function processLogin() } } - if ($this->authSession->getUser()->getExpiresAt()) - { + if ($this->authSession->getUser()->getExpiresAt()) { $this->revokeExpiredAdminUser(); } @@ -155,8 +154,7 @@ public function processProlong() } // todo: don't necessarily have a user here - if ($this->authSession->getUser()->getExpiresAt()) - { + if ($this->authSession->getUser()->getExpiresAt()) { $this->revokeExpiredAdminUser(); } diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index 6a3a0d9f2632d..d729fe78f0f57 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -91,7 +91,8 @@ public function setUp() ['create'] ); - $this->currentSessionMock = $this->createPartialMock(\Magento\Security\Model\AdminSessionInfo::class, [ + $this->currentSessionMock = $this->createPartialMock( + \Magento\Security\Model\AdminSessionInfo::class, [ 'isActive', 'getStatus', 'load', @@ -101,7 +102,8 @@ public function setUp() 'getUserId', 'getSessionId', 'getUpdatedAt' - ]); + ] + ); $this->securityConfigMock = $this->getMockBuilder(\Magento\Security\Model\ConfigInterface::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index 6b258aa8000bd..f1fd45e73c74c 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -16,6 +16,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab /** * Define resource model + * * @return void */ protected function _construct() @@ -44,7 +45,8 @@ protected function _initSelect() /** * Filter for expired, active users. - * @param null $now + * + * @param string $now * @return $this */ public function addActiveExpiredUsersFilter($now = null) diff --git a/app/code/Magento/User/Model/UserValidationRules.php b/app/code/Magento/User/Model/UserValidationRules.php index 7f48ae5dfd4f1..49bc75e5ae8b4 100644 --- a/app/code/Magento/User/Model/UserValidationRules.php +++ b/app/code/Magento/User/Model/UserValidationRules.php @@ -144,6 +144,7 @@ public function addPasswordConfirmationRule( /** * Adds validation rule for expiration date. + * * @param \Magento\Framework\Validator\DataObject $validator * @return \Magento\Framework\Validator\DataObject * @throws \Zend_Validate_Exception diff --git a/app/code/Magento/User/Model/Validator/ExpiresAt.php b/app/code/Magento/User/Model/Validator/ExpiresAt.php index 531d5b2cc3607..09ff78ae53063 100644 --- a/app/code/Magento/User/Model/Validator/ExpiresAt.php +++ b/app/code/Magento/User/Model/Validator/ExpiresAt.php @@ -17,6 +17,7 @@ class ExpiresAt extends AbstractValidator /** * Ensure that the given date is later than the current date. + * * @param String $value * @return bool * @throws \Exception diff --git a/app/code/Magento/User/etc/crontab.xml b/app/code/Magento/User/etc/crontab.xml index a58379f00e952..6919856857b40 100644 --- a/app/code/Magento/User/etc/crontab.xml +++ b/app/code/Magento/User/etc/crontab.xml @@ -1,4 +1,10 @@ <?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd"> diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php index 16909086c081f..c532dc1aa9c94 100644 --- a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php @@ -73,7 +73,5 @@ public function testExecuteWithNonExpiredUser() static::assertEquals(1, $user->getIsActive()); static::assertNotNull($user->getExpiresAt()); static::assertNotNull($token->getId()); - } - } diff --git a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php index f6c562e4e03a5..f11797156031c 100644 --- a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php +++ b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php @@ -53,5 +53,3 @@ $sql = "UPDATE " . $tableName . " SET expires_at = '2010-01-01 00:00:00' WHERE user_id=" . $userIds[0] . ";"; $result = $conn->query($sql); - - From d5855baf0b23dc80b4615b6b3e4b4db491bb0177 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 1 Jun 2019 13:25:11 -0400 Subject: [PATCH 022/734] clean up code style issues (#22833) --- app/code/Magento/User/Cron/DisableExpiredUsers.php | 3 +-- .../testsuite/Magento/User/_files/expired_users.php | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php index 312054dacd744..07a79d08733af 100644 --- a/app/code/Magento/User/Cron/DisableExpiredUsers.php +++ b/app/code/Magento/User/Cron/DisableExpiredUsers.php @@ -51,8 +51,7 @@ public function execute() $users = $this->userCollectionFactory->create() ->addActiveExpiredUsersFilter(); - if ($users->getSize() > 0) - { + if ($users->getSize() > 0) { /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ $currentSessions = $this->adminSessionCollectionFactory->create() ->addFieldToFilter('user_id', ['in' => $users->getAllIds()]) diff --git a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php index f11797156031c..c35a4ac66b9cc 100644 --- a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php +++ b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php @@ -13,9 +13,9 @@ /** @var $model \Magento\User\Model\User */ $model = $objectManager->create(\Magento\User\Model\User::class); -$model->setFirstname("John") - ->setLastname("Doe") - ->setUsername('adminUser3') +$model->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUser3') ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) ->setEmail('adminUser3@example.com') ->setRoleType('G') @@ -31,9 +31,9 @@ $futureDate = new \DateTime(); $futureDate->modify('+10 days'); $model = $objectManager->create(\Magento\User\Model\User::class); -$model->setFirstname("John") - ->setLastname("Doe") - ->setUsername('adminUser4') +$model->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUser4') ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) ->setEmail('adminUser4@example.com') ->setExpiresAt($futureDate->format('Y-m-d H:i:s')) From 439f56576a772764f3f88ee112348bc92ad27cea Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 1 Jun 2019 17:23:37 -0400 Subject: [PATCH 023/734] clean up code style issues (#22833) --- .../Security/Model/AdminSessionsManager.php | 5 +++-- .../Unit/Model/AdminSessionsManagerTest.php | 3 ++- .../Magento/User/Block/User/Edit/Tab/Main.php | 17 +---------------- .../Magento/User/Model/Validator/ExpiresAt.php | 1 + .../Test/Unit/Model/UserValidationRulesTest.php | 5 +++++ 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index dcf6b7c2365f3..2a9024c54ee95 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -324,6 +324,8 @@ protected function createNewSession() } /** + * Create admin session info collection. + * * @return \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection * @since 100.1.0 */ @@ -334,8 +336,7 @@ protected function createAdminSessionInfoCollection() /** * Calculates diff between now and last session updated_at - * and decides whether new prolong must be triggered or not - * + * and decides whether new prolong must be triggered or not. * This is done to limit amount of session prolongs and updates to database * within some period of time - X * X - is calculated in getIntervalBetweenConsecutiveProlongs() diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index d729fe78f0f57..d7495cd9ce173 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -92,7 +92,8 @@ public function setUp() ); $this->currentSessionMock = $this->createPartialMock( - \Magento\Security\Model\AdminSessionInfo::class, [ + \Magento\Security\Model\AdminSessionInfo::class, + [ 'isActive', 'getStatus', 'load', diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index b69e50b7a6de7..9cdec228eb382 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -181,22 +181,7 @@ protected function _prepareForm() 'time_format' => 'hh:mm:ss', 'class' => 'validate-date', ] - ) -// ->setAfterElementHtml("<script type=\"text/javascript\"> -// //<![CDATA[ -// require([ -// 'jquery', -// 'mage/calendar' -// ], function($){ -// $('#expires_at').calendar({ -// hideIfNoPrevNext: true, -// minDate: new Date(), -// showOn: 'button', -// dateFormat: 'yyyy-MM-dd' -// }); }); -// //]]> -// </script>") - ; + ); $baseFieldset->addField('user_roles', 'hidden', ['name' => 'user_roles', 'id' => '_user_roles']); diff --git a/app/code/Magento/User/Model/Validator/ExpiresAt.php b/app/code/Magento/User/Model/Validator/ExpiresAt.php index 09ff78ae53063..1aeba4b3e1aaf 100644 --- a/app/code/Magento/User/Model/Validator/ExpiresAt.php +++ b/app/code/Magento/User/Model/Validator/ExpiresAt.php @@ -10,6 +10,7 @@ /** * Class ExpiresAt + * * @package Magento\User\Model\Validator */ class ExpiresAt extends AbstractValidator diff --git a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php index be23fde541eb2..146476cd25e42 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php @@ -7,6 +7,11 @@ use Magento\User\Model\UserValidationRules; +/** + * Class UserValidationRulesTest + * + * @package Magento\User\Test\Unit\Model + */ class UserValidationRulesTest extends \PHPUnit\Framework\TestCase { /** From a2695192a15a49e3f5d455bd5cae49d72bfbb124 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 09:30:13 +0200 Subject: [PATCH 024/734] Refactoring: Extract Link Processing Logic to own Class "LinkProcessor" --- .../Model/Import/Product.php | 198 +----------- .../Model/Import/Product/LinkProcessor.php | 283 ++++++++++++++++++ 2 files changed, 297 insertions(+), 184 deletions(-) create mode 100644 app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 0b7fbaf86826b..2f6fc64641c98 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -11,6 +11,7 @@ use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\Link; use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor; +use Magento\CatalogImportExport\Model\Import\Product\LinkProcessor; use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; use Magento\CatalogImportExport\Model\StockItemImporterInterface; @@ -742,6 +743,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ private $productRepository; + /** + * @var LinkProcessor + */ + private $linkProcessor; + /** * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param \Magento\ImportExport\Helper\Data $importExportData @@ -787,6 +793,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param StockItemImporterInterface|null $stockItemImporter * @param DateTimeFactory $dateTimeFactory * @param ProductRepositoryInterface|null $productRepository + * @oaram LinkProcessor|null $linkProcessor * @throws LocalizedException * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -836,7 +843,8 @@ public function __construct( MediaGalleryProcessor $mediaProcessor = null, StockItemImporterInterface $stockItemImporter = null, DateTimeFactory $dateTimeFactory = null, - ProductRepositoryInterface $productRepository = null + ProductRepositoryInterface $productRepository = null, + LinkProcessor $linkProcessor = null ) { $this->_eventManager = $eventManager; $this->stockRegistry = $stockRegistry; @@ -872,6 +880,8 @@ public function __construct( $this->mediaProcessor = $mediaProcessor ?: ObjectManager::getInstance()->get(MediaGalleryProcessor::class); $this->stockItemImporter = $stockItemImporter ?: ObjectManager::getInstance() ->get(StockItemImporterInterface::class); + $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() + ->get(LinkProcessor::class); parent::__construct( $jsonHelper, $importExportData, @@ -1248,30 +1258,13 @@ protected function _prepareRowForDb(array $rowData) * * Must be called after ALL products saving done. * + * @deprecated use linkProcessor Directly + * * @return $this */ protected function _saveLinks() { - /** @var Link $resource */ - $resource = $this->_linkFactory->create(); - $mainTable = $resource->getMainTable(); - $positionAttrId = []; - $nextLinkId = $this->_resourceHelper->getNextAutoincrement($mainTable); - - // pre-load 'position' attributes ID for each link type once - foreach ($this->_linkNameToId as $linkId) { - $select = $this->_connection->select()->from( - $resource->getTable('catalog_product_link_attribute'), - ['id' => 'product_link_attribute_id'] - )->where( - 'link_type_id = :link_id AND product_link_attribute_code = :position' - ); - $bind = [':link_id' => $linkId, ':position' => 'position']; - $positionAttrId[$linkId] = $this->_connection->fetchOne($select, $bind); - } - while ($bunch = $this->_dataSourceModel->getNextBunch()) { - $this->processLinkBunches($bunch, $resource, $nextLinkId, $positionAttrId); - } + $this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField()); return $this; } @@ -3034,167 +3027,4 @@ private function getValidationErrorLevel($sku): string ? ProcessingError::ERROR_LEVEL_CRITICAL : ProcessingError::ERROR_LEVEL_NOT_CRITICAL; } - - /** - * Processes link bunches - * - * @param array $bunch - * @param Link $resource - * @param int $nextLinkId - * @param array $positionAttrId - * @return void - */ - private function processLinkBunches( - array $bunch, - Link $resource, - int $nextLinkId, - array $positionAttrId - ): void { - $productIds = []; - $linkRows = []; - $positionRows = []; - - $bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); - foreach ($bunch as $rowData) { - $sku = $rowData[self::COL_SKU]; - $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; - $productIds[] = $productId; - $productLinkKeys = $this->fetchProductLinks($resource, $productId); - $linkNameToId = array_filter( - $this->_linkNameToId, - function ($linkName) use ($rowData) { - return isset($rowData[$linkName . 'sku']); - }, - ARRAY_FILTER_USE_KEY - ); - foreach ($linkNameToId as $linkName => $linkId) { - $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); - $linkPositions = !empty($rowData[$linkName . 'position']) - ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) - : []; - - $linkSkus = array_filter( - $linkSkus, - function ($linkedSku) use ($sku) { - $linkedSku = trim($linkedSku); - return ($this->skuProcessor->getNewSku($linkedSku) !== null || $this->isSkuExist($linkedSku)) - && strcasecmp($linkedSku, $sku) !== 0; - } - ); - foreach ($linkSkus as $linkedKey => $linkedSku) { - $linkedId = $this->getProductLinkedId($linkedSku); - if ($linkedId == null) { - // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" - // link causing fatal errors. - $formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d'; - $exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId)); - $this->_logger->critical($exception); - continue; - } - $linkKey = $this->composeLinkKey($productId, $linkedId, $linkId); - $productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId; - - $linkRows[$linkKey] = $linkRows[$linkKey] ?? [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_id' => $productId, - 'linked_product_id' => $linkedId, - 'link_type_id' => $linkId, - ]; - - if (!empty($linkPositions[$linkedKey])) { - $positionRows[] = [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_link_attribute_id' => $positionAttrId[$linkId], - 'value' => $linkPositions[$linkedKey], - ]; - } - $nextLinkId++; - } - } - } - $this->saveLinksData($resource, $productIds, $linkRows, $positionRows); - } - - /** - * Fetches Product Links - * - * @param Link $resource - * @param int $productId - * @return array - */ - private function fetchProductLinks(Link $resource, int $productId) : array - { - $productLinkKeys = []; - $select = $this->_connection->select()->from( - $resource->getTable('catalog_product_link'), - ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] - )->where( - 'product_id = :product_id' - ); - $bind = [':product_id' => $productId]; - foreach ($this->_connection->fetchAll($select, $bind) as $linkData) { - $linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']); - $productLinkKeys[$linkKey] = $linkData['id']; - } - - return $productLinkKeys; - } - - /** - * Gets the Id of the Sku - * - * @param string $linkedSku - * @return int|null - */ - private function getProductLinkedId(string $linkedSku) : ?int - { - $linkedSku = trim($linkedSku); - $newSku = $this->skuProcessor->getNewSku($linkedSku); - $linkedId = !empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id']; - return $linkedId; - } - - /** - * Saves information about product links - * - * @param Link $resource - * @param array $productIds - * @param array $linkRows - * @param array $positionRows - * @throws LocalizedException - */ - private function saveLinksData(Link $resource, array $productIds, array $linkRows, array $positionRows) - { - $mainTable = $resource->getMainTable(); - if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { - $this->_connection->delete( - $mainTable, - $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)) - ); - } - if ($linkRows) { - $this->_connection->insertOnDuplicate($mainTable, $linkRows, ['link_id']); - } - if ($positionRows) { - // process linked product positions - $this->_connection->insertOnDuplicate( - $resource->getAttributeTypeTable('int'), - $positionRows, - ['value'] - ); - } - } - - /** - * Composes the link key - * - * @param int $productId - * @param int $linkedId - * @param int $linkTypeId - * @return string - */ - private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId) : string - { - return "{$productId}-{$linkedId}-{$linkTypeId}"; - } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php new file mode 100644 index 0000000000000..303ab77f83cad --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -0,0 +1,283 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogImportExport\Model\Import\Product; + +use Magento\Catalog\Model\ResourceModel\Product\Link; +use Magento\Catalog\Model\ResourceModel\Product\LinkFactory; +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Framework\Exception\LocalizedException; +use Magento\ImportExport\Model\Import; +use Magento\ImportExport\Model\ResourceModel\Helper; +use Magento\ImportExport\Model\ResourceModel\Import\Data; +use Psr\Log\LoggerInterface; + +/** + * Class LinkProcessor + * + */ +class LinkProcessor +{ + /** + * Links attribute name-to-link type ID. + * TODO: inject via DI + * @var array + */ + protected $_linkNameToId = [ + '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, + '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, + '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, + ]; + + /** @var LinkFactory */ + private $linkFactory; + + /** @var Helper */ + private $resourceHelper; + + /** @var SkuProcessor */ + protected $skuProcessor; + + /** @var LoggerInterface */ + protected $logger; + + public function __construct( + LinkFactory $linkFactory, + Helper $resourceHelper, + SkuProcessor $skuProcessor, + LoggerInterface $logger + ) { + $this->linkFactory = $linkFactory; + $this->resourceHelper = $resourceHelper; + $this->skuProcessor = $skuProcessor; + $this->logger = $logger; + } + + /** + * Gather and save information about product links. + * + * Must be called after ALL products saving done. + * + * @return $this + * @throws LocalizedException + */ + public function saveLinks( + Product $importEntity, + Data $dataSourceModel, + string $linkField + ) { + /** @var Link $resource */ + $resource = $this->linkFactory->create(); + $mainTable = $resource->getMainTable(); + $positionAttrId = []; + $nextLinkId = $this->resourceHelper->getNextAutoincrement($mainTable); + + // pre-load 'position' attributes ID for each link type once + foreach ($this->_linkNameToId as $linkId) { + $select = $importEntity->getConnection()->select()->from( + $resource->getTable('catalog_product_link_attribute'), + ['id' => 'product_link_attribute_id'] + )->where( + 'link_type_id = :link_id AND product_link_attribute_code = :position' + ); + $bind = [':link_id' => $linkId, ':position' => 'position']; + $positionAttrId[$linkId] = $importEntity->getConnection()->fetchOne($select, $bind); + } + while ($bunch = $dataSourceModel->getNextBunch()) { + $this->processLinkBunches($importEntity, $dataSourceModel, $linkField, $bunch, $resource, $nextLinkId, $positionAttrId); + } + + return $this; + } + + /** + * Processes link bunches + * + * @param array $bunch + * @param Link $resource + * @param int $nextLinkId + * @param array $positionAttrId + * + * @return void + * @throws LocalizedException + */ + private function processLinkBunches( + Product $importEntity, + Data $dataSourceModel, + string $linkField, + array $bunch, + Link $resource, + int $nextLinkId, + array $positionAttrId + ): void { + $productIds = []; + $linkRows = []; + $positionRows = []; + + $bunch = array_filter($bunch, [$importEntity, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); + foreach ($bunch as $rowData) { + $sku = $rowData[Product::COL_SKU]; + $productId = $this->skuProcessor->getNewSku($sku)[$linkField]; + $productIds[] = $productId; + $productLinkKeys = $this->fetchProductLinks($importEntity, $resource, $productId); + $linkNameToId = array_filter( + $this->_linkNameToId, + function ($linkName) use ($rowData) { + return isset($rowData[$linkName . 'sku']); + }, + ARRAY_FILTER_USE_KEY + ); + foreach ($linkNameToId as $linkName => $linkId) { + $linkSkus = explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + $linkPositions = ! empty($rowData[$linkName . 'position']) + ? explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'position']) + : []; + + $linkSkus = array_filter( + $linkSkus, + function ($linkedSku) use ($sku, $importEntity) { + $linkedSku = trim($linkedSku); + + return ( + $this->skuProcessor->getNewSku($linkedSku) !== null + || $this->isSkuExist($importEntity, $linkedSku) + ) + && strcasecmp($linkedSku, $sku) !== 0; + } + ); + foreach ($linkSkus as $linkedKey => $linkedSku) { + $linkedId = $this->getProductLinkedId($linkedSku); + if ($linkedId == null) { + // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" + // link causing fatal errors. + $formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d'; + $exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId)); + $this->logger->critical($exception); + continue; + } + $linkKey = $this->composeLinkKey($productId, $linkedId, $linkId); + $productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId; + + $linkRows[$linkKey] = $linkRows[$linkKey] ?? [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_id' => $productId, + 'linked_product_id' => $linkedId, + 'link_type_id' => $linkId, + ]; + + if (! empty($linkPositions[$linkedKey])) { + $positionRows[] = [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_link_attribute_id' => $positionAttrId[$linkId], + 'value' => $linkPositions[$linkedKey], + ]; + } + $nextLinkId++; + } + } + } + $this->saveLinksData($importEntity, $resource, $productIds, $linkRows, $positionRows); + } + + /** + * Check if product exists for specified SKU + * + * @param string $sku + * @return bool + */ + private function isSkuExist(Product $importEntity, $sku) + { + $sku = strtolower($sku); + return isset($importEntity->getOldSku()[$sku]); + } + + /** + * Fetches Product Links + * + * @param Link $resource + * @param int $productId + * + * @return array + */ + private function fetchProductLinks(Product $importEntity, Link $resource, int $productId): array + { + $productLinkKeys = []; + $select = $importEntity->getConnection()->select()->from( + $resource->getTable('catalog_product_link'), + ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] + )->where( + 'product_id = :product_id' + ); + $bind = [':product_id' => $productId]; + foreach ($importEntity->getConnection()->fetchAll($select, $bind) as $linkData) { + $linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']); + $productLinkKeys[$linkKey] = $linkData['id']; + } + + return $productLinkKeys; + } + + /** + * Gets the Id of the Sku + * + * @param string $linkedSku + * + * @return int|null + */ + private function getProductLinkedId(string $linkedSku): ?int + { + $linkedSku = trim($linkedSku); + $newSku = $this->skuProcessor->getNewSku($linkedSku); + $linkedId = ! empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id']; + + return $linkedId; + } + + /** + * Saves information about product links + * + * @param Link $resource + * @param array $productIds + * @param array $linkRows + * @param array $positionRows + * + * @throws LocalizedException + */ + private function saveLinksData(Product $importEntity, Link $resource, array $productIds, array $linkRows, array $positionRows) + { + $mainTable = $resource->getMainTable(); + if (Import::BEHAVIOR_APPEND != $importEntity->getBehavior() && $productIds) { + $importEntity->getConnection()->delete( + $mainTable, + $importEntity->getConnection()->quoteInto('product_id IN (?)', array_unique($productIds)) + ); + } + if ($linkRows) { + $importEntity->getConnection()->insertOnDuplicate($mainTable, $linkRows, ['link_id']); + } + if ($positionRows) { + // process linked product positions + $importEntity->getConnection()->insertOnDuplicate( + $resource->getAttributeTypeTable('int'), + $positionRows, + ['value'] + ); + } + } + + /** + * Composes the link key + * + * @param int $productId + * @param int $linkedId + * @param int $linkTypeId + * + * @return string + */ + private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId): string + { + return "{$productId}-{$linkedId}-{$linkTypeId}"; + } +} From 095c4346a8c6d47062817b65c64e91c009b840c4 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 09:36:06 +0200 Subject: [PATCH 025/734] Import: Allow injecting own link types to LinkProcessor --- .../Model/Import/Product.php | 4 ++++ .../Model/Import/Product/LinkProcessor.php | 18 +++++++++++++++--- .../Magento/CatalogImportExport/etc/di.xml | 9 +++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 2f6fc64641c98..ab9151b8e1e0a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -222,6 +222,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity /** * Links attribute name-to-link type ID. * + * @deprecated use DI for LinkProcessor class if you want to add additional types + * * @var array */ protected $_linkNameToId = [ @@ -882,6 +884,8 @@ public function __construct( ->get(StockItemImporterInterface::class); $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() ->get(LinkProcessor::class); + $this->linkProcessor->addNameToIds($this->_linkNameToId); + parent::__construct( $jsonHelper, $importExportData, diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index 303ab77f83cad..8f50132e5ec88 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -30,6 +30,10 @@ class LinkProcessor '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, ]; + /** + * @var array + */ + private $linkNameToId; /** @var LinkFactory */ private $linkFactory; @@ -38,21 +42,24 @@ class LinkProcessor private $resourceHelper; /** @var SkuProcessor */ - protected $skuProcessor; + private $skuProcessor; /** @var LoggerInterface */ - protected $logger; + private $logger; public function __construct( LinkFactory $linkFactory, Helper $resourceHelper, SkuProcessor $skuProcessor, - LoggerInterface $logger + LoggerInterface $logger, + array $linkNameToId ) { $this->linkFactory = $linkFactory; $this->resourceHelper = $resourceHelper; $this->skuProcessor = $skuProcessor; $this->logger = $logger; + + $this->linkNameToId = $linkNameToId; } /** @@ -92,6 +99,11 @@ public function saveLinks( return $this; } + public function addNameToIds($nameToIds) + { + $this->linkNameToId = array_merge($nameToIds, $this->linkNameToId); + } + /** * Processes link bunches * diff --git a/app/code/Magento/CatalogImportExport/etc/di.xml b/app/code/Magento/CatalogImportExport/etc/di.xml index 6906272b11d68..64b6e7bcb2afa 100644 --- a/app/code/Magento/CatalogImportExport/etc/di.xml +++ b/app/code/Magento/CatalogImportExport/etc/di.xml @@ -28,4 +28,13 @@ </argument> </arguments> </type> + <type name="Magento\CatalogImportExport\Model\Import\Product\LinkProcessor"> + <arguments> + <argument name="linkNameToId" xsi:type="array"> + <item name="_related_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED</item> + <item name="_crosssell_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL</item> + <item name="_upsell_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL</item> + </argument> + </arguments> + </type> </config> From 84ccd7fc3f9fbe513007c1b3763a6ab4fa2d2886 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 11:47:15 +0200 Subject: [PATCH 026/734] Remove default values from property, as they are injected via DI --- .../Model/Import/Product/LinkProcessor.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index 8f50132e5ec88..a6ffc9a67084f 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -21,15 +21,10 @@ class LinkProcessor { /** - * Links attribute name-to-link type ID. - * TODO: inject via DI * @var array */ - protected $_linkNameToId = [ - '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, - '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, - '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, - ]; + private $_linkNameToId; + /** * @var array */ From 3b2a4fe0c11f50a624b26c116e7e2857967b092b Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 15:59:22 +0200 Subject: [PATCH 027/734] Fix initialisation of linkNameToId, unused variable, run phpcbf --- .../Model/Import/Product/LinkProcessor.php | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index a6ffc9a67084f..9bd454fb84b4f 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -16,30 +16,37 @@ /** * Class LinkProcessor - * */ class LinkProcessor { /** * @var array */ - private $_linkNameToId; + private $_linkNameToId = []; /** * @var array */ private $linkNameToId; - /** @var LinkFactory */ + /** + * @var LinkFactory + */ private $linkFactory; - /** @var Helper */ + /** + * @var Helper + */ private $resourceHelper; - /** @var SkuProcessor */ + /** + * @var SkuProcessor + */ private $skuProcessor; - /** @var LoggerInterface */ + /** + * @var LoggerInterface + */ private $logger; public function __construct( @@ -70,7 +77,9 @@ public function saveLinks( Data $dataSourceModel, string $linkField ) { - /** @var Link $resource */ + /** + * @var Link $resource + */ $resource = $this->linkFactory->create(); $mainTable = $resource->getMainTable(); $positionAttrId = []; @@ -88,7 +97,7 @@ public function saveLinks( $positionAttrId[$linkId] = $importEntity->getConnection()->fetchOne($select, $bind); } while ($bunch = $dataSourceModel->getNextBunch()) { - $this->processLinkBunches($importEntity, $dataSourceModel, $linkField, $bunch, $resource, $nextLinkId, $positionAttrId); + $this->processLinkBunches($importEntity, $linkField, $bunch, $resource, $nextLinkId, $positionAttrId); } return $this; @@ -103,8 +112,8 @@ public function addNameToIds($nameToIds) * Processes link bunches * * @param array $bunch - * @param Link $resource - * @param int $nextLinkId + * @param Link $resource + * @param int $nextLinkId * @param array $positionAttrId * * @return void @@ -112,7 +121,6 @@ public function addNameToIds($nameToIds) */ private function processLinkBunches( Product $importEntity, - Data $dataSourceModel, string $linkField, array $bunch, Link $resource, @@ -191,7 +199,7 @@ function ($linkedSku) use ($sku, $importEntity) { /** * Check if product exists for specified SKU * - * @param string $sku + * @param string $sku * @return bool */ private function isSkuExist(Product $importEntity, $sku) @@ -204,7 +212,7 @@ private function isSkuExist(Product $importEntity, $sku) * Fetches Product Links * * @param Link $resource - * @param int $productId + * @param int $productId * * @return array */ @@ -245,7 +253,7 @@ private function getProductLinkedId(string $linkedSku): ?int /** * Saves information about product links * - * @param Link $resource + * @param Link $resource * @param array $productIds * @param array $linkRows * @param array $positionRows From f06e4d7aed9639aa2eb8a0f27c8c1e0dc727a980 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 19:27:53 +0200 Subject: [PATCH 028/734] Fix testProductWithLinks failing --- .../Model/Import/Product/LinkProcessor.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index 9bd454fb84b4f..fecd0cd06bdc4 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -19,11 +19,6 @@ */ class LinkProcessor { - /** - * @var array - */ - private $_linkNameToId = []; - /** * @var array */ @@ -86,7 +81,7 @@ public function saveLinks( $nextLinkId = $this->resourceHelper->getNextAutoincrement($mainTable); // pre-load 'position' attributes ID for each link type once - foreach ($this->_linkNameToId as $linkId) { + foreach ($this->linkNameToId as $linkId) { $select = $importEntity->getConnection()->select()->from( $resource->getTable('catalog_product_link_attribute'), ['id' => 'product_link_attribute_id'] @@ -138,7 +133,7 @@ private function processLinkBunches( $productIds[] = $productId; $productLinkKeys = $this->fetchProductLinks($importEntity, $resource, $productId); $linkNameToId = array_filter( - $this->_linkNameToId, + $this->linkNameToId, function ($linkName) use ($rowData) { return isset($rowData[$linkName . 'sku']); }, From 7dbdfd5b9e10adbd2842211d693ead06c7c50cfa Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 19:54:29 +0200 Subject: [PATCH 029/734] Fix CodeStyle problems --- .../Model/Import/Product/LinkProcessor.php | 61 +++++++++++++------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index fecd0cd06bdc4..ff5e4527a0ebf 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -25,25 +25,34 @@ class LinkProcessor private $linkNameToId; /** - * @var LinkFactory + * @var LinkFactory */ private $linkFactory; /** - * @var Helper + * @var Helper */ private $resourceHelper; /** - * @var SkuProcessor + * @var SkuProcessor */ private $skuProcessor; /** - * @var LoggerInterface + * @var LoggerInterface */ private $logger; + /** + * LinkProcessor constructor. + * + * @param LinkFactory $linkFactory + * @param Helper $resourceHelper + * @param SkuProcessor $skuProcessor + * @param LoggerInterface $logger + * @param array $linkNameToId + */ public function __construct( LinkFactory $linkFactory, Helper $resourceHelper, @@ -64,6 +73,9 @@ public function __construct( * * Must be called after ALL products saving done. * + * @param Product $importEntity + * @param Data $dataSourceModel + * @param string $linkField * @return $this * @throws LocalizedException */ @@ -71,10 +83,7 @@ public function saveLinks( Product $importEntity, Data $dataSourceModel, string $linkField - ) { - /** - * @var Link $resource - */ + ): void { $resource = $this->linkFactory->create(); $mainTable = $resource->getMainTable(); $positionAttrId = []; @@ -94,11 +103,15 @@ public function saveLinks( while ($bunch = $dataSourceModel->getNextBunch()) { $this->processLinkBunches($importEntity, $linkField, $bunch, $resource, $nextLinkId, $positionAttrId); } - - return $this; } - public function addNameToIds($nameToIds) + /** + * Add link types (exists for backwards compatibility) + * + * @deprecated Use DI to inject to the constructor + * @param array $nameToIds + */ + public function addNameToIds(array $nameToIds): void { $this->linkNameToId = array_merge($nameToIds, $this->linkNameToId); } @@ -106,9 +119,11 @@ public function addNameToIds($nameToIds) /** * Processes link bunches * + * @param Product $importEntity + * @param string $linkField * @param array $bunch - * @param Link $resource - * @param int $nextLinkId + * @param Link $resource + * @param int $nextLinkId * @param array $positionAttrId * * @return void @@ -194,10 +209,11 @@ function ($linkedSku) use ($sku, $importEntity) { /** * Check if product exists for specified SKU * - * @param string $sku + * @param Product $importEntity + * @param string $sku * @return bool */ - private function isSkuExist(Product $importEntity, $sku) + private function isSkuExist(Product $importEntity, string $sku): bool { $sku = strtolower($sku); return isset($importEntity->getOldSku()[$sku]); @@ -206,8 +222,9 @@ private function isSkuExist(Product $importEntity, $sku) /** * Fetches Product Links * + * @param Product $importEntity * @param Link $resource - * @param int $productId + * @param int $productId * * @return array */ @@ -248,15 +265,21 @@ private function getProductLinkedId(string $linkedSku): ?int /** * Saves information about product links * - * @param Link $resource + * @param Product $importEntity + * @param Link $resource * @param array $productIds * @param array $linkRows * @param array $positionRows * * @throws LocalizedException */ - private function saveLinksData(Product $importEntity, Link $resource, array $productIds, array $linkRows, array $positionRows) - { + private function saveLinksData( + Product $importEntity, + Link $resource, + array $productIds, + array $linkRows, + array $positionRows + ) { $mainTable = $resource->getMainTable(); if (Import::BEHAVIOR_APPEND != $importEntity->getBehavior() && $productIds) { $importEntity->getConnection()->delete( From 39542fd5dabf869ecd58b914cb886c368ea6bba0 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 21:10:09 +0200 Subject: [PATCH 030/734] Fix one code sniffer problem; Add phpcs:disable... ...for code style problem existing before and add according FIXME --- .../Magento/CatalogImportExport/Model/Import/Product.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index ab9151b8e1e0a..c229a4656f3c3 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -795,7 +795,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param StockItemImporterInterface|null $stockItemImporter * @param DateTimeFactory $dateTimeFactory * @param ProductRepositoryInterface|null $productRepository - * @oaram LinkProcessor|null $linkProcessor + * @param LinkProcessor|null $linkProcessor * @throws LocalizedException * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -1496,14 +1496,19 @@ public function getImagesFromRow(array $rowData) return [$images, $labels]; } + // phpcs:disable Generic.Metrics.NestingLevel + /** * Gather and save information about product entities. * + * FIXME: Reduce nesting level + * * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @throws LocalizedException */ protected function _saveProducts() @@ -1880,6 +1885,8 @@ protected function _saveProducts() return $this; } + // phpcs:enable + /** * Prepare array with image states (visible or hidden from product page) * From 4fbbcf14b60efb49d2a9f1078109ac2ab6920000 Mon Sep 17 00:00:00 2001 From: Alexander Menk <a.menk@imi.de> Date: Mon, 10 Jun 2019 21:11:05 +0200 Subject: [PATCH 031/734] Do not call deprecated code --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index c229a4656f3c3..045252fd3d3ba 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1123,6 +1123,7 @@ protected function _replaceProducts() * Save products data. * * @return $this + * @throws LocalizedException */ protected function _saveProductsData() { @@ -1130,7 +1131,7 @@ protected function _saveProductsData() foreach ($this->_productTypeModels as $productTypeModel) { $productTypeModel->saveData(); } - $this->_saveLinks(); + $this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField()); $this->_saveStockItem(); if ($this->_replaceFlag) { $this->getOptionEntity()->clearProductsSkuToId(); From 1bfa162aebab5fba1b7b83b783fdb7e6cb65df96 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 12 Jun 2019 14:25:43 +0300 Subject: [PATCH 032/734] refactoring and minor fixes --- .../AdminOpenCustomerGroupEditPageFromGridActionGroup.xml | 4 ++-- .../Test/Mftf/Section/AdminEditCustomerGroupSection.xml | 2 +- .../Test/AdminCustomersDeleteSystemCustomerGroupTest.xml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml index 7b14bd8f84e50..f27f418496204 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml @@ -9,10 +9,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenCustomerGroupEditPageFromGridActionGroup"> <arguments> - <argument name="groupName" type="string"/> + <argument name="groupCode" type="string"/> </arguments> <conditionalClick selector="{{AdminCustomerGroupMainSection.selectFirstRow}}" dependentSelector="{{AdminCustomerGroupMainSection.selectFirstRow}}" visible="true" stepKey="clickSelectButton"/> - <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(groupName)}}" stepKey="clickOnEditCustomerGroup" /> + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(groupCode)}}" stepKey="clickOnEditCustomerGroup" /> <waitForPageLoad stepKey="waitForCustomerGroupEditPage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml index be2490602200f..917af88338d24 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml @@ -9,6 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditCustomerGroupSection"> - <element name="deleteButton" type="button" selector="#delete"/> + <element name="deleteButton" type="button" selector=".page-actions-buttons button#delete"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml index 944207a81ff25..604124f2fcbd9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml @@ -25,22 +25,22 @@ <!--Verify Not Logged In customer group--> <!--Go to Customer Group grid page--> <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckNotLoggedInGroup"/> - <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByNotLoggedInGroupName"> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByNotLoggedInGroup"> <argument name="customerGroupName" value="{{NotLoggedInCustomerGroup.code}}"/> </actionGroup> <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openNotLoggedInCustomerGroupEditPage"> - <argument name="groupName" value="{{NotLoggedInCustomerGroup.code}}"/> + <argument name="groupCode" value="{{NotLoggedInCustomerGroup.code}}"/> </actionGroup> <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForNotLoggedInGroup"/> <!--Verify General customer group--> <!--Go to Customer Group grid page--> <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckGeneralGroup"/> - <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByGeneralGroupName"> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByGeneralGroup"> <argument name="customerGroupName" value="{{GeneralCustomerGroup.code}}"/> </actionGroup> <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openGeneralCustomerGroupEditPage"> - <argument name="groupName" value="{{GeneralCustomerGroup.code}}"/> + <argument name="groupCode" value="{{GeneralCustomerGroup.code}}"/> </actionGroup> <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForGeneralGroup"/> </test> From ae08dada13b265a34f7919d3f374ce55e21385eb Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 21 Jun 2019 12:14:48 -0400 Subject: [PATCH 033/734] move changes out of user and into security (magento/magento2#22833: Short-term admin accounts) --- .../Magento/User/Api/Data/UserInterface.php | 15 ---- .../Magento/User/Block/User/Edit/Tab/Main.php | 2 +- .../Magento/User/Cron/DisableExpiredUsers.php | 68 ------------------- .../Model/ResourceModel/User/Collection.php | 20 +----- app/code/Magento/User/Model/User.php | 44 ++---------- .../User/Model/UserValidationRules.php | 48 ------------- .../Unit/Model/UserValidationRulesTest.php | 11 --- app/code/Magento/User/etc/crontab.xml | 17 ----- app/code/Magento/User/etc/db_schema.xml | 4 +- .../Magento/User/etc/db_schema_whitelist.json | 5 +- .../ResourceModel/User/CollectionTest.php | 43 ------------ .../testsuite/Magento/User/Model/UserTest.php | 11 --- 12 files changed, 10 insertions(+), 278 deletions(-) delete mode 100644 app/code/Magento/User/Cron/DisableExpiredUsers.php delete mode 100644 app/code/Magento/User/etc/crontab.xml delete mode 100644 dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php diff --git a/app/code/Magento/User/Api/Data/UserInterface.php b/app/code/Magento/User/Api/Data/UserInterface.php index 3faec1d10226f..07bc190db5433 100644 --- a/app/code/Magento/User/Api/Data/UserInterface.php +++ b/app/code/Magento/User/Api/Data/UserInterface.php @@ -162,19 +162,4 @@ public function getInterfaceLocale(); * @return $this */ public function setInterfaceLocale($interfaceLocale); - - /** - * Get user expiration date. - * - * @return string - */ - public function getExpiresAt(); - - /** - * Set user expiration date. - * - * @param string $expiresAt - * @return $this - */ - public function setExpiresAt($expiresAt); } diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index 9cdec228eb382..85ac6898a27c3 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -169,7 +169,7 @@ protected function _prepareForm() ] ); } - + // TODO: use store time and convert to GMT $baseFieldset->addField( 'expires_at', 'date', diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php deleted file mode 100644 index 07a79d08733af..0000000000000 --- a/app/code/Magento/User/Cron/DisableExpiredUsers.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\User\Cron; - -use Magento\Security\Model\AdminSessionsManager; - -/** - * Disable expired users. - */ -class DisableExpiredUsers -{ - - /** - * @var \Magento\User\Model\ResourceModel\User\CollectionFactory - */ - private $userCollectionFactory; - /** - * @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory - */ - private $adminSessionCollectionFactory; - /** - * @var \Magento\Security\Model\ConfigInterface - */ - private $securityConfig; - - /** - * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory - * @param \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory - * @param \Magento\Security\Model\ConfigInterface $securityConfig - */ - public function __construct( - \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory, - \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory, - \Magento\Security\Model\ConfigInterface $securityConfig - ) { - $this->userCollectionFactory = $userCollectionFactory; - $this->adminSessionCollectionFactory = $adminSessionCollectionFactory; - $this->securityConfig = $securityConfig; - } - - /** - * Disable all expired user accounts and invalidate their sessions. - */ - public function execute() - { - /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ - $users = $this->userCollectionFactory->create() - ->addActiveExpiredUsersFilter(); - - if ($users->getSize() > 0) { - /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ - $currentSessions = $this->adminSessionCollectionFactory->create() - ->addFieldToFilter('user_id', ['in' => $users->getAllIds()]) - ->addFieldToFilter('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN) - ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); - $currentSessions->setDataToAll('status', AdminSessionsManager::LOGOUT_REASON_USER_EXPIRED) - ->save(); - } - - $users->setDataToAll('expires_at', null) - ->setDataToAll('is_active', 0) - ->save(); - } -} diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index f1fd45e73c74c..422afb47f0a0e 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -13,7 +13,6 @@ */ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection { - /** * Define resource model * @@ -28,6 +27,7 @@ protected function _construct() * Collection Init Select * * @return $this + * @since 101.1.0 */ protected function _initSelect() { @@ -42,22 +42,4 @@ protected function _initSelect() ['role_name'] ); } - - /** - * Filter for expired, active users. - * - * @param string $now - * @return $this - */ - public function addActiveExpiredUsersFilter($now = null) - { - if ($now === null) { - $now = new \DateTime(); - $now->format('Y-m-d H:i:s'); - } - $this->addFieldToFilter('expires_at', ['lt' => $now]) - ->addFieldToFilter('is_active', 1); - - return $this; - } } diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 79f5ef7b3e3d3..c734096ca2014 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -115,12 +115,12 @@ class User extends AbstractModel implements StorageInterface, UserInterface protected $_encryptor; /** - * @deprecated + * @deprecated 101.1.0 */ protected $_transportBuilder; /** - * @deprecated + * @deprecated 101.1.0 */ protected $_storeManager; @@ -140,7 +140,7 @@ class User extends AbstractModel implements StorageInterface, UserInterface private $notificator; /** - * @deprecated + * @deprecated 101.1.0 */ private $deploymentConfig; @@ -212,14 +212,9 @@ protected function _construct() * Removing dependencies and leaving only entity's properties. * * @return string[] - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { - trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); - $properties = parent::__sleep(); return array_diff( $properties, @@ -245,14 +240,9 @@ public function __sleep() * Restoring required objects after serialization. * * @return void - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { - trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); - parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->serializer = $objectManager->get(Json::class); @@ -322,10 +312,6 @@ protected function _getValidationRulesBeforeSave() $this->validationRules->addPasswordConfirmationRule($validator, $this->getPasswordConfirmation()); } } - - if (!empty($this->getExpiresAt())) { - $this->validationRules->addExpiresAtRule($validator); - } return $validator; } @@ -420,10 +406,6 @@ public function getRoles() */ public function getRole() { - if ($this->getData('extracted_role')) { - $this->_role = $this->getData('extracted_role'); - $this->unsetData('extracted_role'); - } if (null === $this->_role) { $this->_role = $this->_roleFactory->create(); $roles = $this->getRoles(); @@ -459,7 +441,7 @@ public function roleUserExists() /** * Send email with reset password confirmation link. * - * @deprecated + * @deprecated 101.1.0 * @see NotificatorInterface::sendForgotPassword() * * @return $this @@ -539,7 +521,7 @@ protected function createChangesDescriptionString() * @throws NotificationExceptionInterface * @return $this * @since 100.1.0 - * @deprecated + * @deprecated 101.1.0 * @see NotificatorInterface::sendUpdated() * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -921,22 +903,6 @@ public function setInterfaceLocale($interfaceLocale) return $this->setData('interface_locale', $interfaceLocale); } - /** - * @inheritDoc - */ - public function getExpiresAt() - { - return $this->_getData('expires_at'); - } - - /** - * @inheritDoc - */ - public function setExpiresAt($expiresAt) - { - return $this->setData('expires_at', $expiresAt); - } - /** * Security check for admin user * diff --git a/app/code/Magento/User/Model/UserValidationRules.php b/app/code/Magento/User/Model/UserValidationRules.php index 49bc75e5ae8b4..e40c785749e03 100644 --- a/app/code/Magento/User/Model/UserValidationRules.php +++ b/app/code/Magento/User/Model/UserValidationRules.php @@ -6,12 +6,10 @@ namespace Magento\User\Model; -use Magento\User\Model\Validator\ExpiresAt; use Magento\Framework\Validator\EmailAddress; use Magento\Framework\Validator\NotEmpty; use Magento\Framework\Validator\Regex; use Magento\Framework\Validator\StringLength; -use Magento\Framework\App\ObjectManager; /** * Class for adding validation rules to an Admin user @@ -25,20 +23,6 @@ class UserValidationRules * Minimum length of admin password */ const MIN_PASSWORD_LENGTH = 7; - /** - * @var Validator\ExpiresAt|null - */ - private $expiresValiator; - - /** - * UserValidationRules constructor. - * @param Validator\ExpiresAt|null $expiresValiator - */ - public function __construct(?ExpiresAt $expiresValiator = null) - { - $this->expiresValiator = $expiresValiator - ?: ObjectManager::getInstance()->get(ExpiresAt::class); - } /** * Adds validation rule for user first name, last name, username and email @@ -141,36 +125,4 @@ public function addPasswordConfirmationRule( $validator->addRule($passwordConfirmation, 'password'); return $validator; } - - /** - * Adds validation rule for expiration date. - * - * @param \Magento\Framework\Validator\DataObject $validator - * @return \Magento\Framework\Validator\DataObject - * @throws \Zend_Validate_Exception - */ - public function addExpiresAtRule(\Magento\Framework\Validator\DataObject $validator) - { - $dateValidator = new \Zend_Validate_Date( - [ - 'format' => \Magento\Framework\Stdlib\DateTime::DATETIME_INTERNAL_FORMAT, - ] - ); - $dateValidator->setMessage( - __('"Expiration date" invalid type entered.'), - \Zend_Validate_Date::INVALID - ); - $dateValidator->setMessage( - __('"Expiration date" is not a valid date.'), - \Zend_Validate_Date::INVALID_DATE - ); - $dateValidator->setMessage( - __('"Expiration date" does not fit the required date format.'), - \Zend_Validate_Date::FALSEFORMAT - ); - $validator->addRule($dateValidator, 'expires_at'); - $validator->addRule($this->expiresValiator, 'expires_at'); - - return $validator; - } } diff --git a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php index 146476cd25e42..5777e3b573890 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserValidationRulesTest.php @@ -7,11 +7,6 @@ use Magento\User\Model\UserValidationRules; -/** - * Class UserValidationRulesTest - * - * @package Magento\User\Test\Unit\Model - */ class UserValidationRulesTest extends \PHPUnit\Framework\TestCase { /** @@ -47,10 +42,4 @@ public function testAddPasswordConfirmationRule() $this->validator->expects($this->once())->method('addRule')->willReturn($this->validator); $this->assertSame($this->validator, $this->rules->addPasswordConfirmationRule($this->validator, '')); } - - public function testAddExpiresAtRule() - { - $this->validator->expects($this->atMost(2))->method('addRule')->willReturn($this->validator); - $this->assertSame($this->validator, $this->rules->addExpiresAtRule($this->validator)); - } } diff --git a/app/code/Magento/User/etc/crontab.xml b/app/code/Magento/User/etc/crontab.xml deleted file mode 100644 index 6919856857b40..0000000000000 --- a/app/code/Magento/User/etc/crontab.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd"> - - <group id="default"> - <job name="user_expire_users" instance="Magento\User\Cron\DisableExpiredUsers" method="execute"> - <schedule>0 * * * *</schedule> - </job> - </group> - -</config> diff --git a/app/code/Magento/User/etc/db_schema.xml b/app/code/Magento/User/etc/db_schema.xml index ddec6dd36eb84..c3356a96b94a7 100644 --- a/app/code/Magento/User/etc/db_schema.xml +++ b/app/code/Magento/User/etc/db_schema.xml @@ -36,9 +36,7 @@ default="0" comment="Failure Number"/> <column xsi:type="timestamp" name="first_failure" on_update="false" nullable="true" comment="First Failure"/> <column xsi:type="timestamp" name="lock_expires" on_update="false" nullable="true" - comment="Expiration Lock Date"/> - <column xsi:type="timestamp" name="expires_at" on_update="false" nullable="true" - comment="User Expiration Date"/> + comment="Expiration Lock Dates"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="user_id"/> </constraint> diff --git a/app/code/Magento/User/etc/db_schema_whitelist.json b/app/code/Magento/User/etc/db_schema_whitelist.json index 3da364b803c5a..c214cae477cd7 100644 --- a/app/code/Magento/User/etc/db_schema_whitelist.json +++ b/app/code/Magento/User/etc/db_schema_whitelist.json @@ -19,8 +19,7 @@ "interface_locale": true, "failures_num": true, "first_failure": true, - "lock_expires": true, - "expires_at": true + "lock_expires": true }, "constraint": { "PRIMARY": true, @@ -43,4 +42,4 @@ "ADMIN_PASSWORDS_USER_ID_ADMIN_USER_USER_ID": true } } -} \ No newline at end of file +} diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php deleted file mode 100644 index 0c68027a36834..0000000000000 --- a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\User\Model\ResourceModel\User; - -/** - * User collection test - * @magentoAppArea adminhtml - */ -class CollectionTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\User\Model\ResourceModel\User\Collection - */ - protected $collection; - - protected function setUp() - { - $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\User\Model\ResourceModel\User\Collection::class - ); - } - - public function testSelectQueryInitialized() - { - static::assertContains( - 'main_table.user_id = user_role.user_id AND user_role.parent_id != 0', - $this->collection->getSelect()->__toString() - ); - } - - /** - * @magentoDataFixture Magento/User/_files/expired_users.php - */ - public function testExpiredActiveUsersFilter() - { - $this->collection->addActiveExpiredUsersFilter(); - static::assertGreaterThanOrEqual(1, $this->collection->getSize()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php index eb5a3a0d11f53..8b85339afd789 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php @@ -387,17 +387,6 @@ public function testBeforeSavePasswordTooShort() $this->_model->save(); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The expiration date must be later than the current date. - * @magentoDbIsolation enabled - */ - public function testBeforeSaveExpireDateBeforeNow() - { - $this->_model->setExpiresAt('2010-01-01 00:00:00'); - $this->_model->save(); - } - /** * @dataProvider beforeSavePasswordInsecureDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException From ad4628b981a5a7afd529226178a87d2f9c518576 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 21 Jun 2019 12:34:17 -0400 Subject: [PATCH 034/734] move changes out of user and into security (magento/magento2#22833: Short-termadmin accounts) --- .../User/Model/Validator/ExpiresAt.php | 38 --------------- .../Unit/Model/Validator/ExpiresAtTest.php | 46 ------------------- 2 files changed, 84 deletions(-) delete mode 100644 app/code/Magento/User/Model/Validator/ExpiresAt.php delete mode 100644 app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php diff --git a/app/code/Magento/User/Model/Validator/ExpiresAt.php b/app/code/Magento/User/Model/Validator/ExpiresAt.php deleted file mode 100644 index 1aeba4b3e1aaf..0000000000000 --- a/app/code/Magento/User/Model/Validator/ExpiresAt.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\User\Model\Validator; - -use Magento\Framework\Validator\AbstractValidator; - -/** - * Class ExpiresAt - * - * @package Magento\User\Model\Validator - */ -class ExpiresAt extends AbstractValidator -{ - - /** - * Ensure that the given date is later than the current date. - * - * @param String $value - * @return bool - * @throws \Exception - */ - public function isValid($value) - { - $currentTime = new \DateTime(); - $expiresAt = new \DateTime($value); - - if ($expiresAt < $currentTime) { - $message = __('The expiration date must be later than the current date.'); - $this->_addMessages([$message]); - } - - return !$this->hasMessages(); - } -} diff --git a/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php b/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php deleted file mode 100644 index 0df6317a38cb3..0000000000000 --- a/app/code/Magento/User/Test/Unit/Model/Validator/ExpiresAtTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\User\Test\Unit\Model\Validator; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -/** - * Class ExpiresAtValidatorTest - * @package Magento\User\Test\Unit\Model - */ -class ExpiresAtTest extends \PHPUnit\Framework\TestCase -{ - - /** @var \Magento\User\Model\Validator\ExpiresAt */ - protected $validator; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->validator = $objectManager->getObject( - \Magento\User\Model\Validator\ExpiresAt::class - ); - } - - public function testIsValidWhenInvalid() - { - static::assertFalse($this->validator->isValid('2018-01-01 00:00:00')); - static::assertContains( - 'The expiration date must be later than the current date.', - $this->validator->getMessages() - ); - } - - public function testIsValidWhenValid() - { - $futureDate = new \DateTime(); - $futureDate->modify('+1 days'); - static::assertTrue($this->validator->isValid($futureDate->format('Y-m-d H:i:s'))); - static::assertEquals([], $this->validator->getMessages()); - } -} From 3c608fd284e485f81f45f0a27563bc6e7b663937 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 21 Jun 2019 12:34:39 -0400 Subject: [PATCH 035/734] move changes out of user and into security (magento/magento2#22833: Short-termadmin accounts) --- .../User/Cron/DisableExpiredUsersTest.php | 77 ------------------- .../Magento/User/_files/expired_users.php | 55 ------------- 2 files changed, 132 deletions(-) delete mode 100644 dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/User/_files/expired_users.php diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php deleted file mode 100644 index c532dc1aa9c94..0000000000000 --- a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\User\Cron; - -use Magento\TestFramework\Helper\Bootstrap; - -/** - * TODO: test logging out sessions - * @magentoAppArea adminhtml - */ -class DisableExpiredUsersTest extends \PHPUnit\Framework\TestCase -{ - - /** - * @magentoDataFixture Magento/User/_files/expired_users.php - */ - public function testExecuteWithExpiredUser() - { - $adminUserNameFromFixture = 'adminUser3'; - - $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); - $tokenService->createAdminAccessToken( - $adminUserNameFromFixture, - \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD - ); - - /** @var \Magento\User\Cron\DisableExpiredUsers $job */ - $job = Bootstrap::getObjectManager()->create(\Magento\User\Cron\DisableExpiredUsers::class); - $job->execute(); - - /** @var \Magento\User\Model\User $user */ - $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername($adminUserNameFromFixture); - - /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ - $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); - $token = $tokenModel->loadByAdminId($user->getId()); - - static::assertEquals(0, $user->getIsActive()); - static::assertNull($user->getExpiresAt()); - static::assertEquals(null, $token->getId()); - } - - /** - * @magentoDataFixture Magento/User/_files/expired_users.php - */ - public function testExecuteWithNonExpiredUser() - { - $adminUserNameFromFixture = 'adminUser4'; - - $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); - $tokenService->createAdminAccessToken( - $adminUserNameFromFixture, - \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD - ); - - /** @var \Magento\User\Cron\DisableExpiredUsers $job */ - $job = Bootstrap::getObjectManager()->create(\Magento\User\Cron\DisableExpiredUsers::class); - $job->execute(); - - /** @var \Magento\User\Model\User $user */ - $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername($adminUserNameFromFixture); - - /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ - $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); - $token = $tokenModel->loadByAdminId($user->getId()); - - static::assertEquals(1, $user->getIsActive()); - static::assertNotNull($user->getExpiresAt()); - static::assertNotNull($token->getId()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php deleted file mode 100644 index c35a4ac66b9cc..0000000000000 --- a/dev/tests/integration/testsuite/Magento/User/_files/expired_users.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - -/** - * Create an admin user with expired access date - */ -$userIds = []; - -/** @var $model \Magento\User\Model\User */ -$model = $objectManager->create(\Magento\User\Model\User::class); -$model->setFirstName("John") - ->setLastName("Doe") - ->setUserName('adminUser3') - ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) - ->setEmail('adminUser3@example.com') - ->setRoleType('G') - ->setResourceId('Magento_Adminhtml::all') - ->setPrivileges("") - ->setAssertId(0) - ->setRoleId(1) - ->setPermission('allow'); -$model->save(); -$userIds[] = $model->getDataByKey('user_id'); - -/** @var $model \Magento\User\Model\User */ -$futureDate = new \DateTime(); -$futureDate->modify('+10 days'); -$model = $objectManager->create(\Magento\User\Model\User::class); -$model->setFirstName("John") - ->setLastName("Doe") - ->setUserName('adminUser4') - ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) - ->setEmail('adminUser4@example.com') - ->setExpiresAt($futureDate->format('Y-m-d H:i:s')) - ->setRoleType('G') - ->setResourceId('Magento_Adminhtml::all') - ->setPrivileges("") - ->setAssertId(0) - ->setRoleId(1) - ->setPermission('allow'); -$model->save(); -$userIds[] = $model->getDataByKey('user_id'); - -// need to bypass model validation to set expired date -$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); -$conn = $resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION); -$tableName = $resource->getTableName('admin_user'); -$sql = "UPDATE " . $tableName . " SET expires_at = '2010-01-01 00:00:00' WHERE user_id=" . - $userIds[0] . ";"; -$result = $conn->query($sql); From e4ec0e26e1e91d31cd2b391835ee86c6815bd556 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 21 Jun 2019 12:42:03 -0400 Subject: [PATCH 036/734] revert merge changes (magento/magento2#22833: Short-term admin accounts) --- .../Model/ResourceModel/User/Collection.php | 1 - app/code/Magento/User/Model/User.php | 24 +++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index 422afb47f0a0e..7683adae84365 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -27,7 +27,6 @@ protected function _construct() * Collection Init Select * * @return $this - * @since 101.1.0 */ protected function _initSelect() { diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index c734096ca2014..d8040b0bbaaac 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -115,12 +115,12 @@ class User extends AbstractModel implements StorageInterface, UserInterface protected $_encryptor; /** - * @deprecated 101.1.0 + * @deprecated */ protected $_transportBuilder; /** - * @deprecated 101.1.0 + * @deprecated */ protected $_storeManager; @@ -140,7 +140,7 @@ class User extends AbstractModel implements StorageInterface, UserInterface private $notificator; /** - * @deprecated 101.1.0 + * @deprecated */ private $deploymentConfig; @@ -212,9 +212,14 @@ protected function _construct() * Removing dependencies and leaving only entity's properties. * * @return string[] + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $properties = parent::__sleep(); return array_diff( $properties, @@ -240,9 +245,14 @@ public function __sleep() * Restoring required objects after serialization. * * @return void + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->serializer = $objectManager->get(Json::class); @@ -406,6 +416,10 @@ public function getRoles() */ public function getRole() { + if ($this->getData('extracted_role')) { + $this->_role = $this->getData('extracted_role'); + $this->unsetData('extracted_role'); + } if (null === $this->_role) { $this->_role = $this->_roleFactory->create(); $roles = $this->getRoles(); @@ -441,7 +455,7 @@ public function roleUserExists() /** * Send email with reset password confirmation link. * - * @deprecated 101.1.0 + * @deprecated * @see NotificatorInterface::sendForgotPassword() * * @return $this @@ -521,7 +535,7 @@ protected function createChangesDescriptionString() * @throws NotificationExceptionInterface * @return $this * @since 100.1.0 - * @deprecated 101.1.0 + * @deprecated * @see NotificatorInterface::sendUpdated() * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ From e51bf0a909ab69ff397dac26a8edcf34e88b3008 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 30 Jun 2019 12:55:13 -0400 Subject: [PATCH 037/734] Create models for userexpiration, add tests and events (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/AdminSessionsManager.php | 75 ++++---- .../Model/Plugin/UserValidationRules.php | 40 ++++ .../Model/ResourceModel/UserExpiration.php | 49 +++++ .../UserExpiration/Collection.php | 75 ++++++++ .../Magento/Security/Model/UserExpiration.php | 62 ++++++ .../Model/UserExpiration/Validator.php | 45 +++++ .../Security/Model/UserExpirationManager.php | 179 ++++++++++++++++++ .../Security/Observer/AfterAdminUserLoad.php | 54 ++++++ .../Security/Observer/AfterAdminUserSave.php | 65 +++++++ .../Observer/BeforeAdminUserAuthenticate.php | 61 ++++++ .../Unit/Model/AdminSessionsManagerTest.php | 2 +- .../Model/Plugin/UserValidationRulesTest.php | 63 ++++++ .../Model/UserExpiration/ValidatorTest.php | 49 +++++ .../Unit/Observer/AfterAdminUserLoadTest.php | 120 ++++++++++++ .../Unit/Observer/AfterAdminUserSaveTest.php | 159 ++++++++++++++++ .../BeforeAdminUserAuthenticateTest.php | 119 ++++++++++++ .../Magento/Security/etc/adminhtml/di.xml | 3 + .../Magento/Security/etc/adminhtml/events.xml | 14 ++ app/code/Magento/Security/etc/crontab.xml | 3 + app/code/Magento/Security/etc/db_schema.xml | 11 ++ .../Security/etc/db_schema_whitelist.json | 10 + app/code/Magento/Security/i18n/en_US.csv | 1 + .../Model/AdminTokenServiceTest.php | 19 ++ .../Model/AdminSessionsManagerTest.php | 27 ++- .../UserExpiration/CollectionTest.php | 62 ++++++ .../Model/UserExpirationManagerTest.php | 148 +++++++++++++++ .../Observer/AfterAdminUserLoadTest.php | 49 +++++ .../Observer/AfterAdminUserSaveTest.php | 101 ++++++++++ .../BeforeAdminUserAuthenticateTest.php | 47 +++++ .../Magento/Security/_files/expired_users.php | 71 +++++++ 30 files changed, 1742 insertions(+), 41 deletions(-) create mode 100644 app/code/Magento/Security/Model/Plugin/UserValidationRules.php create mode 100644 app/code/Magento/Security/Model/ResourceModel/UserExpiration.php create mode 100644 app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php create mode 100644 app/code/Magento/Security/Model/UserExpiration.php create mode 100644 app/code/Magento/Security/Model/UserExpiration/Validator.php create mode 100644 app/code/Magento/Security/Model/UserExpirationManager.php create mode 100644 app/code/Magento/Security/Observer/AfterAdminUserLoad.php create mode 100644 app/code/Magento/Security/Observer/AfterAdminUserSave.php create mode 100644 app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php create mode 100644 app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php create mode 100644 app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php create mode 100644 app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php create mode 100644 app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php create mode 100644 app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php create mode 100644 app/code/Magento/Security/etc/adminhtml/events.xml create mode 100644 dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 2a9024c54ee95..0c1cca617cbc8 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -27,11 +27,6 @@ class AdminSessionsManager */ const LOGOUT_REASON_USER_LOCKED = 10; - /** - * User has been logged out due to an expired user account - */ - const LOGOUT_REASON_USER_EXPIRED = 11; - /** * @var ConfigInterface * @since 100.1.0 @@ -80,6 +75,12 @@ class AdminSessionsManager */ private $maxIntervalBetweenConsecutiveProlongs = 60; + /** + * TODO: make sure we need this here + * @var UserExpirationManager + */ + private $userExpirationManager; + /** * @param ConfigInterface $securityConfig * @param \Magento\Backend\Model\Auth\Session $authSession @@ -87,6 +88,7 @@ class AdminSessionsManager * @param CollectionFactory $adminSessionInfoCollectionFactory * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param RemoteAddress $remoteAddress + * @param UserExpirationManager|null $userExpirationManager */ public function __construct( ConfigInterface $securityConfig, @@ -94,7 +96,8 @@ public function __construct( \Magento\Security\Model\AdminSessionInfoFactory $adminSessionInfoFactory, \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory, \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, - RemoteAddress $remoteAddress + RemoteAddress $remoteAddress, + \Magento\Security\Model\UserExpirationManager $userExpirationManager = null ) { $this->securityConfig = $securityConfig; $this->authSession = $authSession; @@ -102,6 +105,9 @@ public function __construct( $this->adminSessionInfoCollectionFactory = $adminSessionInfoCollectionFactory; $this->dateTime = $dateTime; $this->remoteAddress = $remoteAddress; + $this->userExpirationManager = $userExpirationManager ?: + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Security\Model\UserExpirationManager::class); } /** @@ -127,10 +133,6 @@ public function processLogin() } } - if ($this->authSession->getUser()->getExpiresAt()) { - $this->revokeExpiredAdminUser(); - } - return $this; } @@ -142,7 +144,33 @@ public function processLogin() */ public function processProlong() { + // TODO: is this the right place for this? Or should I use a plugin? This method is called in a plugin + // also, don't want to hit the database every single time. We could put it within the lastProlongIsOldEnough + // in order to reduece database loads, but what if the user is expired already? How granular do we want to get? + // if their session is expired, then they will get logged out anyways, and we can handle deactivating them + // upon login or via the cron + + // already (\Magento\Security\Model\Plugin\AuthSession::aroundProlong, which plugs into + // \Magento\Backend\Model\Auth\Session::prolong, which is called from + // \Magento\Backend\App\Action\Plugin\Authentication::aroundDispatch, which is a plugin to + // \Magento\Backend\App\AbstractAction::dispatch) + + // \Magento\Backend\App\AbstractAction::dispatch is called, which kicks off the around plugin + // \Magento\Backend\App\Action\Plugin\Authentication::aroundDispatch, which calls + // \Magento\Backend\Model\Auth\Session::prolong, which kicks off the around plugin + // \Magento\Security\Model\Plugin\AuthSession::aroundProlong, which calls + // this method. + + // this method will prolong the session only if it's old enough, otherwise it's not called. +// if ($this->userExpirationManager->userIsExpired($this->authSession->getUser())) { +// $this->userExpirationManager->deactivateExpiredUsers([$this->authSession->getUser()->getId()]); +// } + if ($this->lastProlongIsOldEnough()) { + // TODO: throw exception? + if ($this->userExpirationManager->userIsExpired($this->authSession->getUser())) { + $this->userExpirationManager->deactivateExpiredUsers([$this->authSession->getUser()->getId()]); + } $this->getCurrentSession()->setData( 'updated_at', date( @@ -153,11 +181,6 @@ public function processProlong() $this->getCurrentSession()->save(); } - // todo: don't necessarily have a user here - if ($this->authSession->getUser()->getExpiresAt()) { - $this->revokeExpiredAdminUser(); - } - return $this; } @@ -223,11 +246,6 @@ public function getLogoutReasonMessageByStatus($statusCode) 'Your account is temporarily disabled. Please try again later.' ); break; - case self::LOGOUT_REASON_USER_EXPIRED: - $reasonMessage = __( - 'Your account has expired.' - ); - break; default: $reasonMessage = __('Your current session has been expired.'); break; @@ -373,21 +391,4 @@ private function getIntervalBetweenConsecutiveProlongs() ) ); } - - /** - * Check if the current user is expired and, if so, revoke their admin token. - */ - private function revokeExpiredAdminUser() - { - $expiresAt = $this->dateTime->gmtTimestamp($this->authSession->getUser()->getExpiresAt()); - if ($expiresAt < $this->dateTime->gmtTimestamp()) { - $currentSessions = $this->getSessionsForCurrentUser(); - $currentSessions->setDataToAll('status', self::LOGOUT_REASON_USER_EXPIRED) - ->save(); - $this->authSession->getUser() - ->setIsActive(0) - ->setExpiresAt(null) - ->save(); - } - } } diff --git a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php new file mode 100644 index 0000000000000..75b826dfb6b65 --- /dev/null +++ b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\Plugin; + +/** + * \Magento\User\Model\UserValidationRules decorator + * + * @package Magento\Security\Model\Plugin + */ +class UserValidationRules +{ + /**@var \Magento\Security\Model\UserExpiration\Validator */ + private $validator; + + /** + * UserValidationRules constructor. + * + * @param \Magento\Security\Model\UserExpiration\Validator $validator + */ + public function __construct(\Magento\Security\Model\UserExpiration\Validator $validator) + { + $this->validator = $validator; + } + + /** + * @param \Magento\User\Model\UserValidationRules $userValidationRules + * @param \Magento\Framework\Validator\DataObject $result + * @return \Magento\Framework\Validator\DataObject + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterAddUserInfoRules(\Magento\User\Model\UserValidationRules $userValidationRules, $result) + { + return $result->addRule($this->validator, 'expires_at'); + } +} diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php new file mode 100644 index 0000000000000..5afca619c3f7f --- /dev/null +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel; + +/** + * Admin User Expiration resource model + */ +class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +{ + + /** + * Flag that notifies whether Primary key of table is auto-incremented + * + * @var bool + */ + protected $_isPkAutoIncrement = false; + + /** + * @return void + */ + protected function _construct() + { + $this->_init('admin_user_expiration', 'user_id'); + } + + /** + * Perform actions before object save + * + * @param \Magento\Framework\Model\AbstractModel $object + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + { + /** @var $object \Magento\Security\Model\UserExpiration */ + if ($object->getExpiresAt() instanceof \DateTimeInterface) { + + // TODO: use this? need to check if we're ever passing in a \DateTimeInterface or if it's always a string + $object->setExpiresAt($object->getExpiresAt()->format('Y-m-d H:i:s')); + } + + return $this; + } +} diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php new file mode 100644 index 0000000000000..08c72f7d9fd2f --- /dev/null +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel\UserExpiration; + +/** + * Admin user expiration collection + */ +class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection +{ + /** + * @var string + */ + protected $_idFieldName = 'user_id'; + + /** + * @return void + */ + protected function _construct() + { + $this->_init( + \Magento\Security\Model\UserExpiration::class, + \Magento\Security\Model\ResourceModel\UserExpiration::class + ); + } + + /** + * Filter for expired, active users. + * + * @param string $now + * @return $this + */ + public function addActiveExpiredUsersFilter($now = null): Collection + { + if ($now === null) { + $now = new \DateTime(); + $now->format('Y-m-d H:i:s'); + } + $this->getSelect()->joinLeft( + ['user' => $this->getTable('admin_user')], + 'main_table.user_id = user.user_id', + ['is_active'] + ); + $this->addFieldToFilter('expires_at', ['lt' => $now]) + ->addFieldToFilter('user.is_active', 1); + + return $this; + } + + /** + * Filter collection by user id. + * @param array $userIds + * @return Collection + */ + public function addUserIdsFilter($userIds = []): Collection + { + return $this->addFieldToFilter('main_table.user_id', ['in' => $userIds]); + } + + /** + * Get any expired records for the given user. + * + * @param $userId + * @return Collection + */ + public function addExpiredRecordsForUserFilter($userId): Collection + { + return $this->addActiveExpiredUsersFilter() + ->addFieldToFilter('main_table.user_id', $userId); + } +} diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php new file mode 100644 index 0000000000000..eaf2259375f0a --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +/** + * Admin User Expiration model. + * @method string getExpiresAt() + * @method \Magento\Security\Model\UserExpiration setExpiresAt(string $value) + */ +class UserExpiration extends \Magento\Framework\Model\AbstractModel +{ + + /** + * @var UserExpiration\Validator + */ + private $validator; + + /** + * UserExpiration constructor. + * + * @param \Magento\Framework\Model\Context $context + * @param \Magento\Framework\Registry $registry + * @param UserExpiration\Validator $validator + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param array $data + */ + public function __construct( + \Magento\Framework\Model\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Security\Model\UserExpiration\Validator $validator, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [] + ) { + parent::__construct($context, $registry, $resource, $resourceCollection, $data); + $this->validator = $validator; + } + + /** + * Resource initialization + * + * @return void + */ + protected function _construct() + { + $this->_init(\Magento\Security\Model\ResourceModel\UserExpiration::class); + } + + /** + * TODO: remove and use a plugin on UserValidationRules + */ +// protected function _getValidationRulesBeforeSave() +// { +// return $this->validator; +// } +} diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php new file mode 100644 index 0000000000000..4b82f2ab43f07 --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\UserExpiration; + +use Magento\Framework\Validator\AbstractValidator; + +/** + * Class Validator + * + * @package Magento\Security\Model\Validator + */ +class Validator extends AbstractValidator +{ + + /** + * Ensure that the given date is later than the current date. + * + * @param string $value + * @return bool + * @throws \Exception + */ + public function isValid($value) + { + $this->_clearMessages(); + $messages = []; + $expiresAt = $value; + $label = 'Expiration date'; + if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { + $currentTime = new \DateTime(); + $expiresAt = new \DateTime($expiresAt); + + if ($expiresAt < $currentTime) { + $messages['expires_at'] = __('"%1" must be later than the current date.', $label); + } + } + $this->_addMessages($messages); + + return empty($messages); + } +} diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php new file mode 100644 index 0000000000000..bd10b55e1b36d --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +use Magento\Security\Model\ResourceModel\UserExpiration\Collection as ExpiredUsersCollection; + +/** + * Class to handle expired admin users. + */ +class UserExpirationManager +{ + + /** + * @var \Magento\Framework\Stdlib\DateTime\DateTime + */ + private $dateTime; + + /** + * @var ConfigInterface + */ + private $securityConfig; + + /** + * @var ResourceModel\AdminSessionInfo\CollectionFactory + */ + private $adminSessionInfoCollectionFactory; + + /** + * @var \Magento\Backend\Model\Auth\Session + */ + private $authSession; + + /** + * @var ResourceModel\UserExpiration\CollectionFactory + */ + private $userExpirationCollectionFactory; + + /** + * @var \Magento\User\Model\ResourceModel\User\CollectionFactory + */ + private $userCollectionFactory; + + /** + * UserExpirationManager constructor. + * + * @param \Magento\Backend\Model\Auth\Session $authSession + * @param ConfigInterface $securityConfig + * @param ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory + * @param ResourceModel\UserExpiration\CollectionFactory $userExpirationCollectionFactory + * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + */ + public function __construct( + \Magento\Backend\Model\Auth\Session $authSession, + \Magento\Security\Model\ConfigInterface $securityConfig, + \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory, + \Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory $userExpirationCollectionFactory, + \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory, + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + ) { + $this->dateTime = $dateTime; + $this->securityConfig = $securityConfig; + $this->adminSessionInfoCollectionFactory = $adminSessionInfoCollectionFactory; + $this->authSession = $authSession; + $this->userExpirationCollectionFactory = $userExpirationCollectionFactory; + $this->userCollectionFactory = $userCollectionFactory; + } + + /** + * Revoke admin tokens for expired users. + * TODO: any better way than looping? + * TODO: remove + * @param \Magento\User\Model\User $user + * @throws \Exception + */ + public function deactivateExpiredUser(\Magento\User\Model\User $user): void + { + $currentSessions = $this->getSessionsForUser($user); + $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) + ->save(); + $user + ->setIsActive(0) + ->save(); + // TODO: remove expires_at record from new table + } + + /** + * Deactivate expired user accounts and invalidate their sessions. + * + * @param array|null $userIds + */ + public function deactivateExpiredUsers(?array $userIds = null): void + { + /** @var ExpiredUsersCollection $expiredRecords */ + $expiredRecords = $this->userExpirationCollectionFactory->create()->addActiveExpiredUsersFilter(); + if ($userIds != null) { + $expiredRecords->addUserIdsFilter($userIds); + } + + if ($expiredRecords->getSize() > 0) { + // get all active sessions for the users and set them to logged out + /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ + $currentSessions = $this->adminSessionInfoCollectionFactory->create() + ->addFieldToFilter('user_id', ['in' => $expiredRecords->getAllIds()]) + ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); + $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) + ->save(); + } + + // delete expired records + $expiredRecordIds = $expiredRecords->getAllIds(); + $expiredRecords->walk('delete'); + + // set user is_active to 0 + $users = $this->userCollectionFactory->create() + ->addFieldToFilter('main_table.user_id', ['in' => $expiredRecordIds]); + $users->setDataToAll('is_active', 0)->save(); + } + + /** + * Get sessions for the given user. + * TODO: remove + * @param \Magento\User\Model\User $user + * @return ResourceModel\AdminSessionInfo\Collection + */ + private function getSessionsForUser(\Magento\User\Model\User $user) + { + $collection = $this->adminSessionInfoCollectionFactory->create(); + $collection + ->filterByUser($user->getId(), \Magento\Security\Model\AdminSessionInfo::LOGGED_IN) + ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()) + ->loadData(); + + return $collection; + } + + /** + * Check if the given user is expired. + * // TODO: check users expired an hour ago (timezone stuff) + * @param \Magento\User\Model\User $user + * @return bool + */ + public function userIsExpired(\Magento\User\Model\User $user): bool + { + $isExpired = false; + $expiredRecord = $this->userExpirationCollectionFactory->create() + ->addExpiredRecordsForUserFilter($user->getId()) + ->getFirstItem(); // expiresAt: 1561824907, current timestamp: 1561824932 + if ($expiredRecord && $expiredRecord->getId()) { + //$expiresAt = $this->dateTime->gmtTimestamp($expiredRecord->getExpiredAt()); + $expiresAt = $this->dateTime->timestamp($expiredRecord->getExpiresAt()); + $isExpired = $expiresAt < $this->dateTime->gmtTimestamp(); + } + + return $isExpired; + } + + /** + * Check if the current user is expired and, if so, revoke their admin token. + */ + // private function revokeExpiredAdminUser() + // { + // $expiresAt = $this->dateTime->gmtTimestamp($this->authSession->getUser()->getExpiresAt()); + // if ($expiresAt < $this->dateTime->gmtTimestamp()) { + // $currentSessions = $this->getSessionsForCurrentUser(); + // $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) + // ->save(); + // $this->authSession->getUser() + // ->setIsActive(0) + // ->setExpiresAt(null) + // ->save(); + // } + // } +} diff --git a/app/code/Magento/Security/Observer/AfterAdminUserLoad.php b/app/code/Magento/Security/Observer/AfterAdminUserLoad.php new file mode 100644 index 0000000000000..4bc6804276c5c --- /dev/null +++ b/app/code/Magento/Security/Observer/AfterAdminUserLoad.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; + +/** + * Load UserExpiration on admin user. Only needed in the admin to load the expires_at when editing users. + */ +class AfterAdminUserLoad implements ObserverInterface +{ + /** + * @var \Magento\Security\Model\UserExpirationFactory + */ + private $userExpirationFactory; + + /** + * @var \Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResource; + + public function __construct( + \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, + \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + ) { + + $this->userExpirationFactory = $userExpirationFactory; + $this->userExpirationResource = $userExpirationResource; + } + + /** + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + /* @var $user \Magento\User\Model\User */ + $user = $observer->getEvent()->getObject(); + if ($user->getId()) { + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->userExpirationFactory->create(); + $this->userExpirationResource->load($userExpiration, $user->getId()); + if ($userExpiration->getExpiresAt()) { + $user->setExpiresAt($userExpiration->getExpiresAt()); + } + } + } +} diff --git a/app/code/Magento/Security/Observer/AfterAdminUserSave.php b/app/code/Magento/Security/Observer/AfterAdminUserSave.php new file mode 100644 index 0000000000000..37fb6af3ccaa6 --- /dev/null +++ b/app/code/Magento/Security/Observer/AfterAdminUserSave.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; + +/** + * Save UserExpiration on admin user record. + */ +class AfterAdminUserSave implements ObserverInterface +{ + /** + * @var \Magento\Security\Model\UserExpirationFactory + */ + private $userExpirationFactory; + + /** + * @var \Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResource; + + public function __construct( + \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, + \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + ) { + + $this->userExpirationFactory = $userExpirationFactory; + $this->userExpirationResource = $userExpirationResource; + } + + /** + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + /* @var $user \Magento\User\Model\User */ + $user = $observer->getEvent()->getObject(); + if ($user->getId()) { + $expiresAt = $user->getExpiresAt(); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->userExpirationFactory->create(); + $this->userExpirationResource->load($userExpiration, $user->getId()); + + if (empty($expiresAt)) { + // delete it if the admin user clears the field + if ($userExpiration->getId()) { + $this->userExpirationResource->delete($userExpiration); + } + } else { + if (!$userExpiration->getId()) { + $userExpiration->setId($user->getId()); + } + $userExpiration->setExpiresAt($expiresAt); + $this->userExpirationResource->save($userExpiration); + } + } + } +} diff --git a/app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php b/app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php new file mode 100644 index 0000000000000..1c888ec511646 --- /dev/null +++ b/app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\Plugin\AuthenticationException; +use Magento\Security\Model\UserExpirationManager; + +/** + * Class BeforeAdminUserAuthenticate + * + * @package Magento\Security\Observer + */ +class BeforeAdminUserAuthenticate implements ObserverInterface +{ + /** + * @var UserExpirationManager + */ + private $userExpirationManager; + + /** + * @var \Magento\User\Model\User + */ + private $user; + + public function __construct( + \Magento\Security\Model\UserExpirationManager $userExpirationManager, + \Magento\User\Model\User $user + ) { + $this->userExpirationManager = $userExpirationManager; + $this->user = $user; + } + + /** + * @param Observer $observer + * @return void + * @throws AuthenticationException + */ + public function execute(Observer $observer) + { + $username = $observer->getEvent()->getUsername(); + /** @var \Magento\User\Model\User $user */ + $user = $this->user->loadByUsername($username); + + if ($this->userExpirationManager->userIsExpired($user)) { + $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + throw new AuthenticationException( + __( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) + ); + } + } +} diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index d7495cd9ce173..a062cad9d61da 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -50,7 +50,7 @@ class AdminSessionsManagerTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected $objectManager; - /* + /** * @var RemoteAddress */ protected $remoteAddressMock; diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php new file mode 100644 index 0000000000000..4498e514e45d3 --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php @@ -0,0 +1,63 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\Unit\Model\Plugin; + +/** + * Class UserValidationRulesTest + * + * @package Magento\Security\Test\Unit\Model\Plugin + */ +class UserValidationRulesTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \Magento\Security\Model\Plugin\UserValidationRules|\PHPUnit\Framework\MockObject\MockObject + */ + private $plugin; + + /** + * @var \Magento\User\Model\UserValidationRules|\PHPUnit\Framework\MockObject\MockObject + */ + private $userValidationRules; + + /** + * @var \Magento\Framework\Validator\DataObject|\PHPUnit\Framework\MockObject\MockObject + */ + private $validator; + + /** + * @var \Magento\User\Model\UserValidationRules + */ + private $rules; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $userExpirationValidator = $this->createMock(\Magento\Security\Model\UserExpiration\Validator::class); + $this->userValidationRules = $this->createMock(\Magento\User\Model\UserValidationRules::class); + $this->rules = $objectManager->getObject(\Magento\User\Model\UserValidationRules::class); + $this->validator = $this->createMock(\Magento\Framework\Validator\DataObject::class); + $this->plugin = + $objectManager->getObject( + \Magento\Security\Model\Plugin\UserValidationRules::class, + ['validator' => $userExpirationValidator] + ); + } + + public function testAfterAddUserInfoRules() + { + $this->validator->expects(static::exactly(5))->method('addRule')->willReturn($this->validator); + static::assertSame($this->validator, $this->rules->addUserInfoRules($this->validator)); + static::assertSame($this->validator, $this->callAfterAddUserInfoRulesPlugin($this->validator)); + } + + protected function callAfterAddUserInfoRulesPlugin($validator) + { + return $this->plugin->afterAddUserInfoRules($this->userValidationRules, $validator); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php new file mode 100644 index 0000000000000..18e1eb3438bae --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Model\UserExpiration; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Class ExpiresAtValidatorTest + * + * @package Magento\User\Test\Unit\Model + */ +class ValidatorTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \Magento\Security\Model\UserExpiration\Validator + */ + private $validator; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->validator = $objectManager->getObject(\Magento\Security\Model\UserExpiration\Validator::class); + } + + public function testWithPastDate() + { + $testDate = new \DateTime(); + $testDate->modify('-10 days'); + static::assertFalse($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); + static::assertContains( + '"Expiration date" must be later than the current date.', + $this->validator->getMessages() + ); + } + + public function testWithFutureDate() + { + $testDate = new \DateTime(); + $testDate->modify('+10 days'); + static::assertTrue($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); + static::assertEquals([], $this->validator->getMessages()); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php new file mode 100644 index 0000000000000..dfcbcad5780b8 --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Observer; + +/** + * Test class for \Magento\Security\Observer\AfterAdminUserLoad + */ +class AfterAdminUserLoadTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationFactory + */ + private $userExpirationFactoryMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResourceMock; + + /** + * @var \Magento\Security\Observer\AfterAdminUserLoad + */ + private $observer; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer + */ + private $eventObserverMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event + */ + private $eventMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User + */ + private $userMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration + */ + private $userExpirationMock; + + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->userExpirationFactoryMock = $this->createMock(\Magento\Security\Model\UserExpirationFactory::class); + $this->userExpirationResourceMock = $this->createPartialMock( + \Magento\Security\Model\ResourceModel\UserExpiration::class, + ['load'] + ); + $this->observer = $this->objectManager->getObject( + \Magento\Security\Observer\AfterAdminUserLoad::class, + [ + 'userExpirationFactory' => $this->userExpirationFactoryMock, + 'userExpirationResource' => $this->userExpirationResourceMock, + ] + ); + + $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); + $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', 'setExpiresAt']); + $this->userExpirationMock = $this->createPartialMock( + \Magento\Security\Model\UserExpiration::class, + ['getExpiresAt'] + ); + } + + public function testWithExpiredUser() + { + $userId = '123'; + $testDate = new \DateTime(); + $testDate->modify('+10 days'); + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userExpirationFactoryMock->expects(static::once()) + ->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once()) + ->method('load') + ->willReturn($this->userExpirationMock); + $this->userExpirationMock->expects(static::exactly(2)) + ->method('getExpiresAt') + ->willReturn($testDate->format('Y-m-d H:i:s')); + $this->userMock->expects(static::once()) + ->method('setExpiresAt') + ->willReturn($this->userMock); + $this->observer->execute($this->eventObserverMock); + } + + public function testWithNonExpiredUser() + { + $userId = '123'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + $this->userExpirationMock->expects(static::once()) + ->method('getExpiresAt') + ->willReturn(null); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php new file mode 100644 index 0000000000000..439ec3f88548b --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php @@ -0,0 +1,159 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Observer; + +/** + * Test class for \Magento\Security\Observer\AfterAdminUserSave + * + * @package Magento\Security\Test\Unit\Observer + */ +class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationFactory + */ + private $userExpirationFactoryMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResourceMock; + + /** + * @var \Magento\Security\Observer\AfterAdminUserSave + */ + private $observer; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer + */ + private $eventObserverMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event + */ + private $eventMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User + */ + private $userMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration + */ + private $userExpirationMock; + + public function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->userExpirationFactoryMock = $this->createMock(\Magento\Security\Model\UserExpirationFactory::class); + $this->userExpirationResourceMock = $this->createPartialMock( + \Magento\Security\Model\ResourceModel\UserExpiration::class, + ['load', 'save', 'delete'] + ); + $this->observer = $this->objectManager->getObject( + \Magento\Security\Observer\AfterAdminUserSave::class, + [ + 'userExpirationFactory' => $this->userExpirationFactoryMock, + 'userExpirationResource' => $this->userExpirationResourceMock, + ] + ); + $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); + $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', 'getExpiresAt']); + $this->userExpirationMock = $this->createPartialMock( + \Magento\Security\Model\UserExpiration::class, + ['getId', 'getExpiresAt', 'setId', 'setExpiresAt'] + ); + } + + public function testSaveNewUserExpiration() + { + $userId = '123'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(3))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn($this->getExpiresDateTime()); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn(null); + $this->userExpirationMock->expects(static::once())->method('setId')->willReturn($this->userExpirationMock); + $this->userExpirationMock->expects(static::once())->method('setExpiresAt') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('save') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + /** + * @throws \Exception + */ + public function testClearUserExpiration() + { + $userId = '123'; + $this->userExpirationMock->setId($userId); + + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn(null); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn($userId); + $this->userExpirationResourceMock->expects(static::once())->method('delete') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + public function testChangeUserExpiration() + { + $userId = '123'; + $this->userExpirationMock->setId($userId); + + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn($this->getExpiresDateTime()); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn($userId); + $this->userExpirationMock->expects(static::once())->method('setExpiresAt') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('save') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + /** + * @return string + * @throws \Exception + */ + private function getExpiresDateTime() + { + $testDate = new \DateTime(); + $testDate->modify('+10 days'); + return $testDate->format('Y-m-d H:i:s'); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php new file mode 100644 index 0000000000000..9df2a133ff2a9 --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Observer; + +/** + * Test for \Magento\Security\Observer\BeforeAdminUserAuthenticate + * + * @package Magento\Security\Test\Unit\Observer + */ +class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationManager + */ + private $userExpirationManagerMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User + */ + private $userMock; + + /** + * @var \Magento\Security\Observer\BeforeAdminUserAuthenticate + */ + private $observer; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer::class + */ + private $eventObserverMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event + */ + private $eventMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration + */ + private $userExpirationMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->userExpirationManagerMock = $this->createPartialMock( + \Magento\Security\Model\UserExpirationManager::class, + ['userIsExpired', 'deactivateExpiredUsers'] + ); + $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['loadByUsername', 'getId']); + $this->observer = $this->objectManager->getObject( + \Magento\Security\Observer\BeforeAdminUserAuthenticate::class, + [ + 'userExpirationManager' => $this->userExpirationManagerMock, + 'user' => $this->userMock, + ] + ); + $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getUsername']); + $this->userExpirationMock = $this->createPartialMock( + \Magento\Security\Model\UserExpiration::class, + ['getId', 'getExpiresAt', 'setId', 'setExpiresAt'] + ); + } + + /** + * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later + */ + public function testWithExpiredUser() + { + $adminUserId = 123; + $username = 'testuser'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturn($this->userMock); + + $this->userExpirationManagerMock->expects(static::once()) + ->method('userIsExpired') + ->with($this->userMock) + ->willReturn(true); + $this->userMock->expects(static::once())->method('getId')->willReturn($adminUserId); + $this->userExpirationManagerMock->expects(static::once()) + ->method('deactivateExpiredUsers') + ->with([$adminUserId]) + ->willReturn(null); + $this->observer->execute($this->eventObserverMock); + } + + public function testWithNonExpiredUser() + { + $username = 'testuser'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturn($this->userMock); + + $this->userExpirationManagerMock->expects(static::once()) + ->method('userIsExpired') + ->with($this->userMock) + ->willReturn(false); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml index 79477e9443097..8cf5b610ef3c5 100644 --- a/app/code/Magento/Security/etc/adminhtml/di.xml +++ b/app/code/Magento/Security/etc/adminhtml/di.xml @@ -15,6 +15,9 @@ <type name="Magento\Backend\Controller\Adminhtml\Auth\Login"> <plugin name="security_login_form" type="Magento\Security\Model\Plugin\LoginController" /> </type> + <type name="Magento\User\Model\UserValidationRules"> + <plugin name="user_expiration_validator" type="Magento\Security\Model\Plugin\UserValidationRules" /> + </type> <type name="Magento\Security\Model\Plugin\AccountManagement"> <arguments> <argument name="passwordRequestEvent" xsi:type="const">Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST</argument> diff --git a/app/code/Magento/Security/etc/adminhtml/events.xml b/app/code/Magento/Security/etc/adminhtml/events.xml new file mode 100644 index 0000000000000..1bd8e9807c05f --- /dev/null +++ b/app/code/Magento/Security/etc/adminhtml/events.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + + <event name="admin_user_save_after"> + <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserSave"/> + </event> + <event name="admin_user_load_after"> + <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserLoad"/> + </event> + <event name="admin_user_authenticate_before"> + <observer name="captcha" instance="Magento\Security\Observer\BeforeAdminUserAuthenticate"/> + </event> +</config> diff --git a/app/code/Magento/Security/etc/crontab.xml b/app/code/Magento/Security/etc/crontab.xml index a30a43730e6fa..203cf5c2a2389 100644 --- a/app/code/Magento/Security/etc/crontab.xml +++ b/app/code/Magento/Security/etc/crontab.xml @@ -13,5 +13,8 @@ <job name="security_clean_password_reset_request_event_records" instance="Magento\Security\Model\SecurityManager" method="cleanExpiredRecords"> <schedule>0 0 * * *</schedule> </job> + <job name="security_expire_users" instance="Magento\Security\DisableExpiredUsers" method="deactivateExpiredUsers"> + <schedule>0 * * * *</schedule> + </job> </group> </config> diff --git a/app/code/Magento/Security/etc/db_schema.xml b/app/code/Magento/Security/etc/db_schema.xml index ce7143582ce69..ec0f1d72c604e 100644 --- a/app/code/Magento/Security/etc/db_schema.xml +++ b/app/code/Magento/Security/etc/db_schema.xml @@ -55,4 +55,15 @@ <column name="created_at"/> </index> </table> + <table name="admin_user_expiration" resource="default" engine="innodb" comment="Admin User expiration dates table"> + <column xsi:type="int" name="user_id" padding="10" unsigned="true" nullable="false" identity="false" + comment="User ID"/> + <column xsi:type="timestamp" name="expires_at" nullable="false" default="0" comment="User Expiration Date"/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="user_id"/> + </constraint> + <constraint xsi:type="foreign" referenceId="ADMIN_USER_EXPIRATION_USER_ID_ADMIN_USER_USER_ID" + table="admin_user_expiration" column="user_id" referenceTable="admin_user" + referenceColumn="user_id" onDelete="CASCADE"/> + </table> </schema> diff --git a/app/code/Magento/Security/etc/db_schema_whitelist.json b/app/code/Magento/Security/etc/db_schema_whitelist.json index c387b7591c7a5..1f8183e123956 100644 --- a/app/code/Magento/Security/etc/db_schema_whitelist.json +++ b/app/code/Magento/Security/etc/db_schema_whitelist.json @@ -33,5 +33,15 @@ "constraint": { "PRIMARY": true } + }, + "admin_user_expiration": { + "column": { + "user_id": true, + "expires_at": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_USER_EXPIRATION_USER_ID_ADMIN_USER_USER_ID": true + } } } \ No newline at end of file diff --git a/app/code/Magento/Security/i18n/en_US.csv b/app/code/Magento/Security/i18n/en_US.csv index 0cf998b21a1c8..4329e0747d08e 100644 --- a/app/code/Magento/Security/i18n/en_US.csv +++ b/app/code/Magento/Security/i18n/en_US.csv @@ -26,3 +26,4 @@ None,None "Limit the number of password reset request per hour. Use 0 to disable.","Limit the number of password reset request per hour. Use 0 to disable." "Min Time Between Password Reset Requests","Min Time Between Password Reset Requests" "Delay in minutes between password reset requests. Use 0 to disable.","Delay in minutes between password reset requests. Use 0 to disable." +"""%1"" must be later than the current date.","""%1"" must be later than the current date." diff --git a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php index 369d71ddbff9b..21a9f6d942dc3 100644 --- a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php @@ -59,6 +59,25 @@ public function testCreateAdminAccessToken() $this->assertEquals($accessToken, $token); } + /** + * TODO: fix failing test + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @expectedException \Magento\Framework\Exception\AuthenticationException + */ + public function testCreateAdminAccessTokenExpiredUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $this->tokenService->createAdminAccessToken( + $adminUserNameFromFixture, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $this->expectExceptionMessage( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ); + } + /** * @dataProvider validationDataProvider */ diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php index a201dbfdf1b03..74cefb9d41a59 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php @@ -3,8 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Security\Model; +/** + * Class AdminSessionsManagerTest + * TODO: test AdminSessionsManager::processLogin + * TODO: test AdminSessionsManager::processProlong + * + * @package Magento\Security\Model + */ class AdminSessionsManagerTest extends \PHPUnit\Framework\TestCase { /** @@ -61,8 +69,8 @@ protected function setUp() protected function tearDown() { $this->auth = null; - $this->authSession = null; - $this->adminSessionInfo = null; + $this->authSession = null; + $this->adminSessionInfo = null; $this->adminSessionsManager = null; $this->objectManager = null; parent::tearDown(); @@ -125,10 +133,23 @@ public function testTerminateOtherSessionsProcessLogin() $session->load('669e2e3d752e8', 'session_id'); $this->assertEquals( AdminSessionInfo::LOGGED_OUT_BY_LOGIN, - (int) $session->getStatus() + (int)$session->getStatus() ); } + /** + * Test that expired users cannot login. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedException \Magento\Framework\Exception\AuthenticationException + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @magentoDbIsolation enabled + */ + public function testExpiredUserProcessLogin() + { + static::markTestSkipped('to implement'); + } + /** * Test if current session is retrieved * diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php new file mode 100644 index 0000000000000..63f1c377f99cc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel\UserExpiration; + +/** + * Class CollectionTest + * + * @package Magento\Security\Model\ResourceModel\UserExpiration + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory + */ + protected $collectionModelFactory; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $objectManager; + + /** + * Set up + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->collectionModelFactory = $this->objectManager + ->create(\Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory::class); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testExpiredActiveUsersFilter() + { + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create(); + $collectionModel->addActiveExpiredUsersFilter(); + static::assertGreaterThanOrEqual(1, $collectionModel->getSize()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testGetExpiredRecordsForUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create()->addExpiredRecordsForUserFilter($user->getId()); + static::assertGreaterThanOrEqual(1, $collectionModel->getSize()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php new file mode 100644 index 0000000000000..1840748444842 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -0,0 +1,148 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * TODO: test logging out sessions + * TODO: test that you cannot create a token for an expired user (where would I put this test?) + * TODO: test AdminSessionsManager::processLogin + * TODO: test AdminSessionsManager::processProlong (do it here or in the AdminSessionsManagerTest?) + * + * @magentoAppArea adminhtml + */ +class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testUserIsExpired() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->getUserFromUserName($adminUserNameFromFixture); + /** @var \Magento\Security\Model\UserExpirationManager $userExpirationManager */ + $userExpirationManager = Bootstrap::getObjectManager() + ->create(\Magento\Security\Model\UserExpirationManager::class); + static::assertTrue($userExpirationManager->userIsExpired($user)); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testDeactivateExpiredUsersWithExpiredUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + + list($user, $token, $expiredUserModel) = $this->setupCronTests($adminUserNameFromFixture); + + static::assertEquals(0, $user->getIsActive()); + static::assertEquals(null, $token->getId()); + static::assertNull($expiredUserModel->getId()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testDeactivateExpiredUsersWithNonExpiredUser() + { + $adminUserNameFromFixture = 'adminUserNotExpired'; + // log them in + $adminToken = $this->createToken($adminUserNameFromFixture); + + list($user, $token, $expiredUserModel) = $this->setupCronTests($adminUserNameFromFixture); + + static::assertEquals(1, $user->getIsActive()); + static::assertNotNull($token->getId()); + static::assertEquals($expiredUserModel->getUserId(), $user->getId()); + } + + /** + * @param string $adminUserNameFromFixture + * @return array + */ + private function setupCronTests(string $adminUserNameFromFixture): array + { + // TODO: set the user expired after calling this + // TODO: use this to test the observer with exception: + // Magento\Framework\Exception\Plugin\AuthenticationException : The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later. + //$adminToken = $this->createToken($adminUserNameFromFixture); // TODO: this logs the user in, which kicks off the deactivate call + + /** @var \Magento\Security\Model\UserExpirationManager $job */ + $userExpirationManager = Bootstrap::getObjectManager() + ->create(\Magento\Security\Model\UserExpirationManager::class); + $userExpirationManager->deactivateExpiredUsers(); + + /** @var \Magento\User\Model\User $user */ + $user = $this->getUserFromUserName($adminUserNameFromFixture); + + // this is for the API only + $oauthToken = $this->getOauthTokenByUser($user); + $expiredUserModel = $this->getExpiredUserModelByUser($user); + + return [$user, $oauthToken, $expiredUserModel]; + } + + /** + * TODO: this calls user->login and throws an AuthenticationException + * + * @param string $adminUserNameFromFixture + * @return string + * @throws \Magento\Framework\Exception\AuthenticationException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function createToken(string $adminUserNameFromFixture): string + { + /** @var \Magento\Integration\Api\AdminTokenServiceInterface $tokenService */ + $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); + $token = $tokenService->createAdminAccessToken( + $adminUserNameFromFixture, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + return $token; + } + + /** + * @param string $adminUserNameFromFixture + * @return \Magento\User\Model\User + */ + private function getUserFromUserName(string $adminUserNameFromFixture): \Magento\User\Model\User + { + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + return $user; + } + + /** + * @param \Magento\User\Model\User $user + * @return \Magento\Integration\Model\Oauth\Token + */ + private function getOauthTokenByUser(\Magento\User\Model\User $user): \Magento\Integration\Model\Oauth\Token + { + /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ + $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); + $oauthToken = $tokenModel->loadByAdminId($user->getId()); + return $oauthToken; + } + + /** + * @param \Magento\User\Model\User $user + * @return UserExpiration + */ + private function getExpiredUserModelByUser(\Magento\User\Model\User $user): \Magento\Security\Model\UserExpiration + { + /** @var \Magento\Security\Model\UserExpiration $expiredUserModel */ + $expiredUserModel = Bootstrap::getObjectManager()->get(\Magento\Security\Model\UserExpiration::class); + $expiredUserModel->load($user->getId()); + return $expiredUserModel; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php new file mode 100644 index 0000000000000..0fcd90913e562 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Security\Observer\AfterAdminUserLoad + * + * @magentoAppArea adminhtml + */ +class AfterAdminUserLoadTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testWithUserWithExpiration() + { + $adminUserNameFromFixture = 'adminUserExpired'; + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $userId = $user->getId(); + $loadedUser = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $loadedUser->load($userId); + static::assertNotNull($loadedUser->getExpiresAt()); + } + + /** + * @magentoDataFixture Magento/User/_files/dummy_user.php + */ + public function testWithNonExpiredUser() + { + $adminUserNameFromFixture = 'dummy_username'; + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $userId = $user->getId(); + $loadedUser = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $loadedUser->load($userId); + static::assertNull($loadedUser->getExpiresAt()); + } + +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php new file mode 100644 index 0000000000000..6032b651b4dc3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Security\Observer\AfterAdminUserSave + * + * @magentoAppArea adminhtml + */ +class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase +{ + + /** + * Save a new UserExpiration record + * + * @magentoDataFixture Magento/User/_files/dummy_user.php + */ + public function testSaveNewUserExpiration() + { + $adminUserNameFromFixture = 'dummy_username'; + $testDate = $this->getExpiresDateTime(); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt($testDate); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + } + + /** + * Remove the UserExpiration record + * + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testClearUserExpiration() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt(null); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNull($userExpiration->getId()); + } + + /** + * Change the UserExpiration record + * + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testChangeUserExpiration() + { + $adminUserNameFromFixture = 'adminUserNotExpired'; + $testDate = $this->getExpiresDateTime(); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + $existingExpiration = $userExpiration->getExpiresAt(); + + $user->setExpiresAt($testDate); + $user->save(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + static::assertNotEquals($existingExpiration, $userExpiration->getExpiresAt()); + } + + /** + * @return string + * @throws \Exception + */ + private function getExpiresDateTime() + { + $testDate = new \DateTime(); + $testDate->modify('+20 days'); + return $testDate->format('Y-m-d H:i:s'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php new file mode 100644 index 0000000000000..db7a83877d1b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Security\Observer\BeforeAdminUserAuthenticate + * @magentoAppArea adminhtml + */ +class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later + */ + public function testWithExpiredUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $password = \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD; + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->authenticate($adminUserNameFromFixture, $password); + static::assertFalse((bool)$user->getIsActive()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testWithNonExpiredUser() + { + $adminUserNameFromFixture = 'adminUserNotExpired'; + $password = \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD; + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->authenticate($adminUserNameFromFixture, $password); + static::assertTrue((bool)$user->getIsActive()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php new file mode 100644 index 0000000000000..5a3971cd9d0bb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php @@ -0,0 +1,71 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** + * Create an admin user with expired and non-expired access date + */ + +/** @var $userModelNotExpired \Magento\User\Model\User */ +$userModelNotExpired = $objectManager->create(\Magento\User\Model\User::class); +$userModelNotExpired->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUserNotExpired') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUserNotExpired@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow') + ->setIsActive(1) + ->save(); +$futureDate = new \DateTime(); +$futureDate->modify('+10 days'); +$notExpiredRecord = $objectManager->create(\Magento\Security\Model\UserExpiration::class); +$notExpiredRecord + ->setId($userModelNotExpired->getId()) + ->setExpiresAt($futureDate->format('Y-m-d H:i:s')) + ->save(); + +/** @var $userModelExpired \Magento\User\Model\User */ +$pastDate = new \DateTime(); +$pastDate->modify('-10 days'); +$userModelExpired = $objectManager->create(\Magento\User\Model\User::class); +$userModelExpired->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUserExpired') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUserExpired@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow') + ->setIsActive(1) + ->save(); +$expiredRecord = $objectManager->create(\Magento\Security\Model\UserExpiration::class); +$expiredRecord + ->setId($userModelExpired->getId()) + ->setExpiresAt($pastDate->format('Y-m-d H:i:s')) + ->save(); + +// TODO: remove +// need to bypass model validation to set expired date +//$pastDate = new \DateTime(); +//$pastDate->modify('-10 days'); +//$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); +///** @var \Magento\Framework\DB\Adapter\AdapterInterface $conn */ +//$conn = $resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION); +//$conn->update( +// $resource->getTableName('admin_user_expiration'), +// ['expires_at' => $pastDate->format('Y-m-d H:i:s')], +// ['user_id = ?' => (int)$userModelExpired->getId()] +//); From 071f9939233a6988503e3fe097e57d9cd00e47e0 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 30 Jun 2019 16:11:26 -0400 Subject: [PATCH 038/734] Move events out of adminhtml area; use Auth session prolong plugin instead of AdminSessionsManager to check for expired users (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/AdminSessionsManager.php | 46 +----------- .../Security/Model/Plugin/AuthSession.php | 16 +++- .../Security/Model/UserExpirationManager.php | 55 +------------- .../Unit/Model/Plugin/AuthSessionTest.php | 74 ++++++++++++++++++- .../Security/etc/{adminhtml => }/events.xml | 3 +- .../Model/AdminTokenServiceTest.php | 1 - .../Model/AdminSessionsManagerTest.php | 27 +------ .../Security/Model/Plugin/AuthSessionTest.php | 35 +++++++++ .../Model/UserExpirationManagerTest.php | 4 - .../Observer/AfterAdminUserLoadTest.php | 4 +- .../Observer/AfterAdminUserSaveTest.php | 2 - .../BeforeAdminUserAuthenticateTest.php | 1 - 12 files changed, 131 insertions(+), 137 deletions(-) rename app/code/Magento/Security/etc/{adminhtml => }/events.xml (82%) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 0c1cca617cbc8..7503fe04ba480 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -75,12 +75,6 @@ class AdminSessionsManager */ private $maxIntervalBetweenConsecutiveProlongs = 60; - /** - * TODO: make sure we need this here - * @var UserExpirationManager - */ - private $userExpirationManager; - /** * @param ConfigInterface $securityConfig * @param \Magento\Backend\Model\Auth\Session $authSession @@ -88,7 +82,6 @@ class AdminSessionsManager * @param CollectionFactory $adminSessionInfoCollectionFactory * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param RemoteAddress $remoteAddress - * @param UserExpirationManager|null $userExpirationManager */ public function __construct( ConfigInterface $securityConfig, @@ -96,8 +89,7 @@ public function __construct( \Magento\Security\Model\AdminSessionInfoFactory $adminSessionInfoFactory, \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory, \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, - RemoteAddress $remoteAddress, - \Magento\Security\Model\UserExpirationManager $userExpirationManager = null + RemoteAddress $remoteAddress ) { $this->securityConfig = $securityConfig; $this->authSession = $authSession; @@ -105,13 +97,10 @@ public function __construct( $this->adminSessionInfoCollectionFactory = $adminSessionInfoCollectionFactory; $this->dateTime = $dateTime; $this->remoteAddress = $remoteAddress; - $this->userExpirationManager = $userExpirationManager ?: - \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Security\Model\UserExpirationManager::class); } /** - * Handle all others active sessions according Sharing Account Setting and expired users. + * Handle all others active sessions according Sharing Account Setting * * @return $this * @since 100.1.0 @@ -144,33 +133,7 @@ public function processLogin() */ public function processProlong() { - // TODO: is this the right place for this? Or should I use a plugin? This method is called in a plugin - // also, don't want to hit the database every single time. We could put it within the lastProlongIsOldEnough - // in order to reduece database loads, but what if the user is expired already? How granular do we want to get? - // if their session is expired, then they will get logged out anyways, and we can handle deactivating them - // upon login or via the cron - - // already (\Magento\Security\Model\Plugin\AuthSession::aroundProlong, which plugs into - // \Magento\Backend\Model\Auth\Session::prolong, which is called from - // \Magento\Backend\App\Action\Plugin\Authentication::aroundDispatch, which is a plugin to - // \Magento\Backend\App\AbstractAction::dispatch) - - // \Magento\Backend\App\AbstractAction::dispatch is called, which kicks off the around plugin - // \Magento\Backend\App\Action\Plugin\Authentication::aroundDispatch, which calls - // \Magento\Backend\Model\Auth\Session::prolong, which kicks off the around plugin - // \Magento\Security\Model\Plugin\AuthSession::aroundProlong, which calls - // this method. - - // this method will prolong the session only if it's old enough, otherwise it's not called. -// if ($this->userExpirationManager->userIsExpired($this->authSession->getUser())) { -// $this->userExpirationManager->deactivateExpiredUsers([$this->authSession->getUser()->getId()]); -// } - if ($this->lastProlongIsOldEnough()) { - // TODO: throw exception? - if ($this->userExpirationManager->userIsExpired($this->authSession->getUser())) { - $this->userExpirationManager->deactivateExpiredUsers([$this->authSession->getUser()->getId()]); - } $this->getCurrentSession()->setData( 'updated_at', date( @@ -342,8 +305,6 @@ protected function createNewSession() } /** - * Create admin session info collection. - * * @return \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection * @since 100.1.0 */ @@ -354,7 +315,8 @@ protected function createAdminSessionInfoCollection() /** * Calculates diff between now and last session updated_at - * and decides whether new prolong must be triggered or not. + * and decides whether new prolong must be triggered or not + * * This is done to limit amount of session prolongs and updates to database * within some period of time - X * X - is calculated in getIntervalBetweenConsecutiveProlongs() diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index 01203caaa31cd..007f0871f70d1 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -7,6 +7,7 @@ use Magento\Backend\Model\Auth\Session; use Magento\Security\Model\AdminSessionsManager; +use Magento\Security\Model\UserExpirationManager; /** * Magento\Backend\Model\Auth\Session decorator @@ -33,22 +34,32 @@ class AuthSession */ protected $securityCookie; + /** + * @var UserExpirationManager + */ + private $userExpirationManager; + /** * @param \Magento\Framework\App\RequestInterface $request * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param AdminSessionsManager $sessionsManager * @param \Magento\Security\Model\SecurityCookie $securityCookie + * @param UserExpirationManager|null $userExpirationManager */ public function __construct( \Magento\Framework\App\RequestInterface $request, \Magento\Framework\Message\ManagerInterface $messageManager, AdminSessionsManager $sessionsManager, - \Magento\Security\Model\SecurityCookie $securityCookie + \Magento\Security\Model\SecurityCookie $securityCookie, + \Magento\Security\Model\UserExpirationManager $userExpirationManager = null ) { $this->request = $request; $this->messageManager = $messageManager; $this->sessionsManager = $sessionsManager; $this->securityCookie = $securityCookie; + $this->userExpirationManager = $userExpirationManager ?: + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Security\Model\UserExpirationManager::class); } /** @@ -60,7 +71,8 @@ public function __construct( */ public function aroundProlong(Session $session, \Closure $proceed) { - if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus()) { + if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus() || + $this->userExpirationManager->userIsExpired($session->getUser())) { $session->destroy(); $this->addUserLogoutNotification(); return null; diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index bd10b55e1b36d..8204295e2ed23 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -71,24 +71,6 @@ public function __construct( $this->userCollectionFactory = $userCollectionFactory; } - /** - * Revoke admin tokens for expired users. - * TODO: any better way than looping? - * TODO: remove - * @param \Magento\User\Model\User $user - * @throws \Exception - */ - public function deactivateExpiredUser(\Magento\User\Model\User $user): void - { - $currentSessions = $this->getSessionsForUser($user); - $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) - ->save(); - $user - ->setIsActive(0) - ->save(); - // TODO: remove expires_at record from new table - } - /** * Deactivate expired user accounts and invalidate their sessions. * @@ -122,23 +104,6 @@ public function deactivateExpiredUsers(?array $userIds = null): void $users->setDataToAll('is_active', 0)->save(); } - /** - * Get sessions for the given user. - * TODO: remove - * @param \Magento\User\Model\User $user - * @return ResourceModel\AdminSessionInfo\Collection - */ - private function getSessionsForUser(\Magento\User\Model\User $user) - { - $collection = $this->adminSessionInfoCollectionFactory->create(); - $collection - ->filterByUser($user->getId(), \Magento\Security\Model\AdminSessionInfo::LOGGED_IN) - ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()) - ->loadData(); - - return $collection; - } - /** * Check if the given user is expired. * // TODO: check users expired an hour ago (timezone stuff) @@ -150,30 +115,12 @@ public function userIsExpired(\Magento\User\Model\User $user): bool $isExpired = false; $expiredRecord = $this->userExpirationCollectionFactory->create() ->addExpiredRecordsForUserFilter($user->getId()) - ->getFirstItem(); // expiresAt: 1561824907, current timestamp: 1561824932 + ->getFirstItem(); if ($expiredRecord && $expiredRecord->getId()) { - //$expiresAt = $this->dateTime->gmtTimestamp($expiredRecord->getExpiredAt()); $expiresAt = $this->dateTime->timestamp($expiredRecord->getExpiresAt()); $isExpired = $expiresAt < $this->dateTime->gmtTimestamp(); } return $isExpired; } - - /** - * Check if the current user is expired and, if so, revoke their admin token. - */ - // private function revokeExpiredAdminUser() - // { - // $expiresAt = $this->dateTime->gmtTimestamp($this->authSession->getUser()->getExpiresAt()); - // if ($expiresAt < $this->dateTime->gmtTimestamp()) { - // $currentSessions = $this->getSessionsForCurrentUser(); - // $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) - // ->save(); - // $this->authSession->getUser() - // ->setIsActive(0) - // ->setExpiresAt(null) - // ->save(); - // } - // } } diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index 0f7f590b71de4..19c845a66bd86 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -37,6 +37,12 @@ class AuthSessionTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected $objectManager; + /**@var \Magento\Security\Model\UserExpirationManager */ + protected $userExpirationManagerMock; + + /**@var \Magento\User\Model\User */ + protected $userMock; + /** * Init mocks for tests * @return void @@ -61,20 +67,31 @@ public function setUp() $this->securityCookieMock = $this->createPartialMock(SecurityCookie::class, ['setLogoutReasonCookie']); - $this->authSessionMock = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['destroy']); + $this->authSessionMock = $this->createPartialMock( + \Magento\Backend\Model\Auth\Session::class, + ['destroy', 'getUser'] + ); $this->currentSessionMock = $this->createPartialMock( \Magento\Security\Model\AdminSessionInfo::class, ['isLoggedInStatus', 'getStatus', 'isActive'] ); + $this->userExpirationManagerMock = $this->createPartialMock( + \Magento\Security\Model\UserExpirationManager::class, + ['userIsExpired'] + ); + + $this->userMock = $this->createMock(\Magento\User\Model\User::class); + $this->model = $this->objectManager->getObject( \Magento\Security\Model\Plugin\AuthSession::class, [ 'request' => $this->requestMock, 'messageManager' => $this->messageManagerMock, 'sessionsManager' => $this->adminSessionsManagerMock, - 'securityCookie' => $this->securityCookieMock + 'securityCookie' => $this->securityCookieMock, + 'userExpirationManager' => $this->userExpirationManagerMock, ] ); @@ -154,6 +171,50 @@ public function testAroundProlongSessionIsNotActiveAndIsAjaxRequest() $this->model->aroundProlong($this->authSessionMock, $proceed); } + /** + * @return void + */ + public function testAroundProlongSessionIsActiveUserIsExpired() + { + $result = 'result'; + $errorMessage = 'Error Message'; + + $proceed = function () use ($result) { + return $result; + }; + + $this->currentSessionMock->expects($this->once()) + ->method('isLoggedInStatus') + ->willReturn(true); + + $this->authSessionMock->expects($this->once()) + ->method('getUser') + ->willReturn($this->userMock); + + $this->userExpirationManagerMock->expects($this->once()) + ->method('userIsExpired') + ->with($this->userMock) + ->willReturn(true); + + $this->authSessionMock->expects($this->once()) + ->method('destroy'); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('isAjax') + ->willReturn(false); + + $this->adminSessionsManagerMock->expects($this->once()) + ->method('getLogoutReasonMessage') + ->willReturn($errorMessage); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($errorMessage); + + $this->model->aroundProlong($this->authSessionMock, $proceed); + } + /** * @return void */ @@ -168,6 +229,15 @@ public function testAroundProlongSessionIsActive() ->method('isLoggedInStatus') ->willReturn(true); + $this->authSessionMock->expects($this->once()) + ->method('getUser') + ->willReturn($this->userMock); + + $this->userExpirationManagerMock->expects($this->once()) + ->method('userIsExpired') + ->with($this->userMock) + ->willReturn(false); + $this->adminSessionsManagerMock->expects($this->any()) ->method('processProlong'); diff --git a/app/code/Magento/Security/etc/adminhtml/events.xml b/app/code/Magento/Security/etc/events.xml similarity index 82% rename from app/code/Magento/Security/etc/adminhtml/events.xml rename to app/code/Magento/Security/etc/events.xml index 1bd8e9807c05f..640fcf7b3b20e 100644 --- a/app/code/Magento/Security/etc/adminhtml/events.xml +++ b/app/code/Magento/Security/etc/events.xml @@ -1,7 +1,6 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="admin_user_save_after"> <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserSave"/> </event> @@ -9,6 +8,6 @@ <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserLoad"/> </event> <event name="admin_user_authenticate_before"> - <observer name="captcha" instance="Magento\Security\Observer\BeforeAdminUserAuthenticate"/> + <observer name="check_user_expiration" instance="Magento\Security\Observer\BeforeAdminUserAuthenticate"/> </event> </config> diff --git a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php index 21a9f6d942dc3..68e049804e95a 100644 --- a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php @@ -60,7 +60,6 @@ public function testCreateAdminAccessToken() } /** - * TODO: fix failing test * @magentoDataFixture Magento/Security/_files/expired_users.php * @expectedException \Magento\Framework\Exception\AuthenticationException */ diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php index 74cefb9d41a59..a201dbfdf1b03 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php @@ -3,16 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Security\Model; -/** - * Class AdminSessionsManagerTest - * TODO: test AdminSessionsManager::processLogin - * TODO: test AdminSessionsManager::processProlong - * - * @package Magento\Security\Model - */ class AdminSessionsManagerTest extends \PHPUnit\Framework\TestCase { /** @@ -69,8 +61,8 @@ protected function setUp() protected function tearDown() { $this->auth = null; - $this->authSession = null; - $this->adminSessionInfo = null; + $this->authSession = null; + $this->adminSessionInfo = null; $this->adminSessionsManager = null; $this->objectManager = null; parent::tearDown(); @@ -133,23 +125,10 @@ public function testTerminateOtherSessionsProcessLogin() $session->load('669e2e3d752e8', 'session_id'); $this->assertEquals( AdminSessionInfo::LOGGED_OUT_BY_LOGIN, - (int)$session->getStatus() + (int) $session->getStatus() ); } - /** - * Test that expired users cannot login. - * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedException \Magento\Framework\Exception\AuthenticationException - * @magentoDataFixture Magento/Security/_files/expired_users.php - * @magentoDbIsolation enabled - */ - public function testExpiredUserProcessLogin() - { - static::markTestSkipped('to implement'); - } - /** * Test if current session is retrieved * diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index 52268dc96d8a3..a112ba19a6008 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -140,4 +140,39 @@ public function testProcessProlong() $this->assertGreaterThan(strtotime($oldUpdatedAt), strtotime($updatedAt)); } + + /** + */ + public function testProcessProlongWithExpiredUser() + { + $this->auth->login( + \Magento\TestFramework\Bootstrap::ADMIN_NAME, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); + $userExpirationFactory = $this->objectManager->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->setId($user->getId()) + ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) + ->save(); + + // need to trigger a prolong + $sessionId = $this->authSession->getSessionId(); + $prolongsDiff = 4 * log($this->securityConfig->getAdminSessionLifetime()) + 2; + $dateInPast = $this->dateTime->formatDate($this->authSession->getUpdatedAt() - $prolongsDiff); + $this->adminSessionsManager->getCurrentSession() + ->setData( + 'updated_at', + $dateInPast + ) + ->save(); + $this->adminSessionInfo->load($sessionId, 'session_id'); + $this->authSession->prolong(); + static::assertFalse($this->auth->isLoggedIn()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 1840748444842..617837c9eaaf2 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -11,11 +11,7 @@ /** * TODO: test logging out sessions - * TODO: test that you cannot create a token for an expired user (where would I put this test?) - * TODO: test AdminSessionsManager::processLogin - * TODO: test AdminSessionsManager::processProlong (do it here or in the AdminSessionsManagerTest?) * - * @magentoAppArea adminhtml */ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php index 0fcd90913e562..37c2173f675ae 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php @@ -10,9 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Test for \Magento\Security\Observer\AfterAdminUserLoad - * - * @magentoAppArea adminhtml + * Test for \Magento\Security\Observer\AfterAdminUserLoad * */ class AfterAdminUserLoadTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php index 6032b651b4dc3..75b2e1ca2fc32 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php @@ -11,8 +11,6 @@ /** * Test for \Magento\Security\Observer\AfterAdminUserSave - * - * @magentoAppArea adminhtml */ class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php index db7a83877d1b2..9a8f66eb9ef65 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php @@ -11,7 +11,6 @@ /** * Test for \Magento\Security\Observer\BeforeAdminUserAuthenticate - * @magentoAppArea adminhtml */ class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase { From b407c8cbd87fdc04690d08962e7ceeb7be3dd620 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 30 Jun 2019 19:22:31 -0400 Subject: [PATCH 039/734] fix crontab definition, copyright (#22833: Short-term admin accounts) --- .../Test/Unit/Model/AdminSessionsManagerTest.php | 15 ++++----------- app/code/Magento/Security/etc/crontab.xml | 2 +- app/code/Magento/Security/etc/events.xml | 6 ++++++ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php index a062cad9d61da..be0bdaecf8de3 100644 --- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionsManagerTest.php @@ -50,7 +50,7 @@ class AdminSessionsManagerTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected $objectManager; - /** + /* * @var RemoteAddress */ protected $remoteAddressMock; @@ -91,9 +91,7 @@ public function setUp() ['create'] ); - $this->currentSessionMock = $this->createPartialMock( - \Magento\Security\Model\AdminSessionInfo::class, - [ + $this->currentSessionMock = $this->createPartialMock(\Magento\Security\Model\AdminSessionInfo::class, [ 'isActive', 'getStatus', 'load', @@ -103,8 +101,7 @@ public function setUp() 'getUserId', 'getSessionId', 'getUpdatedAt' - ] - ); + ]); $this->securityConfigMock = $this->getMockBuilder(\Magento\Security\Model\ConfigInterface::class) ->disableOriginalConstructor() @@ -154,7 +151,7 @@ public function testProcessLogin() ->method('getSessionId') ->willReturn($sessionId); - $this->authSessionMock->expects($this->any()) + $this->authSessionMock->expects($this->once()) ->method('getUser') ->willReturn($this->userMock); $this->userMock->expects($this->once()) @@ -256,10 +253,6 @@ public function testProcessProlong() ->method('save') ->willReturnSelf(); - $this->authSessionMock->expects($this->once()) - ->method('getUser') - ->willReturn($this->userMock); - $this->model->processProlong(); } diff --git a/app/code/Magento/Security/etc/crontab.xml b/app/code/Magento/Security/etc/crontab.xml index 203cf5c2a2389..5944ab41fc247 100644 --- a/app/code/Magento/Security/etc/crontab.xml +++ b/app/code/Magento/Security/etc/crontab.xml @@ -13,7 +13,7 @@ <job name="security_clean_password_reset_request_event_records" instance="Magento\Security\Model\SecurityManager" method="cleanExpiredRecords"> <schedule>0 0 * * *</schedule> </job> - <job name="security_expire_users" instance="Magento\Security\DisableExpiredUsers" method="deactivateExpiredUsers"> + <job name="security_expire_users" instance="Magento\Security\Model\UserExpirationManager" method="deactivateExpiredUsers"> <schedule>0 * * * *</schedule> </job> </group> diff --git a/app/code/Magento/Security/etc/events.xml b/app/code/Magento/Security/etc/events.xml index 640fcf7b3b20e..63c35d5c31a6f 100644 --- a/app/code/Magento/Security/etc/events.xml +++ b/app/code/Magento/Security/etc/events.xml @@ -1,4 +1,10 @@ <?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="admin_user_save_after"> From 47a9ed7db12ca908bf67ef0d4af17eb91addd19d Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Thu, 4 Jul 2019 12:56:32 -0400 Subject: [PATCH 040/734] Refactor observers, update tests (magento/magento2#22833: Short-term admin accounts) --- .../Model/Plugin/UserValidationRules.php | 2 + .../Model/ResourceModel/UserExpiration.php | 2 + .../UserExpiration/Collection.php | 11 +- .../Security/Model/UserExpirationManager.php | 3 + ...te.php => AdminUserAuthenticateBefore.php} | 12 +- .../Security/Observer/AfterAdminUserLoad.php | 8 + .../Security/Observer/AfterAdminUserSave.php | 8 + .../BeforeAdminUserAuthenticateTest.php | 6 +- app/code/Magento/Security/etc/crontab.xml | 2 +- app/code/Magento/Security/etc/events.xml | 2 +- .../Security/Model/Plugin/AuthSessionTest.php | 3 + .../UserExpiration/CollectionTest.php | 22 ++- .../Model/UserExpirationManagerTest.php | 185 +++++++++++------- ...hp => AdminUserAuthenticateBeforeTest.php} | 4 +- .../Observer/AfterAdminUserLoadTest.php | 1 - .../Magento/Security/_files/expired_users.php | 13 -- 16 files changed, 185 insertions(+), 99 deletions(-) rename app/code/Magento/Security/Observer/{BeforeAdminUserAuthenticate.php => AdminUserAuthenticateBefore.php} (83%) rename dev/tests/integration/testsuite/Magento/Security/Observer/{BeforeAdminUserAuthenticateTest.php => AdminUserAuthenticateBeforeTest.php} (92%) diff --git a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php index 75b826dfb6b65..f400d962644d6 100644 --- a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php +++ b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php @@ -28,6 +28,8 @@ public function __construct(\Magento\Security\Model\UserExpiration\Validator $va } /** + * Add the Expires At validator to user validation rules. + * * @param \Magento\User\Model\UserValidationRules $userValidationRules * @param \Magento\Framework\Validator\DataObject $result * @return \Magento\Framework\Validator\DataObject diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index 5afca619c3f7f..5702b6ce65725 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -21,6 +21,8 @@ class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractD protected $_isPkAutoIncrement = false; /** + * Define main table + * * @return void */ protected function _construct() diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php index 08c72f7d9fd2f..dee92c599f4de 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php @@ -18,6 +18,8 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab protected $_idFieldName = 'user_id'; /** + * Initialize collection + * * @return void */ protected function _construct() @@ -53,7 +55,8 @@ public function addActiveExpiredUsersFilter($now = null): Collection /** * Filter collection by user id. - * @param array $userIds + * + * @param int[] $userIds * @return Collection */ public function addUserIdsFilter($userIds = []): Collection @@ -64,12 +67,12 @@ public function addUserIdsFilter($userIds = []): Collection /** * Get any expired records for the given user. * - * @param $userId + * @param string $userId * @return Collection */ - public function addExpiredRecordsForUserFilter($userId): Collection + public function addExpiredRecordsForUserFilter(string $userId): Collection { return $this->addActiveExpiredUsersFilter() - ->addFieldToFilter('main_table.user_id', $userId); + ->addFieldToFilter('main_table.user_id', (int)$userId); } } diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index 8204295e2ed23..8d0bab3eefa5f 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -90,6 +90,7 @@ public function deactivateExpiredUsers(?array $userIds = null): void $currentSessions = $this->adminSessionInfoCollectionFactory->create() ->addFieldToFilter('user_id', ['in' => $expiredRecords->getAllIds()]) ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); + /** @var \Magento\Security\Model\AdminSessionInfo $currentSession */ $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) ->save(); } @@ -107,12 +108,14 @@ public function deactivateExpiredUsers(?array $userIds = null): void /** * Check if the given user is expired. * // TODO: check users expired an hour ago (timezone stuff) + * * @param \Magento\User\Model\User $user * @return bool */ public function userIsExpired(\Magento\User\Model\User $user): bool { $isExpired = false; + /** @var \Magento\Security\Model\UserExpiration $expiredRecord */ $expiredRecord = $this->userExpirationCollectionFactory->create() ->addExpiredRecordsForUserFilter($user->getId()) ->getFirstItem(); diff --git a/app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php similarity index 83% rename from app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php rename to app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index 1c888ec511646..0aa9e2566a789 100644 --- a/app/code/Magento/Security/Observer/BeforeAdminUserAuthenticate.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -13,11 +13,11 @@ use Magento\Security\Model\UserExpirationManager; /** - * Class BeforeAdminUserAuthenticate + * Check for expired users. * * @package Magento\Security\Observer */ -class BeforeAdminUserAuthenticate implements ObserverInterface +class AdminUserAuthenticateBefore implements ObserverInterface { /** * @var UserExpirationManager @@ -29,6 +29,12 @@ class BeforeAdminUserAuthenticate implements ObserverInterface */ private $user; + /** + * AdminUserAuthenticateBefore constructor. + * + * @param UserExpirationManager $userExpirationManager + * @param \Magento\User\Model\User $user + */ public function __construct( \Magento\Security\Model\UserExpirationManager $userExpirationManager, \Magento\User\Model\User $user @@ -38,6 +44,8 @@ public function __construct( } /** + * Check for expired user when logging in. + * * @param Observer $observer * @return void * @throws AuthenticationException diff --git a/app/code/Magento/Security/Observer/AfterAdminUserLoad.php b/app/code/Magento/Security/Observer/AfterAdminUserLoad.php index 4bc6804276c5c..133b26c2c01a2 100644 --- a/app/code/Magento/Security/Observer/AfterAdminUserLoad.php +++ b/app/code/Magento/Security/Observer/AfterAdminUserLoad.php @@ -25,6 +25,12 @@ class AfterAdminUserLoad implements ObserverInterface */ private $userExpirationResource; + /** + * AfterAdminUserLoad constructor. + * + * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory + * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + */ public function __construct( \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource @@ -35,6 +41,8 @@ public function __construct( } /** + * Set the user expiration date onto the user. + * * @param Observer $observer * @return void */ diff --git a/app/code/Magento/Security/Observer/AfterAdminUserSave.php b/app/code/Magento/Security/Observer/AfterAdminUserSave.php index 37fb6af3ccaa6..247e88a720ceb 100644 --- a/app/code/Magento/Security/Observer/AfterAdminUserSave.php +++ b/app/code/Magento/Security/Observer/AfterAdminUserSave.php @@ -25,6 +25,12 @@ class AfterAdminUserSave implements ObserverInterface */ private $userExpirationResource; + /** + * AfterAdminUserSave constructor. + * + * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory + * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + */ public function __construct( \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource @@ -35,6 +41,8 @@ public function __construct( } /** + * Save user expiration. + * * @param Observer $observer * @return void */ diff --git a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php index 9df2a133ff2a9..263d7b5359b29 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php @@ -8,7 +8,7 @@ namespace Magento\Security\Test\Unit\Observer; /** - * Test for \Magento\Security\Observer\BeforeAdminUserAuthenticate + * Test for \Magento\Security\Observer\AdminUserAuthenticateBefore * * @package Magento\Security\Test\Unit\Observer */ @@ -30,7 +30,7 @@ class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase private $userMock; /** - * @var \Magento\Security\Observer\BeforeAdminUserAuthenticate + * @var \Magento\Security\Observer\AdminUserAuthenticateBefore */ private $observer; @@ -64,7 +64,7 @@ protected function setUp() ); $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['loadByUsername', 'getId']); $this->observer = $this->objectManager->getObject( - \Magento\Security\Observer\BeforeAdminUserAuthenticate::class, + \Magento\Security\Observer\AdminUserAuthenticateBefore::class, [ 'userExpirationManager' => $this->userExpirationManagerMock, 'user' => $this->userMock, diff --git a/app/code/Magento/Security/etc/crontab.xml b/app/code/Magento/Security/etc/crontab.xml index 5944ab41fc247..7ee046ee44850 100644 --- a/app/code/Magento/Security/etc/crontab.xml +++ b/app/code/Magento/Security/etc/crontab.xml @@ -13,7 +13,7 @@ <job name="security_clean_password_reset_request_event_records" instance="Magento\Security\Model\SecurityManager" method="cleanExpiredRecords"> <schedule>0 0 * * *</schedule> </job> - <job name="security_expire_users" instance="Magento\Security\Model\UserExpirationManager" method="deactivateExpiredUsers"> + <job name="security_deactivate_expired_users" instance="Magento\Security\Model\UserExpirationManager" method="deactivateExpiredUsers"> <schedule>0 * * * *</schedule> </job> </group> diff --git a/app/code/Magento/Security/etc/events.xml b/app/code/Magento/Security/etc/events.xml index 63c35d5c31a6f..9db9a67947ca8 100644 --- a/app/code/Magento/Security/etc/events.xml +++ b/app/code/Magento/Security/etc/events.xml @@ -14,6 +14,6 @@ <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserLoad"/> </event> <event name="admin_user_authenticate_before"> - <observer name="check_user_expiration" instance="Magento\Security\Observer\BeforeAdminUserAuthenticate"/> + <observer name="check_user_expiration" instance="Magento\Security\Observer\AdminUserAuthenticateBefore"/> </event> </config> diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index a112ba19a6008..1d86c40018659 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -142,6 +142,9 @@ public function testProcessProlong() } /** + * Test processing prolong with an expired user. + * + * @magentoDbIsolation enabled */ public function testProcessProlongWithExpiredUser() { diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php index 63f1c377f99cc..b78ed9dc82377 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php @@ -38,18 +38,32 @@ protected function setUp() /** * @magentoDataFixture Magento/Security/_files/expired_users.php */ - public function testExpiredActiveUsersFilter() + public function testAddExpiredActiveUsersFilter() { /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ $collectionModel = $this->collectionModelFactory->create(); $collectionModel->addActiveExpiredUsersFilter(); - static::assertGreaterThanOrEqual(1, $collectionModel->getSize()); + static::assertEquals(1, $collectionModel->getSize()); } /** * @magentoDataFixture Magento/Security/_files/expired_users.php */ - public function testGetExpiredRecordsForUser() + public function testAddUserIdsFilter() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create()->addUserIdsFilter([$user->getId()]); + static::assertEquals(1, $collectionModel->getSize()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testAddExpiredRecordsForUserFilter() { $adminUserNameFromFixture = 'adminUserExpired'; $user = $this->objectManager->create(\Magento\User\Model\User::class); @@ -57,6 +71,6 @@ public function testGetExpiredRecordsForUser() /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ $collectionModel = $this->collectionModelFactory->create()->addExpiredRecordsForUserFilter($user->getId()); - static::assertGreaterThanOrEqual(1, $collectionModel->getSize()); + static::assertEquals(1, $collectionModel->getSize()); } } diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 617837c9eaaf2..e07c539d2b49a 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -7,26 +7,70 @@ namespace Magento\Security\Model; -use Magento\TestFramework\Helper\Bootstrap; - /** * TODO: test logging out sessions - * */ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var \Magento\Backend\Model\Auth + */ + private $auth; + + /** + * @var \Magento\Backend\Model\Auth\Session + */ + private $authSession; + + /** + * @var \Magento\Security\Model\AdminSessionInfo + */ + private $adminSessionInfo; + + /** + * @var \Magento\Security\Model\UserExpirationManager + */ + private $userExpirationManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager->get(\Magento\Framework\Config\ScopeInterface::class) + ->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); + $this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class); + $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class); + $this->auth->setAuthStorage($this->authSession); + $this->adminSessionInfo = $this->objectManager->create(\Magento\Security\Model\AdminSessionInfo::class); + $this->userExpirationManager = + $this->objectManager->create(\Magento\Security\Model\UserExpirationManager::class); + } + + /** + * Tear down + */ + protected function tearDown() + { + $this->auth = null; + $this->authSession = null; + $this->adminSessionInfo = null; + $this->userExpirationManager = null; + $this->objectManager = null; + } /** * @magentoDataFixture Magento/Security/_files/expired_users.php */ public function testUserIsExpired() { + static::markTestSkipped(); $adminUserNameFromFixture = 'adminUserExpired'; - $user = $this->getUserFromUserName($adminUserNameFromFixture); - /** @var \Magento\Security\Model\UserExpirationManager $userExpirationManager */ - $userExpirationManager = Bootstrap::getObjectManager() - ->create(\Magento\Security\Model\UserExpirationManager::class); - static::assertTrue($userExpirationManager->userIsExpired($user)); + $user = $this->loadUserByUsername($adminUserNameFromFixture); + static::assertTrue($this->userExpirationManager->userIsExpired($user)); } /** @@ -34,13 +78,19 @@ public function testUserIsExpired() */ public function testDeactivateExpiredUsersWithExpiredUser() { - $adminUserNameFromFixture = 'adminUserExpired'; - - list($user, $token, $expiredUserModel) = $this->setupCronTests($adminUserNameFromFixture); - + $adminUsernameFromFixture = 'adminUserNotExpired'; + $this->loginUser($adminUsernameFromFixture); + $user = $this->loadUserByUsername($adminUsernameFromFixture); + $sessionId = $this->authSession->getSessionId(); + $this->expireUser($user); + $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + $this->adminSessionInfo->load($sessionId, 'session_id'); + $user->reload(); + $userExpirationModel = $this->loadExpiredUserModelByUser($user); static::assertEquals(0, $user->getIsActive()); - static::assertEquals(null, $token->getId()); - static::assertNull($expiredUserModel->getId()); + static::assertNull($userExpirationModel->getId()); + static::assertEquals(AdminSessionInfo::LOGGED_OUT, (int)$this->adminSessionInfo->getStatus()); + $this->auth->logout(); } /** @@ -48,96 +98,95 @@ public function testDeactivateExpiredUsersWithExpiredUser() */ public function testDeactivateExpiredUsersWithNonExpiredUser() { - $adminUserNameFromFixture = 'adminUserNotExpired'; - // log them in - $adminToken = $this->createToken($adminUserNameFromFixture); - - list($user, $token, $expiredUserModel) = $this->setupCronTests($adminUserNameFromFixture); - + // TODO: login fails for the second test that tries to log a user in, doesn't matter which test + // it's trying to create a session for the user ID in the previous test + $adminUsernameFromFixture = 'adminUserNotExpired'; + $this->loginUser($adminUsernameFromFixture); + $user = $this->loadUserByUsername($adminUsernameFromFixture); + $sessionId = $this->authSession->getSessionId(); + $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + $user->reload(); + $userExpirationModel = $this->loadExpiredUserModelByUser($user); + $this->adminSessionInfo->load($sessionId, 'session_id'); static::assertEquals(1, $user->getIsActive()); - static::assertNotNull($token->getId()); - static::assertEquals($expiredUserModel->getUserId(), $user->getId()); + static::assertEquals($user->getId(), $userExpirationModel->getId()); + static::assertEquals(AdminSessionInfo::LOGGED_IN, (int)$this->adminSessionInfo->getStatus()); + $this->auth->logout(); } /** - * @param string $adminUserNameFromFixture - * @return array + * Test deactivating without inputting a user. + * + * @magentoDataFixture Magento/Security/_files/expired_users.php */ - private function setupCronTests(string $adminUserNameFromFixture): array + public function testDeactivateExpiredUsers() { - // TODO: set the user expired after calling this - // TODO: use this to test the observer with exception: - // Magento\Framework\Exception\Plugin\AuthenticationException : The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later. - //$adminToken = $this->createToken($adminUserNameFromFixture); // TODO: this logs the user in, which kicks off the deactivate call - - /** @var \Magento\Security\Model\UserExpirationManager $job */ - $userExpirationManager = Bootstrap::getObjectManager() - ->create(\Magento\Security\Model\UserExpirationManager::class); - $userExpirationManager->deactivateExpiredUsers(); - - /** @var \Magento\User\Model\User $user */ - $user = $this->getUserFromUserName($adminUserNameFromFixture); - - // this is for the API only - $oauthToken = $this->getOauthTokenByUser($user); - $expiredUserModel = $this->getExpiredUserModelByUser($user); - - return [$user, $oauthToken, $expiredUserModel]; + static::markTestSkipped(); + $notExpiredUser = $this->loadUserByUsername('adminUserNotExpired'); + $expiredUser = $this->loadUserByUsername('adminUserExpired'); + $this->userExpirationManager->deactivateExpiredUsers(); + $notExpiredUserExpirationModel = $this->loadExpiredUserModelByUser($notExpiredUser); + $expiredUserExpirationModel = $this->loadExpiredUserModelByUser($expiredUser); + + static::assertNotNull($notExpiredUserExpirationModel->getId()); + static::assertNull($expiredUserExpirationModel->getId()); + $notExpiredUser->reload(); + $expiredUser->reload(); + static::assertEquals($notExpiredUser->getIsActive(), 1); + static::assertEquals($expiredUser->getIsActive(), 0); } /** - * TODO: this calls user->login and throws an AuthenticationException + * Login the given user and return a user model. * - * @param string $adminUserNameFromFixture - * @return string + * @param string $username * @throws \Magento\Framework\Exception\AuthenticationException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\LocalizedException */ - private function createToken(string $adminUserNameFromFixture): string + private function loginUser(string $username) { - /** @var \Magento\Integration\Api\AdminTokenServiceInterface $tokenService */ - $tokenService = Bootstrap::getObjectManager()->get(\Magento\Integration\Api\AdminTokenServiceInterface::class); - $token = $tokenService->createAdminAccessToken( - $adminUserNameFromFixture, + $this->auth->login( + $username, \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD ); - - return $token; } /** - * @param string $adminUserNameFromFixture + * @param $username * @return \Magento\User\Model\User */ - private function getUserFromUserName(string $adminUserNameFromFixture): \Magento\User\Model\User + private function loadUserByUsername(string $username): \Magento\User\Model\User { /** @var \Magento\User\Model\User $user */ - $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername($adminUserNameFromFixture); + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($username); return $user; } /** + * Expire the given user and return the UserExpiration model. + * * @param \Magento\User\Model\User $user - * @return \Magento\Integration\Model\Oauth\Token + * @throws \Exception */ - private function getOauthTokenByUser(\Magento\User\Model\User $user): \Magento\Integration\Model\Oauth\Token + private function expireUser(\Magento\User\Model\User $user) { - /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ - $tokenModel = Bootstrap::getObjectManager()->get(\Magento\Integration\Model\Oauth\Token::class); - $oauthToken = $tokenModel->loadByAdminId($user->getId()); - return $oauthToken; + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->objectManager->create(\Magento\Security\Model\UserExpiration::class); + $userExpiration->setId($user->getId()) + ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) + ->save(); } /** * @param \Magento\User\Model\User $user - * @return UserExpiration + * @return \Magento\Security\Model\UserExpiration */ - private function getExpiredUserModelByUser(\Magento\User\Model\User $user): \Magento\Security\Model\UserExpiration + private function loadExpiredUserModelByUser(\Magento\User\Model\User $user): \Magento\Security\Model\UserExpiration { /** @var \Magento\Security\Model\UserExpiration $expiredUserModel */ - $expiredUserModel = Bootstrap::getObjectManager()->get(\Magento\Security\Model\UserExpiration::class); + $expiredUserModel = $this->objectManager->create(\Magento\Security\Model\UserExpiration::class); $expiredUserModel->load($user->getId()); return $expiredUserModel; } diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php similarity index 92% rename from dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php rename to dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php index 9a8f66eb9ef65..f25b836b80514 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/BeforeAdminUserAuthenticateTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php @@ -10,9 +10,9 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Test for \Magento\Security\Observer\BeforeAdminUserAuthenticate + * Test for \Magento\Security\Observer\AdminUserAuthenticateBefore */ -class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase +class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase { /** diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php index 37c2173f675ae..fa552f04b9f2e 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php @@ -43,5 +43,4 @@ public function testWithNonExpiredUser() $loadedUser->load($userId); static::assertNull($loadedUser->getExpiresAt()); } - } diff --git a/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php index 5a3971cd9d0bb..ac45f120ca6d3 100644 --- a/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php +++ b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php @@ -56,16 +56,3 @@ ->setId($userModelExpired->getId()) ->setExpiresAt($pastDate->format('Y-m-d H:i:s')) ->save(); - -// TODO: remove -// need to bypass model validation to set expired date -//$pastDate = new \DateTime(); -//$pastDate->modify('-10 days'); -//$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); -///** @var \Magento\Framework\DB\Adapter\AdapterInterface $conn */ -//$conn = $resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION); -//$conn->update( -// $resource->getTableName('admin_user_expiration'), -// ['expires_at' => $pastDate->format('Y-m-d H:i:s')], -// ['user_id = ?' => (int)$userModelExpired->getId()] -//); From 6407af86689bd905d1fb98d441147420e7f9ec6e Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 6 Jul 2019 13:51:30 -0400 Subject: [PATCH 041/734] handle dates with store time (magento/magento2#22833: Short-term admin accounts) --- .../Model/ResourceModel/UserExpiration.php | 60 ++++++++++++++++--- .../Magento/Security/Model/UserExpiration.php | 37 +----------- .../Model/UserExpiration/Validator.php | 14 ++++- .../Security/Model/UserExpirationManager.php | 3 +- .../Observer/AdminUserAuthenticateBefore.php | 2 +- .../Magento/User/Block/User/Edit/Tab/Main.php | 12 +++- .../Model/UserExpirationManagerTest.php | 4 +- 7 files changed, 76 insertions(+), 56 deletions(-) diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index 5702b6ce65725..21ee2bc077a2b 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -20,6 +20,35 @@ class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractD */ protected $_isPkAutoIncrement = false; + /** + * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface + */ + private $timezone; + + /** + * @var \Magento\Framework\Stdlib\DateTime\DateTime + */ + private $dateTime; + + /** + * UserExpiration constructor. + * + * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + * @param null $connectionName + */ + public function __construct( + \Magento\Framework\Model\ResourceModel\Db\Context $context, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, + $connectionName = null + ) { + parent::__construct($context, $connectionName); + $this->timezone = $timezone; + $this->dateTime = $dateTime; + } + /** * Define main table * @@ -31,19 +60,34 @@ protected function _construct() } /** - * Perform actions before object save + * Convert to UTC time. * - * @param \Magento\Framework\Model\AbstractModel $object + * @param \Magento\Framework\Model\AbstractModel $userExpiration * @return $this - * @throws \Magento\Framework\Exception\LocalizedException */ - public function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + protected function _beforeSave(\Magento\Framework\Model\AbstractModel $userExpiration) { - /** @var $object \Magento\Security\Model\UserExpiration */ - if ($object->getExpiresAt() instanceof \DateTimeInterface) { + /** @var $userExpiration \Magento\Security\Model\UserExpiration */ + $expiresAt = $userExpiration->getExpiresAt(); + $utcValue = $this->dateTime->gmtDate('Y-m-d H:i:s', $expiresAt); + $userExpiration->setExpiresAt($utcValue); + + return $this; + } - // TODO: use this? need to check if we're ever passing in a \DateTimeInterface or if it's always a string - $object->setExpiresAt($object->getExpiresAt()->format('Y-m-d H:i:s')); + /** + * Convert to store time. + * + * @param \Magento\Framework\Model\AbstractModel $userExpiration + * @return $this|\Magento\Framework\Model\ResourceModel\Db\AbstractDb + * @throws \Exception + */ + protected function _afterLoad(\Magento\Framework\Model\AbstractModel $userExpiration) + { + /** @var $userExpiration \Magento\Security\Model\UserExpiration */ + if ($userExpiration->getExpiresAt()) { + $storeValue = $this->timezone->date($userExpiration->getExpiresAt()); + $userExpiration->setExpiresAt($storeValue); } return $this; diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index eaf2259375f0a..0e70243f30117 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -10,38 +10,11 @@ /** * Admin User Expiration model. * @method string getExpiresAt() - * @method \Magento\Security\Model\UserExpiration setExpiresAt(string $value) + * @method \Magento\Security\Model\UserExpiration setExpiresAt($value) */ class UserExpiration extends \Magento\Framework\Model\AbstractModel { - /** - * @var UserExpiration\Validator - */ - private $validator; - - /** - * UserExpiration constructor. - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param UserExpiration\Validator $validator - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection - * @param array $data - */ - public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Security\Model\UserExpiration\Validator $validator, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] - ) { - parent::__construct($context, $registry, $resource, $resourceCollection, $data); - $this->validator = $validator; - } - /** * Resource initialization * @@ -51,12 +24,4 @@ protected function _construct() { $this->_init(\Magento\Security\Model\ResourceModel\UserExpiration::class); } - - /** - * TODO: remove and use a plugin on UserValidationRules - */ -// protected function _getValidationRulesBeforeSave() -// { -// return $this->validator; -// } } diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index 4b82f2ab43f07..20fca2215a899 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -16,6 +16,15 @@ */ class Validator extends AbstractValidator { + /**@var \Magento\Framework\Stdlib\DateTime\DateTime */ + private $dateTime; + + public function __construct( + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + ) { + + $this->dateTime = $dateTime; + } /** * Ensure that the given date is later than the current date. @@ -31,9 +40,8 @@ public function isValid($value) $expiresAt = $value; $label = 'Expiration date'; if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { - $currentTime = new \DateTime(); - $expiresAt = new \DateTime($expiresAt); - + $currentTime = $this->dateTime->gmtTimestamp(); + $expiresAt = $this->dateTime->gmtTimestamp($value); if ($expiresAt < $currentTime) { $messages['expires_at'] = __('"%1" must be later than the current date.', $label); } diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index 8d0bab3eefa5f..44e8226e391ed 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -10,7 +10,7 @@ use Magento\Security\Model\ResourceModel\UserExpiration\Collection as ExpiredUsersCollection; /** - * Class to handle expired admin users. + * Class to handle admin user expirations. */ class UserExpirationManager { @@ -107,7 +107,6 @@ public function deactivateExpiredUsers(?array $userIds = null): void /** * Check if the given user is expired. - * // TODO: check users expired an hour ago (timezone stuff) * * @param \Magento\User\Model\User $user * @return bool diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index 0aa9e2566a789..d40f25a023bbc 100644 --- a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -56,7 +56,7 @@ public function execute(Observer $observer) /** @var \Magento\User\Model\User $user */ $user = $this->user->loadByUsername($username); - if ($this->userExpirationManager->userIsExpired($user)) { + if ($user->getId() && $this->userExpirationManager->userIsExpired($user)) { $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); throw new AuthenticationException( __( diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index 85ac6898a27c3..c92714ec1eb5a 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -169,7 +169,13 @@ protected function _prepareForm() ] ); } - // TODO: use store time and convert to GMT + + $dateFormat = $this->_localeDate->getDateFormat( + \IntlDateFormatter::MEDIUM + ); + $timeFormat = $this->_localeDate->getTimeFormat( + \IntlDateFormatter::MEDIUM + ); $baseFieldset->addField( 'expires_at', 'date', @@ -177,8 +183,8 @@ protected function _prepareForm() 'name' => 'expires_at', 'label' => __('Expiration Date'), 'title' => __('Expiration Date'), - 'date_format' => 'yyyy-MM-dd', - 'time_format' => 'hh:mm:ss', + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, 'class' => 'validate-date', ] ); diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index e07c539d2b49a..a5142d041f0b0 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -44,8 +44,8 @@ protected function setUp() ->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); $this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class); $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class); - $this->auth->setAuthStorage($this->authSession); $this->adminSessionInfo = $this->objectManager->create(\Magento\Security\Model\AdminSessionInfo::class); + $this->auth->setAuthStorage($this->authSession); $this->userExpirationManager = $this->objectManager->create(\Magento\Security\Model\UserExpirationManager::class); } @@ -67,7 +67,6 @@ protected function tearDown() */ public function testUserIsExpired() { - static::markTestSkipped(); $adminUserNameFromFixture = 'adminUserExpired'; $user = $this->loadUserByUsername($adminUserNameFromFixture); static::assertTrue($this->userExpirationManager->userIsExpired($user)); @@ -121,7 +120,6 @@ public function testDeactivateExpiredUsersWithNonExpiredUser() */ public function testDeactivateExpiredUsers() { - static::markTestSkipped(); $notExpiredUser = $this->loadUserByUsername('adminUserNotExpired'); $expiredUser = $this->loadUserByUsername('adminUserExpired'); $this->userExpirationManager->deactivateExpiredUsers(); From 75664d49208141cdae4ccf976fc5e7456fb66552 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 8 Jul 2019 10:52:08 -0400 Subject: [PATCH 042/734] handle dates with store time (magento/magento2#22833: Short-term admin accounts) --- .../Model/UserExpiration/ValidatorTest.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php index 18e1eb3438bae..bde468c31ac24 100644 --- a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -22,16 +22,33 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase */ private $validator; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Stdlib\DateTime\DateTime + */ + private $dateTimeMock; + protected function setUp() { $objectManager = new ObjectManager($this); - $this->validator = $objectManager->getObject(\Magento\Security\Model\UserExpiration\Validator::class); + $this->dateTimeMock = + $this->createPartialMock(\Magento\Framework\Stdlib\DateTime\DateTime::class, ['gmtTimestamp']); + $this->validator = $objectManager->getObject( + \Magento\Security\Model\UserExpiration\Validator::class, + ['dateTime' => $this->dateTimeMock] + ); } public function testWithPastDate() { + $currentTime = '1562447687'; + $expireTime = '1561324487'; $testDate = new \DateTime(); $testDate->modify('-10 days'); + $this->dateTimeMock->expects(static::exactly(2))->method('gmtTimestamp') + ->willReturnOnConsecutiveCalls( + $currentTime, + $expireTime + ); static::assertFalse($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); static::assertContains( '"Expiration date" must be later than the current date.', @@ -41,8 +58,15 @@ public function testWithPastDate() public function testWithFutureDate() { + $currentTime = '1562447687'; + $expireTime = '1563290841'; $testDate = new \DateTime(); $testDate->modify('+10 days'); + $this->dateTimeMock->expects(static::exactly(2))->method('gmtTimestamp') + ->willReturnOnConsecutiveCalls( + $currentTime, + $expireTime + ); static::assertTrue($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); static::assertEquals([], $this->validator->getMessages()); } From c384fabb5cc324e8ee76ab832335368a96bf8640 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 12 Jul 2019 09:13:12 -0400 Subject: [PATCH 043/734] using new basebox --- pub/phpinfo.php | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pub/phpinfo.php diff --git a/pub/phpinfo.php b/pub/phpinfo.php new file mode 100644 index 0000000000000..e9a1e6e06f0e8 --- /dev/null +++ b/pub/phpinfo.php @@ -0,0 +1,3 @@ +<?php +// Show all information, defaults to INFO_ALL +phpinfo(); From 43f008d7da9b193d2730a552c3f6820688665660 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 24 Jul 2019 12:27:45 +0300 Subject: [PATCH 044/734] refactoring, review changes --- .../AdminCategoriesExpandAllActionGroup.xml | 16 ++++++++ ...AdminCategoriesOpenCategoryActionGroup.xml | 17 ++++++++ ...ategoriesOpenContentSectionActionGroup.xml | 15 +++++++ ...minCategoriesSetDisplayModeActionGroup.xml | 19 +++++++++ ...minCategoriesSetStaticBlockActionGroup.xml | 16 ++++++++ .../AdminOpenCategoriesPageActionGroup.xml | 14 +++++++ .../AdminSaveCategoryActionGroup.xml | 15 +++++++ .../AdminAddCmsBlockToCategoryActionGroup.xml | 39 ------------------- ...> AdminOpenNewCmsBlockPageActionGroup.xml} | 2 +- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 27 ++++++------- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 26 ++++++------- 11 files changed, 138 insertions(+), 68 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{AdminNavigateToCreateNewCMSBlockPageActionGroup.xml => AdminOpenNewCmsBlockPageActionGroup.xml} (85%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml new file mode 100644 index 0000000000000..2361354d5c7d2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminCategoriesExpandAllActionGroup"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + </arguments> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml new file mode 100644 index 0000000000000..3903615597eb3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AdminCategoriesOpenCategoryActionGroup"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + </arguments> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category.name)}}" stepKey="clickCategoryLink"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml new file mode 100644 index 0000000000000..edc3d11bebe66 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.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="AdminCategoriesOpenContentSectionActionGroup"> + <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForContentSectionLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml new file mode 100644 index 0000000000000..7e3877bb76ffc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesSetDisplayModeActionGroup"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <waitForElementVisible selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" stepKey="waitForDisplaySettingsSection"/> + <conditionalClick selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" dependentSelector="{{AdminCategoryDisplaySettingsSection.displayMode}}" visible="false" stepKey="openDisplaySettingsSection"/> + <waitForPageLoad stepKey="waitForDisplaySettingsLoad"/> + <selectOption stepKey="selectStaticBlockOnlyOption" userInput="{{value}}" selector="{{AdminCategoryDisplaySettingsSection.displayMode}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml new file mode 100644 index 0000000000000..1d5308a639d72 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminCategoriesSetStaticBlockActionGroup"> + <arguments> + <argument name="block" defaultValue="_defaultBlock"/> + </arguments> + <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" stepKey="selectBlock" userInput="{{block.title}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml new file mode 100644 index 0000000000000..7f41a0c4eb1a3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminOpenCategoriesPageActionGroup"> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageLoad" after="onCategoryIndexPage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml new file mode 100644 index 0000000000000..535a08382aabb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.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="AdminSaveCategoryActionGroup"> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> + <waitForPageLoad stepKey="waitForCategorySaved"/> + <see userInput="You saved the category." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml deleted file mode 100644 index 728a42e83277c..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminAddCmsBlockToCategoryActionGroup.xml +++ /dev/null @@ -1,39 +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="AdminAddCmsBlockToCategoryActionGroup"> - <arguments> - <argument name="category" defaultValue="_defaultCategory"/> - <argument name="block" defaultValue="_defaultBlock"/> - </arguments> - - <!--Go to Catalog > Categories (choose category)--> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryIndexPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoadAddProducts" after="onCategoryIndexPage"/> - <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll" after="waitForCategoryPageLoadAddProducts"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category.name)}}" stepKey="clickCategoryLink"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - - <!--Content > Add CMS Block: name of saved block--> - <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> - <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> - <waitForPageLoad stepKey="waitForContentLoad"/> - - <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" stepKey="selectBlock" userInput="{{block.title}}"/> - - <!--Display Settings > Display Mode: Static block only--> - <waitForElementVisible selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" stepKey="waitForDisplaySettingsSection"/> - <conditionalClick selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" dependentSelector="{{AdminCategoryDisplaySettingsSection.displayMode}}" visible="false" stepKey="openDisplaySettingsSection"/> - <waitForPageLoad stepKey="waitForDisplaySettingsLoad"/> - <selectOption stepKey="selectStaticBlockOnlyOption" userInput="Static block only" selector="{{AdminCategoryDisplaySettingsSection.displayMode}}"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> - <waitForPageLoad stepKey="waitForCategorySaved"/> - <see userInput="You saved the category." stepKey="seeSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml similarity index 85% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml index 2bb0d6dc58764..39e86973be4c9 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToCreateNewCMSBlockPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminNavigateToCreateNewCMSBlockPageActionGroup"> + <actionGroup name="AdminOpenNewCmsBlockPageActionGroup"> <amOnPage url="{{CmsNewBlock.url}}" stepKey="navigateToCreateNewCMSBlockPage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index 38d5bec9667b9..78286481f27e3 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -19,31 +19,32 @@ </annotations> <before> <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> - <argument name="Block" value="_defaultBlock"/> - </actionGroup> <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="AdminNavigateToCreateNewCMSBlockPageActionGroup" stepKey="navigateToCreateNewCMSBlockPage"/> - <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> + <actionGroup ref="AdminOpenCmsBlockActionGroup" stepKey="openCmsBlock"> + <argument name="block_id" value="$$newDefaultBlock.id$$"/> + </actionGroup> <actionGroup ref="AdminSetCMSBlockDisabledActionGroup" stepKey="disableBlock"/> <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> - <actionGroup ref="AdminOpenCMSBlocksGridActionGroup" stepKey="openCMSBlocksGrid"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilters"/> - <actionGroup ref="SortByIdDescendingActionGroup" stepKey="sortGridByIdDescending"/> - <actionGroup ref="AdminSelectCMSBlockFromGridActionGroup" stepKey="verifyBlockInGrid"> - <argument name="identifier" value="_defaultBlock.identifier"/> - </actionGroup> - - <actionGroup ref="AdminAddCmsBlockToCategoryActionGroup" stepKey="addCmsBlockToCategory"> + <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> + <actionGroup ref="AdminCategoriesExpandAllActionGroup" stepKey="expandAll"/> + <actionGroup ref="AdminCategoriesOpenCategoryActionGroup" stepKey="openCategory"> <argument name="category" value="$$newDefaultCategory$$"/> </actionGroup> + <actionGroup ref="AdminCategoriesOpenContentSectionActionGroup" stepKey="openContentSection"/> + <actionGroup ref="AdminCategoriesSetStaticBlockActionGroup" stepKey="setStaticBlock"/> + <actionGroup ref="AdminCategoriesSetDisplayModeActionGroup" stepKey="setDisplay"> + <argument name="value" value="Static block only"/> + </actionGroup> + <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> <actionGroup ref="AssertNoTextOnCategoryFrontPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml index d4e15c1456712..9f52720a774f8 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -19,30 +19,26 @@ </annotations> <before> <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> - <argument name="Block" value="_defaultBlock"/> - </actionGroup> <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="AdminNavigateToCreateNewCMSBlockPageActionGroup" stepKey="navigateToCreateNewCMSBlockPage"/> - <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> - <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> - - <actionGroup ref="AdminOpenCMSBlocksGridActionGroup" stepKey="openCMSBlocksGrid"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilters"/> - <actionGroup ref="SortByIdDescendingActionGroup" stepKey="sortGridByIdDescending"/> - <actionGroup ref="AdminSelectCMSBlockFromGridActionGroup" stepKey="verifyBlockInGrid"> - <argument name="identifier" value="_defaultBlock.identifier"/> - </actionGroup> - - <actionGroup ref="AdminAddCmsBlockToCategoryActionGroup" stepKey="addCmsBlockToCategory"> + <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> + <actionGroup ref="AdminCategoriesExpandAllActionGroup" stepKey="expandAll"/> + <actionGroup ref="AdminCategoriesOpenCategoryActionGroup" stepKey="openCategory"> <argument name="category" value="$$newDefaultCategory$$"/> </actionGroup> + <actionGroup ref="AdminCategoriesOpenContentSectionActionGroup" stepKey="openContentSection"/> + <actionGroup ref="AdminCategoriesSetStaticBlockActionGroup" stepKey="setStaticBlock"/> + <actionGroup ref="AdminCategoriesSetDisplayModeActionGroup" stepKey="setDisplay"> + <argument name="value" value="Static block only"/> + </actionGroup> + <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> <actionGroup ref="AssertTextOnCategoryFronPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> From 3a27f1cb4e874be8f078ddca771bd1ffff5b89a3 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Fri, 26 Jul 2019 13:03:39 +0300 Subject: [PATCH 045/734] Add test for admin user delete functionality --- .../AdminOpenUserEditPageActionGroup.xml | 24 ++++++ .../AdminUpdateUserRoleActionGroup.xml | 26 ++++++ ...ossibleDeleteYourOwnAccountActionGroup.xml | 18 ++++ .../AssertUserInGridActionGroup.xml | 22 +++++ .../AssertUserNotInGridActionGroup.xml | 22 +++++ .../User/Test/Mftf/Data/UserRoleData.xml | 5 ++ .../Mftf/Section/AdminUserGridSection.xml | 1 + .../Test/AdminDeleteAdminUserEntityTest.xml | 83 +++++++++++++++++++ .../TestCase/DeleteAdminUserEntityTest.xml | 2 + 9 files changed, 203 insertions(+) create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml new file mode 100644 index 0000000000000..02d85d9a392f3 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.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="AdminOpenUserEditPageActionGroup"> + <arguments> + <argument name="user" type="entity"/> + </arguments> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid"/> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> + <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> + <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml new file mode 100644 index 0000000000000..89c7a08188bd4 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml @@ -0,0 +1,26 @@ +<?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="AdminUpdateUserRoleActionGroup"> + <arguments> + <argument name="role" type="entity"/> + </arguments> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> + <click selector="{{AdminNewUserFormSection.userRoleTab}}" stepKey="openUserRoleTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened"/> + <click selector="{{AdminNewUserFormSection.resetFilter}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminNewUserFormSection.roleFilterField}}" userInput="{{role.name}}" stepKey="fillRoleFilterField"/> + <click selector="{{AdminNewUserFormSection.search}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForFiltersApplied"/> + <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(role.name)}}" stepKey="assignRole"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml new file mode 100644 index 0000000000000..6659773f69639 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AssertImpossibleDeleteYourOwnAccountActionGroup"> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteUser"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForElementVisible selector="{{AdminMessagesSection.error}}" stepKey="waitErrorMessage"/> + <see selector="{{AdminMessagesSection.error}}" userInput="You cannot delete your own account." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml new file mode 100644 index 0000000000000..3b60f26a56809 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertUserInGridActionGroup"> + <arguments> + <argument name="userName" type="string"/> + </arguments> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUsersGrid"/> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{userName}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{userName}}" stepKey="seeUser"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml new file mode 100644 index 0000000000000..464c278813238 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertUserNotInGridActionGroup"> + <arguments> + <argument name="userName" type="string"/> + </arguments> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUsersGrid"/> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{userName}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 1c18ca13b9731..eb17530fcf561 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -24,4 +24,9 @@ <data key="resourceAccess">Custom</data> <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> + <entity name="restrictedRole" type="role"> + <data key="name" unique="suffix">Restricted Role </data> + <data key="resourceAccess">Custom</data> + <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings', 'Magento_User::acl', 'Magento_User::acl_users', 'Magento_User::locks', 'Magento_User::acl_roles']</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml index 32e834615a270..76a19156698a4 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml @@ -13,6 +13,7 @@ <element name="usernameInFirstRow" type="text" selector=".col-username"/> <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> <element name="successMessage" type="text" selector=".message-success"/> + <element name="emptyRecords" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> </section> <section name="AdminDeleteUserSection"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml new file mode 100644 index 0000000000000..cc01578bebcf8 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteAdminUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Delete Admin User"/> + <title value="Admin user is not able to delete the own account"/> + <description value="Admin user is not able to delete the own account"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <!--Create New Role--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="restrictedRole"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> + + <!--Assign New Role--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"> + <argument name="role" value="restrictedRole"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> + <actionGroup ref="logout" stepKey="logOutAsDefaultAdminUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <argument name="adminUser" value="NewAdminUser"/> + </actionGroup> + + <!--Assert Impossible Delete Your Own Account--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPageForDeleting"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AssertImpossibleDeleteYourOwnAccountActionGroup" stepKey="assertErrorMessage"/> + <actionGroup ref="AssertUserInGridActionGroup" stepKey="assertUserInGrid"> + <argument name="userName" value="{{NewAdminUser.username}}"/> + </actionGroup> + + <actionGroup ref="logout" stepKey="logOutAsNewUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + + <!--Delete New Role--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> + <argument name="roleName" value="{{restrictedRole.name}}"/> + </actionGroup> + + <!--Delete New User--> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the user."/> + </actionGroup> + <actionGroup ref="AssertUserNotInGridActionGroup" stepKey="assertUserNotInGrid"> + <argument name="userName" value="{{NewAdminUser.username}}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml index 2d44f86a17fe1..58e3fc4e76b30 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml @@ -12,12 +12,14 @@ <data name="systemAdmin/dataset" xsi:type="string">system_admin</data> <constraint name="Magento\User\Test\Constraint\AssertImpossibleDeleteYourOwnAccount" /> <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="DeleteAdminUserEntityTestVariation2"> <data name="isDefaultUser" xsi:type="string">1</data> <data name="systemAdmin/dataset" xsi:type="string">system_admin</data> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessDeleteMessage" /> <constraint name="Magento\User\Test\Constraint\AssertUserNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From aeeffe43622551587b63e09625fa5eeb2b3aed09 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Fri, 26 Jul 2019 15:47:42 +0300 Subject: [PATCH 046/734] Add test for admin user role create functionality --- .../AssertRoleInGridActionGroup.xml | 20 +++++ .../AssertRoleNotInGridActionGroup.xml | 20 +++++ .../User/Test/Mftf/Data/UserRoleData.xml | 6 +- .../Mftf/Section/AdminRoleGridSection.xml | 1 + .../Test/AdminCreateUserRoleEntityTest.xml | 77 +++++++++++++++++++ .../CreateAdminUserRoleEntityTest.xml | 3 + 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml new file mode 100644 index 0000000000000..4703563f57d7a --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertRoleInGridActionGroup"> + <arguments> + <argument name="roleName" type="string"/> + </arguments> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToRolesGrid"/> + <fillField selector="{{AdminRoleGridSection.roleNameFilterTextField}}" userInput="{{roleName}}" stepKey="enterRoleName"/> + <click selector="{{AdminRoleGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminRoleGridSection.roleNameInFirstRow}}" userInput="{{roleName}}" stepKey="seeTheRole"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml new file mode 100644 index 0000000000000..fe7cbb4774b1e --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertRoleNotInGridActionGroup"> + <arguments> + <argument name="roleName" type="string"/> + </arguments> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToRolesGrid"/> + <fillField selector="{{AdminRoleGridSection.roleNameFilterTextField}}" userInput="{{roleName}}" stepKey="enterRoleName"/> + <click selector="{{AdminRoleGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminRoleGridSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 1c18ca13b9731..981d5948a3c88 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -13,12 +13,16 @@ <data key="scope">1</data> <data key="access">1</data> </entity> - <entity name="roleAdministrator" type="role"> <data key="name" unique="suffix">Administrator </data> <data key="resourceAccess">All</data> <data key="resources">[]</data> </entity> + <entity name="roleAdmin" type="role"> + <data key="name" unique="suffix">Admin </data> + <data key="resourceAccess">All</data> + <data key="resources">[]</data> + </entity> <entity name="roleSales" type="role"> <data key="name" unique="suffix">Role Sales </data> <data key="resourceAccess">Custom</data> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml index 8413081237fd1..d540397676594 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml @@ -13,6 +13,7 @@ <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="emptyRecords" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> </section> <section name="AdminDeleteRoleSection"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml new file mode 100644 index 0000000000000..5370f8245a665 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -0,0 +1,77 @@ +<?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="AdminCreateUserRoleEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create User Role"/> + <title value="Creating a new role with different data sets"/> + <description value="Creating a new role with different data sets"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Create a new role with custom access--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="roleSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You saved the role."/> + </actionGroup> + <actionGroup ref="AssertRoleInGridActionGroup" stepKey="assertRoleInGrid"> + <argument name="roleName" value="{{roleSales.name}}"/> + </actionGroup> + + <!--Create a new role with full access--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePageSecondTime"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleFormSecondTime"> + <argument name="role" value="roleAdministrator"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRoleSecondTime"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> + <argument name="message" value="You saved the role."/> + </actionGroup> + <actionGroup ref="AssertRoleInGridActionGroup" stepKey="assertRoleInGridSecondTime"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + + <!--Create a new role using incorrect current_password--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePageThirdTime"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleFormThirdTime"> + <argument name="role" value="roleAdmin"/> + <argument name="currentAdminPassword" value="WrongPassword"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRoleThirdTime"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertErrorMessage"> + <argument name="messageType" value="error"/> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again."/> + </actionGroup> + <actionGroup ref="AssertRoleNotInGridActionGroup" stepKey="assertRoleNotInGrid"> + <argument name="roleName" value="{{roleAdmin.name}}"/> + </actionGroup> + + <!--Delete New Roles--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteSaleRole"> + <argument name="roleName" value="{{roleSales.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteAdministratorRole"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml index 19b7406139584..693ba079cb43f 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml @@ -15,6 +15,7 @@ <data name="role/data/roles_resources" xsi:type="string">Sales</data> <constraint name="Magento\User\Test\Constraint\AssertRoleSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertRoleInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="CreateAdminUserRoleEntityTestVariation2"> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> @@ -22,6 +23,7 @@ <data name="role/data/resource_access" xsi:type="string">All</data> <constraint name="Magento\User\Test\Constraint\AssertRoleSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertRoleInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="CreateAdminUserRoleEntityTestVariation3"> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> @@ -29,6 +31,7 @@ <data name="role/data/resource_access" xsi:type="string">All</data> <constraint name="Magento\User\Test\Constraint\AssertIncorrectUserPassword" /> <constraint name="Magento\User\Test\Constraint\AssertRoleNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From cd53ca43162944b0cca3479345205aa3dcec0e64 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 10:08:40 -0400 Subject: [PATCH 047/734] fix tests (magento/magento2#22833: Short-term admin accounts) --- .../Model/ResourceModel/UserExpiration.php | 4 ++-- .../Model/UserExpirationManagerTest.php | 20 +++---------------- .../Observer/AfterAdminUserSaveTest.php | 11 ++++++---- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index 21ee2bc077a2b..b3163f6ba12f9 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -69,7 +69,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $userExpir { /** @var $userExpiration \Magento\Security\Model\UserExpiration */ $expiresAt = $userExpiration->getExpiresAt(); - $utcValue = $this->dateTime->gmtDate('Y-m-d H:i:s', $expiresAt); + $utcValue = $this->dateTime->gmtDate(null, $expiresAt); $userExpiration->setExpiresAt($utcValue); return $this; @@ -87,7 +87,7 @@ protected function _afterLoad(\Magento\Framework\Model\AbstractModel $userExpira /** @var $userExpiration \Magento\Security\Model\UserExpiration */ if ($userExpiration->getExpiresAt()) { $storeValue = $this->timezone->date($userExpiration->getExpiresAt()); - $userExpiration->setExpiresAt($storeValue); + $userExpiration->setExpiresAt($storeValue->format('Y-m-d H:i:s')); } return $this; diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index a5142d041f0b0..7b9d0ce328203 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -8,7 +8,7 @@ namespace Magento\Security\Model; /** - * TODO: test logging out sessions + * Tests for \Magento\Security\Model\UserExpirationManager */ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase { @@ -50,18 +50,6 @@ protected function setUp() $this->objectManager->create(\Magento\Security\Model\UserExpirationManager::class); } - /** - * Tear down - */ - protected function tearDown() - { - $this->auth = null; - $this->authSession = null; - $this->adminSessionInfo = null; - $this->userExpirationManager = null; - $this->objectManager = null; - } - /** * @magentoDataFixture Magento/Security/_files/expired_users.php */ @@ -74,6 +62,7 @@ public function testUserIsExpired() /** * @magentoDataFixture Magento/Security/_files/expired_users.php + * @magentoAppIsolation enabled */ public function testDeactivateExpiredUsersWithExpiredUser() { @@ -89,16 +78,14 @@ public function testDeactivateExpiredUsersWithExpiredUser() static::assertEquals(0, $user->getIsActive()); static::assertNull($userExpirationModel->getId()); static::assertEquals(AdminSessionInfo::LOGGED_OUT, (int)$this->adminSessionInfo->getStatus()); - $this->auth->logout(); } /** * @magentoDataFixture Magento/Security/_files/expired_users.php + * @magentoAppIsolation enabled */ public function testDeactivateExpiredUsersWithNonExpiredUser() { - // TODO: login fails for the second test that tries to log a user in, doesn't matter which test - // it's trying to create a session for the user ID in the previous test $adminUsernameFromFixture = 'adminUserNotExpired'; $this->loginUser($adminUsernameFromFixture); $user = $this->loadUserByUsername($adminUsernameFromFixture); @@ -110,7 +97,6 @@ public function testDeactivateExpiredUsersWithNonExpiredUser() static::assertEquals(1, $user->getIsActive()); static::assertEquals($user->getId(), $userExpirationModel->getId()); static::assertEquals(AdminSessionInfo::LOGGED_IN, (int)$this->adminSessionInfo->getStatus()); - $this->auth->logout(); } /** diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php index 75b2e1ca2fc32..d33e181192927 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php @@ -23,7 +23,7 @@ class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase public function testSaveNewUserExpiration() { $adminUserNameFromFixture = 'dummy_username'; - $testDate = $this->getExpiresDateTime(); + $testDate = $this->getFutureDateInStoreTime(); $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); $user->loadByUsername($adminUserNameFromFixture); $user->setExpiresAt($testDate); @@ -67,7 +67,7 @@ public function testClearUserExpiration() public function testChangeUserExpiration() { $adminUserNameFromFixture = 'adminUserNotExpired'; - $testDate = $this->getExpiresDateTime(); + $testDate = $this->getFutureDateInStoreTime(); $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); $user->loadByUsername($adminUserNameFromFixture); @@ -90,10 +90,13 @@ public function testChangeUserExpiration() * @return string * @throws \Exception */ - private function getExpiresDateTime() + private function getFutureDateInStoreTime() { + /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface $locale */ + $locale = Bootstrap::getObjectManager()->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); $testDate = new \DateTime(); $testDate->modify('+20 days'); - return $testDate->format('Y-m-d H:i:s'); + $storeDate = $locale->date($testDate); + return $storeDate->format('Y-m-d H:i:s'); } } From 13038fc17a6c8cada2747f92e62670410a6a03b2 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 10:42:46 -0400 Subject: [PATCH 048/734] fix unit test (magento/magento2#22833: Short-term admin accounts) --- .../Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php index 263d7b5359b29..b3aafa7cdfbab 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php @@ -95,7 +95,7 @@ public function testWithExpiredUser() ->method('userIsExpired') ->with($this->userMock) ->willReturn(true); - $this->userMock->expects(static::once())->method('getId')->willReturn($adminUserId); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($adminUserId); $this->userExpirationManagerMock->expects(static::once()) ->method('deactivateExpiredUsers') ->with([$adminUserId]) @@ -105,11 +105,12 @@ public function testWithExpiredUser() public function testWithNonExpiredUser() { + $adminUserId = 123; $username = 'testuser'; $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); $this->userMock->expects(static::once())->method('loadByUsername')->willReturn($this->userMock); - + $this->userMock->expects(static::once())->method('getId')->willReturn($adminUserId); $this->userExpirationManagerMock->expects(static::once()) ->method('userIsExpired') ->with($this->userMock) From 192f8e8518e5027b66b3884a23861eb8d15b7fb9 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 11:36:58 -0400 Subject: [PATCH 049/734] fix phpdoc (magento/magento2#22833: Short-term admin accounts) --- .../Magento/Security/Model/ResourceModel/UserExpiration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index b3163f6ba12f9..62ddd8dcaaa2f 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -36,7 +36,7 @@ class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractD * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime - * @param null $connectionName + * @param string $connectionName */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, From c5ac84153e968e97947862ba4bf0b5c32cf0f075 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 11:38:11 -0400 Subject: [PATCH 050/734] remove phpinfo file (magento/magento2#22833: Short-term admin accounts) --- pub/phpinfo.php | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pub/phpinfo.php diff --git a/pub/phpinfo.php b/pub/phpinfo.php deleted file mode 100644 index e9a1e6e06f0e8..0000000000000 --- a/pub/phpinfo.php +++ /dev/null @@ -1,3 +0,0 @@ -<?php -// Show all information, defaults to INFO_ALL -phpinfo(); From 57dda2ef78fe609e377a1f2b46ec01ae1696663d Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 14:17:34 -0400 Subject: [PATCH 051/734] test fixes (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/UserExpiration/Validator.php | 5 +++++ .../Magento/Security/Model/Plugin/AuthSessionTest.php | 1 + .../Magento/Security/Model/UserExpirationManagerTest.php | 2 ++ 3 files changed, 8 insertions(+) diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index 20fca2215a899..6a1cd5e2ce038 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -19,6 +19,11 @@ class Validator extends AbstractValidator /**@var \Magento\Framework\Stdlib\DateTime\DateTime */ private $dateTime; + /** + * Validator constructor. + * + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + */ public function __construct( \Magento\Framework\Stdlib\DateTime\DateTime $dateTime ) { diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index 1d86c40018659..27b085312186e 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -7,6 +7,7 @@ /** * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AuthSessionTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 7b9d0ce328203..64829566ea3b4 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -9,6 +9,8 @@ /** * Tests for \Magento\Security\Model\UserExpirationManager + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase { From 05fae715b2a50fb601f3453510e605669ce2fc50 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 15:01:57 -0400 Subject: [PATCH 052/734] use convertConfigTimeToUtc to get UTC time (magento/magento2#22833: Short-term admin accounts) --- .../Magento/Security/Model/ResourceModel/UserExpiration.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index 62ddd8dcaaa2f..9ff5d0d9f7400 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -64,12 +64,13 @@ protected function _construct() * * @param \Magento\Framework\Model\AbstractModel $userExpiration * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $userExpiration) { /** @var $userExpiration \Magento\Security\Model\UserExpiration */ $expiresAt = $userExpiration->getExpiresAt(); - $utcValue = $this->dateTime->gmtDate(null, $expiresAt); + $utcValue = $this->timezone->convertConfigTimeToUtc($expiresAt); $userExpiration->setExpiresAt($utcValue); return $this; From 6378fe6e20df122be1026c7c35dc0fdc3a3a46eb Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 29 Jul 2019 15:58:00 -0400 Subject: [PATCH 053/734] remove unused constructor param (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/ResourceModel/UserExpiration.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index 9ff5d0d9f7400..ec4a2bf7e42a0 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -25,28 +25,20 @@ class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractD */ private $timezone; - /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime - */ - private $dateTime; - /** * UserExpiration constructor. * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param string $connectionName */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, $connectionName = null ) { parent::__construct($context, $connectionName); $this->timezone = $timezone; - $this->dateTime = $dateTime; } /** From 02ef2f9b0c3132dfcd1dcece54264004a0fb680e Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 7 Aug 2019 17:05:04 +0300 Subject: [PATCH 054/734] Fixing issues --- ...l => AssertAdminRoleInGridActionGroup.xml} | 4 +-- ...> AssertAdminRoleNotInGridActionGroup.xml} | 4 +-- .../User/Test/Mftf/Data/UserRoleData.xml | 5 ---- .../Test/AdminCreateUserRoleEntityTest.xml | 29 ++++++++++--------- .../CreateAdminUserRoleEntityTest.xml | 3 +- 5 files changed, 20 insertions(+), 25 deletions(-) rename app/code/Magento/User/Test/Mftf/ActionGroup/{AssertRoleInGridActionGroup.xml => AssertAdminRoleInGridActionGroup.xml} (92%) rename app/code/Magento/User/Test/Mftf/ActionGroup/{AssertRoleNotInGridActionGroup.xml => AssertAdminRoleNotInGridActionGroup.xml} (92%) diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml similarity index 92% rename from app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml index 4703563f57d7a..93ff97e8bbf8c 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleInGridActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertRoleInGridActionGroup"> + <actionGroup name="AssertAdminRoleInGridActionGroup"> <arguments> <argument name="roleName" type="string"/> </arguments> @@ -17,4 +17,4 @@ <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> <see selector="{{AdminRoleGridSection.roleNameInFirstRow}}" userInput="{{roleName}}" stepKey="seeTheRole"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml similarity index 92% rename from app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml index fe7cbb4774b1e..bb6af04e0e7b6 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertRoleNotInGridActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertRoleNotInGridActionGroup"> + <actionGroup name="AssertAdminRoleNotInGridActionGroup"> <arguments> <argument name="roleName" type="string"/> </arguments> @@ -17,4 +17,4 @@ <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> <see selector="{{AdminRoleGridSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 981d5948a3c88..c067f3f07c656 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -18,11 +18,6 @@ <data key="resourceAccess">All</data> <data key="resources">[]</data> </entity> - <entity name="roleAdmin" type="role"> - <data key="name" unique="suffix">Admin </data> - <data key="resourceAccess">All</data> - <data key="resources">[]</data> - </entity> <entity name="roleSales" type="role"> <data key="name" unique="suffix">Role Sales </data> <data key="resourceAccess">Custom</data> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml index 5370f8245a665..ffec604793f75 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -34,7 +34,11 @@ <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="You saved the role."/> </actionGroup> - <actionGroup ref="AssertRoleInGridActionGroup" stepKey="assertRoleInGrid"> + <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGrid"> + <argument name="roleName" value="{{roleSales.name}}"/> + </actionGroup> + <!--Delete New Roles--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteSaleRole"> <argument name="roleName" value="{{roleSales.name}}"/> </actionGroup> @@ -47,31 +51,28 @@ <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> <argument name="message" value="You saved the role."/> </actionGroup> - <actionGroup ref="AssertRoleInGridActionGroup" stepKey="assertRoleInGridSecondTime"> + <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGridSecondTime"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + <!--Delete New Roles--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteAdministratorRole"> <argument name="roleName" value="{{roleAdministrator.name}}"/> </actionGroup> <!--Create a new role using incorrect current_password--> <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePageThirdTime"/> <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleFormThirdTime"> - <argument name="role" value="roleAdmin"/> - <argument name="currentAdminPassword" value="WrongPassword"/> + <argument name="role" value="roleAdministrator"/> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID"/> </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRoleThirdTime"/> <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertErrorMessage"> <argument name="messageType" value="error"/> <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again."/> </actionGroup> - <actionGroup ref="AssertRoleNotInGridActionGroup" stepKey="assertRoleNotInGrid"> - <argument name="roleName" value="{{roleAdmin.name}}"/> - </actionGroup> - - <!--Delete New Roles--> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteSaleRole"> - <argument name="roleName" value="{{roleSales.name}}"/> - </actionGroup> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteAdministratorRole"> + <actionGroup ref="AssertAdminRoleNotInGridActionGroup" stepKey="assertRoleNotInGrid"> <argument name="roleName" value="{{roleAdministrator.name}}"/> </actionGroup> + </test> -</tests> \ No newline at end of file +</tests> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml index 693ba079cb43f..4ae4c476a959b 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml @@ -8,14 +8,13 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\User\Test\TestCase\CreateAdminUserRoleEntityTest" summary="Create Admin User Role" ticketId="MAGETWO-23413"> <variation name="CreateAdminUserRoleEntityTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> <data name="role/data/current_password" xsi:type="string">%current_password%</data> <data name="role/data/resource_access" xsi:type="string">Custom</data> <data name="role/data/roles_resources" xsi:type="string">Sales</data> <constraint name="Magento\User\Test\Constraint\AssertRoleSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertRoleInGrid" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="CreateAdminUserRoleEntityTestVariation2"> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> From 1bf38ad09dc48083d95923a3184c571fd1678699 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Thu, 8 Aug 2019 15:40:08 +0300 Subject: [PATCH 055/734] Code refactoring --- .../User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml index ffec604793f75..c50728bfda024 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -37,7 +37,7 @@ <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGrid"> <argument name="roleName" value="{{roleSales.name}}"/> </actionGroup> - <!--Delete New Roles--> + <!--Delete the new role with custom access--> <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteSaleRole"> <argument name="roleName" value="{{roleSales.name}}"/> </actionGroup> @@ -54,7 +54,7 @@ <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGridSecondTime"> <argument name="roleName" value="{{roleAdministrator.name}}"/> </actionGroup> - <!--Delete New Roles--> + <!--Delete the new role with full access--> <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteAdministratorRole"> <argument name="roleName" value="{{roleAdministrator.name}}"/> </actionGroup> From 2ac0fe3ac5c5accab736a3001c70817eaf785117 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 16 Aug 2019 15:53:52 +0530 Subject: [PATCH 056/734] force change files --- app/etc/NonComposerComponentRegistration.php | 0 app/etc/db_schema.xml | 0 app/etc/di.xml | 0 app/etc/registration_globlist.php | 0 app/etc/vendor_path.php | 0 generated/.htaccess | 0 pub/media/.htaccess | 0 pub/media/customer/.htaccess | 0 pub/media/downloadable/.htaccess | 0 pub/media/import/.htaccess | 0 pub/media/theme_customization/.htaccess | 0 pub/static/.htaccess | 0 var/.htaccess | 0 13 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 app/etc/NonComposerComponentRegistration.php mode change 100644 => 100755 app/etc/db_schema.xml mode change 100644 => 100755 app/etc/di.xml mode change 100644 => 100755 app/etc/registration_globlist.php mode change 100644 => 100755 app/etc/vendor_path.php mode change 100644 => 100755 generated/.htaccess mode change 100644 => 100755 pub/media/.htaccess mode change 100644 => 100755 pub/media/customer/.htaccess mode change 100644 => 100755 pub/media/downloadable/.htaccess mode change 100644 => 100755 pub/media/import/.htaccess mode change 100644 => 100755 pub/media/theme_customization/.htaccess mode change 100644 => 100755 pub/static/.htaccess mode change 100644 => 100755 var/.htaccess diff --git a/app/etc/NonComposerComponentRegistration.php b/app/etc/NonComposerComponentRegistration.php old mode 100644 new mode 100755 diff --git a/app/etc/db_schema.xml b/app/etc/db_schema.xml old mode 100644 new mode 100755 diff --git a/app/etc/di.xml b/app/etc/di.xml old mode 100644 new mode 100755 diff --git a/app/etc/registration_globlist.php b/app/etc/registration_globlist.php old mode 100644 new mode 100755 diff --git a/app/etc/vendor_path.php b/app/etc/vendor_path.php old mode 100644 new mode 100755 diff --git a/generated/.htaccess b/generated/.htaccess old mode 100644 new mode 100755 diff --git a/pub/media/.htaccess b/pub/media/.htaccess old mode 100644 new mode 100755 diff --git a/pub/media/customer/.htaccess b/pub/media/customer/.htaccess old mode 100644 new mode 100755 diff --git a/pub/media/downloadable/.htaccess b/pub/media/downloadable/.htaccess old mode 100644 new mode 100755 diff --git a/pub/media/import/.htaccess b/pub/media/import/.htaccess old mode 100644 new mode 100755 diff --git a/pub/media/theme_customization/.htaccess b/pub/media/theme_customization/.htaccess old mode 100644 new mode 100755 diff --git a/pub/static/.htaccess b/pub/static/.htaccess old mode 100644 new mode 100755 diff --git a/var/.htaccess b/var/.htaccess old mode 100644 new mode 100755 From d78361ee7b1ecfd207a0415f982055beceb9fa5e Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 16 Aug 2019 17:26:04 +0530 Subject: [PATCH 057/734] Admin Order - Email is Now Required - Magento 2.3 Updated --- .../Magento/Customer/etc/adminhtml/system.xml | 5 ++++ app/code/Magento/Customer/etc/config.xml | 1 + .../Adminhtml/Order/Create/Form/Account.php | 17 ++++++++++- .../Magento/Sales/Model/AdminOrder/Create.php | 28 +++++++++++++++++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 2bd1041214801..325de3680b463 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -86,6 +86,11 @@ <comment>To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> + <field id="email_required_create_order" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Email is required field for Admin order creation</label> + <comment>If set YES Email field will be required during Admin order creation for new Customer.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> <field id="email_domain" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Default Email Domain</label> </field> diff --git a/app/code/Magento/Customer/etc/config.xml b/app/code/Magento/Customer/etc/config.xml index da4b80536e631..d979cf19fc5f7 100644 --- a/app/code/Magento/Customer/etc/config.xml +++ b/app/code/Magento/Customer/etc/config.xml @@ -15,6 +15,7 @@ <confirm>0</confirm> <default_group>1</default_group> <tax_calculation_address_type>billing</tax_calculation_address_type> + <email_required_create_order>0</email_required_create_order> <email_domain>example.com</email_domain> <email_identity>general</email_identity> <email_template>customer_create_account_email_template</email_template> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 03915c0499367..28b1cc9c80a91 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -39,6 +39,8 @@ class Account extends AbstractForm */ protected $_extensibleDataObjectConverter; + private const XML_PATH_EMAIL_REQUIRED_CREATE_ORDER = 'customer/create_account/email_required_create_order'; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Model\Session\Quote $sessionQuote @@ -147,7 +149,7 @@ protected function _addAdditionalFormElementData(AbstractElement $element) { switch ($element->getId()) { case 'email': - $element->setRequired(1); + $element->setRequired($this->isEmailRequiredCreateOrder()); $element->setClass('validate-email admin__control-text'); break; } @@ -204,4 +206,17 @@ private function extractValuesFromAttributes(array $attributes): array return $formValues; } + + /** + * Retrieve email is required field for admin order creation + * + * @return bool + */ + private function isEmailRequiredCreateOrder() + { + return $this->_scopeConfig->getValue( + self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + } } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 01f1fe850b837..211c6f75dad18 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -33,6 +33,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ const XML_PATH_DEFAULT_EMAIL_DOMAIN = 'customer/create_account/email_domain'; + private const XML_PATH_EMAIL_REQUIRED_CREATE_ORDER = 'customer/create_account/email_required_create_order'; /** * Quote session object * @@ -1369,7 +1370,7 @@ protected function _setQuoteAddress(\Magento\Quote\Model\Quote\Address $address, $data = isset($data['region']) && is_array($data['region']) ? array_merge($data, $data['region']) : $data; $addressForm = $this->_metadataFormFactory->create( - + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, 'adminhtml_customer_address', $data, @@ -2037,7 +2038,30 @@ protected function _validate() */ protected function _getNewCustomerEmail() { - return $this->getData('account/email'); + $emailrequired = $this->_scopeConfig->getValue( + self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if($emailrequired) { + return $this->getData('account/email'); + } else { + $email = $this->getData('account/email'); + if (empty($email)) { + $host = $this->_scopeConfig->getValue( + self::XML_PATH_DEFAULT_EMAIL_DOMAIN, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $account = time(); + $email = $account . '@' . $host; + $account = $this->getData('account'); + $account['email'] = $email; + $this->setData('account', $account); + } + + return $email; + } + } /** From 6d51d53972264487e570f920d459366c81d172d1 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Fri, 16 Aug 2019 17:57:36 +0300 Subject: [PATCH 058/734] code review fixes --- .../AdminOpenCMSBlocksGridActionGroup.xml | 14 -------------- .../AdminOpenNewCmsBlockPageActionGroup.xml | 13 ------------- .../AdminSelectCMSBlockFromGridActionGroup.xml | 16 ---------------- ...torefrontNoTextOnCategoryPageActionGroup.xml} | 2 +- ...tStorefrontTextOnCategoryPageActionGroup.xml} | 2 +- ...bledCmsBlockEntityAndAssignToCategoryTest.xml | 2 +- ...bledCmsBlockEntityAndAssignToCategoryTest.xml | 2 +- 7 files changed, 4 insertions(+), 47 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{AssertNoTextOnCategoryFrontPageActionGroup.xml => AssertStorefrontNoTextOnCategoryPageActionGroup.xml} (93%) rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{AssertTextOnCategoryFronPageActionGroup.xml => AssertStorefrontTextOnCategoryPageActionGroup.xml} (93%) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml deleted file mode 100644 index 2aad658fa1ae9..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml +++ /dev/null @@ -1,14 +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="AdminOpenCMSBlocksGridActionGroup"> - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> -</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml deleted file mode 100644 index 39e86973be4c9..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenNewCmsBlockPageActionGroup.xml +++ /dev/null @@ -1,13 +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="AdminOpenNewCmsBlockPageActionGroup"> - <amOnPage url="{{CmsNewBlock.url}}" stepKey="navigateToCreateNewCMSBlockPage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml deleted file mode 100644 index 46a624430e2f4..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSBlockFromGridActionGroup.xml +++ /dev/null @@ -1,16 +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="AdminSelectCMSBlockFromGridActionGroup"> - <arguments> - <argument name="identifier" defaultValue=""/> - </arguments> - <click selector="{{BlockPageActionsSection.select(identifier)}}" stepKey="clickSelectCreatedCMSBlock" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml similarity index 93% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml index e5994d70e1227..8ca93004c4e1d 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertNoTextOnCategoryFrontPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertNoTextOnCategoryFrontPageActionGroup"> + <actionGroup name="AssertStorefrontNoTextOnCategoryPageActionGroup"> <arguments> <argument name="category" defaultValue="_defaultCategory"/> <argument name="text" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml similarity index 93% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml index 98c25eebfa928..aa761854a10fa 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertTextOnCategoryFronPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertTextOnCategoryFronPageActionGroup"> + <actionGroup name="AssertStorefrontTextOnCategoryPageActionGroup"> <arguments> <argument name="category" defaultValue="_defaultCategory"/> <argument name="text" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index 78286481f27e3..5bb2bb898f8c1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -46,7 +46,7 @@ </actionGroup> <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> - <actionGroup ref="AssertNoTextOnCategoryFrontPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> + <actionGroup ref="AssertStorefrontNoTextOnCategoryPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml index 9f52720a774f8..6ee3f2094f190 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -40,7 +40,7 @@ </actionGroup> <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> - <actionGroup ref="AssertTextOnCategoryFronPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> + <actionGroup ref="AssertStorefrontTextOnCategoryPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> <argument name="category" value="$$newDefaultCategory$$"/> <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> From e0219db6f6ce2b1bbb261bec1d393adbb5d5b934 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Wed, 21 Aug 2019 10:13:05 +0300 Subject: [PATCH 059/734] removed redundant argument in action group --- .../Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml index 2361354d5c7d2..aa4a69f8abf62 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml @@ -8,9 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCategoriesExpandAllActionGroup"> - <arguments> - <argument name="category" defaultValue="_defaultCategory"/> - </arguments> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll"/> </actionGroup> </actionGroups> \ No newline at end of file From 078bd695742a96210b16a4e5bd45b98e8a9b0301 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Mon, 9 Sep 2019 17:46:11 +0530 Subject: [PATCH 060/734] Email field translation --- app/code/Magento/Customer/i18n/en_US.csv | 770 +++++++++--------- .../Adminhtml/Order/Create/Form/Account.php | 4 +- .../Magento/Sales/Model/AdminOrder/Create.php | 7 +- 3 files changed, 391 insertions(+), 390 deletions(-) mode change 100644 => 100755 app/code/Magento/Customer/i18n/en_US.csv mode change 100644 => 100755 app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php mode change 100644 => 100755 app/code/Magento/Sales/Model/AdminOrder/Create.php diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv old mode 100644 new mode 100755 index 3495feb925cb3..74c30a47a835e --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -1,23 +1,23 @@ -"Sign Out","Sign Out" -"Sign In","Sign In" -"You are subscribed to our newsletter.","You are subscribed to our newsletter." -"You aren't subscribed to our newsletter.","You aren't subscribed to our newsletter." -"You have not set a default shipping address.","You have not set a default shipping address." -"You have not set a default billing address.","You have not set a default billing address." -"Address Book","Address Book" -"Edit Address","Edit Address" -"Add New Address","Add New Address" +Sign Out,Sign Out +Sign In,Sign In +You are subscribed to our newsletter.,You are subscribed to our newsletter. +You aren't subscribed to our newsletter.,You aren't subscribed to our newsletter. +You have not set a default shipping address.,You have not set a default shipping address. +You have not set a default billing address.,You have not set a default billing address. +Address Book,Address Book +Edit Address,Edit Address +Add New Address,Add New Address region,region -"Create Order","Create Order" -"Save Customer","Save Customer" -"Delete Customer","Delete Customer" -"Reset Password","Reset Password" -"Are you sure you want to revoke the customer's tokens?","Are you sure you want to revoke the customer's tokens?" -"Force Sign-In","Force Sign-In" -"New Customer","New Customer" -"Save and Continue Edit","Save and Continue Edit" +Create Order,Create Order +Save Customer,Save Customer +Delete Customer,Delete Customer +Reset Password,Reset Password +Are you sure you want to revoke the customer's tokens?,Are you sure you want to revoke the customer's tokens? +Force Sign-In,Force Sign-In +New Customer,New Customer +Save and Continue Edit,Save and Continue Edit Back,Back -"Please select","Please select" +Please select,Please select Reset,Reset ID,ID Product,Product @@ -28,186 +28,186 @@ Total,Total Action,Action Configure,Configure Delete,Delete -"Shopping Cart from %1","Shopping Cart from %1" +Shopping Cart from %1,Shopping Cart from %1 Newsletter,Newsletter -"Newsletter Information","Newsletter Information" -"Subscribed to Newsletter","Subscribed to Newsletter" -"Last Date Subscribed","Last Date Subscribed" -"Last Date Unsubscribed","Last Date Unsubscribed" -"No Newsletter Found","No Newsletter Found" -"Start date","Start date" -"End Date","End Date" -"Receive Date","Receive Date" +Newsletter Information,Newsletter Information +Subscribed to Newsletter,Subscribed to Newsletter +Last Date Subscribed,Last Date Subscribed +Last Date Unsubscribed,Last Date Unsubscribed +No Newsletter Found,No Newsletter Found +Start date,Start date +End Date,End Date +Receive Date,Receive Date Subject,Subject Status,Status Sent,Sent Cancel,Cancel -"Not Sent","Not Sent" +Not Sent,Not Sent Sending,Sending Paused,Paused View,View Unknown,Unknown -"Order #","Order #" +Order #,Order # Purchased,Purchased -"Bill-to Name","Bill-to Name" -"Ship-to Name","Ship-to Name" -"Order Total","Order Total" -"Purchase Point","Purchase Point" -"Customer View","Customer View" -"There are no items in customer's shopping cart.","There are no items in customer's shopping cart." +Bill-to Name,Bill-to Name +Ship-to Name,Ship-to Name +Order Total,Order Total +Purchase Point,Purchase Point +Customer View,Customer View +There are no items in customer's shopping cart.,There are no items in customer's shopping cart. Qty,Qty Confirmed,Confirmed -"Confirmation Required","Confirmation Required" -"Confirmation Not Required","Confirmation Not Required" +Confirmation Required,Confirmation Required +Confirmation Not Required,Confirmation Not Required Indeterminate,Indeterminate -"The customer does not have default billing address.","The customer does not have default billing address." +The customer does not have default billing address.,The customer does not have default billing address. Offline,Offline Online,Online Never,Never Unlocked,Unlocked Locked,Locked -"Deleted Stores","Deleted Stores" -"Add Locale","Add Locale" -"Add Date","Add Date" -"Days in Wish List","Days in Wish List" +Deleted Stores,Deleted Stores +Add Locale,Add Locale +Add Date,Add Date +Days in Wish List,Days in Wish List Unlock,Unlock No,No Yes,Yes -"Delete File","Delete File" +Delete File,Delete File Download,Download -"Delete Image","Delete Image" -"View Full Size","View Full Size" -"All countries","All countries" -"Customer Groups","Customer Groups" -"Add New Customer Group","Add New Customer Group" -"Save Customer Group","Save Customer Group" -"Delete Customer Group","Delete Customer Group" -"New Customer Group","New Customer Group" +Delete Image,Delete Image +View Full Size,View Full Size +All countries,All countries +Customer Groups,Customer Groups +Add New Customer Group,Add New Customer Group +Save Customer Group,Save Customer Group +Delete Customer Group,Delete Customer Group +New Customer Group,New Customer Group "Edit Customer Group ""%1""","Edit Customer Group ""%1""" -"Group Information","Group Information" -"Group Name","Group Name" -"Maximum length must be less then %1 characters.","Maximum length must be less then %1 characters." -"Tax Class","Tax Class" -"The customer is now assigned to Customer Group %s.","The customer is now assigned to Customer Group %s." -"Would you like to change the Customer Group for this order?","Would you like to change the Customer Group for this order?" -"The VAT ID is valid.","The VAT ID is valid." -"The VAT ID entered (%s) is not a valid VAT ID.","The VAT ID entered (%s) is not a valid VAT ID." -"The VAT ID is valid. The current Customer Group will be used.","The VAT ID is valid. The current Customer Group will be used." -"The VAT ID is valid but no Customer Group is assigned for it.","The VAT ID is valid but no Customer Group is assigned for it." +Group Information,Group Information +Group Name,Group Name +Maximum length must be less then %1 characters.,Maximum length must be less then %1 characters. +Tax Class,Tax Class +The customer is now assigned to Customer Group %s.,The customer is now assigned to Customer Group %s. +Would you like to change the Customer Group for this order?,Would you like to change the Customer Group for this order? +The VAT ID is valid.,The VAT ID is valid. +The VAT ID entered (%s) is not a valid VAT ID.,The VAT ID entered (%s) is not a valid VAT ID. +The VAT ID is valid. The current Customer Group will be used.,The VAT ID is valid. The current Customer Group will be used. +The VAT ID is valid but no Customer Group is assigned for it.,The VAT ID is valid but no Customer Group is assigned for it. "Based on the VAT ID, the customer belongs to the Customer Group %s.","Based on the VAT ID, the customer belongs to the Customer Group %s." -"Something went wrong while validating the VAT ID.","Something went wrong while validating the VAT ID." -"The customer would belong to Customer Group %s.","The customer would belong to Customer Group %s." -"There was an error detecting Customer Group.","There was an error detecting Customer Group." -"Validate VAT Number","Validate VAT Number" -"Customer Login","Customer Login" -"Create New Customer Account","Create New Customer Account" -"Date of Birth","Date of Birth" -"Bad request.","Bad request." -"This confirmation key is invalid or has expired.","This confirmation key is invalid or has expired." -"There was an error confirming the account","There was an error confirming the account" +Something went wrong while validating the VAT ID.,Something went wrong while validating the VAT ID. +The customer would belong to Customer Group %s.,The customer would belong to Customer Group %s. +There was an error detecting Customer Group.,There was an error detecting Customer Group. +Validate VAT Number,Validate VAT Number +Customer Login,Customer Login +Create New Customer Account,Create New Customer Account +Date of Birth,Date of Birth +Bad request.,Bad request. +This confirmation key is invalid or has expired.,This confirmation key is invalid or has expired. +There was an error confirming the account,There was an error confirming the account "If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your shipping address for proper VAT calculation.","If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your shipping address for proper VAT calculation." "If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your billing address for proper VAT calculation.","If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your billing address for proper VAT calculation." -"Thank you for registering with %1.","Thank you for registering with %1." -"Please check your email for confirmation key.","Please check your email for confirmation key." -"This email does not require confirmation.","This email does not require confirmation." -"Wrong email.","Wrong email." -"Your password reset link has expired.","Your password reset link has expired." +Thank you for registering with %1.,Thank you for registering with %1. +Please check your email for confirmation key.,Please check your email for confirmation key. +This email does not require confirmation.,This email does not require confirmation. +Wrong email.,Wrong email. +Your password reset link has expired.,Your password reset link has expired. "You must confirm your account. Please check your email for the confirmation link or <a href=""%1"">click here</a> for a new link.","You must confirm your account. Please check your email for the confirmation link or <a href=""%1"">click here</a> for a new link." "There is already an account with this email address. If you are sure that it is your email address, <a href=""%1"">click here</a> to get your password and access your account.","There is already an account with this email address. If you are sure that it is your email address, <a href=""%1"">click here</a> to get your password and access your account." -"We can't save the customer.","We can't save the customer." -"Please make sure your passwords match.","Please make sure your passwords match." +We can't save the customer.,We can't save the customer. +Please make sure your passwords match.,Please make sure your passwords match. "If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your shipping address for proper VAT calculation.","If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your shipping address for proper VAT calculation." "If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your billing address for proper VAT calculation.","If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your billing address for proper VAT calculation." -"Account Information","Account Information" -"You saved the account information.","You saved the account information." -"You did not sign in correctly or your account is temporarily disabled.","You did not sign in correctly or your account is temporarily disabled." -"Password confirmation doesn't match entered password.","Password confirmation doesn't match entered password." -"The password doesn't match this account.","The password doesn't match this account." -"Please correct the email address.","Please correct the email address." -"We're unable to send the password reset email.","We're unable to send the password reset email." -"Please enter your email.","Please enter your email." -"If there is an account associated with %1 you will receive an email with a link to reset your password.","If there is an account associated with %1 you will receive an email with a link to reset your password." -"My Account","My Account" +Account Information,Account Information +You saved the account information.,You saved the account information. +You did not sign in correctly or your account is temporarily disabled.,You did not sign in correctly or your account is temporarily disabled. +Password confirmation doesn't match entered password.,Password confirmation doesn't match entered password. +The password doesn't match this account.,The password doesn't match this account. +Please correct the email address.,Please correct the email address. +We're unable to send the password reset email.,We're unable to send the password reset email. +Please enter your email.,Please enter your email. +If there is an account associated with %1 you will receive an email with a link to reset your password.,If there is an account associated with %1 you will receive an email with a link to reset your password. +My Account,My Account "This account is not confirmed. <a href=""%1"">Click here</a> to resend confirmation email.","This account is not confirmed. <a href=""%1"">Click here</a> to resend confirmation email." -"An unspecified error occurred. Please contact us for assistance.","An unspecified error occurred. Please contact us for assistance." -"A login and a password are required.","A login and a password are required." -"New Password and Confirm New Password values didn't match.","New Password and Confirm New Password values didn't match." -"Please enter a new password.","Please enter a new password." -"You updated your password.","You updated your password." -"Something went wrong while saving the new password.","Something went wrong while saving the new password." -"You deleted the address.","You deleted the address." -"We can't delete the address right now.","We can't delete the address right now." -"You saved the address.","You saved the address." -"We can't save the address.","We can't save the address." -"No customer ID defined.","No customer ID defined." -"Please correct the quote items and try again.","Please correct the quote items and try again." -"You have revoked the customer's tokens.","You have revoked the customer's tokens." -"We can't find a customer to revoke.","We can't find a customer to revoke." -"Something went wrong while saving file.","Something went wrong while saving file." -"You deleted the customer group.","You deleted the customer group." -"The customer group no longer exists.","The customer group no longer exists." +An unspecified error occurred. Please contact us for assistance.,An unspecified error occurred. Please contact us for assistance. +A login and a password are required.,A login and a password are required. +New Password and Confirm New Password values didn't match.,New Password and Confirm New Password values didn't match. +Please enter a new password.,Please enter a new password. +You updated your password.,You updated your password. +Something went wrong while saving the new password.,Something went wrong while saving the new password. +You deleted the address.,You deleted the address. +We can't delete the address right now.,We can't delete the address right now. +You saved the address.,You saved the address. +We can't save the address.,We can't save the address. +No customer ID defined.,No customer ID defined. +Please correct the quote items and try again.,Please correct the quote items and try again. +You have revoked the customer's tokens.,You have revoked the customer's tokens. +We can't find a customer to revoke.,We can't find a customer to revoke. +Something went wrong while saving file.,Something went wrong while saving file. +You deleted the customer group.,You deleted the customer group. +The customer group no longer exists.,The customer group no longer exists. Customers,Customers -"New Group","New Group" -"New Customer Groups","New Customer Groups" -"Edit Group","Edit Group" -"Edit Customer Groups","Edit Customer Groups" -"You saved the customer group.","You saved the customer group." -"Please select customer(s).","Please select customer(s)." -"Customer could not be deleted.","Customer could not be deleted." -"You deleted the customer.","You deleted the customer." -"Something went wrong while editing the customer.","Something went wrong while editing the customer." -"Manage Customers","Manage Customers" -"Please correct the data sent.","Please correct the data sent." -"A total of %1 record(s) were updated.","A total of %1 record(s) were updated." -"A total of %1 record(s) were deleted.","A total of %1 record(s) were deleted." -"The customer will receive an email with a link to reset password.","The customer will receive an email with a link to reset password." -"Something went wrong while resetting customer password.","Something went wrong while resetting customer password." -"You saved the customer.","You saved the customer." -"Something went wrong while saving the customer.","Something went wrong while saving the customer." -"Page not found.","Page not found." -"Customer has been unlocked successfully.","Customer has been unlocked successfully." -"Online Customers","Online Customers" -"Customers Now Online","Customers Now Online" -"Please define Wish List item ID.","Please define Wish List item ID." -"Please load Wish List item.","Please load Wish List item." -"Login successful.","Login successful." -"Invalid login or password.","Invalid login or password." +New Group,New Group +New Customer Groups,New Customer Groups +Edit Group,Edit Group +Edit Customer Groups,Edit Customer Groups +You saved the customer group.,You saved the customer group. +Please select customer(s).,Please select customer(s). +Customer could not be deleted.,Customer could not be deleted. +You deleted the customer.,You deleted the customer. +Something went wrong while editing the customer.,Something went wrong while editing the customer. +Manage Customers,Manage Customers +Please correct the data sent.,Please correct the data sent. +A total of %1 record(s) were updated.,A total of %1 record(s) were updated. +A total of %1 record(s) were deleted.,A total of %1 record(s) were deleted. +The customer will receive an email with a link to reset password.,The customer will receive an email with a link to reset password. +Something went wrong while resetting customer password.,Something went wrong while resetting customer password. +You saved the customer.,You saved the customer. +Something went wrong while saving the customer.,Something went wrong while saving the customer. +Page not found.,Page not found. +Customer has been unlocked successfully.,Customer has been unlocked successfully. +Online Customers,Online Customers +Customers Now Online,Customers Now Online +Please define Wish List item ID.,Please define Wish List item ID. +Please load Wish List item.,Please load Wish List item. +Login successful.,Login successful. +Invalid login or password.,Invalid login or password. """%1"" section source is not supported","""%1"" section source is not supported" -"%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface","%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface" -"No confirmation needed.","No confirmation needed." -"Account already active","Account already active" -"Invalid confirmation token","Invalid confirmation token" -"The account is locked.","The account is locked." -"This account is not confirmed.","This account is not confirmed." +%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface,%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface +No confirmation needed.,No confirmation needed. +Account already active,Account already active +Invalid confirmation token,Invalid confirmation token +The account is locked.,The account is locked. +This account is not confirmed.,This account is not confirmed. "Invalid value of ""%value"" provided for the %fieldName field.","Invalid value of ""%value"" provided for the %fieldName field." -"Please enter a password with at most %1 characters.","Please enter a password with at most %1 characters." -"Please enter a password with at least %1 characters.","Please enter a password with at least %1 characters." -"The password can't begin or end with a space.","The password can't begin or end with a space." +Please enter a password with at most %1 characters.,Please enter a password with at most %1 characters. +Please enter a password with at least %1 characters.,Please enter a password with at least %1 characters. +The password can't begin or end with a space.,The password can't begin or end with a space. "Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.","Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters." -"Password cannot be the same as email address.","Password cannot be the same as email address." -"This customer already exists in this store.","This customer already exists in this store." -"A customer with the same email already exists in an associated website.","A customer with the same email already exists in an associated website." -"%fieldName is a required field.","%fieldName is a required field." -"Reset password token mismatch.","Reset password token mismatch." -"Reset password token expired.","Reset password token expired." -"Please correct the transactional account email type.","Please correct the transactional account email type." +Password cannot be the same as email address.,Password cannot be the same as email address. +This customer already exists in this store.,This customer already exists in this store. +A customer with the same email already exists in an associated website.,A customer with the same email already exists in an associated website. +%fieldName is a required field.,%fieldName is a required field. +Reset password token mismatch.,Reset password token mismatch. +Reset password token expired.,Reset password token expired. +Please correct the transactional account email type.,Please correct the transactional account email type. """%1"" is a required value.","""%1"" is a required value." Global,Global -"Per Website","Per Website" -"We can't share customer accounts globally when the accounts share identical email addresses on more than one website.","We can't share customer accounts globally when the accounts share identical email addresses on more than one website." -"Billing Address","Billing Address" -"Shipping Address","Shipping Address" -"-- Please Select --","-- Please Select --" -"Please enter a valid password reset token.","Please enter a valid password reset token." -"The password can not begin or end with a space.","The password can not begin or end with a space." +Per Website,Per Website +We can't share customer accounts globally when the accounts share identical email addresses on more than one website.,We can't share customer accounts globally when the accounts share identical email addresses on more than one website. +Billing Address,Billing Address +Shipping Address,Shipping Address +-- Please Select --,-- Please Select -- +Please enter a valid password reset token.,Please enter a valid password reset token. +The password can not begin or end with a space.,The password can not begin or end with a space. Admin,Admin -"ALL GROUPS","ALL GROUPS" +ALL GROUPS,ALL GROUPS "No such entity with %fieldName = %fieldValue, %field2Name = %field2Value","No such entity with %fieldName = %fieldValue, %field2Name = %field2Value" -"File can not be saved to the destination folder.","File can not be saved to the destination folder." -"Unable to create directory %1.","Unable to create directory %1." -"Destination folder is not writable or does not exists.","Destination folder is not writable or does not exists." -"Something went wrong while saving the file.","Something went wrong while saving the file." -"Attribute object is undefined","Attribute object is undefined" +File can not be saved to the destination folder.,File can not be saved to the destination folder. +Unable to create directory %1.,Unable to create directory %1. +Destination folder is not writable or does not exists.,Destination folder is not writable or does not exists. +Something went wrong while saving the file.,Something went wrong while saving the file. +Attribute object is undefined,Attribute object is undefined """%1"" invalid type entered.","""%1"" invalid type entered." """%1"" contains non-alphabetic or non-numeric characters.","""%1"" contains non-alphabetic or non-numeric characters." """%1"" is an empty string.","""%1"" is an empty string." @@ -217,19 +217,19 @@ Admin,Admin """%1"" is not a valid hostname.","""%1"" is not a valid hostname." """%1"" uses too many characters.","""%1"" uses too many characters." "'%value%' looks like an IP address, which is not an acceptable format.","'%value%' looks like an IP address, which is not an acceptable format." -"'%value%' looks like a DNS hostname but we cannot match the TLD against known list.","'%value%' looks like a DNS hostname but we cannot match the TLD against known list." -"'%value%' looks like a DNS hostname but contains a dash in an invalid position.","'%value%' looks like a DNS hostname but contains a dash in an invalid position." -"'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'.","'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'." -"'%value%' looks like a DNS hostname but cannot extract TLD part.","'%value%' looks like a DNS hostname but cannot extract TLD part." -"'%value%' does not look like a valid local network name.","'%value%' does not look like a valid local network name." +'%value%' looks like a DNS hostname but we cannot match the TLD against known list.,'%value%' looks like a DNS hostname but we cannot match the TLD against known list. +'%value%' looks like a DNS hostname but contains a dash in an invalid position.,'%value%' looks like a DNS hostname but contains a dash in an invalid position. +'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'.,'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'. +'%value%' looks like a DNS hostname but cannot extract TLD part.,'%value%' looks like a DNS hostname but cannot extract TLD part. +'%value%' does not look like a valid local network name.,'%value%' does not look like a valid local network name. "'%value%' looks like a local network name, which is not an acceptable format.","'%value%' looks like a local network name, which is not an acceptable format." "'%value%' appears to be a DNS hostname, but the given punycode notation cannot be decoded.","'%value%' appears to be a DNS hostname, but the given punycode notation cannot be decoded." """%1"" is not a valid URL.","""%1"" is not a valid URL." """%1"" is not a valid date.","""%1"" is not a valid date." """%1"" does not fit the entered date format.","""%1"" does not fit the entered date format." -"Please enter a valid date between %1 and %2 at %3.","Please enter a valid date between %1 and %2 at %3." -"Please enter a valid date equal to or greater than %1 at %2.","Please enter a valid date equal to or greater than %1 at %2." -"Please enter a valid date less than or equal to %1 at %2.","Please enter a valid date less than or equal to %1 at %2." +Please enter a valid date between %1 and %2 at %3.,Please enter a valid date between %1 and %2 at %3. +Please enter a valid date equal to or greater than %1 at %2.,Please enter a valid date equal to or greater than %1 at %2. +Please enter a valid date less than or equal to %1 at %2.,Please enter a valid date less than or equal to %1 at %2. """%1"" is not a valid file extension.","""%1"" is not a valid file extension." """%1"" is not a valid file.","""%1"" is not a valid file." """%1"" exceeds the allowed file size.","""%1"" exceeds the allowed file size." @@ -239,203 +239,203 @@ Admin,Admin """%1"" length must be equal or greater than %2 characters.","""%1"" length must be equal or greater than %2 characters." """%1"" length must be equal or less than %2 characters.","""%1"" length must be equal or less than %2 characters." label,label -"Please enter a customer email.","Please enter a customer email." -"A customer website ID must be specified when using the website scope.","A customer website ID must be specified when using the website scope." -"Customer Group","Customer Group" +Please enter a customer email.,Please enter a customer email. +A customer website ID must be specified when using the website scope.,A customer website ID must be specified when using the website scope. +Customer Group,Customer Group "You can't delete group ""%1"".","You can't delete group ""%1""." -"Customer Group already exists.","Customer Group already exists." -"Cannot delete group.","Cannot delete group." -"Error during VAT Number verification.","Error during VAT Number verification." -"PHP SOAP extension is required.","PHP SOAP extension is required." -"VAT Number is valid.","VAT Number is valid." -"Please enter a valid VAT number.","Please enter a valid VAT number." -"Your VAT ID was successfully validated.","Your VAT ID was successfully validated." -"You will be charged tax.","You will be charged tax." -"You will not be charged tax.","You will not be charged tax." -"The VAT ID entered (%1) is not a valid VAT ID.","The VAT ID entered (%1) is not a valid VAT ID." -"Your Tax ID cannot be validated.","Your Tax ID cannot be validated." +Customer Group already exists.,Customer Group already exists. +Cannot delete group.,Cannot delete group. +Error during VAT Number verification.,Error during VAT Number verification. +PHP SOAP extension is required.,PHP SOAP extension is required. +VAT Number is valid.,VAT Number is valid. +Please enter a valid VAT number.,Please enter a valid VAT number. +Your VAT ID was successfully validated.,Your VAT ID was successfully validated. +You will be charged tax.,You will be charged tax. +You will not be charged tax.,You will not be charged tax. +The VAT ID entered (%1) is not a valid VAT ID.,The VAT ID entered (%1) is not a valid VAT ID. +Your Tax ID cannot be validated.,Your Tax ID cannot be validated. "If you believe this is an error, please contact us at %1","If you believe this is an error, please contact us at %1" -"No such entity with %fieldName = %fieldValue","No such entity with %fieldName = %fieldValue" +No such entity with %fieldName = %fieldValue,No such entity with %fieldName = %fieldValue Male,Male Female,Female -"Not Specified","Not Specified" -"Thank you for registering with","Thank you for registering with" -"enter your billing address for proper VAT calculation","enter your billing address for proper VAT calculation" -"enter your shipping address for proper VAT calculation","enter your shipping address for proper VAT calculation" -"Please enter new password.","Please enter new password." +Not Specified,Not Specified +Thank you for registering with,Thank you for registering with +enter your billing address for proper VAT calculation,enter your billing address for proper VAT calculation +enter your shipping address for proper VAT calculation,enter your shipping address for proper VAT calculation +Please enter new password.,Please enter new password. message,message NoSuchEntityException,NoSuchEntityException Exception,Exception InputException.,InputException. InputException,InputException -"Exception message","Exception message" -"Validator Exception","Validator Exception" -"Localized Exception","Localized Exception" -"some error","some error" +Exception message,Exception message +Validator Exception,Validator Exception +Localized Exception,Localized Exception +some error,some error frontend_label,frontend_label -"NOT LOGGED IN","NOT LOGGED IN" -"Please enter the state/province.","Please enter the state/province." -"region is a required field.","region is a required field." -"regionId is a required field.","regionId is a required field." +NOT LOGGED IN,NOT LOGGED IN +Please enter the state/province.,Please enter the state/province. +region is a required field.,region is a required field. +regionId is a required field.,regionId is a required field. Label,Label Select...,Select... Edit,Edit Visitor,Visitor Customer,Customer -"No item specified.","No item specified." -"Are you sure you want to remove this item?","Are you sure you want to remove this item?" -"Personal Information","Personal Information" -"Last Logged In:","Last Logged In:" -"Last Logged In (%1):","Last Logged In (%1):" -"Account Lock:","Account Lock:" -"Confirmed email:","Confirmed email:" -"Account Created:","Account Created:" -"Account Created on (%1):","Account Created on (%1):" -"Account Created in:","Account Created in:" -"Customer Group:","Customer Group:" -"Default Billing Address","Default Billing Address" -"Sales Statistics","Sales Statistics" -"Web Site","Web Site" +No item specified.,No item specified. +Are you sure you want to remove this item?,Are you sure you want to remove this item? +Personal Information,Personal Information +Last Logged In:,Last Logged In: +Last Logged In (%1):,Last Logged In (%1): +Account Lock:,Account Lock: +Confirmed email:,Confirmed email: +Account Created:,Account Created: +Account Created on (%1):,Account Created on (%1): +Account Created in:,Account Created in: +Customer Group:,Customer Group: +Default Billing Address,Default Billing Address +Sales Statistics,Sales Statistics +Web Site,Web Site Store,Store -"Store View","Store View" -"Lifetime Sales","Lifetime Sales" -"Average Sale","Average Sale" -"All Store Views","All Store Views" +Store View,Store View +Lifetime Sales,Lifetime Sales +Average Sale,Average Sale +All Store Views,All Store Views Change,Change -"Manage Addresses","Manage Addresses" -"Default Shipping Address","Default Shipping Address" -"Contact Information","Contact Information" -"Change Password","Change Password" +Manage Addresses,Manage Addresses +Default Shipping Address,Default Shipping Address +Contact Information,Contact Information +Change Password,Change Password Newsletters,Newsletters "You are subscribed to ""General Subscription"".","You are subscribed to ""General Subscription""." or,or -"Default Addresses","Default Addresses" -"Change Billing Address","Change Billing Address" -"You have no default billing address in your address book.","You have no default billing address in your address book." -"Change Shipping Address","Change Shipping Address" -"You have no default shipping address in your address book.","You have no default shipping address in your address book." -"Additional Address Entries","Additional Address Entries" -"Delete Address","Delete Address" -"You have no other address entries in your address book.","You have no other address entries in your address book." -"* Required Fields","* Required Fields" +Default Addresses,Default Addresses +Change Billing Address,Change Billing Address +You have no default billing address in your address book.,You have no default billing address in your address book. +Change Shipping Address,Change Shipping Address +You have no default shipping address in your address book.,You have no default shipping address in your address book. +Additional Address Entries,Additional Address Entries +Delete Address,Delete Address +You have no other address entries in your address book.,You have no other address entries in your address book. +* Required Fields,* Required Fields Address,Address -"Street Address","Street Address" -"Street Address %1","Street Address %1" -"VAT Number","VAT Number" +Street Address,Street Address +Street Address %1,Street Address %1 +VAT Number,VAT Number City,City State/Province,State/Province "Please select a region, state or province.","Please select a region, state or province." -"Zip/Postal Code","Zip/Postal Code" +Zip/Postal Code,Zip/Postal Code Country,Country -"It's a default billing address.","It's a default billing address." -"Use as my default billing address","Use as my default billing address" -"It's a default shipping address.","It's a default shipping address." -"Use as my default shipping address","Use as my default shipping address" -"Save Address","Save Address" -"Go back","Go back" -"Please enter your email below and we will send you the confirmation link.","Please enter your email below and we will send you the confirmation link." +It's a default billing address.,It's a default billing address. +Use as my default billing address,Use as my default billing address +It's a default shipping address.,It's a default shipping address. +Use as my default shipping address,Use as my default shipping address +Save Address,Save Address +Go back,Go back +Please enter your email below and we will send you the confirmation link.,Please enter your email below and we will send you the confirmation link. Email,Email -"Send confirmation link","Send confirmation link" -"Back to Sign In","Back to Sign In" -"Change Email","Change Email" -"Change Email and Password","Change Email and Password" -"Current Password","Current Password" -"New Password","New Password" -"Password Strength","Password Strength" -"No Password","No Password" -"Confirm New Password","Confirm New Password" +Send confirmation link,Send confirmation link +Back to Sign In,Back to Sign In +Change Email,Change Email +Change Email and Password,Change Email and Password +Current Password,Current Password +New Password,New Password +Password Strength,Password Strength +No Password,No Password +Confirm New Password,Confirm New Password Save,Save -"Please enter your email address below to receive a password reset link.","Please enter your email address below to receive a password reset link." -"Reset My Password","Reset My Password" -"Registered Customers","Registered Customers" +Please enter your email address below to receive a password reset link.,Please enter your email address below to receive a password reset link. +Reset My Password,Reset My Password +Registered Customers,Registered Customers "If you have an account, sign in with your email address.","If you have an account, sign in with your email address." Password,Password -"Forgot Your Password?","Forgot Your Password?" -"Subscription option","Subscription option" -"General Subscription","General Subscription" -"Sign Up for Newsletter","Sign Up for Newsletter" -"Address Information","Address Information" -"Sign-in Information","Sign-in Information" -"Confirm Password","Confirm Password" -"Create an Account","Create an Account" -"Set a New Password","Set a New Password" -"You have signed out and will go to our homepage in 5 seconds.","You have signed out and will go to our homepage in 5 seconds." -"New Customers","New Customers" +Forgot Your Password?,Forgot Your Password? +Subscription option,Subscription option +General Subscription,General Subscription +Sign Up for Newsletter,Sign Up for Newsletter +Address Information,Address Information +Sign-in Information,Sign-in Information +Confirm Password,Confirm Password +Create an Account,Create an Account +Set a New Password,Set a New Password +You have signed out and will go to our homepage in 5 seconds.,You have signed out and will go to our homepage in 5 seconds. +New Customers,New Customers "Creating an account has many benefits: check out faster, keep more than one address, track orders and more.","Creating an account has many benefits: check out faster, keep more than one address, track orders and more." Company,Company Fax,Fax Gender,Gender Name,Name -"Tax/VAT number","Tax/VAT number" -"Phone Number","Phone Number" -"Welcome to %store_name","Welcome to %store_name" +Tax/VAT number,Tax/VAT number +Phone Number,Phone Number +Welcome to %store_name,Welcome to %store_name "%name,","%name," -"Welcome to %store_name.","Welcome to %store_name." +Welcome to %store_name.,Welcome to %store_name. "To sign in to our site, use these credentials during checkout or on the <a href=""%customer_url"">My Account</a> page:","To sign in to our site, use these credentials during checkout or on the <a href=""%customer_url"">My Account</a> page:" Email:,Email: Password:,Password: -"Password you set when creating account","Password you set when creating account" +Password you set when creating account,Password you set when creating account "Forgot your account password? Click <a href=""%reset_url"">here</a> to reset it.","Forgot your account password? Click <a href=""%reset_url"">here</a> to reset it." "When you sign in to your account, you will be able to:","When you sign in to your account, you will be able to:" -"Proceed through checkout faster","Proceed through checkout faster" -"Check the status of orders","Check the status of orders" -"View past orders","View past orders" -"Store alternative addresses (for shipping to multiple family members and friends)","Store alternative addresses (for shipping to multiple family members and friends)" -"Please confirm your %store_name account","Please confirm your %store_name account" -"You must confirm your %customer_email email before you can sign in (link is only valid once):","You must confirm your %customer_email email before you can sign in (link is only valid once):" -"Confirm Your Account","Confirm Your Account" -"Thank you for confirming your %store_name account.","Thank you for confirming your %store_name account." +Proceed through checkout faster,Proceed through checkout faster +Check the status of orders,Check the status of orders +View past orders,View past orders +Store alternative addresses (for shipping to multiple family members and friends),Store alternative addresses (for shipping to multiple family members and friends) +Please confirm your %store_name account,Please confirm your %store_name account +You must confirm your %customer_email email before you can sign in (link is only valid once):,You must confirm your %customer_email email before you can sign in (link is only valid once): +Confirm Your Account,Confirm Your Account +Thank you for confirming your %store_name account.,Thank you for confirming your %store_name account. "To sign in to our site and set a password, click on the <a href=""%create_password_url"">link</a>:","To sign in to our site and set a password, click on the <a href=""%create_password_url"">link</a>:" -"Your %store_name email has been changed","Your %store_name email has been changed" +Your %store_name email has been changed,Your %store_name email has been changed "Hello,","Hello," -"We have received a request to change the following information associated with your account at %store_name: email.","We have received a request to change the following information associated with your account at %store_name: email." +We have received a request to change the following information associated with your account at %store_name: email.,We have received a request to change the following information associated with your account at %store_name: email. "If you have not authorized this action, please contact us immediately at <a href=""mailto:%store_email"">%store_email</a>","If you have not authorized this action, please contact us immediately at <a href=""mailto:%store_email"">%store_email</a>" "or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>" "Thanks,<br>%store_name","Thanks,<br>%store_name" -"Your %store_name email and password has been changed","Your %store_name email and password has been changed" +Your %store_name email and password has been changed,Your %store_name email and password has been changed "We have received a request to change the following information associated with your account at %store_name: email, password.","We have received a request to change the following information associated with your account at %store_name: email, password." -"Reset your %store_name password","Reset your %store_name password" -"There was recently a request to change the password for your account.","There was recently a request to change the password for your account." +Reset your %store_name password,Reset your %store_name password +There was recently a request to change the password for your account.,There was recently a request to change the password for your account. "If you requested this change, set a new password here:","If you requested this change, set a new password here:" "If you did not make this request, you can ignore this email and your password will remain the same.","If you did not make this request, you can ignore this email and your password will remain the same." -"Your %store_name password has been changed","Your %store_name password has been changed" -"We have received a request to change the following information associated with your account at %store_name: password.","We have received a request to change the following information associated with your account at %store_name: password." -"Checkout as a new customer","Checkout as a new customer" -"Creating an account has many benefits:","Creating an account has many benefits:" -"See order and shipping status","See order and shipping status" -"Track order history","Track order history" -"Check out faster","Check out faster" -"Checkout using your account","Checkout using your account" -"Email Address","Email Address" -"Are you sure you want to do this?","Are you sure you want to do this?" -"Are you sure you want to delete this address?","Are you sure you want to delete this address?" +Your %store_name password has been changed,Your %store_name password has been changed +We have received a request to change the following information associated with your account at %store_name: password.,We have received a request to change the following information associated with your account at %store_name: password. +Checkout as a new customer,Checkout as a new customer +Creating an account has many benefits:,Creating an account has many benefits: +See order and shipping status,See order and shipping status +Track order history,Track order history +Check out faster,Check out faster +Checkout using your account,Checkout using your account +Email Address,Email Address +Are you sure you want to do this?,Are you sure you want to do this? +Are you sure you want to delete this address?,Are you sure you want to delete this address? Weak,Weak Medium,Medium Strong,Strong -"Very Strong","Very Strong" -"Guest checkout is disabled.","Guest checkout is disabled." -"All Customers","All Customers" -"Now Online","Now Online" -"Customers Section","Customers Section" -"Customer Configuration","Customer Configuration" -"Account Sharing Options","Account Sharing Options" -"Share Customer Accounts","Share Customer Accounts" -"Create New Account Options","Create New Account Options" -"Enable Automatic Assignment to Customer Group","Enable Automatic Assignment to Customer Group" -"Tax Calculation Based On","Tax Calculation Based On" -"Default Group","Default Group" -"Group for Valid VAT ID - Domestic","Group for Valid VAT ID - Domestic" -"Group for Valid VAT ID - Intra-Union","Group for Valid VAT ID - Intra-Union" -"Group for Invalid VAT ID","Group for Invalid VAT ID" -"Validation Error Group","Validation Error Group" -"Validate on Each Transaction","Validate on Each Transaction" -"Default Value for Disable Automatic Group Changes Based on VAT ID","Default Value for Disable Automatic Group Changes Based on VAT ID" -"Show VAT Number on Storefront","Show VAT Number on Storefront" +Very Strong,Very Strong +Guest checkout is disabled.,Guest checkout is disabled. +All Customers,All Customers +Now Online,Now Online +Customers Section,Customers Section +Customer Configuration,Customer Configuration +Account Sharing Options,Account Sharing Options +Share Customer Accounts,Share Customer Accounts +Create New Account Options,Create New Account Options +Enable Automatic Assignment to Customer Group,Enable Automatic Assignment to Customer Group +Tax Calculation Based On,Tax Calculation Based On +Default Group,Default Group +Group for Valid VAT ID - Domestic,Group for Valid VAT ID - Domestic +Group for Valid VAT ID - Intra-Union,Group for Valid VAT ID - Intra-Union +Group for Invalid VAT ID,Group for Invalid VAT ID +Validation Error Group,Validation Error Group +Validate on Each Transaction,Validate on Each Transaction +Default Value for Disable Automatic Group Changes Based on VAT ID,Default Value for Disable Automatic Group Changes Based on VAT ID +Show VAT Number on Storefront,Show VAT Number on Storefront "To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.","To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes." -"Default Email Domain","Default Email Domain" -"Default Welcome Email","Default Welcome Email" +Default Email Domain,Default Email Domain +Default Welcome Email,Default Welcome Email "Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." -"Default Welcome Email Without Password","Default Welcome Email Without Password" +Default Welcome Email Without Password,Default Welcome Email Without Password " This email will be sent instead of the Default Welcome Email, if a customer was created without password. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. @@ -443,10 +443,10 @@ Strong,Strong This email will be sent instead of the Default Welcome Email, if a customer was created without password. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. " -"Email Sender","Email Sender" -"Require Emails Confirmation","Require Emails Confirmation" -"Confirmation Link Email","Confirmation Link Email" -"Welcome Email","Welcome Email" +Email Sender,Email Sender +Require Emails Confirmation,Require Emails Confirmation +Confirmation Link Email,Confirmation Link Email +Welcome Email,Welcome Email " This email will be sent instead of the Default Welcome Email, after account confirmation. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. @@ -454,88 +454,90 @@ Strong,Strong This email will be sent instead of the Default Welcome Email, after account confirmation. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. " -"Generate Human-Friendly Customer ID","Generate Human-Friendly Customer ID" -"Password Options","Password Options" -"Forgot Email Template","Forgot Email Template" -"Remind Email Template","Remind Email Template" -"Reset Password Template","Reset Password Template" -"Password Template Email Sender","Password Template Email Sender" -"Recovery Link Expiration Period (hours)","Recovery Link Expiration Period (hours)" -"Please enter a number 1 or greater in this field.","Please enter a number 1 or greater in this field." -"Number of Required Character Classes","Number of Required Character Classes" +Generate Human-Friendly Customer ID,Generate Human-Friendly Customer ID +Password Options,Password Options +Forgot Email Template,Forgot Email Template +Remind Email Template,Remind Email Template +Reset Password Template,Reset Password Template +Password Template Email Sender,Password Template Email Sender +Recovery Link Expiration Period (hours),Recovery Link Expiration Period (hours) +Please enter a number 1 or greater in this field.,Please enter a number 1 or greater in this field. +Number of Required Character Classes,Number of Required Character Classes "Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters.","Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters." -"Minimum Password Length","Minimum Password Length" -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"Use 0 to disable account locking.","Use 0 to disable account locking." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Enable Autocomplete on login/forgot password forms","Enable Autocomplete on login/forgot password forms" -"Account Information Options","Account Information Options" -"Change Email Template","Change Email Template" -"Change Email and Password Template","Change Email and Password Template" -"Name and Address Options","Name and Address Options" -"Number of Lines in a Street Address","Number of Lines in a Street Address" -"Leave empty for default (2). Valid range: 1-4","Leave empty for default (2). Valid range: 1-4" -"Show Prefix","Show Prefix" +Minimum Password Length,Minimum Password Length +Maximum Login Failures to Lockout Account,Maximum Login Failures to Lockout Account +Use 0 to disable account locking.,Use 0 to disable account locking. +Lockout Time (minutes),Lockout Time (minutes) +Enable Autocomplete on login/forgot password forms,Enable Autocomplete on login/forgot password forms +Account Information Options,Account Information Options +Change Email Template,Change Email Template +Change Email and Password Template,Change Email and Password Template +Name and Address Options,Name and Address Options +Number of Lines in a Street Address,Number of Lines in a Street Address +Leave empty for default (2). Valid range: 1-4,Leave empty for default (2). Valid range: 1-4 +Show Prefix,Show Prefix "The title that goes before name (Mr., Mrs., etc.)","The title that goes before name (Mr., Mrs., etc.)" -"Prefix Dropdown Options","Prefix Dropdown Options" +Prefix Dropdown Options,Prefix Dropdown Options " Semicolon (;) separated values.<br/>Leave empty for open text field. "," Semicolon (;) separated values.<br/>Leave empty for open text field. " -"Show Middle Name (initial)","Show Middle Name (initial)" -"Always optional.","Always optional." -"Show Suffix","Show Suffix" +Show Middle Name (initial),Show Middle Name (initial) +Always optional.,Always optional. +Show Suffix,Show Suffix "The suffix that goes after name (Jr., Sr., etc.)","The suffix that goes after name (Jr., Sr., etc.)" -"Suffix Dropdown Options","Suffix Dropdown Options" -"Show Date of Birth","Show Date of Birth" -"Show Tax/VAT Number","Show Tax/VAT Number" -"Show Gender","Show Gender" -"Show Telephone","Show Telephone" -"Show Company","Show Company" -"Show Fax","Show Fax" -"Login Options","Login Options" -"Redirect Customer to Account Dashboard after Logging in","Redirect Customer to Account Dashboard after Logging in" +Suffix Dropdown Options,Suffix Dropdown Options +Show Date of Birth,Show Date of Birth +Show Tax/VAT Number,Show Tax/VAT Number +Show Gender,Show Gender +Show Telephone,Show Telephone +Show Company,Show Company +Show Fax,Show Fax +Login Options,Login Options +Redirect Customer to Account Dashboard after Logging in,Redirect Customer to Account Dashboard after Logging in "Customer will stay on the current page if ""No"" is selected.","Customer will stay on the current page if ""No"" is selected." -"Address Templates","Address Templates" -"Online Customers Options","Online Customers Options" -"Online Minutes Interval","Online Minutes Interval" +Address Templates,Address Templates +Online Customers Options,Online Customers Options +Online Minutes Interval,Online Minutes Interval "Only 'b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub', 'sup', 'ul' tags are allowed","Only 'b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub', 'sup', 'ul' tags are allowed" -"Leave empty for default (15 minutes).","Leave empty for default (15 minutes)." -"Customer Notification","Customer Notification" -"Customer Grid","Customer Grid" -"Rebuild Customer grid index","Rebuild Customer grid index" +Leave empty for default (15 minutes).,Leave empty for default (15 minutes). +Customer Notification,Customer Notification +Customer Grid,Customer Grid +Rebuild Customer grid index,Rebuild Customer grid index Group,Group -"Add New Customer","Add New Customer" -"Are you sure you want to delete the selected customers?","Are you sure you want to delete the selected customers?" -"Delete items","Delete items" -"Subscribe to Newsletter","Subscribe to Newsletter" -"Are you sure you want to unsubscribe the selected customers from the newsletter?","Are you sure you want to unsubscribe the selected customers from the newsletter?" -"Unsubscribe from Newsletter","Unsubscribe from Newsletter" -"Assign a Customer Group","Assign a Customer Group" +Add New Customer,Add New Customer +Are you sure you want to delete the selected customers?,Are you sure you want to delete the selected customers? +Delete items,Delete items +Subscribe to Newsletter,Subscribe to Newsletter +Are you sure you want to unsubscribe the selected customers from the newsletter?,Are you sure you want to unsubscribe the selected customers from the newsletter? +Unsubscribe from Newsletter,Unsubscribe from Newsletter +Assign a Customer Group,Assign a Customer Group Phone,Phone ZIP,ZIP -"Customer Since","Customer Since" -"Confirmed email","Confirmed email" -"Account Created in","Account Created in" -"Tax VAT Number","Tax VAT Number" -"Billing Firstname","Billing Firstname" -"Billing Lastname","Billing Lastname" -"Account Lock","Account Lock" -"First Name","First Name" -"Last Name","Last Name" -"Last Activity","Last Activity" +Customer Since,Customer Since +Confirmed email,Confirmed email +Account Created in,Account Created in +Tax VAT Number,Tax VAT Number +Billing Firstname,Billing Firstname +Billing Lastname,Billing Lastname +Account Lock,Account Lock +First Name,First Name +Last Name,Last Name +Last Activity,Last Activity Type,Type -"Customer Information","Customer Information" +Customer Information,Customer Information "If your Magento installation has multiple websites, you can edit the scope to associate the customer with a specific site.","If your Magento installation has multiple websites, you can edit the scope to associate the customer with a specific site." -"Disable Automatic Group Change Based on VAT ID","Disable Automatic Group Change Based on VAT ID" -"Send Welcome Email From","Send Welcome Email From" -"Are you sure you want to delete this item?","Are you sure you want to delete this item?" +Disable Automatic Group Change Based on VAT ID,Disable Automatic Group Change Based on VAT ID +Send Welcome Email From,Send Welcome Email From +Are you sure you want to delete this item?,Are you sure you want to delete this item? Addresses,Addresses -"Edit Account Information","Edit Account Information" -"Password forgotten","Password forgotten" -"You are signed out","You are signed out" -"Associate to Website","Associate to Website" -"Prefix","Prefix" -"Middle Name/Initial","Middle Name/Initial" -"Suffix","Suffix" +Edit Account Information,Edit Account Information +Password forgotten,Password forgotten +You are signed out,You are signed out +Associate to Website,Associate to Website +Prefix,Prefix +Middle Name/Initial,Middle Name/Initial +Suffix,Suffix +Email is required field for Admin order creation,Email is required field for Admin order creation +If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer. diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php old mode 100644 new mode 100755 index 28b1cc9c80a91..fc48e351b0e7e --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -149,7 +149,7 @@ protected function _addAdditionalFormElementData(AbstractElement $element) { switch ($element->getId()) { case 'email': - $element->setRequired($this->isEmailRequiredCreateOrder()); + $element->setRequired($this->isEmailRequiredToCreateOrder()); $element->setClass('validate-email admin__control-text'); break; } @@ -212,7 +212,7 @@ private function extractValuesFromAttributes(array $attributes): array * * @return bool */ - private function isEmailRequiredCreateOrder() + private function isEmailRequiredToCreateOrder() { return $this->_scopeConfig->getValue( self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php old mode 100644 new mode 100755 index 211c6f75dad18..00a971bdf2b84 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -2043,11 +2043,11 @@ protected function _getNewCustomerEmail() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - if($emailrequired) { + if ($emailrequired) { return $this->getData('account/email'); } else { $email = $this->getData('account/email'); - if (empty($email)) { + if (empty($email)) { $host = $this->_scopeConfig->getValue( self::XML_PATH_DEFAULT_EMAIL_DOMAIN, \Magento\Store\Model\ScopeInterface::SCOPE_STORE @@ -2057,11 +2057,10 @@ protected function _getNewCustomerEmail() $account = $this->getData('account'); $account['email'] = $email; $this->setData('account', $account); - } + } return $email; } - } /** From 7e5bb80c72c62eebd4599c8bce8c68d205ae21bd Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 14 Sep 2019 14:19:47 +0530 Subject: [PATCH 061/734] _getNewCustomerEmail seperate it in differnet method --- .../Magento/Sales/Model/AdminOrder/Create.php | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) mode change 100755 => 100644 app/code/Magento/Sales/Model/AdminOrder/Create.php diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php old mode 100755 new mode 100644 index 00a971bdf2b84..1446f4ad1153d --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -2038,31 +2038,51 @@ protected function _validate() */ protected function _getNewCustomerEmail() { - $emailrequired = $this->_scopeConfig->getValue( - self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); + $emailrequired = $this->getIsEmailRequired(); if ($emailrequired) { return $this->getData('account/email'); } else { $email = $this->getData('account/email'); if (empty($email)) { - $host = $this->_scopeConfig->getValue( - self::XML_PATH_DEFAULT_EMAIL_DOMAIN, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $account = time(); - $email = $account . '@' . $host; - $account = $this->getData('account'); - $account['email'] = $email; - $this->setData('account', $account); + $email = $this-> generateEmail(); } - return $email; } } + /** + * Check email is require + * + * @return bool + */ + protected function getIsEmailRequired() + { + return $this->_scopeConfig->getValue( + self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + } + + /** + * Generate Email + * + * @return string + */ + protected function generateEmail() + { + $host = $this->_scopeConfig->getValue( + self::XML_PATH_DEFAULT_EMAIL_DOMAIN, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $account = time(); + $email = $account . '@' . $host; + $account = $this->getData('account'); + $account['email'] = $email; + $this->setData('account', $account); + return $email; + } + /** * Checks id shipping and billing addresses are equal. * From b5265e8446441ebcb38981b69e3494653c106818 Mon Sep 17 00:00:00 2001 From: Oleksandr Kravchuk <swnsma@gmail.com> Date: Mon, 16 Sep 2019 10:36:46 +0300 Subject: [PATCH 062/734] Update Create.php --- .../Magento/Sales/Model/AdminOrder/Create.php | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 1446f4ad1153d..d51fa0778b192 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -2038,17 +2038,13 @@ protected function _validate() */ protected function _getNewCustomerEmail() { - $emailrequired = $this->getIsEmailRequired(); - - if ($emailrequired) { - return $this->getData('account/email'); - } else { - $email = $this->getData('account/email'); - if (empty($email)) { - $email = $this-> generateEmail(); - } - return $email; + $email = $this->getData('account/email'); + + if ($email || $this->getIsEmailRequired()) { + return $email; } + + return $this->generateEmail(); } /** @@ -2056,9 +2052,9 @@ protected function _getNewCustomerEmail() * * @return bool */ - protected function getIsEmailRequired() + private function getIsEmailRequired(): bool { - return $this->_scopeConfig->getValue( + return (bool)$this->_scopeConfig->getValue( self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); @@ -2069,7 +2065,7 @@ protected function getIsEmailRequired() * * @return string */ - protected function generateEmail() + private function generateEmail(): string { $host = $this->_scopeConfig->getValue( self::XML_PATH_DEFAULT_EMAIL_DOMAIN, @@ -2080,6 +2076,7 @@ protected function generateEmail() $account = $this->getData('account'); $account['email'] = $email; $this->setData('account', $account); + return $email; } From 3c1d73e26e4ff18da5bbc3b2eb3780fcea244dbf Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Tue, 17 Sep 2019 17:26:44 +0530 Subject: [PATCH 063/734] system.xml is placed in sales module --- .../Magento/Customer/etc/adminhtml/system.xml | 5 - app/code/Magento/Customer/etc/config.xml | 1 - app/code/Magento/Customer/i18n/en_US.csv | 2 - .../Magento/Sales/etc/adminhtml/system.xml | 10 + app/code/Magento/Sales/etc/config.xml | 5 + app/code/Magento/Sales/i18n/en_US.csv | 1600 +++++++++-------- 6 files changed, 816 insertions(+), 807 deletions(-) mode change 100755 => 100644 app/code/Magento/Customer/i18n/en_US.csv diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 325de3680b463..2bd1041214801 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -86,11 +86,6 @@ <comment>To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="email_required_create_order" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> - <label>Email is required field for Admin order creation</label> - <comment>If set YES Email field will be required during Admin order creation for new Customer.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> <field id="email_domain" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Default Email Domain</label> </field> diff --git a/app/code/Magento/Customer/etc/config.xml b/app/code/Magento/Customer/etc/config.xml index d979cf19fc5f7..da4b80536e631 100644 --- a/app/code/Magento/Customer/etc/config.xml +++ b/app/code/Magento/Customer/etc/config.xml @@ -15,7 +15,6 @@ <confirm>0</confirm> <default_group>1</default_group> <tax_calculation_address_type>billing</tax_calculation_address_type> - <email_required_create_order>0</email_required_create_order> <email_domain>example.com</email_domain> <email_identity>general</email_identity> <email_template>customer_create_account_email_template</email_template> diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv old mode 100755 new mode 100644 index 74c30a47a835e..4ab5e2b7068fb --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -539,5 +539,3 @@ Associate to Website,Associate to Website Prefix,Prefix Middle Name/Initial,Middle Name/Initial Suffix,Suffix -Email is required field for Admin order creation,Email is required field for Admin order creation -If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer. diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index d1e5680b883c2..5b243786d8fd9 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -446,5 +446,15 @@ </field> </group> </section> + <section id="customer"> + <group id="create_account" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Create New Account Options</label> + <field id="email_required_create_order" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Email is required field for Admin order creation</label> + <comment>If set YES Email field will be required during Admin order creation for new Customer.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index 2480da4ad214b..cb921b8b0a1bc 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -111,5 +111,10 @@ <async_indexing>0</async_indexing> </grid> </dev> + <customer> + <create_account> + <email_required_create_order>0</email_required_create_order> + </create_account> + </customer> </default> </config> diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index c5657f3a309f7..fc1fac1311985 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -1,799 +1,801 @@ -"Credit Memos","Credit Memos" -Orders,Orders -Invoices,Invoices -"We can't get the order instance right now.","We can't get the order instance right now." -"Create New Order","Create New Order" -"Save Order Address","Save Order Address" -Shipping,Shipping -Billing,Billing -"Edit Order %1 %2 Address","Edit Order %1 %2 Address" -"Order Address Information","Order Address Information" -"Please correct the parent block for this block.","Please correct the parent block for this block." -"Submit Comment","Submit Comment" -"Submit Order","Submit Order" -"Are you sure you want to cancel this order?","Are you sure you want to cancel this order?" -Cancel,Cancel -"Billing Address","Billing Address" -"Payment Method","Payment Method" -"Order Comment","Order Comment" -Coupons,Coupons -"Please select a customer","Please select a customer" -"Create New Customer","Create New Customer" -"Account Information","Account Information" -From,From -To,To -Message,Message -"Edit Order #%1","Edit Order #%1" -"Create New Order for %1 in %2","Create New Order for %1 in %2" -"Create New Order in %1","Create New Order in %1" -"Create New Order for %1","Create New Order for %1" -"Create New Order for New Customer","Create New Order for New Customer" -"Items Ordered","Items Ordered" -"This product is disabled.","This product is disabled." -"Buy %1 for price %2","Buy %1 for price %2" -"Item ordered qty","Item ordered qty" -"%1 with %2 discount each","%1 with %2 discount each" -"%1 for %2","%1 for %2" -"* - Enter custom price including tax","* - Enter custom price including tax" -"* - Enter custom price excluding tax","* - Enter custom price excluding tax" -Configure,Configure -"This product does not have any configurable options","This product does not have any configurable options" -"Newsletter Subscription","Newsletter Subscription" -"Please select products","Please select products" -"Add Selected Product(s) to Order","Add Selected Product(s) to Order" -ID,ID -Product,Product -SKU,SKU -Price,Price -Select,Select -Quantity,Quantity -"Shipping Address","Shipping Address" -"Shipping Method","Shipping Method" -"Update Changes","Update Changes" -"Shopping Cart","Shopping Cart" -"Are you sure you want to delete all items from shopping cart?","Are you sure you want to delete all items from shopping cart?" -"Clear Shopping Cart","Clear Shopping Cart" -"Products in Comparison List","Products in Comparison List" -"Recently Compared Products","Recently Compared Products" -"Recently Viewed Products","Recently Viewed Products" -"Last Ordered Items","Last Ordered Items" -"Recently Viewed","Recently Viewed" -"Wish List","Wish List" -"Please select a store","Please select a store" -"Order Totals","Order Totals" -"Shipping Incl. Tax (%1)","Shipping Incl. Tax (%1)" -"Shipping Excl. Tax (%1)","Shipping Excl. Tax (%1)" -"New Credit Memo for Invoice #%1","New Credit Memo for Invoice #%1" -"New Credit Memo for Order #%1","New Credit Memo for Order #%1" -"Refund Shipping (Incl. Tax)","Refund Shipping (Incl. Tax)" -"Refund Shipping (Excl. Tax)","Refund Shipping (Excl. Tax)" -"Refund Shipping","Refund Shipping" -"Update Qty's","Update Qty's" -Refund,Refund -"Refund Offline","Refund Offline" -"Paid Amount","Paid Amount" -"Refund Amount","Refund Amount" -"Shipping Amount","Shipping Amount" -"Shipping Refund","Shipping Refund" -"Order Grand Total","Order Grand Total" -"Adjustment Refund","Adjustment Refund" -"Adjustment Fee","Adjustment Fee" -"Send Email","Send Email" -"Are you sure you want to send a credit memo email to customer?","Are you sure you want to send a credit memo email to customer?" -Void,Void -Print,Print -"The credit memo email was sent.","The credit memo email was sent." -"The credit memo email wasn't sent.","The credit memo email wasn't sent." -"Credit Memo #%1 | %3 | %2 (%4)","Credit Memo #%1 | %3 | %2 (%4)" -"Total Refund","Total Refund" -"New Invoice and Shipment for Order #%1","New Invoice and Shipment for Order #%1" -"New Invoice for Order #%1","New Invoice for Order #%1" -"Submit Invoice and Shipment","Submit Invoice and Shipment" -"Submit Invoice","Submit Invoice" -"Are you sure you want to send an invoice email to customer?","Are you sure you want to send an invoice email to customer?" -"Credit Memo","Credit Memo" -Capture,Capture -"The invoice email was sent.","The invoice email was sent." -"The invoice email wasn't sent.","The invoice email wasn't sent." -"Invoice #%1 | %2 | %4 (%3)","Invoice #%1 | %2 | %4 (%3)" -"Invalid parent block for this block","Invalid parent block for this block" -"Order Statuses","Order Statuses" -"Create New Status","Create New Status" -"Assign Status to State","Assign Status to State" -"Save Status Assignment","Save Status Assignment" -"Assign Order Status to State","Assign Order Status to State" -"Assignment Information","Assignment Information" -"Order Status","Order Status" -"Order State","Order State" -"Use Order Status As Default","Use Order Status As Default" -"Visible On Storefront","Visible On Storefront" -"Edit Order Status","Edit Order Status" -"Save Status","Save Status" -"New Order Status","New Order Status" -"Order Status Information","Order Status Information" -"Status Code","Status Code" -"Status Label","Status Label" -"Store View Specific Labels","Store View Specific Labels" -"Total Paid","Total Paid" -"Total Refunded","Total Refunded" -"Total Due","Total Due" -Edit,Edit -"Are you sure you want to send an order email to customer?","Are you sure you want to send an order email to customer?" -"This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?","This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?" -"Are you sure you want to void the payment?","Are you sure you want to void the payment?" -Hold,Hold -hold,hold -Unhold,Unhold -unhold,unhold -"Are you sure you want to accept this payment?","Are you sure you want to accept this payment?" -"Accept Payment","Accept Payment" -"Are you sure you want to deny this payment?","Are you sure you want to deny this payment?" -"Deny Payment","Deny Payment" -"Get Payment Update","Get Payment Update" -"Invoice and Ship","Invoice and Ship" -Invoice,Invoice -Ship,Ship -Reorder,Reorder -"Order # %1 %2 | %3","Order # %1 %2 | %3" -"This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.","This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed." -"Are you sure? This order will be canceled and a new one will be created instead.","Are you sure? This order will be canceled and a new one will be created instead." -"Save Gift Message","Save Gift Message" -" [deleted]"," [deleted]" -"Order Credit Memos","Order Credit Memos" -"Credit memo #%1 created","Credit memo #%1 created" -"Credit memo #%1 comment added","Credit memo #%1 comment added" -"Shipment #%1 created","Shipment #%1 created" -"Shipment #%1 comment added","Shipment #%1 comment added" -"Invoice #%1 created","Invoice #%1 created" -"Invoice #%1 comment added","Invoice #%1 comment added" -"Tracking number %1 for %2 assigned","Tracking number %1 for %2 assigned" -"Comments History","Comments History" -"Order History","Order History" -Information,Information -"Order Information","Order Information" -"Order Invoices","Order Invoices" -Shipments,Shipments -"Order Shipments","Order Shipments" -Transactions,Transactions -"Order View","Order View" -Any,Any -Specified,Specified -"Applies to Any of the Specified Order Statuses except canceled orders","Applies to Any of the Specified Order Statuses except canceled orders" -"Cart Price Rule","Cart Price Rule" -Yes,Yes -No,No -"Show Actual Values","Show Actual Values" -"New Order RSS","New Order RSS" -Subtotal,Subtotal -"Shipping & Handling","Shipping & Handling" -"Discount (%1)","Discount (%1)" -Discount,Discount -"Grand Total","Grand Total" -Back,Back -Fetch,Fetch -"Transaction # %1 | %2","Transaction # %1 | %2" -N/A,N/A -Key,Key -Value,Value -"We found an invalid entity model.","We found an invalid entity model." -"Order # %1","Order # %1" -"Back to My Orders","Back to My Orders" -"View Another Order","View Another Order" -"About Your Refund","About Your Refund" -"My Orders","My Orders" -"Subscribe to Order Status","Subscribe to Order Status" -"About Your Invoice","About Your Invoice" -"Print Order # %1","Print Order # %1" -"Grand Total to be Charged","Grand Total to be Charged" -Unassign,Unassign -"We can't add this item to your shopping cart right now.","We can't add this item to your shopping cart right now." -"You sent the message.","You sent the message." -Sales,Sales -"Invoice capturing error","Invoice capturing error" -"This order no longer exists.","This order no longer exists." -"Please enter a comment.","Please enter a comment." -"We cannot add order history.","We cannot add order history." -"You updated the order address.","You updated the order address." -"We can't update the order address right now.","We can't update the order address right now." -"You have not canceled the item.","You have not canceled the item." -"You canceled the order.","You canceled the order." -"""%1"" coupon code was not applied. Do not apply discount is selected for item(s)","""%1"" coupon code was not applied. Do not apply discount is selected for item(s)" -"""%1"" coupon code is not valid.","""%1"" coupon code is not valid." -"The coupon code has been accepted.","The coupon code has been accepted." -"Quote item id is not received.","Quote item id is not received." -"Quote item is not loaded.","Quote item is not loaded." -"New Order","New Order" -"You created the order.","You created the order." -"Order saving error: %1","Order saving error: %1" -"Cannot add new comment.","Cannot add new comment." -"The credit memo has been canceled.","The credit memo has been canceled." -"Credit memo has not been canceled.","Credit memo has not been canceled." -"New Memo for #%1","New Memo for #%1" -"New Memo","New Memo" -"The credit memo's total must be positive.","The credit memo's total must be positive." -"Cannot create online refund for Refund to Store Credit.","Cannot create online refund for Refund to Store Credit." -"You created the credit memo.","You created the credit memo." -"We can't save the credit memo right now.","We can't save the credit memo right now." -"We can't update the item's quantity right now.","We can't update the item's quantity right now." -"View Memo for #%1","View Memo for #%1" -"View Memo","View Memo" -"You voided the credit memo.","You voided the credit memo." -"We can't void the credit memo.","We can't void the credit memo." -"The order no longer exists.","The order no longer exists." -"We can't create credit memo for the order.","We can't create credit memo for the order." -"Edit Order","Edit Order" -"You sent the order email.","You sent the order email." -"We can't send the email order right now.","We can't send the email order right now." -"You have not put the order on hold.","You have not put the order on hold." -"You put the order on hold.","You put the order on hold." -"You canceled the invoice.","You canceled the invoice." -"Invoice canceling error","Invoice canceling error" -"The invoice has been captured.","The invoice has been captured." -"The order does not allow an invoice to be created.","The order does not allow an invoice to be created." -"You can't create an invoice without products.","You can't create an invoice without products." -"New Invoice","New Invoice" -"We can't save the invoice right now.","We can't save the invoice right now." -"You created the invoice and shipment.","You created the invoice and shipment." -"The invoice has been created.","The invoice has been created." -"We can't send the invoice email right now.","We can't send the invoice email right now." -"We can't send the shipment right now.","We can't send the shipment right now." -"Cannot update item quantity.","Cannot update item quantity." -"The invoice has been voided.","The invoice has been voided." -"Invoice voiding error","Invoice voiding error" -"%1 order(s) cannot be canceled.","%1 order(s) cannot be canceled." -"You cannot cancel the order(s).","You cannot cancel the order(s)." -"We canceled %1 order(s).","We canceled %1 order(s)." -"%1 order(s) were not put on hold.","%1 order(s) were not put on hold." -"No order(s) were put on hold.","No order(s) were put on hold." -"You have put %1 order(s) on hold.","You have put %1 order(s) on hold." -"%1 order(s) were not released from on hold status.","%1 order(s) were not released from on hold status." -"No order(s) were released from on hold status.","No order(s) were released from on hold status." -"%1 order(s) have been released from on hold status.","%1 order(s) have been released from on hold status." -"There are no printable documents related to selected orders.","There are no printable documents related to selected orders." -"The payment has been accepted.","The payment has been accepted." -"The payment has been denied.","The payment has been denied." -"Transaction has been approved.","Transaction has been approved." -"Transaction has been voided/declined.","Transaction has been voided/declined." -"There is no update for the transaction.","There is no update for the transaction." -"We can't update the payment right now.","We can't update the payment right now." -"You assigned the order status.","You assigned the order status." -"Something went wrong while assigning the order status.","Something went wrong while assigning the order status." -"We can't find this order status.","We can't find this order status." -"Create New Order Status","Create New Order Status" -"We found another order status with the same order status code.","We found another order status with the same order status code." -"You saved the order status.","You saved the order status." -"We can't add the order status right now.","We can't add the order status right now." -"You have unassigned the order status.","You have unassigned the order status." -"Something went wrong while unassigning the order.","Something went wrong while unassigning the order." -"Can't unhold order.","Can't unhold order." -"You released the order from holding status.","You released the order from holding status." -"The order was not on hold.","The order was not on hold." -"Exception occurred during order load","Exception occurred during order load" -"Something went wrong while saving the gift message.","Something went wrong while saving the gift message." -"You saved the gift card message.","You saved the gift card message." -"The payment has been voided.","The payment has been voided." -"We can't void the payment right now.","We can't void the payment right now." -"Please correct the transaction ID and try again.","Please correct the transaction ID and try again." -"The transaction details have been updated.","The transaction details have been updated." -"We can't update the transaction details.","We can't update the transaction details." -"Orders and Returns","Orders and Returns" -"You entered incorrect data. Please try again.","You entered incorrect data. Please try again." -Home,Home -"Go to Home Page","Go to Home Page" -"We can't find this wish list.","We can't find this wish list." -"We could not add a product to cart by the ID ""%1"".","We could not add a product to cart by the ID ""%1""." -"There is an error in one of the option rows.","There is an error in one of the option rows." -"Shipping Address: ","Shipping Address: " -"Billing Address: ","Billing Address: " -"Please specify order items.","Please specify order items." -"Please specify a shipping method.","Please specify a shipping method." -"Please specify a payment method.","Please specify a payment method." -"This payment method is not available.","This payment method is not available." -"Validation is failed.","Validation is failed." -"You did not email your customer. Please check your email settings.","You did not email your customer. Please check your email settings." -"-- Please Select --","-- Please Select --" -"Path ""%1"" is not part of allowed directory ""%2""","Path ""%1"" is not part of allowed directory ""%2""" -"Identifying Fields required","Identifying Fields required" -"Id required","Id required" -"""Invoice Document Validation Error(s):\n"" .","""Invoice Document Validation Error(s):\n"" ." -"Could not save an invoice, see error log for details","Could not save an invoice, see error log for details" -"A hold action is not available.","A hold action is not available." -"You cannot remove the hold.","You cannot remove the hold." -"We cannot cancel this order.","We cannot cancel this order." -Guest,Guest -"Please enter the first name.","Please enter the first name." -"Please enter the last name.","Please enter the last name." -"Please enter the street.","Please enter the street." -"Please enter the city.","Please enter the city." -"Please enter the phone number.","Please enter the phone number." -"Please enter the company.","Please enter the company." -"Please enter the fax number.","Please enter the fax number." -"Please enter the zip/postal code.","Please enter the zip/postal code." -"Please enter the country.","Please enter the country." -"Please enter the state/province.","Please enter the state/province." -"Requested entity doesn't exist","Requested entity doesn't exist" -"Could not delete order address","Could not delete order address" -"Could not save order address","Could not save order address" -Pending,Pending -Refunded,Refunded -Canceled,Canceled -"Unknown State","Unknown State" -"We found an invalid quantity to refund item ""%1"".","We found an invalid quantity to refund item ""%1""." -"The creditmemo contains product item that is not part of the original order.","The creditmemo contains product item that is not part of the original order." -"The quantity to refund must not be greater than the unrefunded quantity.","The quantity to refund must not be greater than the unrefunded quantity." -"Maximum shipping amount allowed to refund is: %1","Maximum shipping amount allowed to refund is: %1" -"Order Id is required for creditmemo document","Order Id is required for creditmemo document" -"The creditmemo contains product SKU ""%1"" that is not part of the original order.","The creditmemo contains product SKU ""%1"" that is not part of the original order." -"The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".","The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1""." -"You can't create a creditmemo without products.","You can't create a creditmemo without products." -"The most money available to refund is %1.","The most money available to refund is %1." -"Could not delete credit memo","Could not delete credit memo" -"Could not save credit memo","Could not save credit memo" -"This order already has associated customer account","This order already has associated customer account" -Paid,Paid -"We cannot register an existing invoice","We cannot register an existing invoice" -"We can't create creditmemo for the invoice.","We can't create creditmemo for the invoice." -"Order Id is required for invoice document","Order Id is required for invoice document" -"The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".","The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1""." -"The invoice contains one or more items that are not part of the original order.","The invoice contains one or more items that are not part of the original order." -"ID required","ID required" -"Unknown Status","Unknown Status" -Ordered,Ordered -Shipped,Shipped -Invoiced,Invoiced -Backordered,Backordered -Returned,Returned -Partial,Partial -Mixed,Mixed -"Registered a Void notification.","Registered a Void notification." -"If the invoice was created offline, try creating an offline credit memo.","If the invoice was created offline, try creating an offline credit memo." -"We refunded %1 online.","We refunded %1 online." -"We refunded %1 offline.","We refunded %1 offline." -"IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.","IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo." -"The credit memo has been created automatically.","The credit memo has been created automatically." -"Registered notification about refunded amount of %1.","Registered notification about refunded amount of %1." -"Canceled order online","Canceled order online" -"Canceled order offline","Canceled order offline" -"Approved the payment online.","Approved the payment online." -"There is no need to approve this payment.","There is no need to approve this payment." -"Denied the payment online","Denied the payment online" -"Registered update about approved payment.","Registered update about approved payment." -"Registered update about denied payment.","Registered update about denied payment." -"There is no update for the payment.","There is no update for the payment." -"Voided authorization.","Voided authorization." -"Amount: %1.","Amount: %1." -"Transaction ID: ""%1""","Transaction ID: ""%1""" -"The payment method you requested is not available.","The payment method you requested is not available." -"The payment disallows storing objects.","The payment disallows storing objects." -"The transaction ""%1"" cannot be captured yet.","The transaction ""%1"" cannot be captured yet." -"The order amount of %1 is pending approval on the payment gateway.","The order amount of %1 is pending approval on the payment gateway." -"Ordered amount of %1","Ordered amount of %1" -"An amount of %1 will be captured after being approved at the payment gateway.","An amount of %1 will be captured after being approved at the payment gateway." -"Registered notification about captured amount of %1.","Registered notification about captured amount of %1." -"Order is suspended as its capture amount %1 is suspected to be fraudulent.","Order is suspended as its capture amount %1 is suspected to be fraudulent." -"The parent transaction ID must have a transaction ID.","The parent transaction ID must have a transaction ID." -"Payment transactions disallow storing objects.","Payment transactions disallow storing objects." -"The transaction ""%1"" (%2) is already closed.","The transaction ""%1"" (%2) is already closed." -"Set order for existing transactions not allowed","Set order for existing transactions not allowed" -"At minimum, you need to set a payment ID.","At minimum, you need to set a payment ID." -Order,Order -Authorization,Authorization -"We found an unsupported transaction type ""%1"".","We found an unsupported transaction type ""%1""." -"Please set a proper payment and order id.","Please set a proper payment and order id." -"Please enter a Transaction ID.","Please enter a Transaction ID." -"You can't do this without a transaction object.","You can't do this without a transaction object." -"Order # ","Order # " -"Order Date: ","Order Date: " -"Sold to:","Sold to:" -"Ship to:","Ship to:" -"Payment Method:","Payment Method:" -"Shipping Method:","Shipping Method:" -"Total Shipping Charges","Total Shipping Charges" -Title,Title -Number,Number -"We found an invalid renderer model.","We found an invalid renderer model." -"Please define the PDF object before using.","Please define the PDF object before using." -"We don't recognize the draw line data. Please define the ""lines"" array.","We don't recognize the draw line data. Please define the ""lines"" array." -Products,Products -"Total (ex)","Total (ex)" -Qty,Qty -Tax,Tax -"Total (inc)","Total (inc)" -"Credit Memo # ","Credit Memo # " -"Invoice # ","Invoice # " -"The order object is not specified.","The order object is not specified." -"The source object is not specified.","The source object is not specified." -"An item object is not specified.","An item object is not specified." -"A PDF object is not specified.","A PDF object is not specified." -"A PDF page object is not specified.","A PDF page object is not specified." -"Excl. Tax","Excl. Tax" -"Incl. Tax","Incl. Tax" -"Packing Slip # ","Packing Slip # " -title,title -"The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.","The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal." -"We cannot register an existing shipment","We cannot register an existing shipment" -"Parent shipment cannot be loaded for track object.","Parent shipment cannot be loaded for track object." -"Order Id is required for shipment document","Order Id is required for shipment document" -"You can't create a shipment without products.","You can't create a shipment without products." -"The shipment contains product SKU ""%1"" that is not part of the original order.","The shipment contains product SKU ""%1"" that is not part of the original order." -"The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".","The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1""." -"Please enter a tracking number.","Please enter a tracking number." -"Could not delete shipment","Could not delete shipment" -"Could not save shipment","Could not save shipment" -"The last status can't be unassigned from its current state.","The last status can't be unassigned from its current state." -"Status can't be unassigned, because it is used by existing order(s).","Status can't be unassigned, because it is used by existing order(s)." -"The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.","The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal." -"An invoice cannot be created when an order has a status of %1","An invoice cannot be created when an order has a status of %1" -"A creditmemo can not be created when an order has a status of %1","A creditmemo can not be created when an order has a status of %1" -"The order does not allow a creditmemo to be created.","The order does not allow a creditmemo to be created." -"A shipment cannot be created when an order has a status of %1","A shipment cannot be created when an order has a status of %1" -"The order does not allow a shipment to be created.","The order does not allow a shipment to be created." -"""Creditmemo Document Validation Error(s):\n"" .","""Creditmemo Document Validation Error(s):\n"" ." -"Could not save a Creditmemo, see error log for details","Could not save a Creditmemo, see error log for details" -"We cannot determine the field name.","We cannot determine the field name." -City,City -Company,Company -Country,Country -Email,Email -"First Name","First Name" -"Last Name","Last Name" -State/Province,State/Province -"Street Address","Street Address" -"Phone Number","Phone Number" -"Zip/Postal Code","Zip/Postal Code" -"We can't save the address:\n%1","We can't save the address:\n%1" -"Cannot save comment:\n%1","Cannot save comment:\n%1" -"We don't have enough information to save the parent transaction ID.","We don't have enough information to save the parent transaction ID." -"We cannot create an empty shipment.","We cannot create an empty shipment." -"Cannot save track:\n%1","Cannot save track:\n%1" -"Cannot unassign status from state","Cannot unassign status from state" -"New Orders","New Orders" -"Order #%1 created at %2","Order #%1 created at %2" -"Details for %1 #%2","Details for %1 #%2" -"Notified Date: %1","Notified Date: %1" -"Comment: %1<br/>","Comment: %1<br/>" -"Current Status: %1<br/>","Current Status: %1<br/>" -"Total: %1<br/>","Total: %1<br/>" -"Order # %1 Notification(s)","Order # %1 Notification(s)" -"You can not cancel Credit Memo","You can not cancel Credit Memo" -"Could not cancel creditmemo","Could not cancel creditmemo" -"We cannot register an existing credit memo.","We cannot register an existing credit memo." -"We found an invalid quantity to invoice item ""%1"".","We found an invalid quantity to invoice item ""%1""." -"The Order State ""%1"" must not be set manually.","The Order State ""%1"" must not be set manually." -"""Shipment Document Validation Error(s):\n"" .","""Shipment Document Validation Error(s):\n"" ." -"Could not save a shipment, see error log for details","Could not save a shipment, see error log for details" -"VAT Request Identifier","VAT Request Identifier" -"VAT Request Date","VAT Request Date" -"Pending Payment","Pending Payment" -Processing,Processing -"On Hold","On Hold" -Complete,Complete -Closed,Closed -"Suspected Fraud","Suspected Fraud" -"Payment Review","Payment Review" -New,New -"test message","test message" -"Email has not been sent","Email has not been sent" -"Authorized amount of %1.","Authorized amount of %1." -"We will authorize %1 after the payment is approved at the payment gateway.","We will authorize %1 after the payment is approved at the payment gateway." -"Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.","Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent." -"Captured amount of %1 online.","Captured amount of %1 online." -"Authorized amount of %1","Authorized amount of %1" -" Transaction ID: ""%1"""," Transaction ID: ""%1""" -View,View -"Group was removed","Group was removed" -"Changing address information will not recalculate shipping, tax or other order amount.","Changing address information will not recalculate shipping, tax or other order amount." -"Comment Text","Comment Text" -"Notify Customer by Email","Notify Customer by Email" -"Visible on Storefront","Visible on Storefront" -Customer,Customer -Notified,Notified -"Not Notified","Not Notified" -"No Payment Methods","No Payment Methods" -"Order Comments","Order Comments" -"Apply Coupon Code","Apply Coupon Code" -Apply,Apply -"Remove Coupon Code","Remove Coupon Code" -Remove,Remove -"Address Information","Address Information" -"Payment & Shipping Information","Payment & Shipping Information" -"Order Total","Order Total" -"Order Currency:","Order Currency:" -"Same As Billing Address","Same As Billing Address" -"Select from existing customer addresses:","Select from existing customer addresses:" -"Add New Address","Add New Address" -"Save in address book","Save in address book" -"You don't need to select a shipping address.","You don't need to select a shipping address." -"Gift Message for the Entire Order","Gift Message for the Entire Order" -"Leave this box blank if you don't want to leave a gift message for the entire order.","Leave this box blank if you don't want to leave a gift message for the entire order." -"Row Subtotal","Row Subtotal" -Action,Action -"No ordered items","No ordered items" -"Update Items and Quantities","Update Items and Quantities" -"Total %1 product(s)","Total %1 product(s)" -Subtotal:,Subtotal: -"Tier Pricing","Tier Pricing" -"Custom Price","Custom Price" -"Please select","Please select" -"Move to Shopping Cart","Move to Shopping Cart" -"Move to Wish List","Move to Wish List" -"Subscribe to Newsletter","Subscribe to Newsletter" -"Click to change shipping method","Click to change shipping method" -"Sorry, no quotes are available for this order.","Sorry, no quotes are available for this order." -"Get shipping methods and rates","Get shipping methods and rates" -"You don't need to select a shipping method.","You don't need to select a shipping method." -"Customer's Activities","Customer's Activities" -Refresh,Refresh -Item,Item -"Add To Order","Add To Order" -"Configure and Add to Order","Configure and Add to Order" -"No items","No items" -"Append Comments","Append Comments" -"Email Order Confirmation","Email Order Confirmation" -"Grand Total Excl. Tax","Grand Total Excl. Tax" -"Grand Total Incl. Tax","Grand Total Incl. Tax" -"Subtotal (Excl. Tax)","Subtotal (Excl. Tax)" -"Subtotal (Incl. Tax)","Subtotal (Incl. Tax)" -"Payment & Shipping Method","Payment & Shipping Method" -"Payment Information","Payment Information" -"The order was placed using %1.","The order was placed using %1." -"Shipping Information","Shipping Information" -"Items to Refund","Items to Refund" -"Return to Stock","Return to Stock" -"Qty to Refund","Qty to Refund" -"Tax Amount","Tax Amount" -"Discount Amount","Discount Amount" -"Row Total","Row Total" -"No Items To Refund","No Items To Refund" -"Credit Memo Comments","Credit Memo Comments" -"Refund Totals","Refund Totals" -"Email Copy of Credit Memo","Email Copy of Credit Memo" -"Please enter a positive number in this field.","Please enter a positive number in this field." -"Items Refunded","Items Refunded" -"No Items","No Items" -"Memo Total","Memo Total" -"Credit Memo History","Credit Memo History" -"Credit Memo Totals","Credit Memo Totals" -"Customer Name: %1","Customer Name: %1" -"Purchased From: %1","Purchased From: %1" -"Gift Message","Gift Message" -From:,From: -To:,To: -Message:,Message: -"Shipping & Handling","Shipping & Handling" -"Gift Options","Gift Options" -"Create Shipment","Create Shipment" -"Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.","Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice." -%1,%1 -"Qty to Invoice","Qty to Invoice" -"Invoice History","Invoice History" -"Invoice Comments","Invoice Comments" -"Invoice Totals","Invoice Totals" -Amount,Amount -"Capture Online","Capture Online" -"Capture Offline","Capture Offline" -"Not Capture","Not Capture" -"The invoice will be created offline without the payment gateway.","The invoice will be created offline without the payment gateway." -"Email Copy of Invoice","Email Copy of Invoice" -"Items Invoiced","Items Invoiced" -"Total Tax","Total Tax" -"From Name","From Name" -"To Name","To Name" -Status,Status -Comment,Comment -"Notification Not Applicable","Notification Not Applicable" -"Order & Account Information","Order & Account Information" -"The order confirmation email was sent","The order confirmation email was sent" -"The order confirmation email is not sent","The order confirmation email is not sent" -"Order Date","Order Date" -"Order Date (%1)","Order Date (%1)" -"Purchased From","Purchased From" -"Link to the New Order","Link to the New Order" -"Link to the Previous Order","Link to the Previous Order" -"Placed from IP","Placed from IP" -"%1 / %2 rate:","%1 / %2 rate:" -"Customer Name","Customer Name" -"Customer Group","Customer Group" -"Notes for this Order","Notes for this Order" -"Comment added","Comment added" -"Transaction Data","Transaction Data" -"Transaction ID","Transaction ID" -"Parent Transaction ID","Parent Transaction ID" -"Order ID","Order ID" -"Transaction Type","Transaction Type" -"Is Closed","Is Closed" -"Created At","Created At" -"Child Transactions","Child Transactions" -"Transaction Details","Transaction Details" -Items,Items -"Gift Message for this Order","Gift Message for this Order" -"Shipped By","Shipped By" -"Tracking Number","Tracking Number" -"Billing Last Name","Billing Last Name" -"Find Order By","Find Order By" -"ZIP Code","ZIP Code" -"Billing ZIP Code","Billing ZIP Code" -Continue,Continue -"Print All Refunds","Print All Refunds" -"Refund #","Refund #" -"Print Refund","Print Refund" -"Product Name","Product Name" -"Order #","Order #" -Date,Date -"Ship To","Ship To" -Actions,Actions -"View Order","View Order" -"You have placed no orders.","You have placed no orders." -"No shipping information available","No shipping information available" -"Print Order","Print Order" -"Print All Invoices","Print All Invoices" -"Invoice #","Invoice #" -"Print Invoice","Print Invoice" -"Qty Invoiced","Qty Invoiced" -Close,Close -"About Your Order","About Your Order" -"<span class=""label"">Order Date:</span> %1","<span class=""label"">Order Date:</span> %1" -"Refund #%1","Refund #%1" -"Shipment #%1","Shipment #%1" -"Qty Shipped","Qty Shipped" -"Recent Orders","Recent Orders" -"View All","View All" -"Gift Message for This Order","Gift Message for This Order" -"Recently Ordered","Recently Ordered" -"Add to Cart","Add to Cart" -Search,Search -"Credit memo for your %store_name order","Credit memo for your %store_name order" -"%name,","%name," -"Thank you for your order from %store_name.","Thank you for your order from %store_name." -"You can check the status of your order by <a href=""%account_url"">logging into your account</a>.","You can check the status of your order by <a href=""%account_url"">logging into your account</a>." -"If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>","If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>" -"or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>" -"Our hours are <span class=""no-link"">%store_hours</span>.","Our hours are <span class=""no-link"">%store_hours</span>." -"Your Credit Memo #%creditmemo_id for Order #%order_id","Your Credit Memo #%creditmemo_id for Order #%order_id" -"Billing Info","Billing Info" -"Shipping Info","Shipping Info" -"Update to your %store_name credit memo","Update to your %store_name credit memo" -"Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.","Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." -"Invoice for your %store_name order","Invoice for your %store_name order" -"Your Invoice #%invoice_id for Order #%order_id","Your Invoice #%invoice_id for Order #%order_id" -"Update to your %store_name invoice","Update to your %store_name invoice" -"Your %store_name order confirmation","Your %store_name order confirmation" -"%customer_name,","%customer_name," -"Once your package ships we will send you a tracking number.","Once your package ships we will send you a tracking number." -"Your Order <span class=""no-link"">#%increment_id</span>","Your Order <span class=""no-link"">#%increment_id</span>" -"Placed on <span class=""no-link"">%created_at</span>","Placed on <span class=""no-link"">%created_at</span>" -"Once your package ships we will send an email with a link to track your order.","Once your package ships we will send an email with a link to track your order." -"Update to your %store_name order","Update to your %store_name order" -"Your %store_name order has shipped","Your %store_name order has shipped" -"Your shipping confirmation is below. Thank you again for your business.","Your shipping confirmation is below. Thank you again for your business." -"Your Shipment #%shipment_id for Order #%order_id","Your Shipment #%shipment_id for Order #%order_id" -"Update to your %store_name shipment","Update to your %store_name shipment" -"Gift Options for ","Gift Options for " -"Add Products","Add Products" -"You have item changes","You have item changes" -Ok,Ok -Operations,Operations -Create,Create -"Send Order Email","Send Order Email" -"Accept or Deny Payment","Accept or Deny Payment" -"Send Sales Emails","Send Sales Emails" -"Sales Section","Sales Section" -"Sales Emails Section","Sales Emails Section" -General,General -"Hide Customer IP","Hide Customer IP" -"Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.","Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos." -"Checkout Totals Sort Order","Checkout Totals Sort Order" -"Allow Reorder","Allow Reorder" -"Invoice and Packing Slip Design","Invoice and Packing Slip Design" -"Logo for PDF Print-outs (200x50)","Logo for PDF Print-outs (200x50)" -"Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.","Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image." -"Logo for HTML Print View","Logo for HTML Print View" -"Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)","Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)" -Address,Address -"Minimum Order Amount","Minimum Order Amount" -Enable,Enable -"Minimum Amount","Minimum Amount" -"Subtotal after discount","Subtotal after discount" -"Include Tax to Amount","Include Tax to Amount" -"Description Message","Description Message" -"This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.","This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount." -"Error to Show in Shopping Cart","Error to Show in Shopping Cart" -"Validate Each Address Separately in Multi-address Checkout","Validate Each Address Separately in Multi-address Checkout" -"Multi-address Description Message","Multi-address Description Message" -"We'll use the default description above if you leave this empty.","We'll use the default description above if you leave this empty." -"Multi-address Error to Show in Shopping Cart","Multi-address Error to Show in Shopping Cart" -"We'll use the default error above if you leave this empty.","We'll use the default error above if you leave this empty." -Dashboard,Dashboard -"Use Aggregated Data","Use Aggregated Data" -"Orders Cron Settings","Orders Cron Settings" -"Pending Payment Order Lifetime (minutes)","Pending Payment Order Lifetime (minutes)" -"Sales Emails","Sales Emails" -"Asynchronous sending","Asynchronous sending" -Enabled,Enabled -"New Order Confirmation Email Sender","New Order Confirmation Email Sender" -"New Order Confirmation Template","New Order Confirmation Template" -"Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." -"New Order Confirmation Template for Guest","New Order Confirmation Template for Guest" -"Send Order Email Copy To","Send Order Email Copy To" -Comma-separated,Comma-separated -"Send Order Email Copy Method","Send Order Email Copy Method" -"Order Comment Email Sender","Order Comment Email Sender" -"Order Comment Email Template","Order Comment Email Template" -"Order Comment Email Template for Guest","Order Comment Email Template for Guest" -"Send Order Comment Email Copy To","Send Order Comment Email Copy To" -"Send Order Comments Email Copy Method","Send Order Comments Email Copy Method" -"Invoice Email Sender","Invoice Email Sender" -"Invoice Email Template","Invoice Email Template" -"Invoice Email Template for Guest","Invoice Email Template for Guest" -"Send Invoice Email Copy To","Send Invoice Email Copy To" -"Send Invoice Email Copy Method","Send Invoice Email Copy Method" -"Invoice Comment Email Sender","Invoice Comment Email Sender" -"Invoice Comment Email Template","Invoice Comment Email Template" -"Invoice Comment Email Template for Guest","Invoice Comment Email Template for Guest" -"Send Invoice Comment Email Copy To","Send Invoice Comment Email Copy To" -"Send Invoice Comments Email Copy Method","Send Invoice Comments Email Copy Method" -Shipment,Shipment -"Shipment Email Sender","Shipment Email Sender" -"Shipment Email Template","Shipment Email Template" -"Shipment Email Template for Guest","Shipment Email Template for Guest" -"Send Shipment Email Copy To","Send Shipment Email Copy To" -"Send Shipment Email Copy Method","Send Shipment Email Copy Method" -"Shipment Comments","Shipment Comments" -"Shipment Comment Email Sender","Shipment Comment Email Sender" -"Shipment Comment Email Template","Shipment Comment Email Template" -"Shipment Comment Email Template for Guest","Shipment Comment Email Template for Guest" -"Send Shipment Comment Email Copy To","Send Shipment Comment Email Copy To" -"Send Shipment Comments Email Copy Method","Send Shipment Comments Email Copy Method" -"Credit Memo Email Sender","Credit Memo Email Sender" -"Credit Memo Email Template","Credit Memo Email Template" -"Credit Memo Email Template for Guest","Credit Memo Email Template for Guest" -"Send Credit Memo Email Copy To","Send Credit Memo Email Copy To" -"Send Credit Memo Email Copy Method","Send Credit Memo Email Copy Method" -"Credit Memo Comment Email Sender","Credit Memo Comment Email Sender" -"Credit Memo Comment Email Template","Credit Memo Comment Email Template" -"Credit Memo Comment Email Template for Guest","Credit Memo Comment Email Template for Guest" -"Send Credit Memo Comment Email Copy To","Send Credit Memo Comment Email Copy To" -"Send Credit Memo Comments Email Copy Method","Send Credit Memo Comments Email Copy Method" -"PDF Print-outs","PDF Print-outs" -"Display Order ID in Header","Display Order ID in Header" -"Customer Order Status Notification","Customer Order Status Notification" -"Asynchronous indexing","Asynchronous indexing" -"Orders and Returns Search Form","Orders and Returns Search Form" -"Anchor Custom Title","Anchor Custom Title" -Template,Template -"Default Template","Default Template" -Name,Name -Phone,Phone -"ZIP/Post Code","ZIP/Post Code" -"Signed-up Point","Signed-up Point" -Website,Website -"Bill-to Name","Bill-to Name" -Created,Created -"Invoice Date","Invoice Date" -"Ship-to Name","Ship-to Name" -"Ship Date","Ship Date" -"Total Quantity","Total Quantity" -"Default Status","Default Status" -"State Code and Title","State Code and Title" -"Item Status","Item Status" -"Original Price","Original Price" -"Tax Percent","Tax Percent" -"All Store Views","All Store Views" -"PDF Credit Memos","PDF Credit Memos" -"Purchase Point","Purchase Point" -"Print Invoices","Print Invoices" -"Print Packing Slips","Print Packing Slips" -"Print Credit Memos","Print Credit Memos" -"Print All","Print All" -"Print Shipping Labels","Print Shipping Labels" -"Purchase Date","Purchase Date" -"Grand Total (Base)","Grand Total (Base)" -"Grand Total (Purchased)","Grand Total (Purchased)" -"Customer Email","Customer Email" -"Shipping and Handling","Shipping and Handling" -"PDF Invoices","PDF Invoices" -"PDF Shipments","PDF Shipments" -"PDF Creditmemos","PDF Creditmemos" -Refunds,Refunds -"Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" -"Allow Zero GrandTotal","Allow Zero GrandTotal" +Credit Memos,Credit Memos,, +Orders,Orders,, +Invoices,Invoices,, +We can't get the order instance right now.,We can't get the order instance right now.,, +Create New Order,Create New Order,, +Save Order Address,Save Order Address,, +Shipping,Shipping,, +Billing,Billing,, +Edit Order %1 %2 Address,Edit Order %1 %2 Address,, +Order Address Information,Order Address Information,, +Please correct the parent block for this block.,Please correct the parent block for this block.,, +Submit Comment,Submit Comment,, +Submit Order,Submit Order,, +Are you sure you want to cancel this order?,Are you sure you want to cancel this order?,, +Cancel,Cancel,, +Billing Address,Billing Address,, +Payment Method,Payment Method,, +Order Comment,Order Comment,, +Coupons,Coupons,, +Please select a customer,Please select a customer,, +Create New Customer,Create New Customer,, +Account Information,Account Information,, +From,From,, +To,To,, +Message,Message,, +Edit Order #%1,Edit Order #%1,, +Create New Order for %1 in %2,Create New Order for %1 in %2,, +Create New Order in %1,Create New Order in %1,, +Create New Order for %1,Create New Order for %1,, +Create New Order for New Customer,Create New Order for New Customer,, +Items Ordered,Items Ordered,, +This product is disabled.,This product is disabled.,, +Buy %1 for price %2,Buy %1 for price %2,, +Item ordered qty,Item ordered qty,, +%1 with %2 discount each,%1 with %2 discount each,, +%1 for %2,%1 for %2,, +* - Enter custom price including tax,* - Enter custom price including tax,, +* - Enter custom price excluding tax,* - Enter custom price excluding tax,, +Configure,Configure,, +This product does not have any configurable options,This product does not have any configurable options,, +Newsletter Subscription,Newsletter Subscription,, +Please select products,Please select products,, +Add Selected Product(s) to Order,Add Selected Product(s) to Order,, +ID,ID,, +Product,Product,, +SKU,SKU,, +Price,Price,, +Select,Select,, +Quantity,Quantity,, +Shipping Address,Shipping Address,, +Shipping Method,Shipping Method,, +Update Changes,Update Changes,, +Shopping Cart,Shopping Cart,, +Are you sure you want to delete all items from shopping cart?,Are you sure you want to delete all items from shopping cart?,, +Clear Shopping Cart,Clear Shopping Cart,, +Products in Comparison List,Products in Comparison List,, +Recently Compared Products,Recently Compared Products,, +Recently Viewed Products,Recently Viewed Products,, +Last Ordered Items,Last Ordered Items,, +Recently Viewed,Recently Viewed,, +Wish List,Wish List,, +Please select a store,Please select a store,, +Order Totals,Order Totals,, +Shipping Incl. Tax (%1),Shipping Incl. Tax (%1),, +Shipping Excl. Tax (%1),Shipping Excl. Tax (%1),, +New Credit Memo for Invoice #%1,New Credit Memo for Invoice #%1,, +New Credit Memo for Order #%1,New Credit Memo for Order #%1,, +Refund Shipping (Incl. Tax),Refund Shipping (Incl. Tax),, +Refund Shipping (Excl. Tax),Refund Shipping (Excl. Tax),, +Refund Shipping,Refund Shipping,, +Update Qty's,Update Qty's,, +Refund,Refund,, +Refund Offline,Refund Offline,, +Paid Amount,Paid Amount,, +Refund Amount,Refund Amount,, +Shipping Amount,Shipping Amount,, +Shipping Refund,Shipping Refund,, +Order Grand Total,Order Grand Total,, +Adjustment Refund,Adjustment Refund,, +Adjustment Fee,Adjustment Fee,, +Send Email,Send Email,, +Are you sure you want to send a credit memo email to customer?,Are you sure you want to send a credit memo email to customer?,, +Void,Void,, +Print,Print,, +The credit memo email was sent.,The credit memo email was sent.,, +The credit memo email wasn't sent.,The credit memo email wasn't sent.,, +Credit Memo #%1 | %3 | %2 (%4),Credit Memo #%1 | %3 | %2 (%4),, +Total Refund,Total Refund,, +New Invoice and Shipment for Order #%1,New Invoice and Shipment for Order #%1,, +New Invoice for Order #%1,New Invoice for Order #%1,, +Submit Invoice and Shipment,Submit Invoice and Shipment,, +Submit Invoice,Submit Invoice,, +Are you sure you want to send an invoice email to customer?,Are you sure you want to send an invoice email to customer?,, +Credit Memo,Credit Memo,, +Capture,Capture,, +The invoice email was sent.,The invoice email was sent.,, +The invoice email wasn't sent.,The invoice email wasn't sent.,, +Invoice #%1 | %2 | %4 (%3),Invoice #%1 | %2 | %4 (%3),, +Invalid parent block for this block,Invalid parent block for this block,, +Order Statuses,Order Statuses,, +Create New Status,Create New Status,, +Assign Status to State,Assign Status to State,, +Save Status Assignment,Save Status Assignment,, +Assign Order Status to State,Assign Order Status to State,, +Assignment Information,Assignment Information,, +Order Status,Order Status,, +Order State,Order State,, +Use Order Status As Default,Use Order Status As Default,, +Visible On Storefront,Visible On Storefront,, +Edit Order Status,Edit Order Status,, +Save Status,Save Status,, +New Order Status,New Order Status,, +Order Status Information,Order Status Information,, +Status Code,Status Code,, +Status Label,Status Label,, +Store View Specific Labels,Store View Specific Labels,, +Total Paid,Total Paid,, +Total Refunded,Total Refunded,, +Total Due,Total Due,, +Edit,Edit,, +Are you sure you want to send an order email to customer?,Are you sure you want to send an order email to customer?,, +"This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?","This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?",, +Are you sure you want to void the payment?,Are you sure you want to void the payment?,, +Hold,Hold,, +hold,hold,, +Unhold,Unhold,, +unhold,unhold,, +Are you sure you want to accept this payment?,Are you sure you want to accept this payment?,, +Accept Payment,Accept Payment,, +Are you sure you want to deny this payment?,Are you sure you want to deny this payment?,, +Deny Payment,Deny Payment,, +Get Payment Update,Get Payment Update,, +Invoice and Ship,Invoice and Ship,, +Invoice,Invoice,, +Ship,Ship,, +Reorder,Reorder,, +Order # %1 %2 | %3,Order # %1 %2 | %3,, +"This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.","This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.",, +Are you sure? This order will be canceled and a new one will be created instead.,Are you sure? This order will be canceled and a new one will be created instead.,, +Save Gift Message,Save Gift Message,, + [deleted], [deleted],, +Order Credit Memos,Order Credit Memos,, +Credit memo #%1 created,Credit memo #%1 created,, +Credit memo #%1 comment added,Credit memo #%1 comment added,, +Shipment #%1 created,Shipment #%1 created,, +Shipment #%1 comment added,Shipment #%1 comment added,, +Invoice #%1 created,Invoice #%1 created,, +Invoice #%1 comment added,Invoice #%1 comment added,, +Tracking number %1 for %2 assigned,Tracking number %1 for %2 assigned,, +Comments History,Comments History,, +Order History,Order History,, +Information,Information,, +Order Information,Order Information,, +Order Invoices,Order Invoices,, +Shipments,Shipments,, +Order Shipments,Order Shipments,, +Transactions,Transactions,, +Order View,Order View,, +Any,Any,, +Specified,Specified,, +Applies to Any of the Specified Order Statuses except canceled orders,Applies to Any of the Specified Order Statuses except canceled orders,, +Cart Price Rule,Cart Price Rule,, +Yes,Yes,, +No,No,, +Show Actual Values,Show Actual Values,, +New Order RSS,New Order RSS,, +Subtotal,Subtotal,, +Shipping & Handling,Shipping & Handling,, +Discount (%1),Discount (%1),, +Discount,Discount,, +Grand Total,Grand Total,, +Back,Back,, +Fetch,Fetch,, +Transaction # %1 | %2,Transaction # %1 | %2,, +N/A,N/A,, +Key,Key,, +Value,Value,, +We found an invalid entity model.,We found an invalid entity model.,, +Order # %1,Order # %1,, +Back to My Orders,Back to My Orders,, +View Another Order,View Another Order,, +About Your Refund,About Your Refund,, +My Orders,My Orders,, +Subscribe to Order Status,Subscribe to Order Status,, +About Your Invoice,About Your Invoice,, +Print Order # %1,Print Order # %1,, +Grand Total to be Charged,Grand Total to be Charged,, +Unassign,Unassign,, +We can't add this item to your shopping cart right now.,We can't add this item to your shopping cart right now.,, +You sent the message.,You sent the message.,, +Sales,Sales,, +Invoice capturing error,Invoice capturing error,, +This order no longer exists.,This order no longer exists.,, +Please enter a comment.,Please enter a comment.,, +We cannot add order history.,We cannot add order history.,, +You updated the order address.,You updated the order address.,, +We can't update the order address right now.,We can't update the order address right now.,, +You have not canceled the item.,You have not canceled the item.,, +You canceled the order.,You canceled the order.,, +"""%1"" coupon code was not applied. Do not apply discount is selected for item(s)","""%1"" coupon code was not applied. Do not apply discount is selected for item(s)",, +"""%1"" coupon code is not valid.","""%1"" coupon code is not valid.",, +The coupon code has been accepted.,The coupon code has been accepted.,, +Quote item id is not received.,Quote item id is not received.,, +Quote item is not loaded.,Quote item is not loaded.,, +New Order,New Order,, +You created the order.,You created the order.,, +Order saving error: %1,Order saving error: %1,, +Cannot add new comment.,Cannot add new comment.,, +The credit memo has been canceled.,The credit memo has been canceled.,, +Credit memo has not been canceled.,Credit memo has not been canceled.,, +New Memo for #%1,New Memo for #%1,, +New Memo,New Memo,, +The credit memo's total must be positive.,The credit memo's total must be positive.,, +Cannot create online refund for Refund to Store Credit.,Cannot create online refund for Refund to Store Credit.,, +You created the credit memo.,You created the credit memo.,, +We can't save the credit memo right now.,We can't save the credit memo right now.,, +We can't update the item's quantity right now.,We can't update the item's quantity right now.,, +View Memo for #%1,View Memo for #%1,, +View Memo,View Memo,, +You voided the credit memo.,You voided the credit memo.,, +We can't void the credit memo.,We can't void the credit memo.,, +The order no longer exists.,The order no longer exists.,, +We can't create credit memo for the order.,We can't create credit memo for the order.,, +Edit Order,Edit Order,, +You sent the order email.,You sent the order email.,, +We can't send the email order right now.,We can't send the email order right now.,, +You have not put the order on hold.,You have not put the order on hold.,, +You put the order on hold.,You put the order on hold.,, +You canceled the invoice.,You canceled the invoice.,, +Invoice canceling error,Invoice canceling error,, +The invoice has been captured.,The invoice has been captured.,, +The order does not allow an invoice to be created.,The order does not allow an invoice to be created.,, +You can't create an invoice without products.,You can't create an invoice without products.,, +New Invoice,New Invoice,, +We can't save the invoice right now.,We can't save the invoice right now.,, +You created the invoice and shipment.,You created the invoice and shipment.,, +The invoice has been created.,The invoice has been created.,, +We can't send the invoice email right now.,We can't send the invoice email right now.,, +We can't send the shipment right now.,We can't send the shipment right now.,, +Cannot update item quantity.,Cannot update item quantity.,, +The invoice has been voided.,The invoice has been voided.,, +Invoice voiding error,Invoice voiding error,, +%1 order(s) cannot be canceled.,%1 order(s) cannot be canceled.,, +You cannot cancel the order(s).,You cannot cancel the order(s).,, +We canceled %1 order(s).,We canceled %1 order(s).,, +%1 order(s) were not put on hold.,%1 order(s) were not put on hold.,, +No order(s) were put on hold.,No order(s) were put on hold.,, +You have put %1 order(s) on hold.,You have put %1 order(s) on hold.,, +%1 order(s) were not released from on hold status.,%1 order(s) were not released from on hold status.,, +No order(s) were released from on hold status.,No order(s) were released from on hold status.,, +%1 order(s) have been released from on hold status.,%1 order(s) have been released from on hold status.,, +There are no printable documents related to selected orders.,There are no printable documents related to selected orders.,, +The payment has been accepted.,The payment has been accepted.,, +The payment has been denied.,The payment has been denied.,, +Transaction has been approved.,Transaction has been approved.,, +Transaction has been voided/declined.,Transaction has been voided/declined.,, +There is no update for the transaction.,There is no update for the transaction.,, +We can't update the payment right now.,We can't update the payment right now.,, +You assigned the order status.,You assigned the order status.,, +Something went wrong while assigning the order status.,Something went wrong while assigning the order status.,, +We can't find this order status.,We can't find this order status.,, +Create New Order Status,Create New Order Status,, +We found another order status with the same order status code.,We found another order status with the same order status code.,, +You saved the order status.,You saved the order status.,, +We can't add the order status right now.,We can't add the order status right now.,, +You have unassigned the order status.,You have unassigned the order status.,, +Something went wrong while unassigning the order.,Something went wrong while unassigning the order.,, +Can't unhold order.,Can't unhold order.,, +You released the order from holding status.,You released the order from holding status.,, +The order was not on hold.,The order was not on hold.,, +Exception occurred during order load,Exception occurred during order load,, +Something went wrong while saving the gift message.,Something went wrong while saving the gift message.,, +You saved the gift card message.,You saved the gift card message.,, +The payment has been voided.,The payment has been voided.,, +We can't void the payment right now.,We can't void the payment right now.,, +Please correct the transaction ID and try again.,Please correct the transaction ID and try again.,, +The transaction details have been updated.,The transaction details have been updated.,, +We can't update the transaction details.,We can't update the transaction details.,, +Orders and Returns,Orders and Returns,, +You entered incorrect data. Please try again.,You entered incorrect data. Please try again.,, +Home,Home,, +Go to Home Page,Go to Home Page,, +We can't find this wish list.,We can't find this wish list.,, +"We could not add a product to cart by the ID ""%1"".","We could not add a product to cart by the ID ""%1"".",, +There is an error in one of the option rows.,There is an error in one of the option rows.,, +Shipping Address: ,Shipping Address: ,, +Billing Address: ,Billing Address: ,, +Please specify order items.,Please specify order items.,, +Please specify a shipping method.,Please specify a shipping method.,, +Please specify a payment method.,Please specify a payment method.,, +This payment method is not available.,This payment method is not available.,, +Validation is failed.,Validation is failed.,, +You did not email your customer. Please check your email settings.,You did not email your customer. Please check your email settings.,, +-- Please Select --,-- Please Select --,, +"Path ""%1"" is not part of allowed directory ""%2""","Path ""%1"" is not part of allowed directory ""%2""",, +Identifying Fields required,Identifying Fields required,, +Id required,Id required,, +"""Invoice Document Validation Error(s):\n"" .","""Invoice Document Validation Error(s):\n"" .",, +"Could not save an invoice, see error log for details","Could not save an invoice, see error log for details",, +A hold action is not available.,A hold action is not available.,, +You cannot remove the hold.,You cannot remove the hold.,, +We cannot cancel this order.,We cannot cancel this order.,, +Guest,Guest,, +Please enter the first name.,Please enter the first name.,, +Please enter the last name.,Please enter the last name.,, +Please enter the street.,Please enter the street.,, +Please enter the city.,Please enter the city.,, +Please enter the phone number.,Please enter the phone number.,, +Please enter the company.,Please enter the company.,, +Please enter the fax number.,Please enter the fax number.,, +Please enter the zip/postal code.,Please enter the zip/postal code.,, +Please enter the country.,Please enter the country.,, +Please enter the state/province.,Please enter the state/province.,, +Requested entity doesn't exist,Requested entity doesn't exist,, +Could not delete order address,Could not delete order address,, +Could not save order address,Could not save order address,, +Pending,Pending,, +Refunded,Refunded,, +Canceled,Canceled,, +Unknown State,Unknown State,, +"We found an invalid quantity to refund item ""%1"".","We found an invalid quantity to refund item ""%1"".",, +The creditmemo contains product item that is not part of the original order.,The creditmemo contains product item that is not part of the original order.,, +The quantity to refund must not be greater than the unrefunded quantity.,The quantity to refund must not be greater than the unrefunded quantity.,, +Maximum shipping amount allowed to refund is: %1,Maximum shipping amount allowed to refund is: %1,, +Order Id is required for creditmemo document,Order Id is required for creditmemo document,, +"The creditmemo contains product SKU ""%1"" that is not part of the original order.","The creditmemo contains product SKU ""%1"" that is not part of the original order.",, +"The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".","The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".",, +You can't create a creditmemo without products.,You can't create a creditmemo without products.,, +The most money available to refund is %1.,The most money available to refund is %1.,, +Could not delete credit memo,Could not delete credit memo,, +Could not save credit memo,Could not save credit memo,, +This order already has associated customer account,This order already has associated customer account,, +Paid,Paid,, +We cannot register an existing invoice,We cannot register an existing invoice,, +We can't create creditmemo for the invoice.,We can't create creditmemo for the invoice.,, +Order Id is required for invoice document,Order Id is required for invoice document,, +"The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".","The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".",, +The invoice contains one or more items that are not part of the original order.,The invoice contains one or more items that are not part of the original order.,, +ID required,ID required,, +Unknown Status,Unknown Status,, +Ordered,Ordered,, +Shipped,Shipped,, +Invoiced,Invoiced,, +Backordered,Backordered,, +Returned,Returned,, +Partial,Partial,, +Mixed,Mixed,, +Registered a Void notification.,Registered a Void notification.,, +"If the invoice was created offline, try creating an offline credit memo.","If the invoice was created offline, try creating an offline credit memo.",, +We refunded %1 online.,We refunded %1 online.,, +We refunded %1 offline.,We refunded %1 offline.,, +"IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.","IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.",, +The credit memo has been created automatically.,The credit memo has been created automatically.,, +Registered notification about refunded amount of %1.,Registered notification about refunded amount of %1.,, +Canceled order online,Canceled order online,, +Canceled order offline,Canceled order offline,, +Approved the payment online.,Approved the payment online.,, +There is no need to approve this payment.,There is no need to approve this payment.,, +Denied the payment online,Denied the payment online,, +Registered update about approved payment.,Registered update about approved payment.,, +Registered update about denied payment.,Registered update about denied payment.,, +There is no update for the payment.,There is no update for the payment.,, +Voided authorization.,Voided authorization.,, +Amount: %1.,Amount: %1.,, +"Transaction ID: ""%1""","Transaction ID: ""%1""",, +The payment method you requested is not available.,The payment method you requested is not available.,, +The payment disallows storing objects.,The payment disallows storing objects.,, +"The transaction ""%1"" cannot be captured yet.","The transaction ""%1"" cannot be captured yet.",, +The order amount of %1 is pending approval on the payment gateway.,The order amount of %1 is pending approval on the payment gateway.,, +Ordered amount of %1,Ordered amount of %1,, +An amount of %1 will be captured after being approved at the payment gateway.,An amount of %1 will be captured after being approved at the payment gateway.,, +Registered notification about captured amount of %1.,Registered notification about captured amount of %1.,, +Order is suspended as its capture amount %1 is suspected to be fraudulent.,Order is suspended as its capture amount %1 is suspected to be fraudulent.,, +The parent transaction ID must have a transaction ID.,The parent transaction ID must have a transaction ID.,, +Payment transactions disallow storing objects.,Payment transactions disallow storing objects.,, +"The transaction ""%1"" (%2) is already closed.","The transaction ""%1"" (%2) is already closed.",, +Set order for existing transactions not allowed,Set order for existing transactions not allowed,, +"At minimum, you need to set a payment ID.","At minimum, you need to set a payment ID.",, +Order,Order,, +Authorization,Authorization,, +"We found an unsupported transaction type ""%1"".","We found an unsupported transaction type ""%1"".",, +Please set a proper payment and order id.,Please set a proper payment and order id.,, +Please enter a Transaction ID.,Please enter a Transaction ID.,, +You can't do this without a transaction object.,You can't do this without a transaction object.,, +Order # ,Order # ,, +Order Date: ,Order Date: ,, +Sold to:,Sold to:,, +Ship to:,Ship to:,, +Payment Method:,Payment Method:,, +Shipping Method:,Shipping Method:,, +Total Shipping Charges,Total Shipping Charges,, +Title,Title,, +Number,Number,, +We found an invalid renderer model.,We found an invalid renderer model.,, +Please define the PDF object before using.,Please define the PDF object before using.,, +"We don't recognize the draw line data. Please define the ""lines"" array.","We don't recognize the draw line data. Please define the ""lines"" array.",, +Products,Products,, +Total (ex),Total (ex),, +Qty,Qty,, +Tax,Tax,, +Total (inc),Total (inc),, +Credit Memo # ,Credit Memo # ,, +Invoice # ,Invoice # ,, +The order object is not specified.,The order object is not specified.,, +The source object is not specified.,The source object is not specified.,, +An item object is not specified.,An item object is not specified.,, +A PDF object is not specified.,A PDF object is not specified.,, +A PDF page object is not specified.,A PDF page object is not specified.,, +Excl. Tax,Excl. Tax,, +Incl. Tax,Incl. Tax,, +Packing Slip # ,Packing Slip # ,, +title,title,, +The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.,The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.,, +We cannot register an existing shipment,We cannot register an existing shipment,, +Parent shipment cannot be loaded for track object.,Parent shipment cannot be loaded for track object.,, +Order Id is required for shipment document,Order Id is required for shipment document,, +You can't create a shipment without products.,You can't create a shipment without products.,, +"The shipment contains product SKU ""%1"" that is not part of the original order.","The shipment contains product SKU ""%1"" that is not part of the original order.",, +"The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".","The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".",, +Please enter a tracking number.,Please enter a tracking number.,, +Could not delete shipment,Could not delete shipment,, +Could not save shipment,Could not save shipment,, +The last status can't be unassigned from its current state.,The last status can't be unassigned from its current state.,, +"Status can't be unassigned, because it is used by existing order(s).","Status can't be unassigned, because it is used by existing order(s).",, +The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.,The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.,, +An invoice cannot be created when an order has a status of %1,An invoice cannot be created when an order has a status of %1,, +A creditmemo can not be created when an order has a status of %1,A creditmemo can not be created when an order has a status of %1,, +The order does not allow a creditmemo to be created.,The order does not allow a creditmemo to be created.,, +A shipment cannot be created when an order has a status of %1,A shipment cannot be created when an order has a status of %1,, +The order does not allow a shipment to be created.,The order does not allow a shipment to be created.,, +"""Creditmemo Document Validation Error(s):\n"" .","""Creditmemo Document Validation Error(s):\n"" .",, +"Could not save a Creditmemo, see error log for details","Could not save a Creditmemo, see error log for details",, +We cannot determine the field name.,We cannot determine the field name.,, +City,City,, +Company,Company,, +Country,Country,, +Email,Email,, +First Name,First Name,, +Last Name,Last Name,, +State/Province,State/Province,, +Street Address,Street Address,, +Phone Number,Phone Number,, +Zip/Postal Code,Zip/Postal Code,, +We can't save the address:\n%1,We can't save the address:\n%1,, +Cannot save comment:\n%1,Cannot save comment:\n%1,, +We don't have enough information to save the parent transaction ID.,We don't have enough information to save the parent transaction ID.,, +We cannot create an empty shipment.,We cannot create an empty shipment.,, +Cannot save track:\n%1,Cannot save track:\n%1,, +Cannot unassign status from state,Cannot unassign status from state,, +New Orders,New Orders,, +Order #%1 created at %2,Order #%1 created at %2,, +Details for %1 #%2,Details for %1 #%2,, +Notified Date: %1,Notified Date: %1,, +Comment: %1<br/>,Comment: %1<br/>,, +Current Status: %1<br/>,Current Status: %1<br/>,, +Total: %1<br/>,Total: %1<br/>,, +Order # %1 Notification(s),Order # %1 Notification(s),, +You can not cancel Credit Memo,You can not cancel Credit Memo,, +Could not cancel creditmemo,Could not cancel creditmemo,, +We cannot register an existing credit memo.,We cannot register an existing credit memo.,, +"We found an invalid quantity to invoice item ""%1"".","We found an invalid quantity to invoice item ""%1"".",, +"The Order State ""%1"" must not be set manually.","The Order State ""%1"" must not be set manually.",, +"""Shipment Document Validation Error(s):\n"" .","""Shipment Document Validation Error(s):\n"" .",, +"Could not save a shipment, see error log for details","Could not save a shipment, see error log for details",, +VAT Request Identifier,VAT Request Identifier,, +VAT Request Date,VAT Request Date,, +Pending Payment,Pending Payment,, +Processing,Processing,, +On Hold,On Hold,, +Complete,Complete,, +Closed,Closed,, +Suspected Fraud,Suspected Fraud,, +Payment Review,Payment Review,, +New,New,, +test message,test message,, +Email has not been sent,Email has not been sent,, +Authorized amount of %1.,Authorized amount of %1.,, +We will authorize %1 after the payment is approved at the payment gateway.,We will authorize %1 after the payment is approved at the payment gateway.,, +Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.,Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.,, +Captured amount of %1 online.,Captured amount of %1 online.,, +Authorized amount of %1,Authorized amount of %1,, +" Transaction ID: ""%1"""," Transaction ID: ""%1""",, +View,View,, +Group was removed,Group was removed,, +"Changing address information will not recalculate shipping, tax or other order amount.","Changing address information will not recalculate shipping, tax or other order amount.",, +Comment Text,Comment Text,, +Notify Customer by Email,Notify Customer by Email,, +Visible on Storefront,Visible on Storefront,, +Customer,Customer,, +Notified,Notified,, +Not Notified,Not Notified,, +No Payment Methods,No Payment Methods,, +Order Comments,Order Comments,, +Apply Coupon Code,Apply Coupon Code,, +Apply,Apply,, +Remove Coupon Code,Remove Coupon Code,, +Remove,Remove,, +Address Information,Address Information,, +Payment &, Shipping Information,Payment &, Shipping Information +Order Total,Order Total,, +Order Currency:,Order Currency:,, +Same As Billing Address,Same As Billing Address,, +Select from existing customer addresses:,Select from existing customer addresses:,, +Add New Address,Add New Address,, +Save in address book,Save in address book,, +You don't need to select a shipping address.,You don't need to select a shipping address.,, +Gift Message for the Entire Order,Gift Message for the Entire Order,, +Leave this box blank if you don't want to leave a gift message for the entire order.,Leave this box blank if you don't want to leave a gift message for the entire order.,, +Row Subtotal,Row Subtotal,, +Action,Action,, +No ordered items,No ordered items,, +Update Items and Quantities,Update Items and Quantities,, +Total %1 product(s),Total %1 product(s),, +Subtotal:,Subtotal:,, +Tier Pricing,Tier Pricing,, +Custom Price,Custom Price,, +Please select,Please select,, +Move to Shopping Cart,Move to Shopping Cart,, +Move to Wish List,Move to Wish List,, +Subscribe to Newsletter,Subscribe to Newsletter,, +Click to change shipping method,Click to change shipping method,, +"Sorry, no quotes are available for this order.","Sorry, no quotes are available for this order.",, +Get shipping methods and rates,Get shipping methods and rates,, +You don't need to select a shipping method.,You don't need to select a shipping method.,, +Customer's Activities,Customer's Activities,, +Refresh,Refresh,, +Item,Item,, +Add To Order,Add To Order,, +Configure and Add to Order,Configure and Add to Order,, +No items,No items,, +Append Comments,Append Comments,, +Email Order Confirmation,Email Order Confirmation,, +Grand Total Excl. Tax,Grand Total Excl. Tax,, +Grand Total Incl. Tax,Grand Total Incl. Tax,, +Subtotal (Excl. Tax),Subtotal (Excl. Tax),, +Subtotal (Incl. Tax),Subtotal (Incl. Tax),, +Payment &, Shipping Method,Payment &, Shipping Method +Payment Information,Payment Information,, +The order was placed using %1.,The order was placed using %1.,, +Shipping Information,Shipping Information,, +Items to Refund,Items to Refund,, +Return to Stock,Return to Stock,, +Qty to Refund,Qty to Refund,, +Tax Amount,Tax Amount,, +Discount Amount,Discount Amount,, +Row Total,Row Total,, +No Items To Refund,No Items To Refund,, +Credit Memo Comments,Credit Memo Comments,, +Refund Totals,Refund Totals,, +Email Copy of Credit Memo,Email Copy of Credit Memo,, +Please enter a positive number in this field.,Please enter a positive number in this field.,, +Items Refunded,Items Refunded,, +No Items,No Items,, +Memo Total,Memo Total,, +Credit Memo History,Credit Memo History,, +Credit Memo Totals,Credit Memo Totals,, +Customer Name: %1,Customer Name: %1,, +Purchased From: %1,Purchased From: %1,, +Gift Message,Gift Message,, +From:,From:,, +To:,To:,, +Message:,Message:,, +Shipping &, Handling,Shipping &, Handling +Gift Options,Gift Options,, +Create Shipment,Create Shipment,, +Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.,Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.,, +%1,%1,, +Qty to Invoice,Qty to Invoice,, +Invoice History,Invoice History,, +Invoice Comments,Invoice Comments,, +Invoice Totals,Invoice Totals,, +Amount,Amount,, +Capture Online,Capture Online,, +Capture Offline,Capture Offline,, +Not Capture,Not Capture,, +The invoice will be created offline without the payment gateway.,The invoice will be created offline without the payment gateway.,, +Email Copy of Invoice,Email Copy of Invoice,, +Items Invoiced,Items Invoiced,, +Total Tax,Total Tax,, +From Name,From Name,, +To Name,To Name,, +Status,Status,, +Comment,Comment,, +Notification Not Applicable,Notification Not Applicable,, +Order & Account Information,Order & Account Information,, +The order confirmation email was sent,The order confirmation email was sent,, +The order confirmation email is not sent,The order confirmation email is not sent,, +Order Date,Order Date,, +Order Date (%1),Order Date (%1),, +Purchased From,Purchased From,, +Link to the New Order,Link to the New Order,, +Link to the Previous Order,Link to the Previous Order,, +Placed from IP,Placed from IP,, +%1 / %2 rate:,%1 / %2 rate:,, +Customer Name,Customer Name,, +Customer Group,Customer Group,, +Notes for this Order,Notes for this Order,, +Comment added,Comment added,, +Transaction Data,Transaction Data,, +Transaction ID,Transaction ID,, +Parent Transaction ID,Parent Transaction ID,, +Order ID,Order ID,, +Transaction Type,Transaction Type,, +Is Closed,Is Closed,, +Created At,Created At,, +Child Transactions,Child Transactions,, +Transaction Details,Transaction Details,, +Items,Items,, +Gift Message for this Order,Gift Message for this Order,, +Shipped By,Shipped By,, +Tracking Number,Tracking Number,, +Billing Last Name,Billing Last Name,, +Find Order By,Find Order By,, +ZIP Code,ZIP Code,, +Billing ZIP Code,Billing ZIP Code,, +Continue,Continue,, +Print All Refunds,Print All Refunds,, +Refund #,Refund #,, +Print Refund,Print Refund,, +Product Name,Product Name,, +Order #,Order #,, +Date,Date,, +Ship To,Ship To,, +Actions,Actions,, +View Order,View Order,, +You have placed no orders.,You have placed no orders.,, +No shipping information available,No shipping information available,, +Print Order,Print Order,, +Print All Invoices,Print All Invoices,, +Invoice #,Invoice #,, +Print Invoice,Print Invoice,, +Qty Invoiced,Qty Invoiced,, +Close,Close,, +About Your Order,About Your Order,, +"<span class=""label"">Order Date:</span> %1","<span class=""label"">Order Date:</span> %1",, +Refund #%1,Refund #%1,, +Shipment #%1,Shipment #%1,, +Qty Shipped,Qty Shipped,, +Recent Orders,Recent Orders,, +View All,View All,, +Gift Message for This Order,Gift Message for This Order,, +Recently Ordered,Recently Ordered,, +Add to Cart,Add to Cart,, +Search,Search,, +Credit memo for your %store_name order,Credit memo for your %store_name order,, +"%name,","%name,",, +Thank you for your order from %store_name.,Thank you for your order from %store_name.,, +"You can check the status of your order by <a href=""%account_url"">logging into your account</a>.","You can check the status of your order by <a href=""%account_url"">logging into your account</a>.",, +"If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>","If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>",, +"or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>",, +"Our hours are <span class=""no-link"">%store_hours</span>.","Our hours are <span class=""no-link"">%store_hours</span>.",, +Your Credit Memo #%creditmemo_id for Order #%order_id,Your Credit Memo #%creditmemo_id for Order #%order_id,, +Billing Info,Billing Info,, +Shipping Info,Shipping Info,, +Update to your %store_name credit memo,Update to your %store_name credit memo,, +Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.,Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.,, +Invoice for your %store_name order,Invoice for your %store_name order,, +Your Invoice #%invoice_id for Order #%order_id,Your Invoice #%invoice_id for Order #%order_id,, +Update to your %store_name invoice,Update to your %store_name invoice,, +Your %store_name order confirmation,Your %store_name order confirmation,, +"%customer_name,","%customer_name,",, +Once your package ships we will send you a tracking number.,Once your package ships we will send you a tracking number.,, +"Your Order <span class=""no-link"">#%increment_id</span>","Your Order <span class=""no-link"">#%increment_id</span>",, +"Placed on <span class=""no-link"">%created_at</span>","Placed on <span class=""no-link"">%created_at</span>",, +Once your package ships we will send an email with a link to track your order.,Once your package ships we will send an email with a link to track your order.,, +Update to your %store_name order,Update to your %store_name order,, +Your %store_name order has shipped,Your %store_name order has shipped,, +Your shipping confirmation is below. Thank you again for your business.,Your shipping confirmation is below. Thank you again for your business.,, +Your Shipment #%shipment_id for Order #%order_id,Your Shipment #%shipment_id for Order #%order_id,, +Update to your %store_name shipment,Update to your %store_name shipment,, +Gift Options for ,Gift Options for ,, +Add Products,Add Products,, +You have item changes,You have item changes,, +Ok,Ok,, +Operations,Operations,, +Create,Create,, +Send Order Email,Send Order Email,, +Accept or Deny Payment,Accept or Deny Payment,, +Send Sales Emails,Send Sales Emails,, +Sales Section,Sales Section,, +Sales Emails Section,Sales Emails Section,, +General,General,, +Hide Customer IP,Hide Customer IP,, +"Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.","Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.",, +Checkout Totals Sort Order,Checkout Totals Sort Order,, +Allow Reorder,Allow Reorder,, +Invoice and Packing Slip Design,Invoice and Packing Slip Design,, +Logo for PDF Print-outs (200x50),Logo for PDF Print-outs (200x50),, +"Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.","Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.",, +Logo for HTML Print View,Logo for HTML Print View,, +"Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)","Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)",, +Address,Address,, +Minimum Order Amount,Minimum Order Amount,, +Enable,Enable,, +Minimum Amount,Minimum Amount,, +Subtotal after discount,Subtotal after discount,, +Include Tax to Amount,Include Tax to Amount,, +Description Message,Description Message,, +This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.,This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.,, +Error to Show in Shopping Cart,Error to Show in Shopping Cart,, +Validate Each Address Separately in Multi-address Checkout,Validate Each Address Separately in Multi-address Checkout,, +Multi-address Description Message,Multi-address Description Message,, +We'll use the default description above if you leave this empty.,We'll use the default description above if you leave this empty.,, +Multi-address Error to Show in Shopping Cart,Multi-address Error to Show in Shopping Cart,, +We'll use the default error above if you leave this empty.,We'll use the default error above if you leave this empty.,, +Dashboard,Dashboard,, +Use Aggregated Data,Use Aggregated Data,, +Orders Cron Settings,Orders Cron Settings,, +Pending Payment Order Lifetime (minutes),Pending Payment Order Lifetime (minutes),, +Sales Emails,Sales Emails,, +Asynchronous sending,Asynchronous sending,, +Enabled,Enabled,, +New Order Confirmation Email Sender,New Order Confirmation Email Sender,, +New Order Confirmation Template,New Order Confirmation Template,, +"Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected.",, +New Order Confirmation Template for Guest,New Order Confirmation Template for Guest,, +Send Order Email Copy To,Send Order Email Copy To,, +Comma-separated,Comma-separated,, +Send Order Email Copy Method,Send Order Email Copy Method,, +Order Comment Email Sender,Order Comment Email Sender,, +Order Comment Email Template,Order Comment Email Template,, +Order Comment Email Template for Guest,Order Comment Email Template for Guest,, +Send Order Comment Email Copy To,Send Order Comment Email Copy To,, +Send Order Comments Email Copy Method,Send Order Comments Email Copy Method,, +Invoice Email Sender,Invoice Email Sender,, +Invoice Email Template,Invoice Email Template,, +Invoice Email Template for Guest,Invoice Email Template for Guest,, +Send Invoice Email Copy To,Send Invoice Email Copy To,, +Send Invoice Email Copy Method,Send Invoice Email Copy Method,, +Invoice Comment Email Sender,Invoice Comment Email Sender,, +Invoice Comment Email Template,Invoice Comment Email Template,, +Invoice Comment Email Template for Guest,Invoice Comment Email Template for Guest,, +Send Invoice Comment Email Copy To,Send Invoice Comment Email Copy To,, +Send Invoice Comments Email Copy Method,Send Invoice Comments Email Copy Method,, +Shipment,Shipment,, +Shipment Email Sender,Shipment Email Sender,, +Shipment Email Template,Shipment Email Template,, +Shipment Email Template for Guest,Shipment Email Template for Guest,, +Send Shipment Email Copy To,Send Shipment Email Copy To,, +Send Shipment Email Copy Method,Send Shipment Email Copy Method,, +Shipment Comments,Shipment Comments,, +Shipment Comment Email Sender,Shipment Comment Email Sender,, +Shipment Comment Email Template,Shipment Comment Email Template,, +Shipment Comment Email Template for Guest,Shipment Comment Email Template for Guest,, +Send Shipment Comment Email Copy To,Send Shipment Comment Email Copy To,, +Send Shipment Comments Email Copy Method,Send Shipment Comments Email Copy Method,, +Credit Memo Email Sender,Credit Memo Email Sender,, +Credit Memo Email Template,Credit Memo Email Template,, +Credit Memo Email Template for Guest,Credit Memo Email Template for Guest,, +Send Credit Memo Email Copy To,Send Credit Memo Email Copy To,, +Send Credit Memo Email Copy Method,Send Credit Memo Email Copy Method,, +Credit Memo Comment Email Sender,Credit Memo Comment Email Sender,, +Credit Memo Comment Email Template,Credit Memo Comment Email Template,, +Credit Memo Comment Email Template for Guest,Credit Memo Comment Email Template for Guest,, +Send Credit Memo Comment Email Copy To,Send Credit Memo Comment Email Copy To,, +Send Credit Memo Comments Email Copy Method,Send Credit Memo Comments Email Copy Method,, +PDF Print-outs,PDF Print-outs,, +Display Order ID in Header,Display Order ID in Header,, +Customer Order Status Notification,Customer Order Status Notification,, +Asynchronous indexing,Asynchronous indexing,, +Orders and Returns Search Form,Orders and Returns Search Form,, +Anchor Custom Title,Anchor Custom Title,, +Template,Template,, +Default Template,Default Template,, +Name,Name,, +Phone,Phone,, +ZIP/Post Code,ZIP/Post Code,, +Signed-up Point,Signed-up Point,, +Website,Website,, +Bill-to Name,Bill-to Name,, +Created,Created,, +Invoice Date,Invoice Date,, +Ship-to Name,Ship-to Name,, +Ship Date,Ship Date,, +Total Quantity,Total Quantity,, +Default Status,Default Status,, +State Code and Title,State Code and Title,, +Item Status,Item Status,, +Original Price,Original Price,, +Tax Percent,Tax Percent,, +All Store Views,All Store Views,, +PDF Credit Memos,PDF Credit Memos,, +Purchase Point,Purchase Point,, +Print Invoices,Print Invoices,, +Print Packing Slips,Print Packing Slips,, +Print Credit Memos,Print Credit Memos,, +Print All,Print All,, +Print Shipping Labels,Print Shipping Labels,, +Purchase Date,Purchase Date,, +Grand Total (Base),Grand Total (Base),, +Grand Total (Purchased),Grand Total (Purchased),, +Customer Email,Customer Email,, +Shipping and Handling,Shipping and Handling,, +PDF Invoices,PDF Invoices,, +PDF Shipments,PDF Shipments,, +PDF Creditmemos,PDF Creditmemos,, +Refunds,Refunds,, +Allow Zero GrandTotal for Creditmemo,Allow Zero GrandTotal for Creditmemo,, +Allow Zero GrandTotal,Allow Zero GrandTotal,, +Email is required field for Admin order creation,Email is required field for Admin order creation,, +If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer.,, From 339c4c2a507767e8cb3badaac5e278430cf63d23 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Tue, 17 Sep 2019 17:39:08 +0530 Subject: [PATCH 064/734] resolved Sales en_US.csv file conflict --- app/code/Magento/Sales/i18n/en_US.csv | 1602 ++++++++++++------------- 1 file changed, 801 insertions(+), 801 deletions(-) diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index fc1fac1311985..84cdfb2824334 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -1,801 +1,801 @@ -Credit Memos,Credit Memos,, -Orders,Orders,, -Invoices,Invoices,, -We can't get the order instance right now.,We can't get the order instance right now.,, -Create New Order,Create New Order,, -Save Order Address,Save Order Address,, -Shipping,Shipping,, -Billing,Billing,, -Edit Order %1 %2 Address,Edit Order %1 %2 Address,, -Order Address Information,Order Address Information,, -Please correct the parent block for this block.,Please correct the parent block for this block.,, -Submit Comment,Submit Comment,, -Submit Order,Submit Order,, -Are you sure you want to cancel this order?,Are you sure you want to cancel this order?,, -Cancel,Cancel,, -Billing Address,Billing Address,, -Payment Method,Payment Method,, -Order Comment,Order Comment,, -Coupons,Coupons,, -Please select a customer,Please select a customer,, -Create New Customer,Create New Customer,, -Account Information,Account Information,, -From,From,, -To,To,, -Message,Message,, -Edit Order #%1,Edit Order #%1,, -Create New Order for %1 in %2,Create New Order for %1 in %2,, -Create New Order in %1,Create New Order in %1,, -Create New Order for %1,Create New Order for %1,, -Create New Order for New Customer,Create New Order for New Customer,, -Items Ordered,Items Ordered,, -This product is disabled.,This product is disabled.,, -Buy %1 for price %2,Buy %1 for price %2,, -Item ordered qty,Item ordered qty,, -%1 with %2 discount each,%1 with %2 discount each,, -%1 for %2,%1 for %2,, -* - Enter custom price including tax,* - Enter custom price including tax,, -* - Enter custom price excluding tax,* - Enter custom price excluding tax,, -Configure,Configure,, -This product does not have any configurable options,This product does not have any configurable options,, -Newsletter Subscription,Newsletter Subscription,, -Please select products,Please select products,, -Add Selected Product(s) to Order,Add Selected Product(s) to Order,, -ID,ID,, -Product,Product,, -SKU,SKU,, -Price,Price,, -Select,Select,, -Quantity,Quantity,, -Shipping Address,Shipping Address,, -Shipping Method,Shipping Method,, -Update Changes,Update Changes,, -Shopping Cart,Shopping Cart,, -Are you sure you want to delete all items from shopping cart?,Are you sure you want to delete all items from shopping cart?,, -Clear Shopping Cart,Clear Shopping Cart,, -Products in Comparison List,Products in Comparison List,, -Recently Compared Products,Recently Compared Products,, -Recently Viewed Products,Recently Viewed Products,, -Last Ordered Items,Last Ordered Items,, -Recently Viewed,Recently Viewed,, -Wish List,Wish List,, -Please select a store,Please select a store,, -Order Totals,Order Totals,, -Shipping Incl. Tax (%1),Shipping Incl. Tax (%1),, -Shipping Excl. Tax (%1),Shipping Excl. Tax (%1),, -New Credit Memo for Invoice #%1,New Credit Memo for Invoice #%1,, -New Credit Memo for Order #%1,New Credit Memo for Order #%1,, -Refund Shipping (Incl. Tax),Refund Shipping (Incl. Tax),, -Refund Shipping (Excl. Tax),Refund Shipping (Excl. Tax),, -Refund Shipping,Refund Shipping,, -Update Qty's,Update Qty's,, -Refund,Refund,, -Refund Offline,Refund Offline,, -Paid Amount,Paid Amount,, -Refund Amount,Refund Amount,, -Shipping Amount,Shipping Amount,, -Shipping Refund,Shipping Refund,, -Order Grand Total,Order Grand Total,, -Adjustment Refund,Adjustment Refund,, -Adjustment Fee,Adjustment Fee,, -Send Email,Send Email,, -Are you sure you want to send a credit memo email to customer?,Are you sure you want to send a credit memo email to customer?,, -Void,Void,, -Print,Print,, -The credit memo email was sent.,The credit memo email was sent.,, -The credit memo email wasn't sent.,The credit memo email wasn't sent.,, -Credit Memo #%1 | %3 | %2 (%4),Credit Memo #%1 | %3 | %2 (%4),, -Total Refund,Total Refund,, -New Invoice and Shipment for Order #%1,New Invoice and Shipment for Order #%1,, -New Invoice for Order #%1,New Invoice for Order #%1,, -Submit Invoice and Shipment,Submit Invoice and Shipment,, -Submit Invoice,Submit Invoice,, -Are you sure you want to send an invoice email to customer?,Are you sure you want to send an invoice email to customer?,, -Credit Memo,Credit Memo,, -Capture,Capture,, -The invoice email was sent.,The invoice email was sent.,, -The invoice email wasn't sent.,The invoice email wasn't sent.,, -Invoice #%1 | %2 | %4 (%3),Invoice #%1 | %2 | %4 (%3),, -Invalid parent block for this block,Invalid parent block for this block,, -Order Statuses,Order Statuses,, -Create New Status,Create New Status,, -Assign Status to State,Assign Status to State,, -Save Status Assignment,Save Status Assignment,, -Assign Order Status to State,Assign Order Status to State,, -Assignment Information,Assignment Information,, -Order Status,Order Status,, -Order State,Order State,, -Use Order Status As Default,Use Order Status As Default,, -Visible On Storefront,Visible On Storefront,, -Edit Order Status,Edit Order Status,, -Save Status,Save Status,, -New Order Status,New Order Status,, -Order Status Information,Order Status Information,, -Status Code,Status Code,, -Status Label,Status Label,, -Store View Specific Labels,Store View Specific Labels,, -Total Paid,Total Paid,, -Total Refunded,Total Refunded,, -Total Due,Total Due,, -Edit,Edit,, -Are you sure you want to send an order email to customer?,Are you sure you want to send an order email to customer?,, -"This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?","This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?",, -Are you sure you want to void the payment?,Are you sure you want to void the payment?,, -Hold,Hold,, -hold,hold,, -Unhold,Unhold,, -unhold,unhold,, -Are you sure you want to accept this payment?,Are you sure you want to accept this payment?,, -Accept Payment,Accept Payment,, -Are you sure you want to deny this payment?,Are you sure you want to deny this payment?,, -Deny Payment,Deny Payment,, -Get Payment Update,Get Payment Update,, -Invoice and Ship,Invoice and Ship,, -Invoice,Invoice,, -Ship,Ship,, -Reorder,Reorder,, -Order # %1 %2 | %3,Order # %1 %2 | %3,, -"This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.","This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.",, -Are you sure? This order will be canceled and a new one will be created instead.,Are you sure? This order will be canceled and a new one will be created instead.,, -Save Gift Message,Save Gift Message,, - [deleted], [deleted],, -Order Credit Memos,Order Credit Memos,, -Credit memo #%1 created,Credit memo #%1 created,, -Credit memo #%1 comment added,Credit memo #%1 comment added,, -Shipment #%1 created,Shipment #%1 created,, -Shipment #%1 comment added,Shipment #%1 comment added,, -Invoice #%1 created,Invoice #%1 created,, -Invoice #%1 comment added,Invoice #%1 comment added,, -Tracking number %1 for %2 assigned,Tracking number %1 for %2 assigned,, -Comments History,Comments History,, -Order History,Order History,, -Information,Information,, -Order Information,Order Information,, -Order Invoices,Order Invoices,, -Shipments,Shipments,, -Order Shipments,Order Shipments,, -Transactions,Transactions,, -Order View,Order View,, -Any,Any,, -Specified,Specified,, -Applies to Any of the Specified Order Statuses except canceled orders,Applies to Any of the Specified Order Statuses except canceled orders,, -Cart Price Rule,Cart Price Rule,, -Yes,Yes,, -No,No,, -Show Actual Values,Show Actual Values,, -New Order RSS,New Order RSS,, -Subtotal,Subtotal,, -Shipping & Handling,Shipping & Handling,, -Discount (%1),Discount (%1),, -Discount,Discount,, -Grand Total,Grand Total,, -Back,Back,, -Fetch,Fetch,, -Transaction # %1 | %2,Transaction # %1 | %2,, -N/A,N/A,, -Key,Key,, -Value,Value,, -We found an invalid entity model.,We found an invalid entity model.,, -Order # %1,Order # %1,, -Back to My Orders,Back to My Orders,, -View Another Order,View Another Order,, -About Your Refund,About Your Refund,, -My Orders,My Orders,, -Subscribe to Order Status,Subscribe to Order Status,, -About Your Invoice,About Your Invoice,, -Print Order # %1,Print Order # %1,, -Grand Total to be Charged,Grand Total to be Charged,, -Unassign,Unassign,, -We can't add this item to your shopping cart right now.,We can't add this item to your shopping cart right now.,, -You sent the message.,You sent the message.,, -Sales,Sales,, -Invoice capturing error,Invoice capturing error,, -This order no longer exists.,This order no longer exists.,, -Please enter a comment.,Please enter a comment.,, -We cannot add order history.,We cannot add order history.,, -You updated the order address.,You updated the order address.,, -We can't update the order address right now.,We can't update the order address right now.,, -You have not canceled the item.,You have not canceled the item.,, -You canceled the order.,You canceled the order.,, -"""%1"" coupon code was not applied. Do not apply discount is selected for item(s)","""%1"" coupon code was not applied. Do not apply discount is selected for item(s)",, -"""%1"" coupon code is not valid.","""%1"" coupon code is not valid.",, -The coupon code has been accepted.,The coupon code has been accepted.,, -Quote item id is not received.,Quote item id is not received.,, -Quote item is not loaded.,Quote item is not loaded.,, -New Order,New Order,, -You created the order.,You created the order.,, -Order saving error: %1,Order saving error: %1,, -Cannot add new comment.,Cannot add new comment.,, -The credit memo has been canceled.,The credit memo has been canceled.,, -Credit memo has not been canceled.,Credit memo has not been canceled.,, -New Memo for #%1,New Memo for #%1,, -New Memo,New Memo,, -The credit memo's total must be positive.,The credit memo's total must be positive.,, -Cannot create online refund for Refund to Store Credit.,Cannot create online refund for Refund to Store Credit.,, -You created the credit memo.,You created the credit memo.,, -We can't save the credit memo right now.,We can't save the credit memo right now.,, -We can't update the item's quantity right now.,We can't update the item's quantity right now.,, -View Memo for #%1,View Memo for #%1,, -View Memo,View Memo,, -You voided the credit memo.,You voided the credit memo.,, -We can't void the credit memo.,We can't void the credit memo.,, -The order no longer exists.,The order no longer exists.,, -We can't create credit memo for the order.,We can't create credit memo for the order.,, -Edit Order,Edit Order,, -You sent the order email.,You sent the order email.,, -We can't send the email order right now.,We can't send the email order right now.,, -You have not put the order on hold.,You have not put the order on hold.,, -You put the order on hold.,You put the order on hold.,, -You canceled the invoice.,You canceled the invoice.,, -Invoice canceling error,Invoice canceling error,, -The invoice has been captured.,The invoice has been captured.,, -The order does not allow an invoice to be created.,The order does not allow an invoice to be created.,, -You can't create an invoice without products.,You can't create an invoice without products.,, -New Invoice,New Invoice,, -We can't save the invoice right now.,We can't save the invoice right now.,, -You created the invoice and shipment.,You created the invoice and shipment.,, -The invoice has been created.,The invoice has been created.,, -We can't send the invoice email right now.,We can't send the invoice email right now.,, -We can't send the shipment right now.,We can't send the shipment right now.,, -Cannot update item quantity.,Cannot update item quantity.,, -The invoice has been voided.,The invoice has been voided.,, -Invoice voiding error,Invoice voiding error,, -%1 order(s) cannot be canceled.,%1 order(s) cannot be canceled.,, -You cannot cancel the order(s).,You cannot cancel the order(s).,, -We canceled %1 order(s).,We canceled %1 order(s).,, -%1 order(s) were not put on hold.,%1 order(s) were not put on hold.,, -No order(s) were put on hold.,No order(s) were put on hold.,, -You have put %1 order(s) on hold.,You have put %1 order(s) on hold.,, -%1 order(s) were not released from on hold status.,%1 order(s) were not released from on hold status.,, -No order(s) were released from on hold status.,No order(s) were released from on hold status.,, -%1 order(s) have been released from on hold status.,%1 order(s) have been released from on hold status.,, -There are no printable documents related to selected orders.,There are no printable documents related to selected orders.,, -The payment has been accepted.,The payment has been accepted.,, -The payment has been denied.,The payment has been denied.,, -Transaction has been approved.,Transaction has been approved.,, -Transaction has been voided/declined.,Transaction has been voided/declined.,, -There is no update for the transaction.,There is no update for the transaction.,, -We can't update the payment right now.,We can't update the payment right now.,, -You assigned the order status.,You assigned the order status.,, -Something went wrong while assigning the order status.,Something went wrong while assigning the order status.,, -We can't find this order status.,We can't find this order status.,, -Create New Order Status,Create New Order Status,, -We found another order status with the same order status code.,We found another order status with the same order status code.,, -You saved the order status.,You saved the order status.,, -We can't add the order status right now.,We can't add the order status right now.,, -You have unassigned the order status.,You have unassigned the order status.,, -Something went wrong while unassigning the order.,Something went wrong while unassigning the order.,, -Can't unhold order.,Can't unhold order.,, -You released the order from holding status.,You released the order from holding status.,, -The order was not on hold.,The order was not on hold.,, -Exception occurred during order load,Exception occurred during order load,, -Something went wrong while saving the gift message.,Something went wrong while saving the gift message.,, -You saved the gift card message.,You saved the gift card message.,, -The payment has been voided.,The payment has been voided.,, -We can't void the payment right now.,We can't void the payment right now.,, -Please correct the transaction ID and try again.,Please correct the transaction ID and try again.,, -The transaction details have been updated.,The transaction details have been updated.,, -We can't update the transaction details.,We can't update the transaction details.,, -Orders and Returns,Orders and Returns,, -You entered incorrect data. Please try again.,You entered incorrect data. Please try again.,, -Home,Home,, -Go to Home Page,Go to Home Page,, -We can't find this wish list.,We can't find this wish list.,, -"We could not add a product to cart by the ID ""%1"".","We could not add a product to cart by the ID ""%1"".",, -There is an error in one of the option rows.,There is an error in one of the option rows.,, -Shipping Address: ,Shipping Address: ,, -Billing Address: ,Billing Address: ,, -Please specify order items.,Please specify order items.,, -Please specify a shipping method.,Please specify a shipping method.,, -Please specify a payment method.,Please specify a payment method.,, -This payment method is not available.,This payment method is not available.,, -Validation is failed.,Validation is failed.,, -You did not email your customer. Please check your email settings.,You did not email your customer. Please check your email settings.,, --- Please Select --,-- Please Select --,, -"Path ""%1"" is not part of allowed directory ""%2""","Path ""%1"" is not part of allowed directory ""%2""",, -Identifying Fields required,Identifying Fields required,, -Id required,Id required,, -"""Invoice Document Validation Error(s):\n"" .","""Invoice Document Validation Error(s):\n"" .",, -"Could not save an invoice, see error log for details","Could not save an invoice, see error log for details",, -A hold action is not available.,A hold action is not available.,, -You cannot remove the hold.,You cannot remove the hold.,, -We cannot cancel this order.,We cannot cancel this order.,, -Guest,Guest,, -Please enter the first name.,Please enter the first name.,, -Please enter the last name.,Please enter the last name.,, -Please enter the street.,Please enter the street.,, -Please enter the city.,Please enter the city.,, -Please enter the phone number.,Please enter the phone number.,, -Please enter the company.,Please enter the company.,, -Please enter the fax number.,Please enter the fax number.,, -Please enter the zip/postal code.,Please enter the zip/postal code.,, -Please enter the country.,Please enter the country.,, -Please enter the state/province.,Please enter the state/province.,, -Requested entity doesn't exist,Requested entity doesn't exist,, -Could not delete order address,Could not delete order address,, -Could not save order address,Could not save order address,, -Pending,Pending,, -Refunded,Refunded,, -Canceled,Canceled,, -Unknown State,Unknown State,, -"We found an invalid quantity to refund item ""%1"".","We found an invalid quantity to refund item ""%1"".",, -The creditmemo contains product item that is not part of the original order.,The creditmemo contains product item that is not part of the original order.,, -The quantity to refund must not be greater than the unrefunded quantity.,The quantity to refund must not be greater than the unrefunded quantity.,, -Maximum shipping amount allowed to refund is: %1,Maximum shipping amount allowed to refund is: %1,, -Order Id is required for creditmemo document,Order Id is required for creditmemo document,, -"The creditmemo contains product SKU ""%1"" that is not part of the original order.","The creditmemo contains product SKU ""%1"" that is not part of the original order.",, -"The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".","The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".",, -You can't create a creditmemo without products.,You can't create a creditmemo without products.,, -The most money available to refund is %1.,The most money available to refund is %1.,, -Could not delete credit memo,Could not delete credit memo,, -Could not save credit memo,Could not save credit memo,, -This order already has associated customer account,This order already has associated customer account,, -Paid,Paid,, -We cannot register an existing invoice,We cannot register an existing invoice,, -We can't create creditmemo for the invoice.,We can't create creditmemo for the invoice.,, -Order Id is required for invoice document,Order Id is required for invoice document,, -"The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".","The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".",, -The invoice contains one or more items that are not part of the original order.,The invoice contains one or more items that are not part of the original order.,, -ID required,ID required,, -Unknown Status,Unknown Status,, -Ordered,Ordered,, -Shipped,Shipped,, -Invoiced,Invoiced,, -Backordered,Backordered,, -Returned,Returned,, -Partial,Partial,, -Mixed,Mixed,, -Registered a Void notification.,Registered a Void notification.,, -"If the invoice was created offline, try creating an offline credit memo.","If the invoice was created offline, try creating an offline credit memo.",, -We refunded %1 online.,We refunded %1 online.,, -We refunded %1 offline.,We refunded %1 offline.,, -"IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.","IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.",, -The credit memo has been created automatically.,The credit memo has been created automatically.,, -Registered notification about refunded amount of %1.,Registered notification about refunded amount of %1.,, -Canceled order online,Canceled order online,, -Canceled order offline,Canceled order offline,, -Approved the payment online.,Approved the payment online.,, -There is no need to approve this payment.,There is no need to approve this payment.,, -Denied the payment online,Denied the payment online,, -Registered update about approved payment.,Registered update about approved payment.,, -Registered update about denied payment.,Registered update about denied payment.,, -There is no update for the payment.,There is no update for the payment.,, -Voided authorization.,Voided authorization.,, -Amount: %1.,Amount: %1.,, -"Transaction ID: ""%1""","Transaction ID: ""%1""",, -The payment method you requested is not available.,The payment method you requested is not available.,, -The payment disallows storing objects.,The payment disallows storing objects.,, -"The transaction ""%1"" cannot be captured yet.","The transaction ""%1"" cannot be captured yet.",, -The order amount of %1 is pending approval on the payment gateway.,The order amount of %1 is pending approval on the payment gateway.,, -Ordered amount of %1,Ordered amount of %1,, -An amount of %1 will be captured after being approved at the payment gateway.,An amount of %1 will be captured after being approved at the payment gateway.,, -Registered notification about captured amount of %1.,Registered notification about captured amount of %1.,, -Order is suspended as its capture amount %1 is suspected to be fraudulent.,Order is suspended as its capture amount %1 is suspected to be fraudulent.,, -The parent transaction ID must have a transaction ID.,The parent transaction ID must have a transaction ID.,, -Payment transactions disallow storing objects.,Payment transactions disallow storing objects.,, -"The transaction ""%1"" (%2) is already closed.","The transaction ""%1"" (%2) is already closed.",, -Set order for existing transactions not allowed,Set order for existing transactions not allowed,, -"At minimum, you need to set a payment ID.","At minimum, you need to set a payment ID.",, -Order,Order,, -Authorization,Authorization,, -"We found an unsupported transaction type ""%1"".","We found an unsupported transaction type ""%1"".",, -Please set a proper payment and order id.,Please set a proper payment and order id.,, -Please enter a Transaction ID.,Please enter a Transaction ID.,, -You can't do this without a transaction object.,You can't do this without a transaction object.,, -Order # ,Order # ,, -Order Date: ,Order Date: ,, -Sold to:,Sold to:,, -Ship to:,Ship to:,, -Payment Method:,Payment Method:,, -Shipping Method:,Shipping Method:,, -Total Shipping Charges,Total Shipping Charges,, -Title,Title,, -Number,Number,, -We found an invalid renderer model.,We found an invalid renderer model.,, -Please define the PDF object before using.,Please define the PDF object before using.,, -"We don't recognize the draw line data. Please define the ""lines"" array.","We don't recognize the draw line data. Please define the ""lines"" array.",, -Products,Products,, -Total (ex),Total (ex),, -Qty,Qty,, -Tax,Tax,, -Total (inc),Total (inc),, -Credit Memo # ,Credit Memo # ,, -Invoice # ,Invoice # ,, -The order object is not specified.,The order object is not specified.,, -The source object is not specified.,The source object is not specified.,, -An item object is not specified.,An item object is not specified.,, -A PDF object is not specified.,A PDF object is not specified.,, -A PDF page object is not specified.,A PDF page object is not specified.,, -Excl. Tax,Excl. Tax,, -Incl. Tax,Incl. Tax,, -Packing Slip # ,Packing Slip # ,, -title,title,, -The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.,The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.,, -We cannot register an existing shipment,We cannot register an existing shipment,, -Parent shipment cannot be loaded for track object.,Parent shipment cannot be loaded for track object.,, -Order Id is required for shipment document,Order Id is required for shipment document,, -You can't create a shipment without products.,You can't create a shipment without products.,, -"The shipment contains product SKU ""%1"" that is not part of the original order.","The shipment contains product SKU ""%1"" that is not part of the original order.",, -"The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".","The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".",, -Please enter a tracking number.,Please enter a tracking number.,, -Could not delete shipment,Could not delete shipment,, -Could not save shipment,Could not save shipment,, -The last status can't be unassigned from its current state.,The last status can't be unassigned from its current state.,, -"Status can't be unassigned, because it is used by existing order(s).","Status can't be unassigned, because it is used by existing order(s).",, -The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.,The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.,, -An invoice cannot be created when an order has a status of %1,An invoice cannot be created when an order has a status of %1,, -A creditmemo can not be created when an order has a status of %1,A creditmemo can not be created when an order has a status of %1,, -The order does not allow a creditmemo to be created.,The order does not allow a creditmemo to be created.,, -A shipment cannot be created when an order has a status of %1,A shipment cannot be created when an order has a status of %1,, -The order does not allow a shipment to be created.,The order does not allow a shipment to be created.,, -"""Creditmemo Document Validation Error(s):\n"" .","""Creditmemo Document Validation Error(s):\n"" .",, -"Could not save a Creditmemo, see error log for details","Could not save a Creditmemo, see error log for details",, -We cannot determine the field name.,We cannot determine the field name.,, -City,City,, -Company,Company,, -Country,Country,, -Email,Email,, -First Name,First Name,, -Last Name,Last Name,, -State/Province,State/Province,, -Street Address,Street Address,, -Phone Number,Phone Number,, -Zip/Postal Code,Zip/Postal Code,, -We can't save the address:\n%1,We can't save the address:\n%1,, -Cannot save comment:\n%1,Cannot save comment:\n%1,, -We don't have enough information to save the parent transaction ID.,We don't have enough information to save the parent transaction ID.,, -We cannot create an empty shipment.,We cannot create an empty shipment.,, -Cannot save track:\n%1,Cannot save track:\n%1,, -Cannot unassign status from state,Cannot unassign status from state,, -New Orders,New Orders,, -Order #%1 created at %2,Order #%1 created at %2,, -Details for %1 #%2,Details for %1 #%2,, -Notified Date: %1,Notified Date: %1,, -Comment: %1<br/>,Comment: %1<br/>,, -Current Status: %1<br/>,Current Status: %1<br/>,, -Total: %1<br/>,Total: %1<br/>,, -Order # %1 Notification(s),Order # %1 Notification(s),, -You can not cancel Credit Memo,You can not cancel Credit Memo,, -Could not cancel creditmemo,Could not cancel creditmemo,, -We cannot register an existing credit memo.,We cannot register an existing credit memo.,, -"We found an invalid quantity to invoice item ""%1"".","We found an invalid quantity to invoice item ""%1"".",, -"The Order State ""%1"" must not be set manually.","The Order State ""%1"" must not be set manually.",, -"""Shipment Document Validation Error(s):\n"" .","""Shipment Document Validation Error(s):\n"" .",, -"Could not save a shipment, see error log for details","Could not save a shipment, see error log for details",, -VAT Request Identifier,VAT Request Identifier,, -VAT Request Date,VAT Request Date,, -Pending Payment,Pending Payment,, -Processing,Processing,, -On Hold,On Hold,, -Complete,Complete,, -Closed,Closed,, -Suspected Fraud,Suspected Fraud,, -Payment Review,Payment Review,, -New,New,, -test message,test message,, -Email has not been sent,Email has not been sent,, -Authorized amount of %1.,Authorized amount of %1.,, -We will authorize %1 after the payment is approved at the payment gateway.,We will authorize %1 after the payment is approved at the payment gateway.,, -Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.,Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.,, -Captured amount of %1 online.,Captured amount of %1 online.,, -Authorized amount of %1,Authorized amount of %1,, -" Transaction ID: ""%1"""," Transaction ID: ""%1""",, -View,View,, -Group was removed,Group was removed,, -"Changing address information will not recalculate shipping, tax or other order amount.","Changing address information will not recalculate shipping, tax or other order amount.",, -Comment Text,Comment Text,, -Notify Customer by Email,Notify Customer by Email,, -Visible on Storefront,Visible on Storefront,, -Customer,Customer,, -Notified,Notified,, -Not Notified,Not Notified,, -No Payment Methods,No Payment Methods,, -Order Comments,Order Comments,, -Apply Coupon Code,Apply Coupon Code,, -Apply,Apply,, -Remove Coupon Code,Remove Coupon Code,, -Remove,Remove,, -Address Information,Address Information,, -Payment &, Shipping Information,Payment &, Shipping Information -Order Total,Order Total,, -Order Currency:,Order Currency:,, -Same As Billing Address,Same As Billing Address,, -Select from existing customer addresses:,Select from existing customer addresses:,, -Add New Address,Add New Address,, -Save in address book,Save in address book,, -You don't need to select a shipping address.,You don't need to select a shipping address.,, -Gift Message for the Entire Order,Gift Message for the Entire Order,, -Leave this box blank if you don't want to leave a gift message for the entire order.,Leave this box blank if you don't want to leave a gift message for the entire order.,, -Row Subtotal,Row Subtotal,, -Action,Action,, -No ordered items,No ordered items,, -Update Items and Quantities,Update Items and Quantities,, -Total %1 product(s),Total %1 product(s),, -Subtotal:,Subtotal:,, -Tier Pricing,Tier Pricing,, -Custom Price,Custom Price,, -Please select,Please select,, -Move to Shopping Cart,Move to Shopping Cart,, -Move to Wish List,Move to Wish List,, -Subscribe to Newsletter,Subscribe to Newsletter,, -Click to change shipping method,Click to change shipping method,, -"Sorry, no quotes are available for this order.","Sorry, no quotes are available for this order.",, -Get shipping methods and rates,Get shipping methods and rates,, -You don't need to select a shipping method.,You don't need to select a shipping method.,, -Customer's Activities,Customer's Activities,, -Refresh,Refresh,, -Item,Item,, -Add To Order,Add To Order,, -Configure and Add to Order,Configure and Add to Order,, -No items,No items,, -Append Comments,Append Comments,, -Email Order Confirmation,Email Order Confirmation,, -Grand Total Excl. Tax,Grand Total Excl. Tax,, -Grand Total Incl. Tax,Grand Total Incl. Tax,, -Subtotal (Excl. Tax),Subtotal (Excl. Tax),, -Subtotal (Incl. Tax),Subtotal (Incl. Tax),, -Payment &, Shipping Method,Payment &, Shipping Method -Payment Information,Payment Information,, -The order was placed using %1.,The order was placed using %1.,, -Shipping Information,Shipping Information,, -Items to Refund,Items to Refund,, -Return to Stock,Return to Stock,, -Qty to Refund,Qty to Refund,, -Tax Amount,Tax Amount,, -Discount Amount,Discount Amount,, -Row Total,Row Total,, -No Items To Refund,No Items To Refund,, -Credit Memo Comments,Credit Memo Comments,, -Refund Totals,Refund Totals,, -Email Copy of Credit Memo,Email Copy of Credit Memo,, -Please enter a positive number in this field.,Please enter a positive number in this field.,, -Items Refunded,Items Refunded,, -No Items,No Items,, -Memo Total,Memo Total,, -Credit Memo History,Credit Memo History,, -Credit Memo Totals,Credit Memo Totals,, -Customer Name: %1,Customer Name: %1,, -Purchased From: %1,Purchased From: %1,, -Gift Message,Gift Message,, -From:,From:,, -To:,To:,, -Message:,Message:,, -Shipping &, Handling,Shipping &, Handling -Gift Options,Gift Options,, -Create Shipment,Create Shipment,, -Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.,Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.,, -%1,%1,, -Qty to Invoice,Qty to Invoice,, -Invoice History,Invoice History,, -Invoice Comments,Invoice Comments,, -Invoice Totals,Invoice Totals,, -Amount,Amount,, -Capture Online,Capture Online,, -Capture Offline,Capture Offline,, -Not Capture,Not Capture,, -The invoice will be created offline without the payment gateway.,The invoice will be created offline without the payment gateway.,, -Email Copy of Invoice,Email Copy of Invoice,, -Items Invoiced,Items Invoiced,, -Total Tax,Total Tax,, -From Name,From Name,, -To Name,To Name,, -Status,Status,, -Comment,Comment,, -Notification Not Applicable,Notification Not Applicable,, -Order & Account Information,Order & Account Information,, -The order confirmation email was sent,The order confirmation email was sent,, -The order confirmation email is not sent,The order confirmation email is not sent,, -Order Date,Order Date,, -Order Date (%1),Order Date (%1),, -Purchased From,Purchased From,, -Link to the New Order,Link to the New Order,, -Link to the Previous Order,Link to the Previous Order,, -Placed from IP,Placed from IP,, -%1 / %2 rate:,%1 / %2 rate:,, -Customer Name,Customer Name,, -Customer Group,Customer Group,, -Notes for this Order,Notes for this Order,, -Comment added,Comment added,, -Transaction Data,Transaction Data,, -Transaction ID,Transaction ID,, -Parent Transaction ID,Parent Transaction ID,, -Order ID,Order ID,, -Transaction Type,Transaction Type,, -Is Closed,Is Closed,, -Created At,Created At,, -Child Transactions,Child Transactions,, -Transaction Details,Transaction Details,, -Items,Items,, -Gift Message for this Order,Gift Message for this Order,, -Shipped By,Shipped By,, -Tracking Number,Tracking Number,, -Billing Last Name,Billing Last Name,, -Find Order By,Find Order By,, -ZIP Code,ZIP Code,, -Billing ZIP Code,Billing ZIP Code,, -Continue,Continue,, -Print All Refunds,Print All Refunds,, -Refund #,Refund #,, -Print Refund,Print Refund,, -Product Name,Product Name,, -Order #,Order #,, -Date,Date,, -Ship To,Ship To,, -Actions,Actions,, -View Order,View Order,, -You have placed no orders.,You have placed no orders.,, -No shipping information available,No shipping information available,, -Print Order,Print Order,, -Print All Invoices,Print All Invoices,, -Invoice #,Invoice #,, -Print Invoice,Print Invoice,, -Qty Invoiced,Qty Invoiced,, -Close,Close,, -About Your Order,About Your Order,, -"<span class=""label"">Order Date:</span> %1","<span class=""label"">Order Date:</span> %1",, -Refund #%1,Refund #%1,, -Shipment #%1,Shipment #%1,, -Qty Shipped,Qty Shipped,, -Recent Orders,Recent Orders,, -View All,View All,, -Gift Message for This Order,Gift Message for This Order,, -Recently Ordered,Recently Ordered,, -Add to Cart,Add to Cart,, -Search,Search,, -Credit memo for your %store_name order,Credit memo for your %store_name order,, -"%name,","%name,",, -Thank you for your order from %store_name.,Thank you for your order from %store_name.,, -"You can check the status of your order by <a href=""%account_url"">logging into your account</a>.","You can check the status of your order by <a href=""%account_url"">logging into your account</a>.",, -"If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>","If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>",, -"or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>",, -"Our hours are <span class=""no-link"">%store_hours</span>.","Our hours are <span class=""no-link"">%store_hours</span>.",, -Your Credit Memo #%creditmemo_id for Order #%order_id,Your Credit Memo #%creditmemo_id for Order #%order_id,, -Billing Info,Billing Info,, -Shipping Info,Shipping Info,, -Update to your %store_name credit memo,Update to your %store_name credit memo,, -Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.,Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.,, -Invoice for your %store_name order,Invoice for your %store_name order,, -Your Invoice #%invoice_id for Order #%order_id,Your Invoice #%invoice_id for Order #%order_id,, -Update to your %store_name invoice,Update to your %store_name invoice,, -Your %store_name order confirmation,Your %store_name order confirmation,, -"%customer_name,","%customer_name,",, -Once your package ships we will send you a tracking number.,Once your package ships we will send you a tracking number.,, -"Your Order <span class=""no-link"">#%increment_id</span>","Your Order <span class=""no-link"">#%increment_id</span>",, -"Placed on <span class=""no-link"">%created_at</span>","Placed on <span class=""no-link"">%created_at</span>",, -Once your package ships we will send an email with a link to track your order.,Once your package ships we will send an email with a link to track your order.,, -Update to your %store_name order,Update to your %store_name order,, -Your %store_name order has shipped,Your %store_name order has shipped,, -Your shipping confirmation is below. Thank you again for your business.,Your shipping confirmation is below. Thank you again for your business.,, -Your Shipment #%shipment_id for Order #%order_id,Your Shipment #%shipment_id for Order #%order_id,, -Update to your %store_name shipment,Update to your %store_name shipment,, -Gift Options for ,Gift Options for ,, -Add Products,Add Products,, -You have item changes,You have item changes,, -Ok,Ok,, -Operations,Operations,, -Create,Create,, -Send Order Email,Send Order Email,, -Accept or Deny Payment,Accept or Deny Payment,, -Send Sales Emails,Send Sales Emails,, -Sales Section,Sales Section,, -Sales Emails Section,Sales Emails Section,, -General,General,, -Hide Customer IP,Hide Customer IP,, -"Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.","Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.",, -Checkout Totals Sort Order,Checkout Totals Sort Order,, -Allow Reorder,Allow Reorder,, -Invoice and Packing Slip Design,Invoice and Packing Slip Design,, -Logo for PDF Print-outs (200x50),Logo for PDF Print-outs (200x50),, -"Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.","Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.",, -Logo for HTML Print View,Logo for HTML Print View,, -"Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)","Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)",, -Address,Address,, -Minimum Order Amount,Minimum Order Amount,, -Enable,Enable,, -Minimum Amount,Minimum Amount,, -Subtotal after discount,Subtotal after discount,, -Include Tax to Amount,Include Tax to Amount,, -Description Message,Description Message,, -This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.,This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.,, -Error to Show in Shopping Cart,Error to Show in Shopping Cart,, -Validate Each Address Separately in Multi-address Checkout,Validate Each Address Separately in Multi-address Checkout,, -Multi-address Description Message,Multi-address Description Message,, -We'll use the default description above if you leave this empty.,We'll use the default description above if you leave this empty.,, -Multi-address Error to Show in Shopping Cart,Multi-address Error to Show in Shopping Cart,, -We'll use the default error above if you leave this empty.,We'll use the default error above if you leave this empty.,, -Dashboard,Dashboard,, -Use Aggregated Data,Use Aggregated Data,, -Orders Cron Settings,Orders Cron Settings,, -Pending Payment Order Lifetime (minutes),Pending Payment Order Lifetime (minutes),, -Sales Emails,Sales Emails,, -Asynchronous sending,Asynchronous sending,, -Enabled,Enabled,, -New Order Confirmation Email Sender,New Order Confirmation Email Sender,, -New Order Confirmation Template,New Order Confirmation Template,, -"Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected.",, -New Order Confirmation Template for Guest,New Order Confirmation Template for Guest,, -Send Order Email Copy To,Send Order Email Copy To,, -Comma-separated,Comma-separated,, -Send Order Email Copy Method,Send Order Email Copy Method,, -Order Comment Email Sender,Order Comment Email Sender,, -Order Comment Email Template,Order Comment Email Template,, -Order Comment Email Template for Guest,Order Comment Email Template for Guest,, -Send Order Comment Email Copy To,Send Order Comment Email Copy To,, -Send Order Comments Email Copy Method,Send Order Comments Email Copy Method,, -Invoice Email Sender,Invoice Email Sender,, -Invoice Email Template,Invoice Email Template,, -Invoice Email Template for Guest,Invoice Email Template for Guest,, -Send Invoice Email Copy To,Send Invoice Email Copy To,, -Send Invoice Email Copy Method,Send Invoice Email Copy Method,, -Invoice Comment Email Sender,Invoice Comment Email Sender,, -Invoice Comment Email Template,Invoice Comment Email Template,, -Invoice Comment Email Template for Guest,Invoice Comment Email Template for Guest,, -Send Invoice Comment Email Copy To,Send Invoice Comment Email Copy To,, -Send Invoice Comments Email Copy Method,Send Invoice Comments Email Copy Method,, -Shipment,Shipment,, -Shipment Email Sender,Shipment Email Sender,, -Shipment Email Template,Shipment Email Template,, -Shipment Email Template for Guest,Shipment Email Template for Guest,, -Send Shipment Email Copy To,Send Shipment Email Copy To,, -Send Shipment Email Copy Method,Send Shipment Email Copy Method,, -Shipment Comments,Shipment Comments,, -Shipment Comment Email Sender,Shipment Comment Email Sender,, -Shipment Comment Email Template,Shipment Comment Email Template,, -Shipment Comment Email Template for Guest,Shipment Comment Email Template for Guest,, -Send Shipment Comment Email Copy To,Send Shipment Comment Email Copy To,, -Send Shipment Comments Email Copy Method,Send Shipment Comments Email Copy Method,, -Credit Memo Email Sender,Credit Memo Email Sender,, -Credit Memo Email Template,Credit Memo Email Template,, -Credit Memo Email Template for Guest,Credit Memo Email Template for Guest,, -Send Credit Memo Email Copy To,Send Credit Memo Email Copy To,, -Send Credit Memo Email Copy Method,Send Credit Memo Email Copy Method,, -Credit Memo Comment Email Sender,Credit Memo Comment Email Sender,, -Credit Memo Comment Email Template,Credit Memo Comment Email Template,, -Credit Memo Comment Email Template for Guest,Credit Memo Comment Email Template for Guest,, -Send Credit Memo Comment Email Copy To,Send Credit Memo Comment Email Copy To,, -Send Credit Memo Comments Email Copy Method,Send Credit Memo Comments Email Copy Method,, -PDF Print-outs,PDF Print-outs,, -Display Order ID in Header,Display Order ID in Header,, -Customer Order Status Notification,Customer Order Status Notification,, -Asynchronous indexing,Asynchronous indexing,, -Orders and Returns Search Form,Orders and Returns Search Form,, -Anchor Custom Title,Anchor Custom Title,, -Template,Template,, -Default Template,Default Template,, -Name,Name,, -Phone,Phone,, -ZIP/Post Code,ZIP/Post Code,, -Signed-up Point,Signed-up Point,, -Website,Website,, -Bill-to Name,Bill-to Name,, -Created,Created,, -Invoice Date,Invoice Date,, -Ship-to Name,Ship-to Name,, -Ship Date,Ship Date,, -Total Quantity,Total Quantity,, -Default Status,Default Status,, -State Code and Title,State Code and Title,, -Item Status,Item Status,, -Original Price,Original Price,, -Tax Percent,Tax Percent,, -All Store Views,All Store Views,, -PDF Credit Memos,PDF Credit Memos,, -Purchase Point,Purchase Point,, -Print Invoices,Print Invoices,, -Print Packing Slips,Print Packing Slips,, -Print Credit Memos,Print Credit Memos,, -Print All,Print All,, -Print Shipping Labels,Print Shipping Labels,, -Purchase Date,Purchase Date,, -Grand Total (Base),Grand Total (Base),, -Grand Total (Purchased),Grand Total (Purchased),, -Customer Email,Customer Email,, -Shipping and Handling,Shipping and Handling,, -PDF Invoices,PDF Invoices,, -PDF Shipments,PDF Shipments,, -PDF Creditmemos,PDF Creditmemos,, -Refunds,Refunds,, -Allow Zero GrandTotal for Creditmemo,Allow Zero GrandTotal for Creditmemo,, -Allow Zero GrandTotal,Allow Zero GrandTotal,, -Email is required field for Admin order creation,Email is required field for Admin order creation,, -If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer.,, +"Credit Memos","Credit Memos" +Orders,Orders +Invoices,Invoices +"We can't get the order instance right now.","We can't get the order instance right now." +"Create New Order","Create New Order" +"Save Order Address","Save Order Address" +Shipping,Shipping +Billing,Billing +"Edit Order %1 %2 Address","Edit Order %1 %2 Address" +"Order Address Information","Order Address Information" +"Please correct the parent block for this block.","Please correct the parent block for this block." +"Submit Comment","Submit Comment" +"Submit Order","Submit Order" +"Are you sure you want to cancel this order?","Are you sure you want to cancel this order?" +Cancel,Cancel +"Billing Address","Billing Address" +"Payment Method","Payment Method" +"Order Comment","Order Comment" +Coupons,Coupons +"Please select a customer","Please select a customer" +"Create New Customer","Create New Customer" +"Account Information","Account Information" +From,From +To,To +Message,Message +"Edit Order #%1","Edit Order #%1" +"Create New Order for %1 in %2","Create New Order for %1 in %2" +"Create New Order in %1","Create New Order in %1" +"Create New Order for %1","Create New Order for %1" +"Create New Order for New Customer","Create New Order for New Customer" +"Items Ordered","Items Ordered" +"This product is disabled.","This product is disabled." +"Buy %1 for price %2","Buy %1 for price %2" +"Item ordered qty","Item ordered qty" +"%1 with %2 discount each","%1 with %2 discount each" +"%1 for %2","%1 for %2" +"* - Enter custom price including tax","* - Enter custom price including tax" +"* - Enter custom price excluding tax","* - Enter custom price excluding tax" +Configure,Configure +"This product does not have any configurable options","This product does not have any configurable options" +"Newsletter Subscription","Newsletter Subscription" +"Please select products","Please select products" +"Add Selected Product(s) to Order","Add Selected Product(s) to Order" +ID,ID +Product,Product +SKU,SKU +Price,Price +Select,Select +Quantity,Quantity +"Shipping Address","Shipping Address" +"Shipping Method","Shipping Method" +"Update Changes","Update Changes" +"Shopping Cart","Shopping Cart" +"Are you sure you want to delete all items from shopping cart?","Are you sure you want to delete all items from shopping cart?" +"Clear Shopping Cart","Clear Shopping Cart" +"Products in Comparison List","Products in Comparison List" +"Recently Compared Products","Recently Compared Products" +"Recently Viewed Products","Recently Viewed Products" +"Last Ordered Items","Last Ordered Items" +"Recently Viewed","Recently Viewed" +"Wish List","Wish List" +"Please select a store","Please select a store" +"Order Totals","Order Totals" +"Shipping Incl. Tax (%1)","Shipping Incl. Tax (%1)" +"Shipping Excl. Tax (%1)","Shipping Excl. Tax (%1)" +"New Credit Memo for Invoice #%1","New Credit Memo for Invoice #%1" +"New Credit Memo for Order #%1","New Credit Memo for Order #%1" +"Refund Shipping (Incl. Tax)","Refund Shipping (Incl. Tax)" +"Refund Shipping (Excl. Tax)","Refund Shipping (Excl. Tax)" +"Refund Shipping","Refund Shipping" +"Update Qty's","Update Qty's" +Refund,Refund +"Refund Offline","Refund Offline" +"Paid Amount","Paid Amount" +"Refund Amount","Refund Amount" +"Shipping Amount","Shipping Amount" +"Shipping Refund","Shipping Refund" +"Order Grand Total","Order Grand Total" +"Adjustment Refund","Adjustment Refund" +"Adjustment Fee","Adjustment Fee" +"Send Email","Send Email" +"Are you sure you want to send a credit memo email to customer?","Are you sure you want to send a credit memo email to customer?" +Void,Void +Print,Print +"The credit memo email was sent.","The credit memo email was sent." +"The credit memo email wasn't sent.","The credit memo email wasn't sent." +"Credit Memo #%1 | %3 | %2 (%4)","Credit Memo #%1 | %3 | %2 (%4)" +"Total Refund","Total Refund" +"New Invoice and Shipment for Order #%1","New Invoice and Shipment for Order #%1" +"New Invoice for Order #%1","New Invoice for Order #%1" +"Submit Invoice and Shipment","Submit Invoice and Shipment" +"Submit Invoice","Submit Invoice" +"Are you sure you want to send an invoice email to customer?","Are you sure you want to send an invoice email to customer?" +"Credit Memo","Credit Memo" +Capture,Capture +"The invoice email was sent.","The invoice email was sent." +"The invoice email wasn't sent.","The invoice email wasn't sent." +"Invoice #%1 | %2 | %4 (%3)","Invoice #%1 | %2 | %4 (%3)" +"Invalid parent block for this block","Invalid parent block for this block" +"Order Statuses","Order Statuses" +"Create New Status","Create New Status" +"Assign Status to State","Assign Status to State" +"Save Status Assignment","Save Status Assignment" +"Assign Order Status to State","Assign Order Status to State" +"Assignment Information","Assignment Information" +"Order Status","Order Status" +"Order State","Order State" +"Use Order Status As Default","Use Order Status As Default" +"Visible On Storefront","Visible On Storefront" +"Edit Order Status","Edit Order Status" +"Save Status","Save Status" +"New Order Status","New Order Status" +"Order Status Information","Order Status Information" +"Status Code","Status Code" +"Status Label","Status Label" +"Store View Specific Labels","Store View Specific Labels" +"Total Paid","Total Paid" +"Total Refunded","Total Refunded" +"Total Due","Total Due" +Edit,Edit +"Are you sure you want to send an order email to customer?","Are you sure you want to send an order email to customer?" +"This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?","This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you want to continue?" +"Are you sure you want to void the payment?","Are you sure you want to void the payment?" +Hold,Hold +hold,hold +Unhold,Unhold +unhold,unhold +"Are you sure you want to accept this payment?","Are you sure you want to accept this payment?" +"Accept Payment","Accept Payment" +"Are you sure you want to deny this payment?","Are you sure you want to deny this payment?" +"Deny Payment","Deny Payment" +"Get Payment Update","Get Payment Update" +"Invoice and Ship","Invoice and Ship" +Invoice,Invoice +Ship,Ship +Reorder,Reorder +"Order # %1 %2 | %3","Order # %1 %2 | %3" +"This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed.","This order contains (%1) items and therefore cannot be edited through the admin interface. If you wish to continue editing, the (%2) items will be removed, the order will be canceled and a new order will be placed." +"Are you sure? This order will be canceled and a new one will be created instead.","Are you sure? This order will be canceled and a new one will be created instead." +"Save Gift Message","Save Gift Message" +" [deleted]"," [deleted]" +"Order Credit Memos","Order Credit Memos" +"Credit memo #%1 created","Credit memo #%1 created" +"Credit memo #%1 comment added","Credit memo #%1 comment added" +"Shipment #%1 created","Shipment #%1 created" +"Shipment #%1 comment added","Shipment #%1 comment added" +"Invoice #%1 created","Invoice #%1 created" +"Invoice #%1 comment added","Invoice #%1 comment added" +"Tracking number %1 for %2 assigned","Tracking number %1 for %2 assigned" +"Comments History","Comments History" +"Order History","Order History" +Information,Information +"Order Information","Order Information" +"Order Invoices","Order Invoices" +Shipments,Shipments +"Order Shipments","Order Shipments" +Transactions,Transactions +"Order View","Order View" +Any,Any +Specified,Specified +"Applies to Any of the Specified Order Statuses except canceled orders","Applies to Any of the Specified Order Statuses except canceled orders" +"Cart Price Rule","Cart Price Rule" +Yes,Yes +No,No +"Show Actual Values","Show Actual Values" +"New Order RSS","New Order RSS" +Subtotal,Subtotal +"Shipping & Handling","Shipping & Handling" +"Discount (%1)","Discount (%1)" +Discount,Discount +"Grand Total","Grand Total" +Back,Back +Fetch,Fetch +"Transaction # %1 | %2","Transaction # %1 | %2" +N/A,N/A +Key,Key +Value,Value +"We found an invalid entity model.","We found an invalid entity model." +"Order # %1","Order # %1" +"Back to My Orders","Back to My Orders" +"View Another Order","View Another Order" +"About Your Refund","About Your Refund" +"My Orders","My Orders" +"Subscribe to Order Status","Subscribe to Order Status" +"About Your Invoice","About Your Invoice" +"Print Order # %1","Print Order # %1" +"Grand Total to be Charged","Grand Total to be Charged" +Unassign,Unassign +"We can't add this item to your shopping cart right now.","We can't add this item to your shopping cart right now." +"You sent the message.","You sent the message." +Sales,Sales +"Invoice capturing error","Invoice capturing error" +"This order no longer exists.","This order no longer exists." +"Please enter a comment.","Please enter a comment." +"We cannot add order history.","We cannot add order history." +"You updated the order address.","You updated the order address." +"We can't update the order address right now.","We can't update the order address right now." +"You have not canceled the item.","You have not canceled the item." +"You canceled the order.","You canceled the order." +"""%1"" coupon code was not applied. Do not apply discount is selected for item(s)","""%1"" coupon code was not applied. Do not apply discount is selected for item(s)" +"""%1"" coupon code is not valid.","""%1"" coupon code is not valid." +"The coupon code has been accepted.","The coupon code has been accepted." +"Quote item id is not received.","Quote item id is not received." +"Quote item is not loaded.","Quote item is not loaded." +"New Order","New Order" +"You created the order.","You created the order." +"Order saving error: %1","Order saving error: %1" +"Cannot add new comment.","Cannot add new comment." +"The credit memo has been canceled.","The credit memo has been canceled." +"Credit memo has not been canceled.","Credit memo has not been canceled." +"New Memo for #%1","New Memo for #%1" +"New Memo","New Memo" +"The credit memo's total must be positive.","The credit memo's total must be positive." +"Cannot create online refund for Refund to Store Credit.","Cannot create online refund for Refund to Store Credit." +"You created the credit memo.","You created the credit memo." +"We can't save the credit memo right now.","We can't save the credit memo right now." +"We can't update the item's quantity right now.","We can't update the item's quantity right now." +"View Memo for #%1","View Memo for #%1" +"View Memo","View Memo" +"You voided the credit memo.","You voided the credit memo." +"We can't void the credit memo.","We can't void the credit memo." +"The order no longer exists.","The order no longer exists." +"We can't create credit memo for the order.","We can't create credit memo for the order." +"Edit Order","Edit Order" +"You sent the order email.","You sent the order email." +"We can't send the email order right now.","We can't send the email order right now." +"You have not put the order on hold.","You have not put the order on hold." +"You put the order on hold.","You put the order on hold." +"You canceled the invoice.","You canceled the invoice." +"Invoice canceling error","Invoice canceling error" +"The invoice has been captured.","The invoice has been captured." +"The order does not allow an invoice to be created.","The order does not allow an invoice to be created." +"You can't create an invoice without products.","You can't create an invoice without products." +"New Invoice","New Invoice" +"We can't save the invoice right now.","We can't save the invoice right now." +"You created the invoice and shipment.","You created the invoice and shipment." +"The invoice has been created.","The invoice has been created." +"We can't send the invoice email right now.","We can't send the invoice email right now." +"We can't send the shipment right now.","We can't send the shipment right now." +"Cannot update item quantity.","Cannot update item quantity." +"The invoice has been voided.","The invoice has been voided." +"Invoice voiding error","Invoice voiding error" +"%1 order(s) cannot be canceled.","%1 order(s) cannot be canceled." +"You cannot cancel the order(s).","You cannot cancel the order(s)." +"We canceled %1 order(s).","We canceled %1 order(s)." +"%1 order(s) were not put on hold.","%1 order(s) were not put on hold." +"No order(s) were put on hold.","No order(s) were put on hold." +"You have put %1 order(s) on hold.","You have put %1 order(s) on hold." +"%1 order(s) were not released from on hold status.","%1 order(s) were not released from on hold status." +"No order(s) were released from on hold status.","No order(s) were released from on hold status." +"%1 order(s) have been released from on hold status.","%1 order(s) have been released from on hold status." +"There are no printable documents related to selected orders.","There are no printable documents related to selected orders." +"The payment has been accepted.","The payment has been accepted." +"The payment has been denied.","The payment has been denied." +"Transaction has been approved.","Transaction has been approved." +"Transaction has been voided/declined.","Transaction has been voided/declined." +"There is no update for the transaction.","There is no update for the transaction." +"We can't update the payment right now.","We can't update the payment right now." +"You assigned the order status.","You assigned the order status." +"Something went wrong while assigning the order status.","Something went wrong while assigning the order status." +"We can't find this order status.","We can't find this order status." +"Create New Order Status","Create New Order Status" +"We found another order status with the same order status code.","We found another order status with the same order status code." +"You saved the order status.","You saved the order status." +"We can't add the order status right now.","We can't add the order status right now." +"You have unassigned the order status.","You have unassigned the order status." +"Something went wrong while unassigning the order.","Something went wrong while unassigning the order." +"Can't unhold order.","Can't unhold order." +"You released the order from holding status.","You released the order from holding status." +"The order was not on hold.","The order was not on hold." +"Exception occurred during order load","Exception occurred during order load" +"Something went wrong while saving the gift message.","Something went wrong while saving the gift message." +"You saved the gift card message.","You saved the gift card message." +"The payment has been voided.","The payment has been voided." +"We can't void the payment right now.","We can't void the payment right now." +"Please correct the transaction ID and try again.","Please correct the transaction ID and try again." +"The transaction details have been updated.","The transaction details have been updated." +"We can't update the transaction details.","We can't update the transaction details." +"Orders and Returns","Orders and Returns" +"You entered incorrect data. Please try again.","You entered incorrect data. Please try again." +Home,Home +"Go to Home Page","Go to Home Page" +"We can't find this wish list.","We can't find this wish list." +"We could not add a product to cart by the ID ""%1"".","We could not add a product to cart by the ID ""%1""." +"There is an error in one of the option rows.","There is an error in one of the option rows." +"Shipping Address: ","Shipping Address: " +"Billing Address: ","Billing Address: " +"Please specify order items.","Please specify order items." +"Please specify a shipping method.","Please specify a shipping method." +"Please specify a payment method.","Please specify a payment method." +"This payment method is not available.","This payment method is not available." +"Validation is failed.","Validation is failed." +"You did not email your customer. Please check your email settings.","You did not email your customer. Please check your email settings." +"-- Please Select --","-- Please Select --" +"Path ""%1"" is not part of allowed directory ""%2""","Path ""%1"" is not part of allowed directory ""%2""" +"Identifying Fields required","Identifying Fields required" +"Id required","Id required" +"""Invoice Document Validation Error(s):\n"" .","""Invoice Document Validation Error(s):\n"" ." +"Could not save an invoice, see error log for details","Could not save an invoice, see error log for details" +"A hold action is not available.","A hold action is not available." +"You cannot remove the hold.","You cannot remove the hold." +"We cannot cancel this order.","We cannot cancel this order." +Guest,Guest +"Please enter the first name.","Please enter the first name." +"Please enter the last name.","Please enter the last name." +"Please enter the street.","Please enter the street." +"Please enter the city.","Please enter the city." +"Please enter the phone number.","Please enter the phone number." +"Please enter the company.","Please enter the company." +"Please enter the fax number.","Please enter the fax number." +"Please enter the zip/postal code.","Please enter the zip/postal code." +"Please enter the country.","Please enter the country." +"Please enter the state/province.","Please enter the state/province." +"Requested entity doesn't exist","Requested entity doesn't exist" +"Could not delete order address","Could not delete order address" +"Could not save order address","Could not save order address" +Pending,Pending +Refunded,Refunded +Canceled,Canceled +"Unknown State","Unknown State" +"We found an invalid quantity to refund item ""%1"".","We found an invalid quantity to refund item ""%1""." +"The creditmemo contains product item that is not part of the original order.","The creditmemo contains product item that is not part of the original order." +"The quantity to refund must not be greater than the unrefunded quantity.","The quantity to refund must not be greater than the unrefunded quantity." +"Maximum shipping amount allowed to refund is: %1","Maximum shipping amount allowed to refund is: %1" +"Order Id is required for creditmemo document","Order Id is required for creditmemo document" +"The creditmemo contains product SKU ""%1"" that is not part of the original order.","The creditmemo contains product SKU ""%1"" that is not part of the original order." +"The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1"".","The quantity to creditmemo must not be greater than the unrefunded quantity for product SKU ""%1""." +"You can't create a creditmemo without products.","You can't create a creditmemo without products." +"The most money available to refund is %1.","The most money available to refund is %1." +"Could not delete credit memo","Could not delete credit memo" +"Could not save credit memo","Could not save credit memo" +"This order already has associated customer account","This order already has associated customer account" +Paid,Paid +"We cannot register an existing invoice","We cannot register an existing invoice" +"We can't create creditmemo for the invoice.","We can't create creditmemo for the invoice." +"Order Id is required for invoice document","Order Id is required for invoice document" +"The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1"".","The quantity to invoice must not be greater than the uninvoiced quantity for product SKU ""%1""." +"The invoice contains one or more items that are not part of the original order.","The invoice contains one or more items that are not part of the original order." +"ID required","ID required" +"Unknown Status","Unknown Status" +Ordered,Ordered +Shipped,Shipped +Invoiced,Invoiced +Backordered,Backordered +Returned,Returned +Partial,Partial +Mixed,Mixed +"Registered a Void notification.","Registered a Void notification." +"If the invoice was created offline, try creating an offline credit memo.","If the invoice was created offline, try creating an offline credit memo." +"We refunded %1 online.","We refunded %1 online." +"We refunded %1 offline.","We refunded %1 offline." +"IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo.","IPN ""Refunded"". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: ""%2"". Credit Memo has not been created. Please create offline Credit Memo." +"The credit memo has been created automatically.","The credit memo has been created automatically." +"Registered notification about refunded amount of %1.","Registered notification about refunded amount of %1." +"Canceled order online","Canceled order online" +"Canceled order offline","Canceled order offline" +"Approved the payment online.","Approved the payment online." +"There is no need to approve this payment.","There is no need to approve this payment." +"Denied the payment online","Denied the payment online" +"Registered update about approved payment.","Registered update about approved payment." +"Registered update about denied payment.","Registered update about denied payment." +"There is no update for the payment.","There is no update for the payment." +"Voided authorization.","Voided authorization." +"Amount: %1.","Amount: %1." +"Transaction ID: ""%1""","Transaction ID: ""%1""" +"The payment method you requested is not available.","The payment method you requested is not available." +"The payment disallows storing objects.","The payment disallows storing objects." +"The transaction ""%1"" cannot be captured yet.","The transaction ""%1"" cannot be captured yet." +"The order amount of %1 is pending approval on the payment gateway.","The order amount of %1 is pending approval on the payment gateway." +"Ordered amount of %1","Ordered amount of %1" +"An amount of %1 will be captured after being approved at the payment gateway.","An amount of %1 will be captured after being approved at the payment gateway." +"Registered notification about captured amount of %1.","Registered notification about captured amount of %1." +"Order is suspended as its capture amount %1 is suspected to be fraudulent.","Order is suspended as its capture amount %1 is suspected to be fraudulent." +"The parent transaction ID must have a transaction ID.","The parent transaction ID must have a transaction ID." +"Payment transactions disallow storing objects.","Payment transactions disallow storing objects." +"The transaction ""%1"" (%2) is already closed.","The transaction ""%1"" (%2) is already closed." +"Set order for existing transactions not allowed","Set order for existing transactions not allowed" +"At minimum, you need to set a payment ID.","At minimum, you need to set a payment ID." +Order,Order +Authorization,Authorization +"We found an unsupported transaction type ""%1"".","We found an unsupported transaction type ""%1""." +"Please set a proper payment and order id.","Please set a proper payment and order id." +"Please enter a Transaction ID.","Please enter a Transaction ID." +"You can't do this without a transaction object.","You can't do this without a transaction object." +"Order # ","Order # " +"Order Date: ","Order Date: " +"Sold to:","Sold to:" +"Ship to:","Ship to:" +"Payment Method:","Payment Method:" +"Shipping Method:","Shipping Method:" +"Total Shipping Charges","Total Shipping Charges" +Title,Title +Number,Number +"We found an invalid renderer model.","We found an invalid renderer model." +"Please define the PDF object before using.","Please define the PDF object before using." +"We don't recognize the draw line data. Please define the ""lines"" array.","We don't recognize the draw line data. Please define the ""lines"" array." +Products,Products +"Total (ex)","Total (ex)" +Qty,Qty +Tax,Tax +"Total (inc)","Total (inc)" +"Credit Memo # ","Credit Memo # " +"Invoice # ","Invoice # " +"The order object is not specified.","The order object is not specified." +"The source object is not specified.","The source object is not specified." +"An item object is not specified.","An item object is not specified." +"A PDF object is not specified.","A PDF object is not specified." +"A PDF page object is not specified.","A PDF page object is not specified." +"Excl. Tax","Excl. Tax" +"Incl. Tax","Incl. Tax" +"Packing Slip # ","Packing Slip # " +title,title +"The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal.","The PDF total model %1 must be or extend \Magento\Sales\Model\Order\Pdf\Total\DefaultTotal." +"We cannot register an existing shipment","We cannot register an existing shipment" +"Parent shipment cannot be loaded for track object.","Parent shipment cannot be loaded for track object." +"Order Id is required for shipment document","Order Id is required for shipment document" +"You can't create a shipment without products.","You can't create a shipment without products." +"The shipment contains product SKU ""%1"" that is not part of the original order.","The shipment contains product SKU ""%1"" that is not part of the original order." +"The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1"".","The quantity to ship must not be greater than the unshipped quantity for product SKU ""%1""." +"Please enter a tracking number.","Please enter a tracking number." +"Could not delete shipment","Could not delete shipment" +"Could not save shipment","Could not save shipment" +"The last status can't be unassigned from its current state.","The last status can't be unassigned from its current state." +"Status can't be unassigned, because it is used by existing order(s).","Status can't be unassigned, because it is used by existing order(s)." +"The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal.","The total model should be extended from \Magento\Sales\Model\Order\Total\AbstractTotal." +"An invoice cannot be created when an order has a status of %1","An invoice cannot be created when an order has a status of %1" +"A creditmemo can not be created when an order has a status of %1","A creditmemo can not be created when an order has a status of %1" +"The order does not allow a creditmemo to be created.","The order does not allow a creditmemo to be created." +"A shipment cannot be created when an order has a status of %1","A shipment cannot be created when an order has a status of %1" +"The order does not allow a shipment to be created.","The order does not allow a shipment to be created." +"""Creditmemo Document Validation Error(s):\n"" .","""Creditmemo Document Validation Error(s):\n"" ." +"Could not save a Creditmemo, see error log for details","Could not save a Creditmemo, see error log for details" +"We cannot determine the field name.","We cannot determine the field name." +City,City +Company,Company +Country,Country +Email,Email +"First Name","First Name" +"Last Name","Last Name" +State/Province,State/Province +"Street Address","Street Address" +"Phone Number","Phone Number" +"Zip/Postal Code","Zip/Postal Code" +"We can't save the address:\n%1","We can't save the address:\n%1" +"Cannot save comment:\n%1","Cannot save comment:\n%1" +"We don't have enough information to save the parent transaction ID.","We don't have enough information to save the parent transaction ID." +"We cannot create an empty shipment.","We cannot create an empty shipment." +"Cannot save track:\n%1","Cannot save track:\n%1" +"Cannot unassign status from state","Cannot unassign status from state" +"New Orders","New Orders" +"Order #%1 created at %2","Order #%1 created at %2" +"Details for %1 #%2","Details for %1 #%2" +"Notified Date: %1","Notified Date: %1" +"Comment: %1<br/>","Comment: %1<br/>" +"Current Status: %1<br/>","Current Status: %1<br/>" +"Total: %1<br/>","Total: %1<br/>" +"Order # %1 Notification(s)","Order # %1 Notification(s)" +"You can not cancel Credit Memo","You can not cancel Credit Memo" +"Could not cancel creditmemo","Could not cancel creditmemo" +"We cannot register an existing credit memo.","We cannot register an existing credit memo." +"We found an invalid quantity to invoice item ""%1"".","We found an invalid quantity to invoice item ""%1""." +"The Order State ""%1"" must not be set manually.","The Order State ""%1"" must not be set manually." +"""Shipment Document Validation Error(s):\n"" .","""Shipment Document Validation Error(s):\n"" ." +"Could not save a shipment, see error log for details","Could not save a shipment, see error log for details" +"VAT Request Identifier","VAT Request Identifier" +"VAT Request Date","VAT Request Date" +"Pending Payment","Pending Payment" +Processing,Processing +"On Hold","On Hold" +Complete,Complete +Closed,Closed +"Suspected Fraud","Suspected Fraud" +"Payment Review","Payment Review" +New,New +"test message","test message" +"Email has not been sent","Email has not been sent" +"Authorized amount of %1.","Authorized amount of %1." +"We will authorize %1 after the payment is approved at the payment gateway.","We will authorize %1 after the payment is approved at the payment gateway." +"Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.","Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent." +"Captured amount of %1 online.","Captured amount of %1 online." +"Authorized amount of %1","Authorized amount of %1" +" Transaction ID: ""%1"""," Transaction ID: ""%1""" +View,View +"Group was removed","Group was removed" +"Changing address information will not recalculate shipping, tax or other order amount.","Changing address information will not recalculate shipping, tax or other order amount." +"Comment Text","Comment Text" +"Notify Customer by Email","Notify Customer by Email" +"Visible on Storefront","Visible on Storefront" +Customer,Customer +Notified,Notified +"Not Notified","Not Notified" +"No Payment Methods","No Payment Methods" +"Order Comments","Order Comments" +"Apply Coupon Code","Apply Coupon Code" +Apply,Apply +"Remove Coupon Code","Remove Coupon Code" +Remove,Remove +"Address Information","Address Information" +"Payment & Shipping Information","Payment & Shipping Information" +"Order Total","Order Total" +"Order Currency:","Order Currency:" +"Same As Billing Address","Same As Billing Address" +"Select from existing customer addresses:","Select from existing customer addresses:" +"Add New Address","Add New Address" +"Save in address book","Save in address book" +"You don't need to select a shipping address.","You don't need to select a shipping address." +"Gift Message for the Entire Order","Gift Message for the Entire Order" +"Leave this box blank if you don't want to leave a gift message for the entire order.","Leave this box blank if you don't want to leave a gift message for the entire order." +"Row Subtotal","Row Subtotal" +Action,Action +"No ordered items","No ordered items" +"Update Items and Quantities","Update Items and Quantities" +"Total %1 product(s)","Total %1 product(s)" +Subtotal:,Subtotal: +"Tier Pricing","Tier Pricing" +"Custom Price","Custom Price" +"Please select","Please select" +"Move to Shopping Cart","Move to Shopping Cart" +"Move to Wish List","Move to Wish List" +"Subscribe to Newsletter","Subscribe to Newsletter" +"Click to change shipping method","Click to change shipping method" +"Sorry, no quotes are available for this order.","Sorry, no quotes are available for this order." +"Get shipping methods and rates","Get shipping methods and rates" +"You don't need to select a shipping method.","You don't need to select a shipping method." +"Customer's Activities","Customer's Activities" +Refresh,Refresh +Item,Item +"Add To Order","Add To Order" +"Configure and Add to Order","Configure and Add to Order" +"No items","No items" +"Append Comments","Append Comments" +"Email Order Confirmation","Email Order Confirmation" +"Grand Total Excl. Tax","Grand Total Excl. Tax" +"Grand Total Incl. Tax","Grand Total Incl. Tax" +"Subtotal (Excl. Tax)","Subtotal (Excl. Tax)" +"Subtotal (Incl. Tax)","Subtotal (Incl. Tax)" +"Payment & Shipping Method","Payment & Shipping Method" +"Payment Information","Payment Information" +"The order was placed using %1.","The order was placed using %1." +"Shipping Information","Shipping Information" +"Items to Refund","Items to Refund" +"Return to Stock","Return to Stock" +"Qty to Refund","Qty to Refund" +"Tax Amount","Tax Amount" +"Discount Amount","Discount Amount" +"Row Total","Row Total" +"No Items To Refund","No Items To Refund" +"Credit Memo Comments","Credit Memo Comments" +"Refund Totals","Refund Totals" +"Email Copy of Credit Memo","Email Copy of Credit Memo" +"Please enter a positive number in this field.","Please enter a positive number in this field." +"Items Refunded","Items Refunded" +"No Items","No Items" +"Memo Total","Memo Total" +"Credit Memo History","Credit Memo History" +"Credit Memo Totals","Credit Memo Totals" +"Customer Name: %1","Customer Name: %1" +"Purchased From: %1","Purchased From: %1" +"Gift Message","Gift Message" +From:,From: +To:,To: +Message:,Message: +"Shipping & Handling","Shipping & Handling" +"Gift Options","Gift Options" +"Create Shipment","Create Shipment" +"Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice.","Invoice and shipment types do not match for some items on this order. You can create a shipment only after creating the invoice." +%1,%1 +"Qty to Invoice","Qty to Invoice" +"Invoice History","Invoice History" +"Invoice Comments","Invoice Comments" +"Invoice Totals","Invoice Totals" +Amount,Amount +"Capture Online","Capture Online" +"Capture Offline","Capture Offline" +"Not Capture","Not Capture" +"The invoice will be created offline without the payment gateway.","The invoice will be created offline without the payment gateway." +"Email Copy of Invoice","Email Copy of Invoice" +"Items Invoiced","Items Invoiced" +"Total Tax","Total Tax" +"From Name","From Name" +"To Name","To Name" +Status,Status +Comment,Comment +"Notification Not Applicable","Notification Not Applicable" +"Order & Account Information","Order & Account Information" +"The order confirmation email was sent","The order confirmation email was sent" +"The order confirmation email is not sent","The order confirmation email is not sent" +"Order Date","Order Date" +"Order Date (%1)","Order Date (%1)" +"Purchased From","Purchased From" +"Link to the New Order","Link to the New Order" +"Link to the Previous Order","Link to the Previous Order" +"Placed from IP","Placed from IP" +"%1 / %2 rate:","%1 / %2 rate:" +"Customer Name","Customer Name" +"Customer Group","Customer Group" +"Notes for this Order","Notes for this Order" +"Comment added","Comment added" +"Transaction Data","Transaction Data" +"Transaction ID","Transaction ID" +"Parent Transaction ID","Parent Transaction ID" +"Order ID","Order ID" +"Transaction Type","Transaction Type" +"Is Closed","Is Closed" +"Created At","Created At" +"Child Transactions","Child Transactions" +"Transaction Details","Transaction Details" +Items,Items +"Gift Message for this Order","Gift Message for this Order" +"Shipped By","Shipped By" +"Tracking Number","Tracking Number" +"Billing Last Name","Billing Last Name" +"Find Order By","Find Order By" +"ZIP Code","ZIP Code" +"Billing ZIP Code","Billing ZIP Code" +Continue,Continue +"Print All Refunds","Print All Refunds" +"Refund #","Refund #" +"Print Refund","Print Refund" +"Product Name","Product Name" +"Order #","Order #" +Date,Date +"Ship To","Ship To" +Actions,Actions +"View Order","View Order" +"You have placed no orders.","You have placed no orders." +"No shipping information available","No shipping information available" +"Print Order","Print Order" +"Print All Invoices","Print All Invoices" +"Invoice #","Invoice #" +"Print Invoice","Print Invoice" +"Qty Invoiced","Qty Invoiced" +Close,Close +"About Your Order","About Your Order" +"<span class=""label"">Order Date:</span> %1","<span class=""label"">Order Date:</span> %1" +"Refund #%1","Refund #%1" +"Shipment #%1","Shipment #%1" +"Qty Shipped","Qty Shipped" +"Recent Orders","Recent Orders" +"View All","View All" +"Gift Message for This Order","Gift Message for This Order" +"Recently Ordered","Recently Ordered" +"Add to Cart","Add to Cart" +Search,Search +"Credit memo for your %store_name order","Credit memo for your %store_name order" +"%name,","%name," +"Thank you for your order from %store_name.","Thank you for your order from %store_name." +"You can check the status of your order by <a href=""%account_url"">logging into your account</a>.","You can check the status of your order by <a href=""%account_url"">logging into your account</a>." +"If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>","If you have questions about your order, you can email us at <a href=""mailto:%store_email"">%store_email</a>" +"or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>" +"Our hours are <span class=""no-link"">%store_hours</span>.","Our hours are <span class=""no-link"">%store_hours</span>." +"Your Credit Memo #%creditmemo_id for Order #%order_id","Your Credit Memo #%creditmemo_id for Order #%order_id" +"Billing Info","Billing Info" +"Shipping Info","Shipping Info" +"Update to your %store_name credit memo","Update to your %store_name credit memo" +"Your order #%increment_id has been updated with a status of <strong>%order_status</strong>.","Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." +"Invoice for your %store_name order","Invoice for your %store_name order" +"Your Invoice #%invoice_id for Order #%order_id","Your Invoice #%invoice_id for Order #%order_id" +"Update to your %store_name invoice","Update to your %store_name invoice" +"Your %store_name order confirmation","Your %store_name order confirmation" +"%customer_name,","%customer_name," +"Once your package ships we will send you a tracking number.","Once your package ships we will send you a tracking number." +"Your Order <span class=""no-link"">#%increment_id</span>","Your Order <span class=""no-link"">#%increment_id</span>" +"Placed on <span class=""no-link"">%created_at</span>","Placed on <span class=""no-link"">%created_at</span>" +"Once your package ships we will send an email with a link to track your order.","Once your package ships we will send an email with a link to track your order." +"Update to your %store_name order","Update to your %store_name order" +"Your %store_name order has shipped","Your %store_name order has shipped" +"Your shipping confirmation is below. Thank you again for your business.","Your shipping confirmation is below. Thank you again for your business." +"Your Shipment #%shipment_id for Order #%order_id","Your Shipment #%shipment_id for Order #%order_id" +"Update to your %store_name shipment","Update to your %store_name shipment" +"Gift Options for ","Gift Options for " +"Add Products","Add Products" +"You have item changes","You have item changes" +Ok,Ok +Operations,Operations +Create,Create +"Send Order Email","Send Order Email" +"Accept or Deny Payment","Accept or Deny Payment" +"Send Sales Emails","Send Sales Emails" +"Sales Section","Sales Section" +"Sales Emails Section","Sales Emails Section" +General,General +"Hide Customer IP","Hide Customer IP" +"Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.","Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos." +"Checkout Totals Sort Order","Checkout Totals Sort Order" +"Allow Reorder","Allow Reorder" +"Invoice and Packing Slip Design","Invoice and Packing Slip Design" +"Logo for PDF Print-outs (200x50)","Logo for PDF Print-outs (200x50)" +"Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image.","Your default logo will be used in PDF and HTML documents.<br />(jpeg, tiff, png) If your pdf image is distorted, try to use larger file-size image." +"Logo for HTML Print View","Logo for HTML Print View" +"Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)","Logo for HTML documents only. If empty, default will be used.<br />(jpeg, gif, png)" +Address,Address +"Minimum Order Amount","Minimum Order Amount" +Enable,Enable +"Minimum Amount","Minimum Amount" +"Subtotal after discount","Subtotal after discount" +"Include Tax to Amount","Include Tax to Amount" +"Description Message","Description Message" +"This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount.","This message will be shown in the shopping cart when the subtotal (after discount) is lower than the minimum allowed amount." +"Error to Show in Shopping Cart","Error to Show in Shopping Cart" +"Validate Each Address Separately in Multi-address Checkout","Validate Each Address Separately in Multi-address Checkout" +"Multi-address Description Message","Multi-address Description Message" +"We'll use the default description above if you leave this empty.","We'll use the default description above if you leave this empty." +"Multi-address Error to Show in Shopping Cart","Multi-address Error to Show in Shopping Cart" +"We'll use the default error above if you leave this empty.","We'll use the default error above if you leave this empty." +Dashboard,Dashboard +"Use Aggregated Data","Use Aggregated Data" +"Orders Cron Settings","Orders Cron Settings" +"Pending Payment Order Lifetime (minutes)","Pending Payment Order Lifetime (minutes)" +"Sales Emails","Sales Emails" +"Asynchronous sending","Asynchronous sending" +Enabled,Enabled +"New Order Confirmation Email Sender","New Order Confirmation Email Sender" +"New Order Confirmation Template","New Order Confirmation Template" +"Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." +"New Order Confirmation Template for Guest","New Order Confirmation Template for Guest" +"Send Order Email Copy To","Send Order Email Copy To" +Comma-separated,Comma-separated +"Send Order Email Copy Method","Send Order Email Copy Method" +"Order Comment Email Sender","Order Comment Email Sender" +"Order Comment Email Template","Order Comment Email Template" +"Order Comment Email Template for Guest","Order Comment Email Template for Guest" +"Send Order Comment Email Copy To","Send Order Comment Email Copy To" +"Send Order Comments Email Copy Method","Send Order Comments Email Copy Method" +"Invoice Email Sender","Invoice Email Sender" +"Invoice Email Template","Invoice Email Template" +"Invoice Email Template for Guest","Invoice Email Template for Guest" +"Send Invoice Email Copy To","Send Invoice Email Copy To" +"Send Invoice Email Copy Method","Send Invoice Email Copy Method" +"Invoice Comment Email Sender","Invoice Comment Email Sender" +"Invoice Comment Email Template","Invoice Comment Email Template" +"Invoice Comment Email Template for Guest","Invoice Comment Email Template for Guest" +"Send Invoice Comment Email Copy To","Send Invoice Comment Email Copy To" +"Send Invoice Comments Email Copy Method","Send Invoice Comments Email Copy Method" +Shipment,Shipment +"Shipment Email Sender","Shipment Email Sender" +"Shipment Email Template","Shipment Email Template" +"Shipment Email Template for Guest","Shipment Email Template for Guest" +"Send Shipment Email Copy To","Send Shipment Email Copy To" +"Send Shipment Email Copy Method","Send Shipment Email Copy Method" +"Shipment Comments","Shipment Comments" +"Shipment Comment Email Sender","Shipment Comment Email Sender" +"Shipment Comment Email Template","Shipment Comment Email Template" +"Shipment Comment Email Template for Guest","Shipment Comment Email Template for Guest" +"Send Shipment Comment Email Copy To","Send Shipment Comment Email Copy To" +"Send Shipment Comments Email Copy Method","Send Shipment Comments Email Copy Method" +"Credit Memo Email Sender","Credit Memo Email Sender" +"Credit Memo Email Template","Credit Memo Email Template" +"Credit Memo Email Template for Guest","Credit Memo Email Template for Guest" +"Send Credit Memo Email Copy To","Send Credit Memo Email Copy To" +"Send Credit Memo Email Copy Method","Send Credit Memo Email Copy Method" +"Credit Memo Comment Email Sender","Credit Memo Comment Email Sender" +"Credit Memo Comment Email Template","Credit Memo Comment Email Template" +"Credit Memo Comment Email Template for Guest","Credit Memo Comment Email Template for Guest" +"Send Credit Memo Comment Email Copy To","Send Credit Memo Comment Email Copy To" +"Send Credit Memo Comments Email Copy Method","Send Credit Memo Comments Email Copy Method" +"PDF Print-outs","PDF Print-outs" +"Display Order ID in Header","Display Order ID in Header" +"Customer Order Status Notification","Customer Order Status Notification" +"Asynchronous indexing","Asynchronous indexing" +"Orders and Returns Search Form","Orders and Returns Search Form" +"Anchor Custom Title","Anchor Custom Title" +Template,Template +"Default Template","Default Template" +Name,Name +Phone,Phone +"ZIP/Post Code","ZIP/Post Code" +"Signed-up Point","Signed-up Point" +Website,Website +"Bill-to Name","Bill-to Name" +Created,Created +"Invoice Date","Invoice Date" +"Ship-to Name","Ship-to Name" +"Ship Date","Ship Date" +"Total Quantity","Total Quantity" +"Default Status","Default Status" +"State Code and Title","State Code and Title" +"Item Status","Item Status" +"Original Price","Original Price" +"Tax Percent","Tax Percent" +"All Store Views","All Store Views" +"PDF Credit Memos","PDF Credit Memos" +"Purchase Point","Purchase Point" +"Print Invoices","Print Invoices" +"Print Packing Slips","Print Packing Slips" +"Print Credit Memos","Print Credit Memos" +"Print All","Print All" +"Print Shipping Labels","Print Shipping Labels" +"Purchase Date","Purchase Date" +"Grand Total (Base)","Grand Total (Base)" +"Grand Total (Purchased)","Grand Total (Purchased)" +"Customer Email","Customer Email" +"Shipping and Handling","Shipping and Handling" +"PDF Invoices","PDF Invoices" +"PDF Shipments","PDF Shipments" +"PDF Creditmemos","PDF Creditmemos" +Refunds,Refunds +"Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" +"Allow Zero GrandTotal","Allow Zero GrandTotal" +Email is required field for Admin order creation,Email is required field for Admin order creation +If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer. From 0627db9895e20c2152efb4684dd60f625c42fcc9 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 21 Sep 2019 16:55:25 +0530 Subject: [PATCH 065/734] boolean function starts with is --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 app/code/Magento/Sales/Model/AdminOrder/Create.php diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php old mode 100644 new mode 100755 index fd1fb472719d4..bc4c7a1ab47cf --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -2039,7 +2039,7 @@ protected function _getNewCustomerEmail() { $email = $this->getData('account/email'); - if ($email || $this->getIsEmailRequired()) { + if ($email || $this->isEmailRequired()) { return $email; } @@ -2051,7 +2051,7 @@ protected function _getNewCustomerEmail() * * @return bool */ - private function getIsEmailRequired(): bool + private function isEmailRequired(): bool { return (bool)$this->_scopeConfig->getValue( self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, From 1e9e9407dee36b5d6daf18c50bc96923f00928f3 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 21 Sep 2019 17:08:57 +0530 Subject: [PATCH 066/734] discarding customer language file --- app/code/Magento/Customer/i18n/en_US.csv | 768 +++++++++++------------ 1 file changed, 384 insertions(+), 384 deletions(-) diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv index 4ab5e2b7068fb..3495feb925cb3 100644 --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -1,23 +1,23 @@ -Sign Out,Sign Out -Sign In,Sign In -You are subscribed to our newsletter.,You are subscribed to our newsletter. -You aren't subscribed to our newsletter.,You aren't subscribed to our newsletter. -You have not set a default shipping address.,You have not set a default shipping address. -You have not set a default billing address.,You have not set a default billing address. -Address Book,Address Book -Edit Address,Edit Address -Add New Address,Add New Address +"Sign Out","Sign Out" +"Sign In","Sign In" +"You are subscribed to our newsletter.","You are subscribed to our newsletter." +"You aren't subscribed to our newsletter.","You aren't subscribed to our newsletter." +"You have not set a default shipping address.","You have not set a default shipping address." +"You have not set a default billing address.","You have not set a default billing address." +"Address Book","Address Book" +"Edit Address","Edit Address" +"Add New Address","Add New Address" region,region -Create Order,Create Order -Save Customer,Save Customer -Delete Customer,Delete Customer -Reset Password,Reset Password -Are you sure you want to revoke the customer's tokens?,Are you sure you want to revoke the customer's tokens? -Force Sign-In,Force Sign-In -New Customer,New Customer -Save and Continue Edit,Save and Continue Edit +"Create Order","Create Order" +"Save Customer","Save Customer" +"Delete Customer","Delete Customer" +"Reset Password","Reset Password" +"Are you sure you want to revoke the customer's tokens?","Are you sure you want to revoke the customer's tokens?" +"Force Sign-In","Force Sign-In" +"New Customer","New Customer" +"Save and Continue Edit","Save and Continue Edit" Back,Back -Please select,Please select +"Please select","Please select" Reset,Reset ID,ID Product,Product @@ -28,186 +28,186 @@ Total,Total Action,Action Configure,Configure Delete,Delete -Shopping Cart from %1,Shopping Cart from %1 +"Shopping Cart from %1","Shopping Cart from %1" Newsletter,Newsletter -Newsletter Information,Newsletter Information -Subscribed to Newsletter,Subscribed to Newsletter -Last Date Subscribed,Last Date Subscribed -Last Date Unsubscribed,Last Date Unsubscribed -No Newsletter Found,No Newsletter Found -Start date,Start date -End Date,End Date -Receive Date,Receive Date +"Newsletter Information","Newsletter Information" +"Subscribed to Newsletter","Subscribed to Newsletter" +"Last Date Subscribed","Last Date Subscribed" +"Last Date Unsubscribed","Last Date Unsubscribed" +"No Newsletter Found","No Newsletter Found" +"Start date","Start date" +"End Date","End Date" +"Receive Date","Receive Date" Subject,Subject Status,Status Sent,Sent Cancel,Cancel -Not Sent,Not Sent +"Not Sent","Not Sent" Sending,Sending Paused,Paused View,View Unknown,Unknown -Order #,Order # +"Order #","Order #" Purchased,Purchased -Bill-to Name,Bill-to Name -Ship-to Name,Ship-to Name -Order Total,Order Total -Purchase Point,Purchase Point -Customer View,Customer View -There are no items in customer's shopping cart.,There are no items in customer's shopping cart. +"Bill-to Name","Bill-to Name" +"Ship-to Name","Ship-to Name" +"Order Total","Order Total" +"Purchase Point","Purchase Point" +"Customer View","Customer View" +"There are no items in customer's shopping cart.","There are no items in customer's shopping cart." Qty,Qty Confirmed,Confirmed -Confirmation Required,Confirmation Required -Confirmation Not Required,Confirmation Not Required +"Confirmation Required","Confirmation Required" +"Confirmation Not Required","Confirmation Not Required" Indeterminate,Indeterminate -The customer does not have default billing address.,The customer does not have default billing address. +"The customer does not have default billing address.","The customer does not have default billing address." Offline,Offline Online,Online Never,Never Unlocked,Unlocked Locked,Locked -Deleted Stores,Deleted Stores -Add Locale,Add Locale -Add Date,Add Date -Days in Wish List,Days in Wish List +"Deleted Stores","Deleted Stores" +"Add Locale","Add Locale" +"Add Date","Add Date" +"Days in Wish List","Days in Wish List" Unlock,Unlock No,No Yes,Yes -Delete File,Delete File +"Delete File","Delete File" Download,Download -Delete Image,Delete Image -View Full Size,View Full Size -All countries,All countries -Customer Groups,Customer Groups -Add New Customer Group,Add New Customer Group -Save Customer Group,Save Customer Group -Delete Customer Group,Delete Customer Group -New Customer Group,New Customer Group +"Delete Image","Delete Image" +"View Full Size","View Full Size" +"All countries","All countries" +"Customer Groups","Customer Groups" +"Add New Customer Group","Add New Customer Group" +"Save Customer Group","Save Customer Group" +"Delete Customer Group","Delete Customer Group" +"New Customer Group","New Customer Group" "Edit Customer Group ""%1""","Edit Customer Group ""%1""" -Group Information,Group Information -Group Name,Group Name -Maximum length must be less then %1 characters.,Maximum length must be less then %1 characters. -Tax Class,Tax Class -The customer is now assigned to Customer Group %s.,The customer is now assigned to Customer Group %s. -Would you like to change the Customer Group for this order?,Would you like to change the Customer Group for this order? -The VAT ID is valid.,The VAT ID is valid. -The VAT ID entered (%s) is not a valid VAT ID.,The VAT ID entered (%s) is not a valid VAT ID. -The VAT ID is valid. The current Customer Group will be used.,The VAT ID is valid. The current Customer Group will be used. -The VAT ID is valid but no Customer Group is assigned for it.,The VAT ID is valid but no Customer Group is assigned for it. +"Group Information","Group Information" +"Group Name","Group Name" +"Maximum length must be less then %1 characters.","Maximum length must be less then %1 characters." +"Tax Class","Tax Class" +"The customer is now assigned to Customer Group %s.","The customer is now assigned to Customer Group %s." +"Would you like to change the Customer Group for this order?","Would you like to change the Customer Group for this order?" +"The VAT ID is valid.","The VAT ID is valid." +"The VAT ID entered (%s) is not a valid VAT ID.","The VAT ID entered (%s) is not a valid VAT ID." +"The VAT ID is valid. The current Customer Group will be used.","The VAT ID is valid. The current Customer Group will be used." +"The VAT ID is valid but no Customer Group is assigned for it.","The VAT ID is valid but no Customer Group is assigned for it." "Based on the VAT ID, the customer belongs to the Customer Group %s.","Based on the VAT ID, the customer belongs to the Customer Group %s." -Something went wrong while validating the VAT ID.,Something went wrong while validating the VAT ID. -The customer would belong to Customer Group %s.,The customer would belong to Customer Group %s. -There was an error detecting Customer Group.,There was an error detecting Customer Group. -Validate VAT Number,Validate VAT Number -Customer Login,Customer Login -Create New Customer Account,Create New Customer Account -Date of Birth,Date of Birth -Bad request.,Bad request. -This confirmation key is invalid or has expired.,This confirmation key is invalid or has expired. -There was an error confirming the account,There was an error confirming the account +"Something went wrong while validating the VAT ID.","Something went wrong while validating the VAT ID." +"The customer would belong to Customer Group %s.","The customer would belong to Customer Group %s." +"There was an error detecting Customer Group.","There was an error detecting Customer Group." +"Validate VAT Number","Validate VAT Number" +"Customer Login","Customer Login" +"Create New Customer Account","Create New Customer Account" +"Date of Birth","Date of Birth" +"Bad request.","Bad request." +"This confirmation key is invalid or has expired.","This confirmation key is invalid or has expired." +"There was an error confirming the account","There was an error confirming the account" "If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your shipping address for proper VAT calculation.","If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your shipping address for proper VAT calculation." "If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your billing address for proper VAT calculation.","If you are a registered VAT customer, please click <a href=""%1"">here</a> to enter your billing address for proper VAT calculation." -Thank you for registering with %1.,Thank you for registering with %1. -Please check your email for confirmation key.,Please check your email for confirmation key. -This email does not require confirmation.,This email does not require confirmation. -Wrong email.,Wrong email. -Your password reset link has expired.,Your password reset link has expired. +"Thank you for registering with %1.","Thank you for registering with %1." +"Please check your email for confirmation key.","Please check your email for confirmation key." +"This email does not require confirmation.","This email does not require confirmation." +"Wrong email.","Wrong email." +"Your password reset link has expired.","Your password reset link has expired." "You must confirm your account. Please check your email for the confirmation link or <a href=""%1"">click here</a> for a new link.","You must confirm your account. Please check your email for the confirmation link or <a href=""%1"">click here</a> for a new link." "There is already an account with this email address. If you are sure that it is your email address, <a href=""%1"">click here</a> to get your password and access your account.","There is already an account with this email address. If you are sure that it is your email address, <a href=""%1"">click here</a> to get your password and access your account." -We can't save the customer.,We can't save the customer. -Please make sure your passwords match.,Please make sure your passwords match. +"We can't save the customer.","We can't save the customer." +"Please make sure your passwords match.","Please make sure your passwords match." "If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your shipping address for proper VAT calculation.","If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your shipping address for proper VAT calculation." "If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your billing address for proper VAT calculation.","If you are a registered VAT customer, please <a href=""%1"">click here</a> to enter your billing address for proper VAT calculation." -Account Information,Account Information -You saved the account information.,You saved the account information. -You did not sign in correctly or your account is temporarily disabled.,You did not sign in correctly or your account is temporarily disabled. -Password confirmation doesn't match entered password.,Password confirmation doesn't match entered password. -The password doesn't match this account.,The password doesn't match this account. -Please correct the email address.,Please correct the email address. -We're unable to send the password reset email.,We're unable to send the password reset email. -Please enter your email.,Please enter your email. -If there is an account associated with %1 you will receive an email with a link to reset your password.,If there is an account associated with %1 you will receive an email with a link to reset your password. -My Account,My Account +"Account Information","Account Information" +"You saved the account information.","You saved the account information." +"You did not sign in correctly or your account is temporarily disabled.","You did not sign in correctly or your account is temporarily disabled." +"Password confirmation doesn't match entered password.","Password confirmation doesn't match entered password." +"The password doesn't match this account.","The password doesn't match this account." +"Please correct the email address.","Please correct the email address." +"We're unable to send the password reset email.","We're unable to send the password reset email." +"Please enter your email.","Please enter your email." +"If there is an account associated with %1 you will receive an email with a link to reset your password.","If there is an account associated with %1 you will receive an email with a link to reset your password." +"My Account","My Account" "This account is not confirmed. <a href=""%1"">Click here</a> to resend confirmation email.","This account is not confirmed. <a href=""%1"">Click here</a> to resend confirmation email." -An unspecified error occurred. Please contact us for assistance.,An unspecified error occurred. Please contact us for assistance. -A login and a password are required.,A login and a password are required. -New Password and Confirm New Password values didn't match.,New Password and Confirm New Password values didn't match. -Please enter a new password.,Please enter a new password. -You updated your password.,You updated your password. -Something went wrong while saving the new password.,Something went wrong while saving the new password. -You deleted the address.,You deleted the address. -We can't delete the address right now.,We can't delete the address right now. -You saved the address.,You saved the address. -We can't save the address.,We can't save the address. -No customer ID defined.,No customer ID defined. -Please correct the quote items and try again.,Please correct the quote items and try again. -You have revoked the customer's tokens.,You have revoked the customer's tokens. -We can't find a customer to revoke.,We can't find a customer to revoke. -Something went wrong while saving file.,Something went wrong while saving file. -You deleted the customer group.,You deleted the customer group. -The customer group no longer exists.,The customer group no longer exists. +"An unspecified error occurred. Please contact us for assistance.","An unspecified error occurred. Please contact us for assistance." +"A login and a password are required.","A login and a password are required." +"New Password and Confirm New Password values didn't match.","New Password and Confirm New Password values didn't match." +"Please enter a new password.","Please enter a new password." +"You updated your password.","You updated your password." +"Something went wrong while saving the new password.","Something went wrong while saving the new password." +"You deleted the address.","You deleted the address." +"We can't delete the address right now.","We can't delete the address right now." +"You saved the address.","You saved the address." +"We can't save the address.","We can't save the address." +"No customer ID defined.","No customer ID defined." +"Please correct the quote items and try again.","Please correct the quote items and try again." +"You have revoked the customer's tokens.","You have revoked the customer's tokens." +"We can't find a customer to revoke.","We can't find a customer to revoke." +"Something went wrong while saving file.","Something went wrong while saving file." +"You deleted the customer group.","You deleted the customer group." +"The customer group no longer exists.","The customer group no longer exists." Customers,Customers -New Group,New Group -New Customer Groups,New Customer Groups -Edit Group,Edit Group -Edit Customer Groups,Edit Customer Groups -You saved the customer group.,You saved the customer group. -Please select customer(s).,Please select customer(s). -Customer could not be deleted.,Customer could not be deleted. -You deleted the customer.,You deleted the customer. -Something went wrong while editing the customer.,Something went wrong while editing the customer. -Manage Customers,Manage Customers -Please correct the data sent.,Please correct the data sent. -A total of %1 record(s) were updated.,A total of %1 record(s) were updated. -A total of %1 record(s) were deleted.,A total of %1 record(s) were deleted. -The customer will receive an email with a link to reset password.,The customer will receive an email with a link to reset password. -Something went wrong while resetting customer password.,Something went wrong while resetting customer password. -You saved the customer.,You saved the customer. -Something went wrong while saving the customer.,Something went wrong while saving the customer. -Page not found.,Page not found. -Customer has been unlocked successfully.,Customer has been unlocked successfully. -Online Customers,Online Customers -Customers Now Online,Customers Now Online -Please define Wish List item ID.,Please define Wish List item ID. -Please load Wish List item.,Please load Wish List item. -Login successful.,Login successful. -Invalid login or password.,Invalid login or password. +"New Group","New Group" +"New Customer Groups","New Customer Groups" +"Edit Group","Edit Group" +"Edit Customer Groups","Edit Customer Groups" +"You saved the customer group.","You saved the customer group." +"Please select customer(s).","Please select customer(s)." +"Customer could not be deleted.","Customer could not be deleted." +"You deleted the customer.","You deleted the customer." +"Something went wrong while editing the customer.","Something went wrong while editing the customer." +"Manage Customers","Manage Customers" +"Please correct the data sent.","Please correct the data sent." +"A total of %1 record(s) were updated.","A total of %1 record(s) were updated." +"A total of %1 record(s) were deleted.","A total of %1 record(s) were deleted." +"The customer will receive an email with a link to reset password.","The customer will receive an email with a link to reset password." +"Something went wrong while resetting customer password.","Something went wrong while resetting customer password." +"You saved the customer.","You saved the customer." +"Something went wrong while saving the customer.","Something went wrong while saving the customer." +"Page not found.","Page not found." +"Customer has been unlocked successfully.","Customer has been unlocked successfully." +"Online Customers","Online Customers" +"Customers Now Online","Customers Now Online" +"Please define Wish List item ID.","Please define Wish List item ID." +"Please load Wish List item.","Please load Wish List item." +"Login successful.","Login successful." +"Invalid login or password.","Invalid login or password." """%1"" section source is not supported","""%1"" section source is not supported" -%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface,%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface -No confirmation needed.,No confirmation needed. -Account already active,Account already active -Invalid confirmation token,Invalid confirmation token -The account is locked.,The account is locked. -This account is not confirmed.,This account is not confirmed. +"%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface","%1 doesn't extend \Magento\Customer\CustomerData\SectionSourceInterface" +"No confirmation needed.","No confirmation needed." +"Account already active","Account already active" +"Invalid confirmation token","Invalid confirmation token" +"The account is locked.","The account is locked." +"This account is not confirmed.","This account is not confirmed." "Invalid value of ""%value"" provided for the %fieldName field.","Invalid value of ""%value"" provided for the %fieldName field." -Please enter a password with at most %1 characters.,Please enter a password with at most %1 characters. -Please enter a password with at least %1 characters.,Please enter a password with at least %1 characters. -The password can't begin or end with a space.,The password can't begin or end with a space. +"Please enter a password with at most %1 characters.","Please enter a password with at most %1 characters." +"Please enter a password with at least %1 characters.","Please enter a password with at least %1 characters." +"The password can't begin or end with a space.","The password can't begin or end with a space." "Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.","Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters." -Password cannot be the same as email address.,Password cannot be the same as email address. -This customer already exists in this store.,This customer already exists in this store. -A customer with the same email already exists in an associated website.,A customer with the same email already exists in an associated website. -%fieldName is a required field.,%fieldName is a required field. -Reset password token mismatch.,Reset password token mismatch. -Reset password token expired.,Reset password token expired. -Please correct the transactional account email type.,Please correct the transactional account email type. +"Password cannot be the same as email address.","Password cannot be the same as email address." +"This customer already exists in this store.","This customer already exists in this store." +"A customer with the same email already exists in an associated website.","A customer with the same email already exists in an associated website." +"%fieldName is a required field.","%fieldName is a required field." +"Reset password token mismatch.","Reset password token mismatch." +"Reset password token expired.","Reset password token expired." +"Please correct the transactional account email type.","Please correct the transactional account email type." """%1"" is a required value.","""%1"" is a required value." Global,Global -Per Website,Per Website -We can't share customer accounts globally when the accounts share identical email addresses on more than one website.,We can't share customer accounts globally when the accounts share identical email addresses on more than one website. -Billing Address,Billing Address -Shipping Address,Shipping Address --- Please Select --,-- Please Select -- -Please enter a valid password reset token.,Please enter a valid password reset token. -The password can not begin or end with a space.,The password can not begin or end with a space. +"Per Website","Per Website" +"We can't share customer accounts globally when the accounts share identical email addresses on more than one website.","We can't share customer accounts globally when the accounts share identical email addresses on more than one website." +"Billing Address","Billing Address" +"Shipping Address","Shipping Address" +"-- Please Select --","-- Please Select --" +"Please enter a valid password reset token.","Please enter a valid password reset token." +"The password can not begin or end with a space.","The password can not begin or end with a space." Admin,Admin -ALL GROUPS,ALL GROUPS +"ALL GROUPS","ALL GROUPS" "No such entity with %fieldName = %fieldValue, %field2Name = %field2Value","No such entity with %fieldName = %fieldValue, %field2Name = %field2Value" -File can not be saved to the destination folder.,File can not be saved to the destination folder. -Unable to create directory %1.,Unable to create directory %1. -Destination folder is not writable or does not exists.,Destination folder is not writable or does not exists. -Something went wrong while saving the file.,Something went wrong while saving the file. -Attribute object is undefined,Attribute object is undefined +"File can not be saved to the destination folder.","File can not be saved to the destination folder." +"Unable to create directory %1.","Unable to create directory %1." +"Destination folder is not writable or does not exists.","Destination folder is not writable or does not exists." +"Something went wrong while saving the file.","Something went wrong while saving the file." +"Attribute object is undefined","Attribute object is undefined" """%1"" invalid type entered.","""%1"" invalid type entered." """%1"" contains non-alphabetic or non-numeric characters.","""%1"" contains non-alphabetic or non-numeric characters." """%1"" is an empty string.","""%1"" is an empty string." @@ -217,19 +217,19 @@ Attribute object is undefined,Attribute object is undefined """%1"" is not a valid hostname.","""%1"" is not a valid hostname." """%1"" uses too many characters.","""%1"" uses too many characters." "'%value%' looks like an IP address, which is not an acceptable format.","'%value%' looks like an IP address, which is not an acceptable format." -'%value%' looks like a DNS hostname but we cannot match the TLD against known list.,'%value%' looks like a DNS hostname but we cannot match the TLD against known list. -'%value%' looks like a DNS hostname but contains a dash in an invalid position.,'%value%' looks like a DNS hostname but contains a dash in an invalid position. -'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'.,'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'. -'%value%' looks like a DNS hostname but cannot extract TLD part.,'%value%' looks like a DNS hostname but cannot extract TLD part. -'%value%' does not look like a valid local network name.,'%value%' does not look like a valid local network name. +"'%value%' looks like a DNS hostname but we cannot match the TLD against known list.","'%value%' looks like a DNS hostname but we cannot match the TLD against known list." +"'%value%' looks like a DNS hostname but contains a dash in an invalid position.","'%value%' looks like a DNS hostname but contains a dash in an invalid position." +"'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'.","'%value%' looks like a DNS hostname but we cannot match it against the hostname schema for TLD '%tld%'." +"'%value%' looks like a DNS hostname but cannot extract TLD part.","'%value%' looks like a DNS hostname but cannot extract TLD part." +"'%value%' does not look like a valid local network name.","'%value%' does not look like a valid local network name." "'%value%' looks like a local network name, which is not an acceptable format.","'%value%' looks like a local network name, which is not an acceptable format." "'%value%' appears to be a DNS hostname, but the given punycode notation cannot be decoded.","'%value%' appears to be a DNS hostname, but the given punycode notation cannot be decoded." """%1"" is not a valid URL.","""%1"" is not a valid URL." """%1"" is not a valid date.","""%1"" is not a valid date." """%1"" does not fit the entered date format.","""%1"" does not fit the entered date format." -Please enter a valid date between %1 and %2 at %3.,Please enter a valid date between %1 and %2 at %3. -Please enter a valid date equal to or greater than %1 at %2.,Please enter a valid date equal to or greater than %1 at %2. -Please enter a valid date less than or equal to %1 at %2.,Please enter a valid date less than or equal to %1 at %2. +"Please enter a valid date between %1 and %2 at %3.","Please enter a valid date between %1 and %2 at %3." +"Please enter a valid date equal to or greater than %1 at %2.","Please enter a valid date equal to or greater than %1 at %2." +"Please enter a valid date less than or equal to %1 at %2.","Please enter a valid date less than or equal to %1 at %2." """%1"" is not a valid file extension.","""%1"" is not a valid file extension." """%1"" is not a valid file.","""%1"" is not a valid file." """%1"" exceeds the allowed file size.","""%1"" exceeds the allowed file size." @@ -239,203 +239,203 @@ Please enter a valid date less than or equal to %1 at %2.,Please enter a valid d """%1"" length must be equal or greater than %2 characters.","""%1"" length must be equal or greater than %2 characters." """%1"" length must be equal or less than %2 characters.","""%1"" length must be equal or less than %2 characters." label,label -Please enter a customer email.,Please enter a customer email. -A customer website ID must be specified when using the website scope.,A customer website ID must be specified when using the website scope. -Customer Group,Customer Group +"Please enter a customer email.","Please enter a customer email." +"A customer website ID must be specified when using the website scope.","A customer website ID must be specified when using the website scope." +"Customer Group","Customer Group" "You can't delete group ""%1"".","You can't delete group ""%1""." -Customer Group already exists.,Customer Group already exists. -Cannot delete group.,Cannot delete group. -Error during VAT Number verification.,Error during VAT Number verification. -PHP SOAP extension is required.,PHP SOAP extension is required. -VAT Number is valid.,VAT Number is valid. -Please enter a valid VAT number.,Please enter a valid VAT number. -Your VAT ID was successfully validated.,Your VAT ID was successfully validated. -You will be charged tax.,You will be charged tax. -You will not be charged tax.,You will not be charged tax. -The VAT ID entered (%1) is not a valid VAT ID.,The VAT ID entered (%1) is not a valid VAT ID. -Your Tax ID cannot be validated.,Your Tax ID cannot be validated. +"Customer Group already exists.","Customer Group already exists." +"Cannot delete group.","Cannot delete group." +"Error during VAT Number verification.","Error during VAT Number verification." +"PHP SOAP extension is required.","PHP SOAP extension is required." +"VAT Number is valid.","VAT Number is valid." +"Please enter a valid VAT number.","Please enter a valid VAT number." +"Your VAT ID was successfully validated.","Your VAT ID was successfully validated." +"You will be charged tax.","You will be charged tax." +"You will not be charged tax.","You will not be charged tax." +"The VAT ID entered (%1) is not a valid VAT ID.","The VAT ID entered (%1) is not a valid VAT ID." +"Your Tax ID cannot be validated.","Your Tax ID cannot be validated." "If you believe this is an error, please contact us at %1","If you believe this is an error, please contact us at %1" -No such entity with %fieldName = %fieldValue,No such entity with %fieldName = %fieldValue +"No such entity with %fieldName = %fieldValue","No such entity with %fieldName = %fieldValue" Male,Male Female,Female -Not Specified,Not Specified -Thank you for registering with,Thank you for registering with -enter your billing address for proper VAT calculation,enter your billing address for proper VAT calculation -enter your shipping address for proper VAT calculation,enter your shipping address for proper VAT calculation -Please enter new password.,Please enter new password. +"Not Specified","Not Specified" +"Thank you for registering with","Thank you for registering with" +"enter your billing address for proper VAT calculation","enter your billing address for proper VAT calculation" +"enter your shipping address for proper VAT calculation","enter your shipping address for proper VAT calculation" +"Please enter new password.","Please enter new password." message,message NoSuchEntityException,NoSuchEntityException Exception,Exception InputException.,InputException. InputException,InputException -Exception message,Exception message -Validator Exception,Validator Exception -Localized Exception,Localized Exception -some error,some error +"Exception message","Exception message" +"Validator Exception","Validator Exception" +"Localized Exception","Localized Exception" +"some error","some error" frontend_label,frontend_label -NOT LOGGED IN,NOT LOGGED IN -Please enter the state/province.,Please enter the state/province. -region is a required field.,region is a required field. -regionId is a required field.,regionId is a required field. +"NOT LOGGED IN","NOT LOGGED IN" +"Please enter the state/province.","Please enter the state/province." +"region is a required field.","region is a required field." +"regionId is a required field.","regionId is a required field." Label,Label Select...,Select... Edit,Edit Visitor,Visitor Customer,Customer -No item specified.,No item specified. -Are you sure you want to remove this item?,Are you sure you want to remove this item? -Personal Information,Personal Information -Last Logged In:,Last Logged In: -Last Logged In (%1):,Last Logged In (%1): -Account Lock:,Account Lock: -Confirmed email:,Confirmed email: -Account Created:,Account Created: -Account Created on (%1):,Account Created on (%1): -Account Created in:,Account Created in: -Customer Group:,Customer Group: -Default Billing Address,Default Billing Address -Sales Statistics,Sales Statistics -Web Site,Web Site +"No item specified.","No item specified." +"Are you sure you want to remove this item?","Are you sure you want to remove this item?" +"Personal Information","Personal Information" +"Last Logged In:","Last Logged In:" +"Last Logged In (%1):","Last Logged In (%1):" +"Account Lock:","Account Lock:" +"Confirmed email:","Confirmed email:" +"Account Created:","Account Created:" +"Account Created on (%1):","Account Created on (%1):" +"Account Created in:","Account Created in:" +"Customer Group:","Customer Group:" +"Default Billing Address","Default Billing Address" +"Sales Statistics","Sales Statistics" +"Web Site","Web Site" Store,Store -Store View,Store View -Lifetime Sales,Lifetime Sales -Average Sale,Average Sale -All Store Views,All Store Views +"Store View","Store View" +"Lifetime Sales","Lifetime Sales" +"Average Sale","Average Sale" +"All Store Views","All Store Views" Change,Change -Manage Addresses,Manage Addresses -Default Shipping Address,Default Shipping Address -Contact Information,Contact Information -Change Password,Change Password +"Manage Addresses","Manage Addresses" +"Default Shipping Address","Default Shipping Address" +"Contact Information","Contact Information" +"Change Password","Change Password" Newsletters,Newsletters "You are subscribed to ""General Subscription"".","You are subscribed to ""General Subscription""." or,or -Default Addresses,Default Addresses -Change Billing Address,Change Billing Address -You have no default billing address in your address book.,You have no default billing address in your address book. -Change Shipping Address,Change Shipping Address -You have no default shipping address in your address book.,You have no default shipping address in your address book. -Additional Address Entries,Additional Address Entries -Delete Address,Delete Address -You have no other address entries in your address book.,You have no other address entries in your address book. -* Required Fields,* Required Fields +"Default Addresses","Default Addresses" +"Change Billing Address","Change Billing Address" +"You have no default billing address in your address book.","You have no default billing address in your address book." +"Change Shipping Address","Change Shipping Address" +"You have no default shipping address in your address book.","You have no default shipping address in your address book." +"Additional Address Entries","Additional Address Entries" +"Delete Address","Delete Address" +"You have no other address entries in your address book.","You have no other address entries in your address book." +"* Required Fields","* Required Fields" Address,Address -Street Address,Street Address -Street Address %1,Street Address %1 -VAT Number,VAT Number +"Street Address","Street Address" +"Street Address %1","Street Address %1" +"VAT Number","VAT Number" City,City State/Province,State/Province "Please select a region, state or province.","Please select a region, state or province." -Zip/Postal Code,Zip/Postal Code +"Zip/Postal Code","Zip/Postal Code" Country,Country -It's a default billing address.,It's a default billing address. -Use as my default billing address,Use as my default billing address -It's a default shipping address.,It's a default shipping address. -Use as my default shipping address,Use as my default shipping address -Save Address,Save Address -Go back,Go back -Please enter your email below and we will send you the confirmation link.,Please enter your email below and we will send you the confirmation link. +"It's a default billing address.","It's a default billing address." +"Use as my default billing address","Use as my default billing address" +"It's a default shipping address.","It's a default shipping address." +"Use as my default shipping address","Use as my default shipping address" +"Save Address","Save Address" +"Go back","Go back" +"Please enter your email below and we will send you the confirmation link.","Please enter your email below and we will send you the confirmation link." Email,Email -Send confirmation link,Send confirmation link -Back to Sign In,Back to Sign In -Change Email,Change Email -Change Email and Password,Change Email and Password -Current Password,Current Password -New Password,New Password -Password Strength,Password Strength -No Password,No Password -Confirm New Password,Confirm New Password +"Send confirmation link","Send confirmation link" +"Back to Sign In","Back to Sign In" +"Change Email","Change Email" +"Change Email and Password","Change Email and Password" +"Current Password","Current Password" +"New Password","New Password" +"Password Strength","Password Strength" +"No Password","No Password" +"Confirm New Password","Confirm New Password" Save,Save -Please enter your email address below to receive a password reset link.,Please enter your email address below to receive a password reset link. -Reset My Password,Reset My Password -Registered Customers,Registered Customers +"Please enter your email address below to receive a password reset link.","Please enter your email address below to receive a password reset link." +"Reset My Password","Reset My Password" +"Registered Customers","Registered Customers" "If you have an account, sign in with your email address.","If you have an account, sign in with your email address." Password,Password -Forgot Your Password?,Forgot Your Password? -Subscription option,Subscription option -General Subscription,General Subscription -Sign Up for Newsletter,Sign Up for Newsletter -Address Information,Address Information -Sign-in Information,Sign-in Information -Confirm Password,Confirm Password -Create an Account,Create an Account -Set a New Password,Set a New Password -You have signed out and will go to our homepage in 5 seconds.,You have signed out and will go to our homepage in 5 seconds. -New Customers,New Customers +"Forgot Your Password?","Forgot Your Password?" +"Subscription option","Subscription option" +"General Subscription","General Subscription" +"Sign Up for Newsletter","Sign Up for Newsletter" +"Address Information","Address Information" +"Sign-in Information","Sign-in Information" +"Confirm Password","Confirm Password" +"Create an Account","Create an Account" +"Set a New Password","Set a New Password" +"You have signed out and will go to our homepage in 5 seconds.","You have signed out and will go to our homepage in 5 seconds." +"New Customers","New Customers" "Creating an account has many benefits: check out faster, keep more than one address, track orders and more.","Creating an account has many benefits: check out faster, keep more than one address, track orders and more." Company,Company Fax,Fax Gender,Gender Name,Name -Tax/VAT number,Tax/VAT number -Phone Number,Phone Number -Welcome to %store_name,Welcome to %store_name +"Tax/VAT number","Tax/VAT number" +"Phone Number","Phone Number" +"Welcome to %store_name","Welcome to %store_name" "%name,","%name," -Welcome to %store_name.,Welcome to %store_name. +"Welcome to %store_name.","Welcome to %store_name." "To sign in to our site, use these credentials during checkout or on the <a href=""%customer_url"">My Account</a> page:","To sign in to our site, use these credentials during checkout or on the <a href=""%customer_url"">My Account</a> page:" Email:,Email: Password:,Password: -Password you set when creating account,Password you set when creating account +"Password you set when creating account","Password you set when creating account" "Forgot your account password? Click <a href=""%reset_url"">here</a> to reset it.","Forgot your account password? Click <a href=""%reset_url"">here</a> to reset it." "When you sign in to your account, you will be able to:","When you sign in to your account, you will be able to:" -Proceed through checkout faster,Proceed through checkout faster -Check the status of orders,Check the status of orders -View past orders,View past orders -Store alternative addresses (for shipping to multiple family members and friends),Store alternative addresses (for shipping to multiple family members and friends) -Please confirm your %store_name account,Please confirm your %store_name account -You must confirm your %customer_email email before you can sign in (link is only valid once):,You must confirm your %customer_email email before you can sign in (link is only valid once): -Confirm Your Account,Confirm Your Account -Thank you for confirming your %store_name account.,Thank you for confirming your %store_name account. +"Proceed through checkout faster","Proceed through checkout faster" +"Check the status of orders","Check the status of orders" +"View past orders","View past orders" +"Store alternative addresses (for shipping to multiple family members and friends)","Store alternative addresses (for shipping to multiple family members and friends)" +"Please confirm your %store_name account","Please confirm your %store_name account" +"You must confirm your %customer_email email before you can sign in (link is only valid once):","You must confirm your %customer_email email before you can sign in (link is only valid once):" +"Confirm Your Account","Confirm Your Account" +"Thank you for confirming your %store_name account.","Thank you for confirming your %store_name account." "To sign in to our site and set a password, click on the <a href=""%create_password_url"">link</a>:","To sign in to our site and set a password, click on the <a href=""%create_password_url"">link</a>:" -Your %store_name email has been changed,Your %store_name email has been changed +"Your %store_name email has been changed","Your %store_name email has been changed" "Hello,","Hello," -We have received a request to change the following information associated with your account at %store_name: email.,We have received a request to change the following information associated with your account at %store_name: email. +"We have received a request to change the following information associated with your account at %store_name: email.","We have received a request to change the following information associated with your account at %store_name: email." "If you have not authorized this action, please contact us immediately at <a href=""mailto:%store_email"">%store_email</a>","If you have not authorized this action, please contact us immediately at <a href=""mailto:%store_email"">%store_email</a>" "or call us at <a href=""tel:%store_phone"">%store_phone</a>","or call us at <a href=""tel:%store_phone"">%store_phone</a>" "Thanks,<br>%store_name","Thanks,<br>%store_name" -Your %store_name email and password has been changed,Your %store_name email and password has been changed +"Your %store_name email and password has been changed","Your %store_name email and password has been changed" "We have received a request to change the following information associated with your account at %store_name: email, password.","We have received a request to change the following information associated with your account at %store_name: email, password." -Reset your %store_name password,Reset your %store_name password -There was recently a request to change the password for your account.,There was recently a request to change the password for your account. +"Reset your %store_name password","Reset your %store_name password" +"There was recently a request to change the password for your account.","There was recently a request to change the password for your account." "If you requested this change, set a new password here:","If you requested this change, set a new password here:" "If you did not make this request, you can ignore this email and your password will remain the same.","If you did not make this request, you can ignore this email and your password will remain the same." -Your %store_name password has been changed,Your %store_name password has been changed -We have received a request to change the following information associated with your account at %store_name: password.,We have received a request to change the following information associated with your account at %store_name: password. -Checkout as a new customer,Checkout as a new customer -Creating an account has many benefits:,Creating an account has many benefits: -See order and shipping status,See order and shipping status -Track order history,Track order history -Check out faster,Check out faster -Checkout using your account,Checkout using your account -Email Address,Email Address -Are you sure you want to do this?,Are you sure you want to do this? -Are you sure you want to delete this address?,Are you sure you want to delete this address? +"Your %store_name password has been changed","Your %store_name password has been changed" +"We have received a request to change the following information associated with your account at %store_name: password.","We have received a request to change the following information associated with your account at %store_name: password." +"Checkout as a new customer","Checkout as a new customer" +"Creating an account has many benefits:","Creating an account has many benefits:" +"See order and shipping status","See order and shipping status" +"Track order history","Track order history" +"Check out faster","Check out faster" +"Checkout using your account","Checkout using your account" +"Email Address","Email Address" +"Are you sure you want to do this?","Are you sure you want to do this?" +"Are you sure you want to delete this address?","Are you sure you want to delete this address?" Weak,Weak Medium,Medium Strong,Strong -Very Strong,Very Strong -Guest checkout is disabled.,Guest checkout is disabled. -All Customers,All Customers -Now Online,Now Online -Customers Section,Customers Section -Customer Configuration,Customer Configuration -Account Sharing Options,Account Sharing Options -Share Customer Accounts,Share Customer Accounts -Create New Account Options,Create New Account Options -Enable Automatic Assignment to Customer Group,Enable Automatic Assignment to Customer Group -Tax Calculation Based On,Tax Calculation Based On -Default Group,Default Group -Group for Valid VAT ID - Domestic,Group for Valid VAT ID - Domestic -Group for Valid VAT ID - Intra-Union,Group for Valid VAT ID - Intra-Union -Group for Invalid VAT ID,Group for Invalid VAT ID -Validation Error Group,Validation Error Group -Validate on Each Transaction,Validate on Each Transaction -Default Value for Disable Automatic Group Changes Based on VAT ID,Default Value for Disable Automatic Group Changes Based on VAT ID -Show VAT Number on Storefront,Show VAT Number on Storefront +"Very Strong","Very Strong" +"Guest checkout is disabled.","Guest checkout is disabled." +"All Customers","All Customers" +"Now Online","Now Online" +"Customers Section","Customers Section" +"Customer Configuration","Customer Configuration" +"Account Sharing Options","Account Sharing Options" +"Share Customer Accounts","Share Customer Accounts" +"Create New Account Options","Create New Account Options" +"Enable Automatic Assignment to Customer Group","Enable Automatic Assignment to Customer Group" +"Tax Calculation Based On","Tax Calculation Based On" +"Default Group","Default Group" +"Group for Valid VAT ID - Domestic","Group for Valid VAT ID - Domestic" +"Group for Valid VAT ID - Intra-Union","Group for Valid VAT ID - Intra-Union" +"Group for Invalid VAT ID","Group for Invalid VAT ID" +"Validation Error Group","Validation Error Group" +"Validate on Each Transaction","Validate on Each Transaction" +"Default Value for Disable Automatic Group Changes Based on VAT ID","Default Value for Disable Automatic Group Changes Based on VAT ID" +"Show VAT Number on Storefront","Show VAT Number on Storefront" "To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.","To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes." -Default Email Domain,Default Email Domain -Default Welcome Email,Default Welcome Email +"Default Email Domain","Default Email Domain" +"Default Welcome Email","Default Welcome Email" "Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." -Default Welcome Email Without Password,Default Welcome Email Without Password +"Default Welcome Email Without Password","Default Welcome Email Without Password" " This email will be sent instead of the Default Welcome Email, if a customer was created without password. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. @@ -443,10 +443,10 @@ Default Welcome Email Without Password,Default Welcome Email Without Password This email will be sent instead of the Default Welcome Email, if a customer was created without password. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. " -Email Sender,Email Sender -Require Emails Confirmation,Require Emails Confirmation -Confirmation Link Email,Confirmation Link Email -Welcome Email,Welcome Email +"Email Sender","Email Sender" +"Require Emails Confirmation","Require Emails Confirmation" +"Confirmation Link Email","Confirmation Link Email" +"Welcome Email","Welcome Email" " This email will be sent instead of the Default Welcome Email, after account confirmation. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. @@ -454,88 +454,88 @@ Welcome Email,Welcome Email This email will be sent instead of the Default Welcome Email, after account confirmation. <br /><br /> Email template chosen based on theme fallback when ""Default"" option is selected. " -Generate Human-Friendly Customer ID,Generate Human-Friendly Customer ID -Password Options,Password Options -Forgot Email Template,Forgot Email Template -Remind Email Template,Remind Email Template -Reset Password Template,Reset Password Template -Password Template Email Sender,Password Template Email Sender -Recovery Link Expiration Period (hours),Recovery Link Expiration Period (hours) -Please enter a number 1 or greater in this field.,Please enter a number 1 or greater in this field. -Number of Required Character Classes,Number of Required Character Classes +"Generate Human-Friendly Customer ID","Generate Human-Friendly Customer ID" +"Password Options","Password Options" +"Forgot Email Template","Forgot Email Template" +"Remind Email Template","Remind Email Template" +"Reset Password Template","Reset Password Template" +"Password Template Email Sender","Password Template Email Sender" +"Recovery Link Expiration Period (hours)","Recovery Link Expiration Period (hours)" +"Please enter a number 1 or greater in this field.","Please enter a number 1 or greater in this field." +"Number of Required Character Classes","Number of Required Character Classes" "Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters.","Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters." -Minimum Password Length,Minimum Password Length -Maximum Login Failures to Lockout Account,Maximum Login Failures to Lockout Account -Use 0 to disable account locking.,Use 0 to disable account locking. -Lockout Time (minutes),Lockout Time (minutes) -Enable Autocomplete on login/forgot password forms,Enable Autocomplete on login/forgot password forms -Account Information Options,Account Information Options -Change Email Template,Change Email Template -Change Email and Password Template,Change Email and Password Template -Name and Address Options,Name and Address Options -Number of Lines in a Street Address,Number of Lines in a Street Address -Leave empty for default (2). Valid range: 1-4,Leave empty for default (2). Valid range: 1-4 -Show Prefix,Show Prefix +"Minimum Password Length","Minimum Password Length" +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"Use 0 to disable account locking.","Use 0 to disable account locking." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Enable Autocomplete on login/forgot password forms","Enable Autocomplete on login/forgot password forms" +"Account Information Options","Account Information Options" +"Change Email Template","Change Email Template" +"Change Email and Password Template","Change Email and Password Template" +"Name and Address Options","Name and Address Options" +"Number of Lines in a Street Address","Number of Lines in a Street Address" +"Leave empty for default (2). Valid range: 1-4","Leave empty for default (2). Valid range: 1-4" +"Show Prefix","Show Prefix" "The title that goes before name (Mr., Mrs., etc.)","The title that goes before name (Mr., Mrs., etc.)" -Prefix Dropdown Options,Prefix Dropdown Options +"Prefix Dropdown Options","Prefix Dropdown Options" " Semicolon (;) separated values.<br/>Leave empty for open text field. "," Semicolon (;) separated values.<br/>Leave empty for open text field. " -Show Middle Name (initial),Show Middle Name (initial) -Always optional.,Always optional. -Show Suffix,Show Suffix +"Show Middle Name (initial)","Show Middle Name (initial)" +"Always optional.","Always optional." +"Show Suffix","Show Suffix" "The suffix that goes after name (Jr., Sr., etc.)","The suffix that goes after name (Jr., Sr., etc.)" -Suffix Dropdown Options,Suffix Dropdown Options -Show Date of Birth,Show Date of Birth -Show Tax/VAT Number,Show Tax/VAT Number -Show Gender,Show Gender -Show Telephone,Show Telephone -Show Company,Show Company -Show Fax,Show Fax -Login Options,Login Options -Redirect Customer to Account Dashboard after Logging in,Redirect Customer to Account Dashboard after Logging in +"Suffix Dropdown Options","Suffix Dropdown Options" +"Show Date of Birth","Show Date of Birth" +"Show Tax/VAT Number","Show Tax/VAT Number" +"Show Gender","Show Gender" +"Show Telephone","Show Telephone" +"Show Company","Show Company" +"Show Fax","Show Fax" +"Login Options","Login Options" +"Redirect Customer to Account Dashboard after Logging in","Redirect Customer to Account Dashboard after Logging in" "Customer will stay on the current page if ""No"" is selected.","Customer will stay on the current page if ""No"" is selected." -Address Templates,Address Templates -Online Customers Options,Online Customers Options -Online Minutes Interval,Online Minutes Interval +"Address Templates","Address Templates" +"Online Customers Options","Online Customers Options" +"Online Minutes Interval","Online Minutes Interval" "Only 'b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub', 'sup', 'ul' tags are allowed","Only 'b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub', 'sup', 'ul' tags are allowed" -Leave empty for default (15 minutes).,Leave empty for default (15 minutes). -Customer Notification,Customer Notification -Customer Grid,Customer Grid -Rebuild Customer grid index,Rebuild Customer grid index +"Leave empty for default (15 minutes).","Leave empty for default (15 minutes)." +"Customer Notification","Customer Notification" +"Customer Grid","Customer Grid" +"Rebuild Customer grid index","Rebuild Customer grid index" Group,Group -Add New Customer,Add New Customer -Are you sure you want to delete the selected customers?,Are you sure you want to delete the selected customers? -Delete items,Delete items -Subscribe to Newsletter,Subscribe to Newsletter -Are you sure you want to unsubscribe the selected customers from the newsletter?,Are you sure you want to unsubscribe the selected customers from the newsletter? -Unsubscribe from Newsletter,Unsubscribe from Newsletter -Assign a Customer Group,Assign a Customer Group +"Add New Customer","Add New Customer" +"Are you sure you want to delete the selected customers?","Are you sure you want to delete the selected customers?" +"Delete items","Delete items" +"Subscribe to Newsletter","Subscribe to Newsletter" +"Are you sure you want to unsubscribe the selected customers from the newsletter?","Are you sure you want to unsubscribe the selected customers from the newsletter?" +"Unsubscribe from Newsletter","Unsubscribe from Newsletter" +"Assign a Customer Group","Assign a Customer Group" Phone,Phone ZIP,ZIP -Customer Since,Customer Since -Confirmed email,Confirmed email -Account Created in,Account Created in -Tax VAT Number,Tax VAT Number -Billing Firstname,Billing Firstname -Billing Lastname,Billing Lastname -Account Lock,Account Lock -First Name,First Name -Last Name,Last Name -Last Activity,Last Activity +"Customer Since","Customer Since" +"Confirmed email","Confirmed email" +"Account Created in","Account Created in" +"Tax VAT Number","Tax VAT Number" +"Billing Firstname","Billing Firstname" +"Billing Lastname","Billing Lastname" +"Account Lock","Account Lock" +"First Name","First Name" +"Last Name","Last Name" +"Last Activity","Last Activity" Type,Type -Customer Information,Customer Information +"Customer Information","Customer Information" "If your Magento installation has multiple websites, you can edit the scope to associate the customer with a specific site.","If your Magento installation has multiple websites, you can edit the scope to associate the customer with a specific site." -Disable Automatic Group Change Based on VAT ID,Disable Automatic Group Change Based on VAT ID -Send Welcome Email From,Send Welcome Email From -Are you sure you want to delete this item?,Are you sure you want to delete this item? +"Disable Automatic Group Change Based on VAT ID","Disable Automatic Group Change Based on VAT ID" +"Send Welcome Email From","Send Welcome Email From" +"Are you sure you want to delete this item?","Are you sure you want to delete this item?" Addresses,Addresses -Edit Account Information,Edit Account Information -Password forgotten,Password forgotten -You are signed out,You are signed out -Associate to Website,Associate to Website -Prefix,Prefix -Middle Name/Initial,Middle Name/Initial -Suffix,Suffix +"Edit Account Information","Edit Account Information" +"Password forgotten","Password forgotten" +"You are signed out","You are signed out" +"Associate to Website","Associate to Website" +"Prefix","Prefix" +"Middle Name/Initial","Middle Name/Initial" +"Suffix","Suffix" From 6dcdd8af62b234b8c14cf221757b3489822188e2 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 21 Sep 2019 17:18:10 +0530 Subject: [PATCH 067/734] Discard NonComposerComponentRegistration file --- app/etc/NonComposerComponentRegistration.php | 33 -------------------- 1 file changed, 33 deletions(-) delete mode 100755 app/etc/NonComposerComponentRegistration.php diff --git a/app/etc/NonComposerComponentRegistration.php b/app/etc/NonComposerComponentRegistration.php deleted file mode 100755 index a7377ebfca3af..0000000000000 --- a/app/etc/NonComposerComponentRegistration.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -//Register components (via a list of glob patterns) -namespace Magento\NonComposerComponentRegistration; - -use RuntimeException; - -/** - * Include files from a list of glob patterns - * - * @throws RuntimeException - * @return void - */ -$main = function () -{ - $globPatterns = require __DIR__ . '/registration_globlist.php'; - $baseDir = dirname(dirname(__DIR__)) . '/'; - - foreach ($globPatterns as $globPattern) { - // Sorting is disabled intentionally for performance improvement - $files = glob($baseDir . $globPattern, GLOB_NOSORT); - if ($files === false) { - throw new RuntimeException("glob(): error with '$baseDir$globPattern'"); - } - array_map(function ($file) { require_once $file; }, $files); - } -}; - -$main(); From 0e392e7b161371176c348caac07619e24584ac36 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 21 Sep 2019 17:25:30 +0530 Subject: [PATCH 068/734] discard files --- app/etc/db_schema.xml | 17 - app/etc/di.xml | 1784 ----------------------- app/etc/registration_globlist.php | 19 - app/etc/vendor_path.php | 5 - generated/.htaccess | 8 - pub/media/.htaccess | 134 -- pub/media/customer/.htaccess | 8 - pub/media/downloadable/.htaccess | 8 - pub/media/import/.htaccess | 8 - pub/media/theme_customization/.htaccess | 10 - pub/static/.htaccess | 133 -- var/.htaccess | 8 - 12 files changed, 2142 deletions(-) delete mode 100755 app/etc/db_schema.xml delete mode 100755 app/etc/di.xml delete mode 100755 app/etc/registration_globlist.php delete mode 100755 app/etc/vendor_path.php delete mode 100755 generated/.htaccess delete mode 100755 pub/media/.htaccess delete mode 100755 pub/media/customer/.htaccess delete mode 100755 pub/media/downloadable/.htaccess delete mode 100755 pub/media/import/.htaccess delete mode 100755 pub/media/theme_customization/.htaccess delete mode 100755 pub/static/.htaccess delete mode 100755 var/.htaccess diff --git a/app/etc/db_schema.xml b/app/etc/db_schema.xml deleted file mode 100755 index d7af9091b238d..0000000000000 --- a/app/etc/db_schema.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> - <table name="patch_list" resource="default" comment="List of data/schema patches"> - <column xsi:type="int" name="patch_id" identity="true" comment="Patch Auto Increment" /> - <column xsi:type="varchar" name="patch_name" length="1024" nullable="false" comment="Patch Class Name" /> - <constraint xsi:type="primary" referenceId="PRIMARY"> - <column name="patch_id" /> - </constraint> - </table> -</schema> diff --git a/app/etc/di.xml b/app/etc/di.xml deleted file mode 100755 index 882d1be623988..0000000000000 --- a/app/etc/di.xml +++ /dev/null @@ -1,1784 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="DateTimeInterface" type="DateTime" /> - <preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\Monolog" /> - <preference for="Magento\Framework\EntityManager\EntityMetadataInterface" type="Magento\Framework\EntityManager\EntityMetadata" /> - <preference for="Magento\Framework\EntityManager\HydratorInterface" type="Magento\Framework\EntityManager\Hydrator" /> - <preference for="Magento\Framework\View\Template\Html\MinifierInterface" type="Magento\Framework\View\Template\Html\Minifier" /> - <preference for="Magento\Framework\Model\Entity\ScopeInterface" type="Magento\Framework\Model\Entity\Scope" /> - <preference for="Magento\Framework\ObjectManager\FactoryInterface" type="Magento\Framework\ObjectManager\Factory\Dynamic\Developer" /> - <preference for="Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface" type="Magento\Framework\Search\Adapter\Mysql\Filter\Preprocessor" /> - <preference for="Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface" type="Magento\Framework\Search\Adapter\Mysql\Field\Resolver" /> - <preference for="Magento\Framework\Search\Request\Aggregation\StatusInterface" type="Magento\Framework\Search\Request\Aggregation\Status" /> - <preference for="Magento\Framework\Search\Adapter\Mysql\Field\FieldInterface" type="Magento\Framework\Search\Adapter\Mysql\Field\Field"/> - <preference for="Magento\Framework\Search\Adapter\Aggregation\AggregationResolverInterface" type="Magento\Framework\Search\Adapter\Aggregation\AggregationResolver"/> - <preference for="Magento\Framework\App\RequestInterface" type="Magento\Framework\App\Request\Http" /> - <preference for="Magento\Framework\App\PlainTextRequestInterface" type="Magento\Framework\App\Request\Http" /> - <preference for="Magento\Framework\App\RequestContentInterface" type="Magento\Framework\App\Request\Http" /> - <preference for="Magento\Framework\App\Request\PathInfoProcessorInterface" type="Magento\Store\App\Request\PathInfoProcessor" /> - <preference for="Magento\Framework\App\ResponseInterface" type="Magento\Framework\App\Response\Http" /> - <preference for="Magento\Framework\App\RouterListInterface" type="Magento\Framework\App\RouterList" /> - <preference for="Magento\Framework\App\FrontControllerInterface" type="Magento\Framework\App\FrontController" /> - <preference for="Magento\Framework\App\CacheInterface" type="Magento\Framework\App\Cache\Proxy" /> - <preference for="Magento\Framework\App\Cache\StateInterface" type="Magento\Framework\App\Cache\State" /> - <preference for="Magento\Framework\App\Cache\TypeListInterface" type="Magento\Framework\App\Cache\TypeList" /> - <preference for="Magento\Framework\App\ObjectManager\ConfigWriterInterface" type="Magento\Framework\App\ObjectManager\ConfigWriter\Filesystem" /> - <preference for="Magento\Store\Model\StoreManagerInterface" type="Magento\Store\Model\StoreManager" /> - <preference for="Magento\Framework\View\DesignInterface" type="Magento\Theme\Model\View\Design\Proxy" /> - <preference for="Magento\Framework\View\Design\ThemeInterface" type="Magento\Theme\Model\Theme" /> - <preference for="Magento\Framework\View\Design\Theme\ResolverInterface" type="Magento\Theme\Model\Theme\Resolver" /> - <preference for="Magento\Framework\View\ConfigInterface" type="Magento\Framework\View\Config" /> - <preference for="Magento\Framework\View\Asset\Bundle\ConfigInterface" type="Magento\Framework\View\Asset\Bundle\Config" /> - <preference for="Magento\Framework\Locale\ListsInterface" type="Magento\Framework\Locale\TranslatedLists" /> - <preference for="Magento\Framework\Locale\AvailableLocalesInterface" type="Magento\Framework\Locale\Deployed\Codes" /> - <preference for="Magento\Framework\Locale\OptionInterface" type="Magento\Framework\Locale\Deployed\Options" /> - <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Proxy" /> - <preference for="Magento\Framework\Api\AttributeTypeResolverInterface" type="Magento\Framework\Reflection\AttributeTypeResolver" /> - <preference for="Magento\Framework\Api\Search\SearchResultInterface" type="Magento\Framework\Api\Search\SearchResult" /> - <preference for="Magento\Framework\Api\Search\SearchCriteriaInterface" type="Magento\Framework\Api\Search\SearchCriteria"/> - <preference for="Magento\Framework\Api\Search\DocumentInterface" type="Magento\Framework\Api\Search\Document" /> - <preference for="Magento\Framework\Api\Search\AggregationInterface" type="Magento\Framework\Search\Response\Aggregation" /> - <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> - </arguments> - </type> - <preference for="Magento\Framework\Api\ExtensionAttribute\JoinDataInterface" type="Magento\Framework\Api\ExtensionAttribute\JoinData" /> - <preference for="Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface" type="Magento\Framework\Api\ExtensionAttribute\JoinProcessor" /> - <preference for="Magento\Framework\Locale\ConfigInterface" type="Magento\Framework\Locale\Config" /> - <preference for="Magento\Framework\Notification\NotifierInterface" type="Magento\Framework\Notification\NotifierPool" /> - <preference for="Magento\Framework\UrlInterface" type="Magento\Framework\Url" /> - <preference for="Magento\Framework\Url\EncoderInterface" type="Magento\Framework\Url\Encoder" /> - <preference for="Magento\Framework\Url\DecoderInterface" type="Magento\Framework\Url\Decoder" /> - <preference for="Magento\Framework\Data\Collection\Db\FetchStrategyInterface" type="Magento\Framework\Data\Collection\Db\FetchStrategy\Query" /> - <preference for="Magento\Framework\Config\ScopeInterface" type="Magento\Framework\Config\Scope" /> - <preference for="Magento\Framework\Config\FileResolverInterface" type="Magento\Framework\App\Config\FileResolver" /> - <preference for="Magento\Framework\Config\CacheInterface" type="Magento\Framework\App\Cache\Type\Config" /> - <preference for="Magento\Framework\Config\ValidationStateInterface" type="Magento\Framework\App\Arguments\ValidationState" /> - <preference for="Magento\Framework\Module\ModuleListInterface" type="Magento\Framework\Module\ModuleList" /> - <preference for="Magento\Framework\Module\ModuleManagerInterface" type="Magento\Framework\Module\Manager" /> - <preference for="Magento\Framework\Component\ComponentRegistrarInterface" type="Magento\Framework\Component\ComponentRegistrar"/> - <preference for="Magento\Framework\Event\ConfigInterface" type="Magento\Framework\Event\Config" /> - <preference for="Magento\Framework\Event\InvokerInterface" type="Magento\Framework\Event\Invoker\InvokerDefault" /> - <preference for="Magento\Framework\Interception\PluginListInterface" type="Magento\Framework\Interception\PluginList\PluginList" /> - <preference for="Magento\Framework\Event\ManagerInterface" type="Magento\Framework\Event\Manager\Proxy" /> - <preference for="Magento\Framework\View\LayoutInterface" type="Magento\Framework\View\Layout" /> - <preference for="Magento\Framework\View\Layout\ProcessorInterface" type="Magento\Framework\View\Model\Layout\Merge" /> - <preference for="Magento\Framework\View\Layout\LayoutCacheKeyInterface" type="Magento\Framework\View\Model\Layout\CacheKey" /> - <preference for="Magento\Framework\View\Url\ConfigInterface" type="Magento\Framework\View\Url\Config" /> - <preference for="Magento\Framework\App\Route\ConfigInterface" type="Magento\Framework\App\Route\Config" /> - <preference for="Magento\Framework\App\ResourceConnection\ConfigInterface" type="Magento\Framework\App\ResourceConnection\Config\Proxy" /> - <preference for="Magento\Framework\Oauth\OauthInterface" type="Magento\Framework\Oauth\Oauth"/> - <preference for="Magento\Framework\View\Design\Theme\Domain\PhysicalInterface" type="Magento\Theme\Model\Theme\Domain\Physical" /> - <preference for="Magento\Framework\View\Design\Theme\Domain\VirtualInterface" type="Magento\Theme\Model\Theme\Domain\Virtual" /> - <preference for="Magento\Framework\View\Design\Theme\Domain\StagingInterface" type="Magento\Theme\Model\Theme\Domain\Staging" /> - <preference for="Magento\Framework\Json\EncoderInterface" type="Magento\Framework\Json\Encoder" /> - <preference for="Magento\Framework\Json\DecoderInterface" type="Magento\Framework\Json\Decoder" /> - <preference for="Magento\Framework\Message\ManagerInterface" type="Magento\Framework\Message\Manager" /> - <preference for="Magento\Framework\App\Config\ValueInterface" type="Magento\Framework\App\Config\Value" /> - <preference for="Magento\Framework\Interception\ChainInterface" type="Magento\Framework\Interception\Chain\Chain" /> - <preference for="Magento\Framework\Module\Output\ConfigInterface" type="Magento\Framework\Module\Output\Config" /> - <preference for="Magento\Framework\View\Design\Theme\CustomizationInterface" type="Magento\Framework\View\Design\Theme\Customization" /> - <preference for="Magento\Framework\View\Asset\ConfigInterface" type="Magento\Framework\View\Asset\Config" /> - <preference for="Magento\Framework\Image\Adapter\ConfigInterface" type="Magento\Framework\Image\Adapter\Config" /> - <preference for="Magento\Framework\Image\Adapter\UploadConfigInterface" type="Magento\Framework\Image\Adapter\Config" /> - <preference for="Magento\Framework\View\Design\Theme\Image\PathInterface" type="Magento\Theme\Model\Theme\Image\Path" /> - <preference for="Magento\Framework\Session\Config\ConfigInterface" type="Magento\Framework\Session\Config" /> - <preference for="Magento\Framework\Session\SidResolverInterface" type="Magento\Framework\Session\SidResolver\Proxy" /> - <preference for="Magento\Framework\Stdlib\Cookie\CookieScopeInterface" type="Magento\Framework\Stdlib\Cookie\CookieScope" /> - <preference for="Magento\Framework\Stdlib\Cookie\CookieReaderInterface" type="Magento\Framework\Stdlib\Cookie\PhpCookieReader" /> - <preference for="Magento\Framework\Stdlib\CookieManagerInterface" type="Magento\Framework\Stdlib\Cookie\PhpCookieManager" /> - <preference for="Magento\Framework\TranslateInterface" type="Magento\Framework\Translate" /> - <preference for="Magento\Framework\Config\ScopeListInterface" type="interceptionConfigScope" /> - <preference for="Magento\Framework\View\Design\Theme\Label\ListInterface" type="Magento\Theme\Model\ResourceModel\Theme\Collection" /> - <preference for="Magento\Framework\Mview\ConfigInterface" type="Magento\Framework\Mview\Config" /> - <preference for="Magento\Framework\Mview\ViewInterface" type="Magento\Framework\Mview\View" /> - <preference for="Magento\Framework\Mview\ProcessorInterface" type="Magento\Framework\Mview\Processor" /> - <preference for="Magento\Framework\Mview\View\CollectionInterface" type="Magento\Framework\Mview\View\Collection" /> - <preference for="Magento\Framework\Mview\View\SubscriptionInterface" type="Magento\Framework\Mview\View\Subscription" /> - <preference for="Magento\Framework\Mview\View\ChangelogInterface" type="Magento\Framework\Mview\View\Changelog" /> - <preference for="Magento\Framework\Api\MetadataServiceInterface" type="Magento\Framework\Api\DefaultMetadataService"/> - <preference for="Magento\Framework\Api\MetadataObjectInterface" type="Magento\Framework\Api\AttributeMetadata"/> - <preference for="Magento\Framework\Api\SearchCriteriaInterface" type="Magento\Framework\Api\SearchCriteria"/> - <preference for="Magento\Framework\App\Rss\UrlBuilderInterface" type="Magento\Framework\App\Rss\UrlBuilder"/> - <preference for="Magento\Framework\DB\LoggerInterface" type="Magento\Framework\DB\Logger\LoggerProxy"/> - <preference for="Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface" type="Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql"/> - <preference for="Magento\Framework\DB\QueryInterface" type="Magento\Framework\DB\Query"/> - <preference for="Magento\Framework\App\ProductMetadataInterface" type="Magento\Framework\App\ProductMetadata"/> - <preference for="Magento\Framework\Acl\Data\CacheInterface" type="Magento\Framework\Acl\Data\Cache" /> - <preference for="Magento\Framework\App\AreaInterface" type="Magento\Framework\App\Area" /> - <preference for="Magento\Framework\Setup\ModuleDataSetupInterface" type="Magento\Setup\Module\DataSetup" /> - <preference for="Magento\Framework\AuthorizationInterface" type="Magento\Framework\Authorization" /> - <preference for="Magento\Framework\Authorization\PolicyInterface" type="Magento\Framework\Authorization\Policy\DefaultPolicy" /> - <preference for="Magento\Framework\Authorization\RoleLocatorInterface" type="Magento\Framework\Authorization\RoleLocator\DefaultRoleLocator" /> - <preference for="Magento\Framework\Session\SessionManagerInterface" type="Magento\Framework\Session\Generic" /> - <preference for="Magento\Framework\App\Config\ScopeConfigInterface" type="Magento\Framework\App\Config" /> - <preference for="Magento\Framework\App\Config\ReinitableConfigInterface" type="Magento\Framework\App\ReinitableConfig" /> - <preference for="Magento\Framework\App\Config\MutableScopeConfigInterface" type="Magento\Framework\App\MutableScopeConfig" /> - <preference for="Magento\Framework\App\Config\Storage\WriterInterface" type="Magento\Framework\App\Config\Storage\Writer" /> - <preference for="Magento\Framework\Config\ConverterInterface" type="Magento\Framework\Config\Converter\Dom"/> - <preference for="Magento\Framework\App\DefaultPathInterface" type="Magento\Framework\App\DefaultPath\DefaultPath" /> - <preference for="Magento\Framework\Encryption\EncryptorInterface" type="Magento\Framework\Encryption\Encryptor" /> - <preference for="Magento\Framework\Filter\Encrypt\AdapterInterface" type="Magento\Framework\Filter\Encrypt\Basic" /> - <preference for="Magento\Framework\Cache\ConfigInterface" type="Magento\Framework\Cache\Config" /> - <preference for="Magento\Framework\View\Asset\MergeStrategyInterface" type="Magento\Framework\View\Asset\MergeStrategy\Direct" /> - <preference for="Magento\Framework\App\ViewInterface" type="Magento\Framework\App\View" /> - <preference for="Magento\Framework\Data\Collection\EntityFactoryInterface" type="Magento\Framework\Data\Collection\EntityFactory" /> - <preference for="Magento\Framework\Translate\InlineInterface" type="Magento\Framework\Translate\Inline" /> - <preference for="Magento\Framework\Session\ValidatorInterface" type="Magento\Framework\Session\Validator" /> - <preference for="Magento\Framework\Session\StorageInterface" type="Magento\Framework\Session\Storage" /> - <preference for="Magento\Framework\App\Request\DataPersistorInterface" type="Magento\Framework\App\Request\DataPersistor" /> - <preference for="Magento\Framework\Url\RouteParamsResolverInterface" type="Magento\Framework\Url\RouteParamsResolver" /> - <preference for="Magento\Framework\Url\RouteParamsPreprocessorInterface" type="Magento\Framework\Url\RouteParamsPreprocessorComposite" /> - <preference for="Magento\Framework\Url\ModifierInterface" type="Magento\Framework\Url\ModifierComposite" /> - <preference for="Magento\Framework\Url\QueryParamsResolverInterface" type="Magento\Framework\Url\QueryParamsResolver" /> - <preference for="Magento\Framework\Url\ScopeResolverInterface" type="Magento\Framework\Url\ScopeResolver" /> - <preference for="Magento\Framework\Url\SecurityInfoInterface" type="Magento\Framework\Url\SecurityInfo\Proxy" /> - <preference for="Magento\Framework\Locale\CurrencyInterface" type="Magento\Framework\Locale\Currency" /> - <preference for="Magento\Framework\CurrencyInterface" type="Magento\Framework\Currency" /> - <preference for="Magento\Framework\Locale\FormatInterface" type="Magento\Framework\Locale\Format" /> - <preference for="Magento\Framework\Locale\ResolverInterface" type="Magento\Framework\Locale\Resolver" /> - <preference for="Magento\Framework\Stdlib\DateTime\TimezoneInterface" type="Magento\Framework\Stdlib\DateTime\Timezone" /> - <preference for="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverterInterface" type="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverter" /> - <preference for="Magento\Framework\Communication\ConfigInterface" type="Magento\Framework\Communication\Config" /> - <preference for="Magento\Framework\Module\ResourceInterface" type="Magento\Framework\Module\ModuleResource" /> - <preference for="Magento\Framework\Pricing\Amount\AmountInterface" type="Magento\Framework\Pricing\Amount\Base" /> - <preference for="Magento\Framework\Api\SearchResultsInterface" type="Magento\Framework\Api\SearchResults" /> - <preference for="Magento\Framework\Api\AttributeInterface" type="Magento\Framework\Api\AttributeValue" /> - <preference for="Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface" type="Magento\Framework\Model\ResourceModel\Db\TransactionManager" /> - <preference for="Magento\Framework\Api\Data\ImageContentInterface" type="Magento\Framework\Api\ImageContent" /> - <preference for="Magento\Framework\Api\ImageContentValidatorInterface" type="Magento\Framework\Api\ImageContentValidator" /> - <preference for="Magento\Framework\Api\ImageProcessorInterface" type="Magento\Framework\Api\ImageProcessor" /> - <preference for="Magento\Framework\Code\Reader\ClassReaderInterface" type="Magento\Framework\Code\Reader\ClassReader" /> - <preference for="Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface" type="Magento\Framework\Stdlib\DateTime\DateTimeFormatter"/> - <preference for="Magento\Framework\Api\Search\SearchInterface" type="Magento\Framework\Search\Search"/> - <preference for="Magento\Framework\View\Design\FileResolution\Fallback\ResolverInterface" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple" /> - <preference for="Cm\RedisSession\Handler\ConfigInterface" type="Magento\Framework\Session\SaveHandler\Redis\Config"/> - <preference for="Cm\RedisSession\Handler\LoggerInterface" type="Magento\Framework\Session\SaveHandler\Redis\Logger"/> - <preference for="Magento\Framework\EntityManager\MapperInterface" type="Magento\Framework\EntityManager\CompositeMapper"/> - <preference for="Magento\Framework\Console\CommandListInterface" type="Magento\Framework\Console\CommandList"/> - <preference for="Magento\Framework\DataObject\IdentityGeneratorInterface" type="Magento\Framework\DataObject\IdentityService" /> - <preference for="Magento\Framework\Serialize\SerializerInterface" type="Magento\Framework\Serialize\Serializer\Json" /> - <preference for="Magento\Framework\App\Scope\ValidatorInterface" type="Magento\Framework\App\Scope\Validator"/> - <preference for="Magento\Framework\App\ScopeResolverInterface" type="Magento\Framework\App\ScopeResolver" /> - <preference for="Magento\Framework\App\ScopeInterface" type="Magento\Framework\App\ScopeDefault" /> - <preference for="Magento\Framework\View\Design\Theme\ListInterface" type="Magento\Framework\View\Design\Theme\ThemeList" /> - <preference for="Magento\Framework\View\Design\Theme\ThemeProviderInterface" type="Magento\Framework\View\Design\Theme\ThemeProvider" /> - <preference for="Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface" type="Magento\Framework\View\Asset\PreProcessor\ChainFactory"/> - <preference for="Magento\Framework\Css\PreProcessor\ErrorHandlerInterface" type="Magento\Framework\Css\PreProcessor\ErrorHandler" /> - <preference for="Magento\Framework\View\Asset\PreProcessor\Helper\SortInterface" type="Magento\Framework\View\Asset\PreProcessor\Helper\Sort"/> - <preference for="Magento\Framework\App\View\Deployment\Version\StorageInterface" type="Magento\Framework\App\View\Deployment\Version\Storage\File"/> - <preference for="Magento\Framework\View\Page\FaviconInterface" type="Magento\Theme\Model\Favicon\Favicon" /> - <preference for="Magento\Framework\View\Element\Message\InterpretationStrategyInterface" type="Magento\Framework\View\Element\Message\InterpretationMediator" /> - <preference for="Magento\Framework\App\FeedInterface" type="Magento\Framework\App\Feed" /> - <preference for="Magento\Framework\App\FeedFactoryInterface" type="Magento\Framework\App\FeedFactory" /> - <preference for="Magento\Framework\Indexer\Config\DependencyInfoProviderInterface" type="Magento\Framework\Indexer\Config\DependencyInfoProvider" /> - <preference for="Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface" type="Magento\Eav\Model\TypeLocator\ComplexType"/> - <preference for="Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaReaderInterface" type="Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DbSchemaReader" /> - <preference for="Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaWriterInterface" type="Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DbSchemaWriter" /> - <preference for="Magento\Framework\Setup\Declaration\Schema\SchemaConfigInterface" type="Magento\Framework\Setup\Declaration\Schema\SchemaConfig" /> - <preference for="Magento\Framework\Setup\Declaration\Schema\DataSavior\DumpAccessorInterface" type="Magento\Framework\Setup\Declaration\Schema\FileSystem\Csv" /> - <preference for="Magento\Framework\MessageQueue\PublisherInterface" type="Magento\Framework\MessageQueue\PublisherPool" /> - <preference for="Magento\Framework\MessageQueue\BulkPublisherInterface" type="Magento\Framework\MessageQueue\Bulk\PublisherPool" /> - <preference for="Magento\Framework\MessageQueue\MessageIdGeneratorInterface" type="Magento\Framework\MessageQueue\MessageIdGenerator" /> - <preference for="Magento\Framework\MessageQueue\Consumer\ConfigInterface" type="Magento\Framework\MessageQueue\Consumer\Config" /> - <preference for="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem\HandlerInterface" type="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem\Handler" /> - <preference for="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItemInterface" type="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem" /> - <preference for="Magento\Framework\MessageQueue\Consumer\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Consumer\Config\CompositeValidator" /> - <preference for="Magento\Framework\MessageQueue\Consumer\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Consumer\Config\CompositeReader" /> - <preference for="Magento\Framework\Amqp\Topology\BindingInstallerInterface" type="Magento\Framework\Amqp\Topology\BindingInstaller" /> - <preference for="Magento\Framework\MessageQueue\Topology\ConfigInterface" type="Magento\Framework\MessageQueue\Topology\Config" /> - <preference for="Magento\Framework\MessageQueue\Topology\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Topology\Config\CompositeReader" /> - <preference for="Magento\Framework\MessageQueue\Topology\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Topology\Config\CompositeValidator" /> - <preference for="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface" type="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem" /> - <preference for="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface" type="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\Binding" /> - <preference for="Magento\Framework\MessageQueue\Publisher\ConfigInterface" type="Magento\Framework\MessageQueue\Publisher\Config" /> - <preference for="Magento\Framework\MessageQueue\Publisher\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Publisher\Config\CompositeReader" /> - <preference for="Magento\Framework\MessageQueue\Publisher\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Publisher\Config\CompositeValidator" /> - <preference for="Magento\Framework\MessageQueue\Publisher\Config\PublisherConnectionInterface" type="Magento\Framework\MessageQueue\Publisher\Config\PublisherConnection" /> - <preference for="Magento\Framework\MessageQueue\Publisher\Config\PublisherConfigItemInterface" type="Magento\Framework\MessageQueue\Publisher\Config\PublisherConfigItem" /> - <preference for="Magento\Framework\MessageQueue\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\ExchangeFactory" /> - <preference for="Magento\Framework\MessageQueue\Bulk\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\Bulk\ExchangeFactory" /> - <preference for="Magento\Framework\MessageQueue\QueueFactoryInterface" type="Magento\Framework\MessageQueue\QueueFactory" /> - <preference for="Magento\Framework\Search\Request\IndexScopeResolverInterface" type="Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver"/> - <preference for="Magento\Framework\HTTP\ClientInterface" type="Magento\Framework\HTTP\Client\Curl" /> - <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" /> - <type name="Magento\Framework\Acl\Data\Cache"> - <arguments> - <argument name="aclBuilder" xsi:type="object">Magento\Framework\Acl\Builder\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Logger\Handler\Base"> - <arguments> - <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> - </arguments> - </type> - <type name="Magento\Framework\Logger\Handler\System"> - <arguments> - <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> - </arguments> - </type> - <preference for="Magento\AsynchronousOperations\Model\ConfigInterface" type="Magento\WebapiAsync\Model\Config\Proxy" /> - <type name="Magento\Framework\Communication\Config\CompositeReader"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="asyncServiceReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\WebapiAsync\Code\Generator\Config\RemoteServiceReader\Communication</item> - <item name="sortOrder" xsi:type="string">0</item> - </item> - <item name="xmlReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\XmlReader</item> - <item name="sortOrder" xsi:type="string">10</item> - </item> - <item name="envReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\EnvReader</item> - <item name="sortOrder" xsi:type="string">20</item> - </item> - <item name="remoteServiceReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Code\Generator\Config\RemoteServiceReader\Communication</item> - <item name="sortOrder" xsi:type="string">5</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Logger\Monolog"> - <arguments> - <argument name="name" xsi:type="string">main</argument> - <argument name="handlers" xsi:type="array"> - <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item> - <item name="debug" xsi:type="object">Magento\Framework\Logger\Handler\Debug</item> - <item name="syslog" xsi:type="object">Magento\Framework\Logger\Handler\Syslog</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Logger\Handler\Syslog"> - <arguments> - <argument name="ident" xsi:type="string">Magento</argument> - </arguments> - </type> - <type name="Magento\Framework\Model\Context"> - <arguments> - <argument name="actionValidator" xsi:type="object">Magento\Framework\Model\ActionValidator\RemoveAction\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Api\FilterBuilder" shared="false" /> - <type name="Magento\Framework\Api\SearchCriteriaBuilder" shared="false" /> - <type name="Magento\Framework\View\Layout\Builder" shared="false" /> - <type name="Magento\Framework\View\Page\Builder" shared="false" /> - <type name="Magento\Framework\Message\Manager"> - <arguments> - <argument name="session" xsi:type="object">Magento\Framework\Message\Session\Proxy</argument> - <argument name="exceptionMessageFactory" xsi:type="object">Magento\Framework\Message\ExceptionMessageLookupFactory</argument> - </arguments> - </type> - <type name="Magento\Framework\View\BlockPool" shared="false" /> - <type name="Magento\Framework\App\Request\Http"> - <arguments> - <argument name="pathInfoProcessor" xsi:type="object">Magento\Backend\App\Request\PathInfoProcessor\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Response\Http"> - <arguments> - <argument name="sessionConfig" xsi:type="object">Magento\Framework\Session\Config\ConfigInterface\Proxy</argument> - </arguments> - </type> - <preference for="Magento\Framework\Session\SaveHandlerInterface" type="Magento\Framework\Session\SaveHandler" /> - <type name="Magento\Framework\Session\SaveHandlerFactory"> - <arguments> - <argument name="handlers" xsi:type="array"> - <item name="db" xsi:type="string">Magento\Framework\Session\SaveHandler\DbTable</item> - <item name="redis" xsi:type="string">Magento\Framework\Session\SaveHandler\Redis</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\FeedFactory"> - <arguments> - <argument name="formats" xsi:type="array"> - <item name="rss" xsi:type="string">Magento\Framework\App\Feed</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Session\SaveHandler\Redis"> - <arguments> - <argument name="config" xsi:type="object">Cm\RedisSession\Handler\ConfigInterface</argument> - <argument name="logger" xsi:type="object">Cm\RedisSession\Handler\LoggerInterface</argument> - </arguments> - </type> - <virtualType name="interceptionConfigScope" type="Magento\Framework\Config\Scope"> - <arguments> - <argument name="defaultScope" xsi:type="string">global</argument> - </arguments> - </virtualType> - <type name="Magento\Framework\App\State"> - <arguments> - <argument name="mode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Asset\Source"> - <arguments> - <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Page\Config\Renderer"> - <arguments> - <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Arguments\ValidationState"> - <arguments> - <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Cache\Frontend\Factory"> - <arguments> - <argument name="enforcedOptions" xsi:type="init_parameter">Magento\Framework\App\Cache\Frontend\Factory::PARAM_CACHE_FORCED_OPTIONS</argument> - <argument name="decorators" xsi:type="array"> - <item name="tag" xsi:type="array"> - <item name="class" xsi:type="string">Magento\Framework\Cache\Frontend\Decorator\TagScope</item> - <item name="parameters" xsi:type="array"> - <item name="tag" xsi:type="string">MAGE</item> - </item> - </item> - <item name="logger" xsi:type="array"> - <item name="class" xsi:type="string">Magento\Framework\Cache\Frontend\Decorator\Logger</item> - </item> - </argument> - <argument name="resource" xsi:type="object">Magento\Framework\App\ResourceConnection\Proxy</argument> - </arguments> - </type> - <type name="Magento\Backend\App\Area\FrontNameResolver"> - <arguments> - <argument name="defaultFrontName" xsi:type="init_parameter">Magento\Backend\Setup\ConfigOptionsList::CONFIG_PATH_BACKEND_FRONTNAME</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Cache\State"> - <arguments> - <argument name="banAll" xsi:type="init_parameter">Magento\Framework\App\Cache\State::PARAM_BAN_CACHE</argument> - </arguments> - </type> - <type name="Magento\Store\Model\StoreManager"> - <arguments> - <argument name="scopeCode" xsi:type="init_parameter">Magento\Store\Model\StoreManager::PARAM_RUN_CODE</argument> - <argument name="scopeType" xsi:type="init_parameter">Magento\Store\Model\StoreManager::PARAM_RUN_TYPE</argument> - </arguments> - </type> - <type name="Magento\Framework\Translate"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Translate</argument> - <argument name="locale" xsi:type="object">Magento\Framework\Locale\Resolver\Proxy</argument> - <argument name="translate" xsi:type="object">Magento\Framework\Translate\ResourceInterface\Proxy</argument> - <argument name="request" xsi:type="object">Magento\Framework\App\Request\Http\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Helper\Context"> - <arguments> - <argument name="translateInline" xsi:type="object">Magento\Framework\Translate\InlineInterface\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Data\Structure" shared="false" /> - <type name="Magento\Framework\View\Layout\Data\Structure" shared="false" /> - <type name="Magento\Theme\Model\View\Design"> - <arguments> - <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Acl" shared="false" /> - <type name="Magento\Framework\App\ObjectManager\ConfigLoader"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\ObjectManager\ConfigCache"> - <arguments> - <argument name="cacheFrontend" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - </arguments> - </type> - <type name="Magento\Framework\Cache\Config\Data"> - <arguments> - <argument name="cacheId" xsi:type="string">config_cache</argument> - <argument name="reader" xsi:type="object">Magento\Framework\Cache\Config\Reader\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Interception\Config\Config"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> - <argument name="cacheId" xsi:type="string">interception</argument> - </arguments> - </type> - <type name="Magento\Framework\Interception\Config\CacheManager"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - </arguments> - </type> - <type name="Magento\Framework\Interception\PluginList\PluginList"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> - <argument name="cacheId" xsi:type="string">plugin-list</argument> - <argument name="scopePriorityScheme" xsi:type="array"> - <item name="first" xsi:type="string">global</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\ResourceConnection"> - <arguments> - <argument name="connectionFactory" xsi:type="object">Magento\Framework\App\ResourceConnection\ConnectionFactory</argument> - </arguments> - </type> - <type name="Magento\Framework\App\ResourceConnection\Config"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\App\ResourceConnection\Config\Reader\Proxy</argument> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\ResourceConnection\Config\Reader"> - <arguments> - <argument name="fileResolver" xsi:type="object">Magento\Framework\App\Config\FileResolver\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Config\Scope"> - <arguments> - <argument name="defaultScope" xsi:type="string">primary</argument> - <argument name="areaList" xsi:type="object">Magento\Framework\App\AreaList\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Url"> - <arguments> - <argument name="session" xsi:type="object">Magento\Framework\Session\Generic\Proxy</argument> - <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> - </arguments> - </type> - <virtualType name="layoutArgumentReaderInterpreter" type="Magento\Framework\Data\Argument\Interpreter\Composite"> - <arguments> - <argument name="interpreters" xsi:type="array"> - <item name="options" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item> - <item name="array" xsi:type="object">layoutArrayArgumentReaderInterpreterProxy</item> - <item name="boolean" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item> - <item name="number" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item> - <item name="string" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item> - <item name="null" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item> - <item name="object" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> - <item name="url" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> - <item name="helper" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> - </argument> - <argument name="discriminator" xsi:type="const">Magento\Framework\View\Model\Layout\Merge::TYPE_ATTRIBUTE</argument> - </arguments> - </virtualType> - <virtualType name="layoutArgumentGeneratorInterpreterInternal" type="Magento\Framework\Data\Argument\Interpreter\Composite"> - <arguments> - <argument name="interpreters" xsi:type="array"> - <item name="options" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item> - <item name="array" xsi:type="object">layoutArrayArgumentGeneratorInterpreterProxy</item> - <item name="boolean" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item> - <item name="number" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item> - <item name="string" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item> - <item name="null" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item> - <item name="object" xsi:type="object">layoutObjectArgumentInterpreter</item> - <item name="url" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Url</item> - <item name="helper" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\HelperMethod</item> - </argument> - <argument name="discriminator" xsi:type="const">Magento\Framework\View\Model\Layout\Merge::TYPE_ATTRIBUTE</argument> - </arguments> - </virtualType> - <virtualType name="layoutArgumentGeneratorInterpreter" type="Magento\Framework\View\Layout\Argument\Interpreter\Decorator\Updater"> - <arguments> - <argument name="subject" xsi:type="object">layoutArgumentGeneratorInterpreterInternal</argument> - </arguments> - </virtualType> - <virtualType name="layoutArrayArgumentReaderInterpreter" type="Magento\Framework\Data\Argument\Interpreter\ArrayType"> - <arguments> - <argument name="itemInterpreter" xsi:type="object">layoutArgumentReaderInterpreter</argument> - </arguments> - </virtualType> - <virtualType name="layoutArrayArgumentGeneratorInterpreter" type="Magento\Framework\Data\Argument\Interpreter\ArrayType"> - <arguments> - <argument name="itemInterpreter" xsi:type="object">layoutArgumentGeneratorInterpreterInternal</argument> - </arguments> - </virtualType> - <!-- - Array item can be of any type just like an argument, including array type itself, which creates circular dependency. - Proxy is used to resolve the circular dependency, so that array items undergo the same interpretation as arguments. - --> - <virtualType name="layoutArrayArgumentReaderInterpreterProxy" type="Magento\Framework\Data\Argument\InterpreterInterface\Proxy"> - <arguments> - <argument name="instanceName" xsi:type="string">layoutArrayArgumentReaderInterpreter</argument> - </arguments> - </virtualType> - <virtualType name="layoutArrayArgumentGeneratorInterpreterProxy" type="Magento\Framework\Data\Argument\InterpreterInterface\Proxy"> - <arguments> - <argument name="instanceName" xsi:type="string">layoutArrayArgumentGeneratorInterpreter</argument> - </arguments> - </virtualType> - <virtualType name="layoutObjectArgumentInterpreter" type="Magento\Framework\View\Layout\Argument\Interpreter\DataObject"> - <arguments> - <argument name="expectedClass" xsi:type="string">Magento\Framework\View\Element\Block\ArgumentInterface</argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\Argument\Interpreter\NamedParams"> - <arguments> - <argument name="paramInterpreter" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</argument> - </arguments> - </type> - <virtualType name="containerRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> - <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\Reader\Container"> - <arguments> - <argument name="readerPool" xsi:type="object">containerRenderPool</argument> - </arguments> - </type> - <virtualType name="blockRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> - <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> - <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\Reader\Block"> - <arguments> - <argument name="readerPool" xsi:type="object">blockRenderPool</argument> - <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> - <argument name="argumentInterpreter" xsi:type="object">layoutArgumentReaderInterpreter</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Layout\Reader\UiComponent"> - <arguments> - <argument name="readerPool" xsi:type="object">blockRenderPool</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Layout\ConfigCondition"> - <arguments> - <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> - </arguments> - </type> - <virtualType name="bodyRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> - <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> - <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Page\Config\Reader\Body"> - <arguments> - <argument name="readerPool" xsi:type="object">bodyRenderPool</argument> - </arguments> - </type> - <virtualType name="commonRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="html" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Html</item> - <item name="head" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Head</item> - <item name="body" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Body</item> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> - <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> - <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout"> - <arguments> - <argument name="readerPool" xsi:type="object" shared="false">commonRenderPool</argument> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Layout</argument> - </arguments> - </type> - <virtualType name="genericLayoutRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> - <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> - <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Result\Layout"> - <arguments> - <argument name="layoutReaderPool" xsi:type="object">genericLayoutRenderPool</argument> - </arguments> - </type> - <virtualType name="pageConfigRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="html" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Html</item> - <item name="head" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Head</item> - <item name="body" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Body</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\GeneratorPool"> - <arguments> - <argument name="generators" xsi:type="array"> - <item name="head" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Head</item> - <item name="body" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Body</item> - <item name="block" xsi:type="object">Magento\Framework\View\Layout\Generator\Block</item> - <item name="container" xsi:type="object">Magento\Framework\View\Layout\Generator\Container</item> - <item name="uiComponent" xsi:type="object">Magento\Framework\View\Layout\Generator\UiComponent</item> - </argument> - </arguments> - </type> - <virtualType name="pageLayoutGeneratorPool" type="Magento\Framework\View\Layout\GeneratorPool"> - <arguments> - <argument name="generators" xsi:type="array"> - <item name="head" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Head</item> - <item name="body" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Body</item> - <item name="block" xsi:type="object">Magento\Framework\View\Layout\Generator\Block</item> - <item name="container" xsi:type="object">Magento\Framework\View\Layout\Generator\Container</item> - <item name="uiComponent" xsi:type="object">Magento\Framework\View\Layout\Generator\UiComponent</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Result\Page"> - <arguments> - <argument name="layoutReaderPool" xsi:type="object">pageConfigRenderPool</argument> - <argument name="generatorPool" xsi:type="object">pageLayoutGeneratorPool</argument> - <argument name="template" xsi:type="string">Magento_Theme::root.phtml</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Layout\Generator\Block"> - <arguments> - <argument name="argumentInterpreter" xsi:type="object">layoutArgumentGeneratorInterpreter</argument> - </arguments> - </type> - <type name="Magento\Framework\Mview\View"> - <arguments> - <argument name="state" xsi:type="object" shared="false">Magento\Indexer\Model\Mview\View\State</argument> - <argument name="changelog" xsi:type="object" shared="false">Magento\Framework\Mview\View\Changelog</argument> - </arguments> - </type> - <type name="Magento\Framework\Mview\Config"> - <arguments> - <argument name="configData" xsi:type="object">Magento\Framework\Mview\Config\Data\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Mview\Config\Data"> - <arguments> - <argument name="stateCollection" xsi:type="object" shared="false">Magento\Framework\Mview\View\State\CollectionInterface</argument> - </arguments> - </type> - <type name="Magento\Framework\App\View\Asset\Publisher" shared="false" /> - <type name="Magento\Framework\View\Asset\PreProcessor\FileNameResolver"> - <arguments> - <argument name="alternativeSources" xsi:type="array"> - <item name="css" xsi:type="object">AlternativeSourceProcessors</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\StaticResource"> - <arguments> - <argument name="response" xsi:type="object" shared="false">Magento\MediaStorage\Model\File\Storage\Response</argument> - <argument name="publisher" xsi:type="object">developerPublisher</argument> - </arguments> - </type> - <virtualType name="AlternativeSourceProcessors" type="Magento\Framework\View\Asset\PreProcessor\AlternativeSource"> - <arguments> - <argument name="filenameResolver" xsi:type="object">Magento\Framework\View\Asset\PreProcessor\MinificationFilenameResolver</argument> - <argument name="lockName" xsi:type="string">alternative-source-css</argument> - <argument name="lockerProcess" xsi:type="object">Magento\Framework\View\Asset\LockerProcess</argument> - <argument name="alternatives" xsi:type="array"> - <item name="less" xsi:type="array"> - <item name="class" xsi:type="string">Magento\Framework\Css\PreProcessor\Adapter\Less\Processor</item> - </item> - </argument> - </arguments> - </virtualType> - <virtualType name="developerPublisher" type="Magento\Framework\App\View\Asset\Publisher"> - <arguments> - <argument name="materializationStrategyFactory" xsi:type="object">developerMaterialization</argument> - </arguments> - </virtualType> - <virtualType name="developerMaterialization" type="Magento\Framework\App\View\Asset\MaterializationStrategy\Factory"> - <arguments> - <argument name="strategiesList" xsi:type="array"> - <item name="view_preprocessed" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Symlink</item> - <item name="default" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Copy</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Design\FileResolution\Fallback\File"> - <arguments> - <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Design\FileResolution\Fallback\TemplateFile"> - <arguments> - <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Design\FileResolution\Fallback\LocaleFile"> - <arguments> - <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> - </arguments> - </type> - - <virtualType name="viewFileMinifiedFallbackResolver" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Minification"> - <arguments> - <argument name="fallback" xsi:type="object">viewFileFallbackResolver</argument> - </arguments> - </virtualType> - <virtualType name="viewFileFallbackResolver" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative"/> - <type name="Magento\Framework\View\Design\FileResolution\Fallback\StaticFile"> - <arguments> - <argument name="resolver" xsi:type="object">viewFileMinifiedFallbackResolver</argument> - </arguments> - </type> - <type name="Magento\Framework\Code\Generator"> - <arguments> - <argument name="generatedEntities" xsi:type="array"> - <item name="extensionInterfaceFactory" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceFactoryGenerator</item> - <item name="factory" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Factory</item> - <item name="proxy" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Proxy</item> - <item name="interceptor" xsi:type="string">\Magento\Framework\Interception\Code\Generator\Interceptor</item> - <item name="logger" xsi:type="string">\Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger</item> - <item name="mapper" xsi:type="string">\Magento\Framework\Api\Code\Generator\Mapper</item> - <item name="persistor" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Persistor</item> - <item name="repository" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Repository</item> - <item name="convertor" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Converter</item> - <item name="searchResults" xsi:type="string">\Magento\Framework\Api\Code\Generator\SearchResults</item> - <item name="extensionInterface" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator</item> - <item name="extension" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator</item> - <item name="remote" xsi:type="string">\Magento\Framework\MessageQueue\Code\Generator\RemoteServiceGenerator</item> - <item name="proxyDeferred" xsi:type="string">\Magento\Framework\Async\Code\Generator\ProxyDeferredGenerator</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\Cache\Frontend\Pool"> - <arguments> - <argument name="frontendSettings" xsi:type="array"> - <item name="page_cache" xsi:type="array"> - <item name="backend_options" xsi:type="array"> - <item name="cache_dir" xsi:type="string">page_cache</item> - </item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\Cache\Type\FrontendPool"> - <arguments> - <argument name="typeFrontendMap" xsi:type="array"> - <item name="full_page" xsi:type="string">page_cache</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Translate\Inline"> - <arguments> - <argument name="parser" xsi:type="object">Magento\Framework\Translate\Inline\ParserInterface\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Layout\ScheduledStructure" shared="false" /> - <type name="Magento\Framework\View\Page\Config\Structure" shared="false" /> - <type name="Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Container"> - <arguments> - <argument name="buckets" xsi:type="array"> - <item name="termBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Term</item> - <item name="rangeBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Range</item> - <item name="dynamicBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Dynamic</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Search\Dynamic\Algorithm\Repository"> - <arguments> - <argument name="algorithms" xsi:type="array"> - <item name="auto" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Auto</item> - <item name="manual" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Manual</item> - <item name="improved" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Improved</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\View\Model\Layout\Merge"> - <arguments> - <argument name="fileSource" xsi:type="object">Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy</argument> - <argument name="pageLayoutFileSource" xsi:type="object">pageLayoutFileCollectorAggregated</argument> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Layout</argument> - <argument name="layoutCacheKey" xsi:type="object">Magento\Framework\View\Layout\LayoutCacheKeyInterface</argument> - </arguments> - </type> - <type name="CSSmin"> - <arguments> - <argument name="raise_php_limits" xsi:type="boolean">false</argument> - </arguments> - </type> - <type name="Magento\Framework\App\DefaultPath\DefaultPath"> - <arguments> - <argument name="parts" xsi:type="array"> - <item name="module" xsi:type="string">core</item> - <item name="controller" xsi:type="string">index</item> - <item name="action" xsi:type="string">index</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Data\Collection\Db\FetchStrategy\Cache"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Collection</argument> - <argument name="cacheIdPrefix" xsi:type="string">collection_</argument> - <argument name="cacheLifetime" xsi:type="string">86400</argument> - </arguments> - </type> - <type name="Magento\Framework\Event\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\Event\Config\Reader\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Asset\Collection" shared="false" /> - <virtualType name="layoutFileSourceBase" type="Magento\Framework\View\File\Collector\Base"> - <arguments> - <argument name="subDir" xsi:type="string">layout</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceBase</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceBaseFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="layoutFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"> - <arguments> - <argument name="subDir" xsi:type="string">layout</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceTheme</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceThemeFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="layoutFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> - <arguments> - <argument name="subDir" xsi:type="string">layout/override/base</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceOverrideBase</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceOverrideBaseFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="layoutFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> - <arguments> - <argument name="subDir" xsi:type="string">layout/override/theme</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceOverrideTheme</argument> - </arguments> - </virtualType> - <virtualType name="layoutFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">layoutFileSourceOverrideThemeFiltered</argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\File\Collector\Aggregated"> - <arguments> - <argument name="baseFiles" xsi:type="object">layoutFileSourceBaseSorted</argument> - <argument name="themeFiles" xsi:type="object">layoutFileSourceThemeSorted</argument> - <argument name="overrideBaseFiles" xsi:type="object">layoutFileSourceOverrideBaseSorted</argument> - <argument name="overrideThemeFiles" xsi:type="object">layoutFileSourceOverrideThemeSorted</argument> - </arguments> - </type> - <virtualType name="pageLayoutFileSourceBase" type="Magento\Framework\View\File\Collector\Base"> - <arguments> - <argument name="subDir" xsi:type="string">page_layout</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceBase</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceBaseFiltered</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"> - <arguments> - <argument name="subDir" xsi:type="string">page_layout</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceTheme</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceThemeFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageLayoutFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> - <arguments> - <argument name="subDir" xsi:type="string">page_layout/override/base</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideBase</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideBaseFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageLayoutFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> - <arguments> - <argument name="subDir" xsi:type="string">page_layout/override/theme</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideTheme</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideThemeFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageLayoutFileCollectorAggregated" type="Magento\Framework\View\Layout\File\Collector\Aggregated"> - <arguments> - <argument name="baseFiles" xsi:type="object">pageLayoutFileSourceBaseSorted</argument> - <argument name="themeFiles" xsi:type="object">pageLayoutFileSourceThemeSorted</argument> - <argument name="overrideBaseFiles" xsi:type="object">pageLayoutFileSourceOverrideBaseSorted</argument> - <argument name="overrideThemeFiles" xsi:type="object">pageLayoutFileSourceOverrideThemeSorted</argument> - </arguments> - </virtualType> - - <virtualType name="pageFileSourceBase" type="Magento\Framework\View\File\Collector\Base"/> - <virtualType name="pageFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceBase</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceBaseFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"/> - <virtualType name="pageFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceTheme</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceThemeFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> - <arguments> - <argument name="subDir" xsi:type="string">page/override</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceOverrideBase</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceOverrideBaseFiltered</argument> - </arguments> - </virtualType> - - <virtualType name="pageFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> - <arguments> - <argument name="subDir" xsi:type="string">override/theme</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceOverrideTheme</argument> - </arguments> - </virtualType> - <virtualType name="pageFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> - <arguments> - <argument name="subject" xsi:type="object">pageFileSourceOverrideThemeFiltered</argument> - </arguments> - </virtualType> - <virtualType name="pageLayoutRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> - <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Page\Layout\Reader"> - <arguments> - <argument name="pageLayoutFileSource" xsi:type="object">pageLayoutFileCollectorAggregated</argument> - <argument name="reader" xsi:type="object">pageLayoutRenderPool</argument> - </arguments> - </type> - <type name="Magento\Framework\View\PageLayout\File\Collector\Aggregated"> - <arguments> - <argument name="baseFiles" xsi:type="object">pageFileSourceBaseSorted</argument> - <argument name="themeFiles" xsi:type="object">pageFileSourceThemeSorted</argument> - <argument name="overrideBaseFiles" xsi:type="object">pageFileSourceOverrideBaseSorted</argument> - <argument name="overrideThemeFiles" xsi:type="object">pageFileSourceOverrideThemeSorted</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Design\Theme\Image"> - <arguments> - <argument name="uploader" xsi:type="object">Magento\Framework\View\Design\Theme\Image\Uploader\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Config\Initial"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\App\Config\Initial\Reader\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Config\Initial\Reader"> - <arguments> - <argument name="converter" xsi:type="object">Magento\Framework\App\Config\Initial\Converter</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Route\Config"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\App\Route\Config\Reader\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Session\Validator"> - <arguments> - <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> - <argument name="skippedUserAgentList" xsi:type="array"> - <item name="flash" xsi:type="string">Shockwave Flash</item> - <item name="flash_mac" xsi:type="string"><![CDATA[Adobe Flash Player\s{1,}\w{1,10}]]></item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\DataObject\Copy\Config"> - <arguments> - <argument name="dataStorage" xsi:type="object">Magento\Framework\DataObject\Copy\Config\Data\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\DataObject\Copy\Config\Reader"> - <arguments> - <argument name="fileName" xsi:type="string">fieldset.xml</argument> - <argument name="schemaLocator" xsi:type="object">Magento\Framework\DataObject\Copy\Config\SchemaLocator</argument> - </arguments> - </type> - <type name="Magento\Framework\DataObject\Copy\Config\SchemaLocator"> - <arguments> - <argument name="schema" xsi:type="string">urn:magento:framework:DataObject/etc/fieldset.xsd</argument> - <argument name="perFileSchema" xsi:type="string">urn:magento:framework:DataObject/etc/fieldset_file.xsd</argument> - </arguments> - </type> - <type name="Magento\Framework\DataObject\Copy\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\DataObject\Copy\Config\Reader\Proxy</argument> - <argument name="cacheId" xsi:type="string">fieldset_config</argument> - </arguments> - </type> - <type name="Magento\Framework\Image"> - <arguments> - <argument name="adapter" xsi:type="object">Magento\Framework\Image\Adapter\Gd2</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Layout\PageType\Config\Reader"> - <arguments> - <argument name="fileName" xsi:type="string">page_types.xml</argument> - <argument name="converter" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Converter</argument> - <argument name="schemaLocator" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\SchemaLocator</argument> - <argument name="defaultScope" xsi:type="string">frontend</argument> - </arguments> - </type> - <virtualType name="Magento\Framework\View\Layout\PageType\Config\Data" type="Magento\Framework\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Reader</argument> - <argument name="cacheId" xsi:type="string">page_types_config</argument> - </arguments> - </virtualType> - <type name="Magento\Framework\View\Layout\PageType\Config"> - <arguments> - <argument name="dataStorage" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Data</argument> - </arguments> - </type> - <virtualType name="Magento\Framework\Message\Session\Storage" type="Magento\Framework\Session\Storage"> - <arguments> - <argument name="namespace" xsi:type="string">message</argument> - </arguments> - </virtualType> - <type name="Magento\Framework\Message\Session"> - <arguments> - <argument name="storage" xsi:type="object">Magento\Framework\Message\Session\Storage</argument> - </arguments> - </type> - <type name="Magento\Framework\Url\ScopeResolver"> - <arguments> - <argument name="areaCode" xsi:type="string">frontend</argument> - </arguments> - </type> - - <type name="Magento\Framework\Module\ModuleList\Loader"> - <arguments> - <argument name="filesystemDriver" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> - </arguments> - </type> - <type name="Magento\Framework\Module\Setup\MigrationData"> - <arguments> - <argument name="data" xsi:type="array"> - <item name="plain" xsi:type="string"><![CDATA[/^(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)::.*?$/sui]]></item> - <item name="wiki" xsi:type="string"><![CDATA[/{{(block|widget).*?(class|type)=\"(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?}}/sui]]></item> - <item name="xml" xsi:type="string"><![CDATA[/<block.*?class=\"(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?>/sui]]></item> - <item name="serialized" xsi:type="string"><![CDATA[#(?P<string>s:\d+:"(?P<alias>[a-z]+[_a-z\d]*?/[a-z]+[_a-z\d]*?)")#sui]]></item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Webapi\Rest\Request\DeserializerFactory"> - <arguments> - <argument name="deserializers" xsi:type="array"> - <item name="application_json" xsi:type="array"> - <item name="type" xsi:type="string">application/json</item> - <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Json</item> - </item> - <item name="application_xml" xsi:type="array"> - <item name="type" xsi:type="string">application/xml</item> - <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> - </item> - <item name="application_xhtml_xml" xsi:type="array"> - <item name="type" xsi:type="string">application/xhtml+xml</item> - <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> - </item> - <item name="text_xml" xsi:type="array"> - <item name="type" xsi:type="string">text/xml</item> - <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Validator\Factory"> - <arguments> - <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> - </arguments> - </type> - <type name="Magento\Server\Reflection" shared="false" /> - <type name="Magento\Framework\Reflection\DataObjectProcessor"> - <arguments> - <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessor\Proxy</argument> - <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Url\Decoder"> - <arguments> - <argument name="urlBuilder" xsi:type="object">Magento\Framework\UrlInterface</argument> - </arguments> - </type> - <type name="Magento\Framework\Api\Search\SearchCriteriaBuilder" shared="false"/> - <type name="Magento\Framework\Api\Search\FilterGroupBuilder" shared="false"/> - <type name="Magento\Framework\Config\View"> - <arguments> - <argument name="fileName" xsi:type="string">view.xml</argument> - <argument name="converter" xsi:type="object">Magento\Framework\Config\Converter</argument> - <argument name="schemaLocator" xsi:type="object">Magento\Framework\Config\SchemaLocator</argument> - <argument name="fileResolver" xsi:type="object">Magento\Framework\Config\FileResolver</argument> - </arguments> - </type> - <type name="Magento\Framework\DB\SelectFactory"> - <arguments> - <argument name="selectRenderer" xsi:type="object">Magento\Framework\DB\Select\RendererProxy</argument> - </arguments> - </type> - <type name="Magento\Framework\Data\Form\Filter\Date"> - <arguments> - <argument name="localeResolver" xsi:type="object">Magento\Framework\Locale\ResolverInterface</argument> - </arguments> - </type> - <type name="Magento\Framework\DB\Select\SelectRenderer"> - <arguments> - <argument name="renderers" xsi:type="array"> - <item name="distinct" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\DistinctRenderer</item> - <item name="sort" xsi:type="string">100</item> - <item name="part" xsi:type="string">distinct</item> - </item> - <item name="columns" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\ColumnsRenderer</item> - <item name="sort" xsi:type="string">200</item> - <item name="part" xsi:type="string">columns</item> - </item> - <item name="union" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\UnionRenderer</item> - <item name="sort" xsi:type="string">300</item> - <item name="part" xsi:type="string">union</item> - </item> - <item name="from" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\FromRenderer</item> - <item name="sort" xsi:type="string">400</item> - <item name="part" xsi:type="string">from</item> - </item> - <item name="where" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\WhereRenderer</item> - <item name="sort" xsi:type="string">500</item> - <item name="part" xsi:type="string">where</item> - </item> - <item name="group" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\GroupRenderer</item> - <item name="sort" xsi:type="string">600</item> - <item name="part" xsi:type="string">group</item> - </item> - <item name="having" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\HavingRenderer</item> - <item name="sort" xsi:type="string">700</item> - <item name="part" xsi:type="string">having</item> - </item> - <item name="order" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\OrderRenderer</item> - <item name="sort" xsi:type="string">800</item> - <item name="part" xsi:type="string">order</item> - </item> - <item name="limit" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\LimitRenderer</item> - <item name="sort" xsi:type="string">900</item> - <item name="part" xsi:type="string">limitcount</item> - </item> - <item name="for_update" xsi:type="array"> - <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\ForUpdateRenderer</item> - <item name="sort" xsi:type="string">1000</item> - <item name="part" xsi:type="string">forupdate</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\EntityManager\OperationPool"> - <arguments> - <argument name="operations" xsi:type="array"> - <item name="default" xsi:type="array"> - <item name="checkIfExists" xsi:type="string">Magento\Framework\EntityManager\Operation\CheckIfExists</item> - <item name="read" xsi:type="string">Magento\Framework\EntityManager\Operation\Read</item> - <item name="create" xsi:type="string">Magento\Framework\EntityManager\Operation\Create</item> - <item name="update" xsi:type="string">Magento\Framework\EntityManager\Operation\Update</item> - <item name="delete" xsi:type="string">Magento\Framework\EntityManager\Operation\Delete</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\Cache\FlushCacheByTags"> - <arguments> - <argument name="cacheList" xsi:type="array"> - <item name="block_html" xsi:type="const">Magento\Framework\App\Cache\Type\Block::TYPE_IDENTIFIER</item> - <item name="collections" xsi:type="const">Magento\Framework\App\Cache\Type\Collection::TYPE_IDENTIFIER</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\EntityManager\CompositeMapper"> - <arguments> - <argument name="mappers" xsi:type="array"> - <item name="mapper" xsi:type="object">Magento\Framework\EntityManager\Mapper</item> - </argument> - </arguments> - </type> - <preference for="Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor" /> - <type name="Magento\Framework\Api\SearchCriteria\CollectionProcessor"> - <arguments> - <argument name="processors" xsi:type="array"> - <item name="filters" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor</item> - <item name="sorting" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\SortingProcessor</item> - <item name="pagination" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\PaginationProcessor</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\DB\Select\QueryModifierFactory"> - <arguments> - <argument name="queryModifiers" xsi:type="array"> - <item name="in" xsi:type="string">Magento\Framework\DB\Select\InQueryModifier</item> - <item name="like" xsi:type="string">Magento\Framework\DB\Select\LikeQueryModifier</item> - <item name="composite" xsi:type="string">Magento\Framework\DB\Select\CompositeQueryModifier</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\DB\TemporaryTableService"> - <arguments> - <argument name="allowedIndexMethods" xsi:type="array"> - <item name="HASH" xsi:type="string">HASH</item> - <item name="BTREE" xsi:type="string">BTREE</item> - </argument> - <argument name="allowedEngines" xsi:type="array"> - <item name="INNODB" xsi:type="string">INNODB</item> - <item name="MEMORY" xsi:type="string">MEMORY</item> - <item name="MYISAM" xsi:type="string">MYISAM</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\DB\FieldDataConverter"> - <arguments> - <argument name="envBatchSize" xsi:type="init_parameter">Magento\Framework\DB\FieldDataConverter::BATCH_SIZE_VARIABLE_NAME</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Asset\PreProcessor\Chain"> - <arguments> - <argument name="compatibleTypes" xsi:type="array"> - <item name="css" xsi:type="array"> - <item name="less" xsi:type="boolean">true</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\View\Asset\PreProcessor\Pool"> - <arguments> - <argument name="defaultPreprocessor" xsi:type="string">Magento\Framework\View\Asset\PreProcessor\Passthrough</argument> - </arguments> - </type> - <type name="Magento\Framework\App\View\Deployment\Version\Storage\File"> - <arguments> - <argument name="directoryCode" xsi:type="const">Magento\Framework\App\Filesystem\DirectoryList::STATIC_VIEW</argument> - <argument name="fileName" xsi:type="string">deployed_version.txt</argument> - </arguments> - </type> - <type name="Magento\Framework\Locale\Resolver"> - <arguments> - <argument name="defaultLocalePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_LOCALE</argument> - <argument name="scopeType" xsi:type="const">Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT</argument> - </arguments> - </type> - <type name="Magento\Framework\View\Element\Message\Renderer\RenderersPool"> - <arguments> - <argument name="renderers" xsi:type="array"> - <item name="escape_renderer" xsi:type="object">Magento\Framework\View\Element\Message\Renderer\EscapeRenderer</item> - <item name="block_renderer" xsi:type="object">Magento\Framework\View\Element\Message\Renderer\BlockRenderer</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> - <arguments> - <argument name="configurationsMap" xsi:type="array"> - <item name="default_message_identifier" xsi:type="array"> - <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\EscapeRenderer::CODE</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\DB\Logger\LoggerProxy"> - <arguments> - <argument name="loggerAlias" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_OUTPUT</argument> - <argument name="logAllQueries" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_LOG_EVERYTHING</argument> - <argument name="logQueryTime" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_QUERY_TIME_THRESHOLD</argument> - <argument name="logCallStack" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_INCLUDE_STACKTRACE</argument> - </arguments> - </type> - <type name="Magento\Framework\App\Config\MetadataConfigTypeProcessor"> - <arguments> - <argument name="configSource" xsi:type="object">Magento\Config\App\Config\Source\EnvironmentConfigSource</argument> - </arguments> - </type> - <type name="Magento\Framework\Message\ExceptionMessageFactoryPool"> - <arguments> - <argument name="defaultExceptionMessageFactory" xsi:type="object">Magento\Framework\Message\ExceptionMessageFactory</argument> - </arguments> - </type> - <type name="Magento\Framework\Mview\View\Subscription"> - <arguments> - <argument name="ignoredUpdateColumns" xsi:type="array"> - <item name="updated_at" xsi:type="string">updated_at</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Dto\ElementFactory"> - <arguments> - <argument name="typeFactories" xsi:type="array"> - <item name="table" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Table</item> - <item name="decimal" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> - <item name="float" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> - <item name="double" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> - <item name="smallint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> - <item name="tinyint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> - <item name="bigint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> - <item name="int" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> - <item name="date" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Date</item> - <item name="timestamp" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Timestamp</item> - <item name="datetime" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Timestamp</item> - <item name="longtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\LongText</item> - <item name="mediumtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumText</item> - <item name="text" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Text</item> - <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> - <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> - <item name="blob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Blob</item> - <item name="mediumblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumBlob</item> - <item name="longblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\LongBlob</item> - <item name="boolean" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Boolean</item> - <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Unique</item> - <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Primary</item> - <item name="foreign" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Foreign</item> - <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Index</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Db\DefinitionAggregator"> - <arguments> - <argument name="definitionProcessors" xsi:type="array"> - <item name="boolean" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Boolean</item> - <item name="int" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> - <item name="smallint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> - <item name="tinyint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> - <item name="bigint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> - <item name="decimal" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> - <item name="float" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> - <item name="double" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> - <item name="text" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="blob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="mediumblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="longblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="mediumtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="longtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> - <item name="datetime" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Timestamp</item> - <item name="date" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Date</item> - <item name="timestamp" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Timestamp</item> - <item name="char" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> - <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> - <item name="binary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> - <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> - <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Index</item> - <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> - <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> - <item name="constraint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> - <item name="reference" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\ForeignKey</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Operations\AddColumn"> - <arguments> - <argument name="triggers" xsi:type="array"> - <item name="migrateDataFromSameTable" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DDL\Triggers\MigrateDataFrom</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Declaration\ReaderComposite"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="xml" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\FileSystem\XmlReader</item> - </argument> - </arguments> - </type> - <virtualType name="Magento\Framework\Setup\Declaration\Schema\Config\SchemaLocator" type="Magento\Framework\Config\SchemaLocator"> - <arguments> - <argument name="realPath" xsi:type="string">urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd</argument> - </arguments> - </virtualType> - <virtualType name="Magento\Framework\Setup\Declaration\Schema\FileSystem\XmlReader" type="Magento\Framework\Config\Reader\Filesystem"> - <arguments> - <argument name="fileResolver" xsi:type="object">Magento\Framework\Config\FileResolverByModule</argument> - <argument name="converter" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Config\Converter</argument> - <argument name="schemaLocator" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Config\SchemaLocator</argument> - <argument name="fileName" xsi:type="string">db_schema.xml</argument> - <argument name="idAttributes" xsi:type="array"> - <item name="/schema/table" xsi:type="string">name</item> - <item name="/schema/table/column" xsi:type="string">name</item> - <item name="/schema/table/constraint" xsi:type="string">referenceId</item> - <item name="/schema/table/index" xsi:type="string">referenceId</item> - <item name="/schema/table/index/column" xsi:type="string">name</item> - <item name="/schema/table/constraint/column" xsi:type="string">name</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Framework\Setup\Declaration\Schema\OperationsExecutor"> - <arguments> - <argument name="operations" xsi:type="array"> - <item name="recreate_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ReCreateTable</item> - <item name="create_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\CreateTable</item> - <item name="drop_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropTable</item> - <item name="drop_reference" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropReference</item> - <item name="modify_column" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ModifyColumn</item> - <item name="add_column" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\AddColumn</item> - <item name="drop_element" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropElement</item> - <item name="add_complex_element" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\AddComplexElement</item> - <item name="modify_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ModifyTable</item> - </argument> - <argument name="dataSaviorsCollection" xsi:type="array"> - <item name="table_savior" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\DataSavior\TableSavior</item> - <item name="column_savior" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\DataSavior\ColumnSavior</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Sharding"> - <arguments> - <argument name="resources" xsi:type="array"> - <item name="default" xsi:type="string">default</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationComposite"> - <arguments> - <argument name="rules" xsi:type="array"> - <item name="check_references" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\CheckReferenceColumnHasIndex</item> - <item name="real_types" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\RealTypes</item> - <item name="check_primary_key" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\PrimaryKeyCanBeCreated</item> - <item name="inconsistence_references" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\IncosistentReferenceDefinition</item> - <item name="auto_increment_validation" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\AutoIncrementColumnValidation</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\SchemaListener"> - <arguments> - <argument name="definitionMappers" xsi:type="array"> - <item name="integer" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> - <item name="tinyint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> - <item name="smallint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> - <item name="mediumint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> - <item name="bigint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> - <item name="decimal" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> - <item name="float" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> - <item name="numeric" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> - <item name="text" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="mediumtext" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="longtext" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="blob" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="mediumblob" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="longblog" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="varbinary" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="varchar" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> - <item name="timestamp" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> - <item name="datetime" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> - <item name="date" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\DateDefinition</item> - <item name="boolean" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\BooleanDefinition</item> - </argument> - </arguments> - </type> - <virtualType name="\Magento\Framework\Setup\Patch\SchemaPatchReader" type="\Magento\Framework\Setup\Patch\PatchReader"> - <arguments> - <argument name="type" xsi:type="string">schema</argument> - </arguments> - </virtualType> - <virtualType name="\Magento\Framework\Setup\Patch\DataPatchReader" type="\Magento\Framework\Setup\Patch\PatchReader"> - <arguments> - <argument name="type" xsi:type="string">data</argument> - </arguments> - </virtualType> - <type name="\Magento\Framework\Setup\Patch\PatchApplier"> - <arguments> - <argument name="dataPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\DataPatchReader</argument> - <argument name="schemaPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\SchemaPatchReader</argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Patch\UpToDateData"> - <arguments> - <argument name="dataPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\DataPatchReader</argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Patch\UpToDateSchema"> - <arguments> - <argument name="schemaReader" xsi:type="object">\Magento\Framework\Setup\Patch\SchemaPatchReader</argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Config\CompositeReader"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="xmlReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Xml</item> - <item name="sortOrder" xsi:type="string">10</item> - </item> - <item name="envReader" xsi:type="array"> - <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Env</item> - <item name="sortOrder" xsi:type="string">20</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Config\Reader\Xml\CompositeConverter"> - <arguments> - <argument name="converters" xsi:type="array"> - <item name="topicConfig" xsi:type="array"> - <item name="converter" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Xml\Converter\TopicConfig</item> - <item name="sortOrder" xsi:type="string">20</item> - </item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Consumer\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">Magento\Framework\MessageQueue\Consumer\Config\CompositeReader</argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Consumer\Config\CompositeReader"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="xmlReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Consumer\Config\Xml\Reader</item> - <item name="envReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Consumer\Config\Env\Reader</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Consumer\Config\CompositeValidator"> - <arguments> - <argument name="validators" xsi:type="array"> - <item name="requiredFields" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Consumer\Config\Validator\RequiredFields</item> - <item name="fieldTypes" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Consumer\Config\Validator\FieldsTypes</item> - <item name="handlers" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Consumer\Config\Validator\Handlers</item> - <item name="consumerInstance" xsi:type="object" sortOrder="40">Magento\Framework\MessageQueue\Consumer\Config\Validator\ConsumerInstance</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Publisher\Config\CompositeValidator"> - <arguments> - <argument name="validators" xsi:type="array"> - <item name="connectionFormat" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Publisher\Config\Validator\Format</item> - <item name="enabledConnection" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Publisher\Config\Validator\EnabledConnection</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Publisher\Config\CompositeReader"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="asyncServiceReader" xsi:type="object" sortOrder="0">Magento\WebapiAsync\Code\Generator\Config\RemoteServiceReader\Publisher</item> - <item name="remoteServiceReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Publisher\Config\RemoteService\Reader</item> - <item name="xmlReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Publisher\Config\Xml\Reader</item> - <item name="envReader" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Publisher\Config\Env\Reader</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Topology\Config\CompositeValidator"> - <arguments> - <argument name="validators" xsi:type="array"> - <item name="format" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Topology\Config\Validator\Format</item> - <item name="fieldsTypes" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Topology\Config\Validator\FieldsTypes</item> - <item name="dependantFields" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Topology\Config\Validator\DependentFields</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Topology\Config\CompositeReader"> - <arguments> - <argument name="readers" xsi:type="array"> - <item name="remoteServiceReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Topology\Config\RemoteService\Reader</item> - <item name="xmlReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Topology\Config\Xml\Reader</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Amqp\Topology\BindingInstaller"> - <arguments> - <argument name="installers" xsi:type="array"> - <item name="queue" xsi:type="object">Magento\Framework\Amqp\Topology\BindingInstallerType\Queue</item> - <item name="exchange" xsi:type="object">Magento\Framework\Amqp\Topology\BindingInstallerType\Exchange</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Config\Reader\Env\Converter\Publisher"> - <arguments> - <argument name="connectionToExchangeMap" xsi:type="array"> - <item name="amqp" xsi:type="string">magento</item> - <item name="db" xsi:type="string">magento-db</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\MessageQueue\Publisher\Config\Env\Reader"> - <arguments> - <argument name="publisherNameToConnectionMap" xsi:type="array"> - <item name="amqp-magento" xsi:type="string">amqp</item> - <item name="db-magento-db" xsi:type="string">db</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Setup\Declaration\Schema\Operations\CreateTable"> - <arguments> - <argument name="triggers" xsi:type="array"> - <item name="migrateDataFromAnotherTable" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DDL\Triggers\MigrateDataFromAnotherTable</item> - </argument> - </arguments> - </type> - <!-- \Magento\Framework\MessageQueue\Bulk\PublisherPool is @api --> - <virtualType name="Magento\Framework\MessageQueue\Bulk\PublisherPool" type="Magento\Framework\MessageQueue\PublisherPool" /> - <type name="Magento\Framework\Session\Config"> - <arguments> - <argument name="scopeType" xsi:type="const">Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT</argument> - </arguments> - </type> - <virtualType name="CsrfRequestValidator" type="Magento\Framework\App\Request\CsrfValidator" /> - <virtualType name="RequestValidator" type="Magento\Framework\App\Request\CompositeValidator"> - <arguments> - <argument name="validators" xsi:type="array"> - <item name="csrf_validator" xsi:type="object">CsrfRequestValidator</item> - <item name="http_method_validator" xsi:type="object"> - Magento\Framework\App\Request\HttpMethodValidator - </item> - </argument> - </arguments> - </virtualType> - <preference for="Magento\Framework\App\Request\ValidatorInterface" type="RequestValidator" /> - <type name="Magento\Framework\App\Request\HttpMethodMap"> - <arguments> - <argument name="map" xsi:type="array"> - <item name="OPTIONS" xsi:type="string">\Magento\Framework\App\Action\HttpOptionsActionInterface</item> - <item name="GET" xsi:type="string">\Magento\Framework\App\Action\HttpGetActionInterface</item> - <item name="HEAD" xsi:type="string">\Magento\Framework\App\Action\HttpGetActionInterface</item> - <item name="POST" xsi:type="string">\Magento\Framework\App\Action\HttpPostActionInterface</item> - <item name="PUT" xsi:type="string">\Magento\Framework\App\Action\HttpPutActionInterface</item> - <item name="PATCH" xsi:type="string">\Magento\Framework\App\Action\HttpPatchActionInterface</item> - <item name="DELETE" xsi:type="string">\Magento\Framework\App\Action\HttpDeleteActionInterface</item> - <item name="CONNECT" xsi:type="string">\Magento\Framework\App\Action\HttpConnectActionInterface</item> - <item name="PROPFIND" xsi:type="string">\Magento\Framework\App\Action\HttpPropfindActionInterface</item> - <item name="TRACE" xsi:type="string">\Magento\Framework\App\Action\HttpTraceActionInterface</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\App\ScopeResolverPool"> - <arguments> - <argument name="scopeResolvers" xsi:type="array"> - <item name="default" xsi:type="object">Magento\Framework\App\ScopeResolver</item> - </argument> - </arguments> - </type> - <type name="Magento\Framework\Cache\LockGuardedCacheLoader"> - <arguments> - <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> - <argument name="lockTimeout" xsi:type="number">10000</argument> - <argument name="delayTimeout" xsi:type="number">20</argument> - </arguments> - </type> - <preference for="Magento\Framework\HTTP\AsyncClientInterface" type="Magento\Framework\HTTP\AsyncClient\GuzzleAsyncClient" /> - <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompareInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompare"/> - <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillPutInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillPut"/> - <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillReadInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillRead"/> - <preference for="Magento\Framework\MessageQueue\CallbackInvokerInterface" type="Magento\Framework\MessageQueue\CallbackInvoker"/> - <preference for="Magento\Framework\Mail\EmailMessageInterface" - type="Magento\Framework\Mail\EmailMessage" /> - <preference for="Magento\Framework\Mail\MimeMessageInterface" - type="Magento\Framework\Mail\MimeMessage" /> - <preference for="Magento\Framework\Mail\MimePartInterface" - type="Magento\Framework\Mail\MimePart" /> - <type name="Magento\Framework\DB\Adapter\AdapterInterface"> - <plugin name="execute_commit_callbacks" type="Magento\Framework\Model\ExecuteCommitCallbacks" /> - </type> -</config> diff --git a/app/etc/registration_globlist.php b/app/etc/registration_globlist.php deleted file mode 100755 index 23caae00cb303..0000000000000 --- a/app/etc/registration_globlist.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * Glob patterns relative to the project root directory, used by - * registration.php to generate a list of includes. - */ -return [ - 'app/code/*/*/cli_commands.php', - 'app/code/*/*/registration.php', - 'app/design/*/*/*/registration.php', - 'app/i18n/*/*/registration.php', - 'lib/internal/*/*/registration.php', - 'lib/internal/*/*/*/registration.php', - 'setup/src/*/*/registration.php' -]; diff --git a/app/etc/vendor_path.php b/app/etc/vendor_path.php deleted file mode 100755 index b6af03ef32fa4..0000000000000 --- a/app/etc/vendor_path.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php -/** - * Path to Composer vendor directory - */ -return './vendor'; diff --git a/generated/.htaccess b/generated/.htaccess deleted file mode 100755 index 707c26b075e16..0000000000000 --- a/generated/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - diff --git a/pub/media/.htaccess b/pub/media/.htaccess deleted file mode 100755 index d68d163d7a6b5..0000000000000 --- a/pub/media/.htaccess +++ /dev/null @@ -1,134 +0,0 @@ -Options -Indexes - -<IfModule mod_php5.c> -php_flag engine 0 -</IfModule> - -<IfModule mod_php7.c> -php_flag engine 0 -</IfModule> - -AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi -Options -ExecCGI - -<FilesMatch ".+\.(ph(p[3457]?|t|tml)|[aj]sp|p[ly]|sh|cgi|shtml?|html?)$"> -SetHandler default-handler -</FilesMatch> - -<IfModule mod_rewrite.c> - -############################################ -## enable rewrites - - Options +FollowSymLinks - RewriteEngine on - - ## you can put here your pub/media folder path relative to web root - #RewriteBase /magento/pub/media/ - -############################################ -## never rewrite for existing files - RewriteCond %{REQUEST_FILENAME} !-f - -############################################ -## rewrite everything else to get.php - - RewriteRule .* ../get.php [L] -</IfModule> - -############################################ -## setting MIME types - -# JavaScript -AddType application/javascript js jsonp -AddType application/json json - -# CSS -AddType text/css css - -# Images and icons -AddType image/x-icon ico -AddType image/gif gif -AddType image/png png -AddType image/jpeg jpg -AddType image/jpeg jpeg - -# SVG -AddType image/svg+xml svg - -# Fonts -AddType application/vnd.ms-fontobject eot -AddType application/x-font-ttf ttf -AddType application/x-font-otf otf -AddType application/x-font-woff woff -AddType application/font-woff2 woff2 - -# Flash -AddType application/x-shockwave-flash swf - -# Archives and exports -AddType application/zip gzip -AddType application/x-gzip gz gzip -AddType application/x-bzip2 bz2 -AddType text/csv csv -AddType application/xml xml - -<IfModule mod_headers.c> - - <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$> - Header append Cache-Control public - </FilesMatch> - - <FilesMatch .*\.(zip|gz|gzip|bz2|csv|xml)$> - Header append Cache-Control no-store - </FilesMatch> - -</IfModule> - -<IfModule mod_expires.c> - -############################################ -## Add default Expires header -## http://developer.yahoo.com/performance/rules.html#expires - - ExpiresActive On - - # Data - <FilesMatch \.(zip|gz|gzip|bz2|csv|xml)$> - ExpiresDefault "access plus 0 seconds" - </FilesMatch> - ExpiresByType text/xml "access plus 0 seconds" - ExpiresByType text/csv "access plus 0 seconds" - ExpiresByType application/json "access plus 0 seconds" - ExpiresByType application/zip "access plus 0 seconds" - ExpiresByType application/x-gzip "access plus 0 seconds" - ExpiresByType application/x-bzip2 "access plus 0 seconds" - - # CSS, JavaScript - <FilesMatch \.(css|js)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType text/css "access plus 1 year" - ExpiresByType application/javascript "access plus 1 year" - - # Favicon, images, flash - <FilesMatch \.(ico|gif|png|jpg|jpeg|swf|svg)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType image/gif "access plus 1 year" - ExpiresByType image/png "access plus 1 year" - ExpiresByType image/jpg "access plus 1 year" - ExpiresByType image/jpeg "access plus 1 year" - ExpiresByType image/svg+xml "access plus 1 year" - - # Fonts - <FilesMatch \.(eot|ttf|otf|svg|woff|woff2)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType application/vnd.ms-fontobject "access plus 1 year" - ExpiresByType application/x-font-ttf "access plus 1 year" - ExpiresByType application/x-font-otf "access plus 1 year" - ExpiresByType application/x-font-woff "access plus 1 year" - ExpiresByType application/font-woff2 "access plus 1 year" - -</IfModule> diff --git a/pub/media/customer/.htaccess b/pub/media/customer/.htaccess deleted file mode 100755 index 707c26b075e16..0000000000000 --- a/pub/media/customer/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - diff --git a/pub/media/downloadable/.htaccess b/pub/media/downloadable/.htaccess deleted file mode 100755 index 707c26b075e16..0000000000000 --- a/pub/media/downloadable/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - diff --git a/pub/media/import/.htaccess b/pub/media/import/.htaccess deleted file mode 100755 index 707c26b075e16..0000000000000 --- a/pub/media/import/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - diff --git a/pub/media/theme_customization/.htaccess b/pub/media/theme_customization/.htaccess deleted file mode 100755 index 2b93da6b4c079..0000000000000 --- a/pub/media/theme_customization/.htaccess +++ /dev/null @@ -1,10 +0,0 @@ -Options -Indexes -<Files ~ "\.xml$"> - <IfVersion < 2.4> - order allow,deny - deny from all - </IfVersion> - <IfVersion >= 2.4> - Require all denied - </IfVersion> -</Files> diff --git a/pub/static/.htaccess b/pub/static/.htaccess deleted file mode 100755 index a5aa6fb0d5cfd..0000000000000 --- a/pub/static/.htaccess +++ /dev/null @@ -1,133 +0,0 @@ -<IfModule mod_php5.c> -php_flag engine 0 -</IfModule> - -<IfModule mod_php7.c> -php_flag engine 0 -</IfModule> - -# To avoid situation when web server automatically adds extension to path -Options -MultiViews - -<IfModule mod_rewrite.c> - RewriteEngine On - - ## you can put here your pub/static folder path relative to web root - #RewriteBase /magento/pub/static/ - - # Remove signature of the static files that is used to overcome the browser cache - RewriteRule ^version.+?/(.+)$ $1 [L] - - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-l - - RewriteRule .* ../static.php?resource=$0 [L] - # Detects if moxieplayer request with uri params and redirects to uri without params - <Files moxieplayer.swf> - RewriteCond %{QUERY_STRING} !^$ - RewriteRule ^(.*)$ %{REQUEST_URI}? [R=301,L] - </Files> -</IfModule> - -############################################ -## setting MIME types - -# JavaScript -AddType application/javascript js jsonp -AddType application/json json - -# HTML - -AddType text/html html - -# CSS -AddType text/css css - -# Images and icons -AddType image/x-icon ico -AddType image/gif gif -AddType image/png png -AddType image/jpeg jpg -AddType image/jpeg jpeg - -# SVG -AddType image/svg+xml svg - -# Fonts -AddType application/vnd.ms-fontobject eot -AddType application/x-font-ttf ttf -AddType application/x-font-otf otf -AddType application/x-font-woff woff -AddType application/font-woff2 woff2 - -# Flash -AddType application/x-shockwave-flash swf - -# Archives and exports -AddType application/zip gzip -AddType application/x-gzip gz gzip -AddType application/x-bzip2 bz2 -AddType text/csv csv -AddType application/xml xml - -<IfModule mod_headers.c> - - <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$> - Header append Cache-Control public - </FilesMatch> - - <FilesMatch .*\.(zip|gz|gzip|bz2|csv|xml)$> - Header append Cache-Control no-store - </FilesMatch> - -</IfModule> - -<IfModule mod_expires.c> - -############################################ -## Add default Expires header -## http://developer.yahoo.com/performance/rules.html#expires - - ExpiresActive On - - # Data - <FilesMatch \.(zip|gz|gzip|bz2|csv|xml)$> - ExpiresDefault "access plus 0 seconds" - </FilesMatch> - ExpiresByType text/xml "access plus 0 seconds" - ExpiresByType text/csv "access plus 0 seconds" - ExpiresByType application/json "access plus 0 seconds" - ExpiresByType application/zip "access plus 0 seconds" - ExpiresByType application/x-gzip "access plus 0 seconds" - ExpiresByType application/x-bzip2 "access plus 0 seconds" - - # CSS, JavaScript, html - <FilesMatch \.(css|js|html|json)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType text/css "access plus 1 year" - ExpiresByType text/html "access plus 1 year" - ExpiresByType application/javascript "access plus 1 year" - ExpiresByType application/json "access plus 1 year" - - # Favicon, images, flash - <FilesMatch \.(ico|gif|png|jpg|jpeg|swf|svg)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType image/gif "access plus 1 year" - ExpiresByType image/png "access plus 1 year" - ExpiresByType image/jpg "access plus 1 year" - ExpiresByType image/jpeg "access plus 1 year" - ExpiresByType image/svg+xml "access plus 1 year" - - # Fonts - <FilesMatch \.(eot|ttf|otf|svg|woff|woff2)$> - ExpiresDefault "access plus 1 year" - </FilesMatch> - ExpiresByType application/vnd.ms-fontobject "access plus 1 year" - ExpiresByType application/x-font-ttf "access plus 1 year" - ExpiresByType application/x-font-otf "access plus 1 year" - ExpiresByType application/x-font-woff "access plus 1 year" - ExpiresByType application/font-woff2 "access plus 1 year" - -</IfModule> diff --git a/var/.htaccess b/var/.htaccess deleted file mode 100755 index 707c26b075e16..0000000000000 --- a/var/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - From 437cbf0a81b7f8045579a357529c4f9eecf7ec32 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 21 Sep 2019 12:16:00 -0400 Subject: [PATCH 069/734] suppress error about "sessions or cookies while not being a part of HTML Presentation layer" (#22833) --- app/code/Magento/Security/Model/UserExpirationManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index 44e8226e391ed..aa3ac177bb914 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -11,6 +11,7 @@ /** * Class to handle admin user expirations. + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class UserExpirationManager { From 60f5710e0c2789187082ed96d8f24a416a426fa4 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 22 Sep 2019 17:08:45 -0400 Subject: [PATCH 070/734] add MFTF test for creating new user with valid expiration date (#22833) --- ...teNewUserWithUserExpirationActionGroup.xml | 34 +++++++++++++++++++ .../Security/Test/Mftf/Data/UserData.xml | 29 ++++++++++++++++ .../Mftf/Section/AdminEditUserSection.xml | 13 +++++++ .../Mftf/Section/AdminNewUserFormSection.xml | 13 +++++++ ...erCreateNewUserWithValidExpirationTest.xml | 30 ++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Data/UserData.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml new file mode 100644 index 0000000000000..a0250ca3ef4fc --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml @@ -0,0 +1,34 @@ +<?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="AdminCreateNewUserWithUserExpirationActionGroup"> + <annotations> + <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided User with an expiration date and saves.</description> + </annotations> + <arguments> + <argument name="user" defaultValue="NewAdminUserWithValidExpiration"/> + </arguments> + + <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> + <waitForPageLoad stepKey="waitForUsersPage" /> + <fillField selector="{{AdminNewUserFormSection.username}}" userInput="{{user.username}}" stepKey="enterUserName" /> + <fillField selector="{{AdminNewUserFormSection.firstname}}" userInput="{{user.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{AdminNewUserFormSection.lastname}}" userInput="{{user.lastName}}" stepKey="enterLastName" /> + <fillField selector="{{AdminNewUserFormSection.email}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail" /> + <fillField selector="{{AdminNewUserFormSection.password}}" userInput="{{user.password}}" stepKey="enterPassword" /> + <fillField selector="{{AdminNewUserFormSection.passwordConfirmation}}" userInput="{{user.password}}" stepKey="confirmPassword" /> + <fillField selector="{{AdminNewUserFormSection.userExpiresField}}" userInput="{{user.expires_at}}" stepKey="enterUserExpiration" /> + <fillField selector="{{AdminNewUserFormSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Data/UserData.xml b/app/code/Magento/Security/Test/Mftf/Data/UserData.xml new file mode 100644 index 0000000000000..5256d2ca4a28e --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Data/UserData.xml @@ -0,0 +1,29 @@ +<?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="NewAdminUserWithValidExpiration" type="user"> + <data key="username" unique="suffix">adminExpired</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">true</data> + <data key="is_active_label">Active</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <data key="expires_at">Sep 3, 2030 4:42:36 PM</data> + <array key="roles"> + <item>1</item> + </array> + </entity> +</entities> diff --git a/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml b/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml new file mode 100644 index 0000000000000..d7acf2466c09e --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml @@ -0,0 +1,13 @@ +<?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="AdminEditUserSection"> + <element name="userExpiresField" type="input" selector="#user_expires_at"/> + </section> +</sections> diff --git a/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml new file mode 100644 index 0000000000000..1d1aba4da07dd --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -0,0 +1,13 @@ +<?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="AdminNewUserFormSection"> + <element name="userExpiresField" type="input" selector="#page_tabs_main_section_content input[name='expires_at']"/> + </section> +</sections> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml new file mode 100644 index 0000000000000..f84d9baa8ebfe --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml @@ -0,0 +1,30 @@ +<?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="AdminUserCreateNewUserWithExpirationTest"> + <annotations> + <features value="Security"/> + <stories value="Create new user with expiration date."/> + <title value="Create new user with expiration date"/> + <description value="Create new user with expiration date."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + <before> + <!-- Log in to Admin Panel --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <!-- Create new user with user expiration set --> + <actionGroup ref="AdminCreateNewUserWithUserExpirationActionGroup" stepKey="createNewUserWithValidExpiration"> + <argument name="user" value="NewAdminUserWithValidExpiration" /> + </actionGroup> + </test> +</tests> From 9e82e91bbcf4db0fe4815d774bdb03cc4fcd75cc Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 22 Sep 2019 17:51:40 -0400 Subject: [PATCH 071/734] add MFTF test for creating new user with invalid expiration date (#22833) --- ...reateNewUserWithExpirationActionGroup.xml} | 5 +-- ...inSaveUserInvalidExpirationActionGroup.xml | 16 ++++++++++ .../AdminSaveUserSuccessActionGroup.xml | 16 ++++++++++ .../Security/Test/Mftf/Data/UserData.xml | 7 +++- ...CreateNewUserWithInvalidExpirationTest.xml | 32 +++++++++++++++++++ ...erCreateNewUserWithValidExpirationTest.xml | 14 ++++---- 6 files changed, 79 insertions(+), 11 deletions(-) rename app/code/Magento/Security/Test/Mftf/ActionGroup/{AdminCreateNewUserWithUserExpirationActionGroup.xml => AdminCreateNewUserWithExpirationActionGroup.xml} (93%) create mode 100644 app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml similarity index 93% rename from app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml rename to app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml index a0250ca3ef4fc..5ebd9d723ed26 100644 --- a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithUserExpirationActionGroup.xml +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml @@ -5,10 +5,9 @@ * 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="AdminCreateNewUserWithUserExpirationActionGroup"> + <actionGroup name="AdminCreateNewUserWithExpirationActionGroup"> <annotations> <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided User with an expiration date and saves.</description> </annotations> @@ -28,7 +27,5 @@ <fillField selector="{{AdminNewUserFormSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> <waitForPageLoad stepKey="waitForSaveTheUser" /> - <see userInput="You saved the user." stepKey="seeSuccessMessage" /> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml new file mode 100644 index 0000000000000..8fc890acebf84 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminSaveUserInvalidExpirationActionGroup"> + <annotations> + <description>Error message for saving an admin user with an invalid expiration date.</description> + </annotations> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='"Expiration date" must be later than the current date.' stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml new file mode 100644 index 0000000000000..5c9b520a19a40 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminSaveUserSuccessActionGroup"> + <annotations> + <description>Success message for saving an admin user successfully.</description> + </annotations> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Data/UserData.xml b/app/code/Magento/Security/Test/Mftf/Data/UserData.xml index 5256d2ca4a28e..43c75da1c37ee 100644 --- a/app/code/Magento/Security/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/Security/Test/Mftf/Data/UserData.xml @@ -8,8 +8,9 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NewAdminUserWithValidExpiration" type="user"> - <data key="username" unique="suffix">adminExpired</data> + <data key="username" unique="suffix">adminExpiresAt</data> <data key="firstname">John</data> <data key="lastname">Doe</data> <data key="email" unique="prefix">admin@example.com</data> @@ -26,4 +27,8 @@ <item>1</item> </array> </entity> + + <entity name="NewAdminUserWithInvalidExpiration" type="user" extends="NewAdminUserWithValidExpiration"> + <data key="expires_at">Sep 3, 2000 4:42:36 PM</data> + </entity> </entities> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml new file mode 100644 index 0000000000000..18c999a89b2b1 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml @@ -0,0 +1,32 @@ +<?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="AdminUserCreateNewUserWithInvalidExpirationTest"> + <annotations> + <features value="Security"/> + <stories value="Create new user with invalid expiration date."/> + <title value="Create new user with invalid expiration date"/> + <description value="Create new user with invalid expiration date."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security_userexpiration"/> + </annotations> + <before> + <!-- Log in to Admin Panel --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <!-- Create new user with user expiration set --> + <actionGroup ref="AdminCreateNewUserWithExpirationActionGroup" stepKey="fillInNewUserWithInvalidExpiration"> + <argument name="user" value="NewAdminUserWithInvalidExpiration" /> + </actionGroup> + + <actionGroup ref="AdminSaveUserInvalidExpirationActionGroup" stepKey="saveNewUserWithInvalidExpirationError" /> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml index f84d9baa8ebfe..cfc94920a13b6 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml @@ -7,15 +7,15 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUserCreateNewUserWithExpirationTest"> + <test name="AdminUserCreateNewUserWithValidExpirationTest"> <annotations> <features value="Security"/> - <stories value="Create new user with expiration date."/> - <title value="Create new user with expiration date"/> - <description value="Create new user with expiration date."/> + <stories value="Create new user with valid expiration date."/> + <title value="Create new user with valid expiration date"/> + <description value="Create new user with valid expiration date."/> <testCaseId value="" /> <severity value="CRITICAL"/> - <group value="security"/> + <group value="security_userexpiration"/> </annotations> <before> <!-- Log in to Admin Panel --> @@ -23,8 +23,10 @@ </before> <!-- Create new user with user expiration set --> - <actionGroup ref="AdminCreateNewUserWithUserExpirationActionGroup" stepKey="createNewUserWithValidExpiration"> + <actionGroup ref="AdminCreateNewUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="user" value="NewAdminUserWithValidExpiration" /> </actionGroup> + + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess" /> </test> </tests> From d4bcc7f18efa9f3e9feb4d837872c2f0c9162431 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Mon, 23 Sep 2019 10:50:49 +0530 Subject: [PATCH 072/734] file permission change for account.php and create.php file --- .../Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php | 0 app/code/Magento/Sales/Model/AdminOrder/Create.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php mode change 100755 => 100644 app/code/Magento/Sales/Model/AdminOrder/Create.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php old mode 100755 new mode 100644 diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php old mode 100755 new mode 100644 From 4e4e6a296ab4176cdf1c525a0128a66137147451 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Mon, 23 Sep 2019 12:49:37 +0530 Subject: [PATCH 073/734] added required discared files --- app/etc/NonComposerComponentRegistration.php | 33 + app/etc/db_schema.xml | 17 + app/etc/di.xml | 1780 ++++++++++++++++++ app/etc/registration_globlist.php | 19 + app/etc/vendor_path.php | 5 + generated/.htaccess | 8 + pub/media/.htaccess | 134 ++ pub/media/customer/.htaccess | 8 + pub/media/downloadable/.htaccess | 8 + pub/media/import/.htaccess | 8 + pub/media/theme_customization/.htaccess | 10 + pub/static/.htaccess | 133 ++ var/.htaccess | 8 + 13 files changed, 2171 insertions(+) create mode 100644 app/etc/NonComposerComponentRegistration.php create mode 100644 app/etc/db_schema.xml create mode 100644 app/etc/di.xml create mode 100644 app/etc/registration_globlist.php create mode 100644 app/etc/vendor_path.php create mode 100644 generated/.htaccess create mode 100644 pub/media/.htaccess create mode 100644 pub/media/customer/.htaccess create mode 100644 pub/media/downloadable/.htaccess create mode 100644 pub/media/import/.htaccess create mode 100644 pub/media/theme_customization/.htaccess create mode 100644 pub/static/.htaccess create mode 100644 var/.htaccess diff --git a/app/etc/NonComposerComponentRegistration.php b/app/etc/NonComposerComponentRegistration.php new file mode 100644 index 0000000000000..a7377ebfca3af --- /dev/null +++ b/app/etc/NonComposerComponentRegistration.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +//Register components (via a list of glob patterns) +namespace Magento\NonComposerComponentRegistration; + +use RuntimeException; + +/** + * Include files from a list of glob patterns + * + * @throws RuntimeException + * @return void + */ +$main = function () +{ + $globPatterns = require __DIR__ . '/registration_globlist.php'; + $baseDir = dirname(dirname(__DIR__)) . '/'; + + foreach ($globPatterns as $globPattern) { + // Sorting is disabled intentionally for performance improvement + $files = glob($baseDir . $globPattern, GLOB_NOSORT); + if ($files === false) { + throw new RuntimeException("glob(): error with '$baseDir$globPattern'"); + } + array_map(function ($file) { require_once $file; }, $files); + } +}; + +$main(); diff --git a/app/etc/db_schema.xml b/app/etc/db_schema.xml new file mode 100644 index 0000000000000..d7af9091b238d --- /dev/null +++ b/app/etc/db_schema.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> + <table name="patch_list" resource="default" comment="List of data/schema patches"> + <column xsi:type="int" name="patch_id" identity="true" comment="Patch Auto Increment" /> + <column xsi:type="varchar" name="patch_name" length="1024" nullable="false" comment="Patch Class Name" /> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="patch_id" /> + </constraint> + </table> +</schema> diff --git a/app/etc/di.xml b/app/etc/di.xml new file mode 100644 index 0000000000000..1a74fd9d7f840 --- /dev/null +++ b/app/etc/di.xml @@ -0,0 +1,1780 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="DateTimeInterface" type="DateTime" /> + <preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\Monolog" /> + <preference for="Magento\Framework\EntityManager\EntityMetadataInterface" type="Magento\Framework\EntityManager\EntityMetadata" /> + <preference for="Magento\Framework\EntityManager\HydratorInterface" type="Magento\Framework\EntityManager\Hydrator" /> + <preference for="Magento\Framework\View\Template\Html\MinifierInterface" type="Magento\Framework\View\Template\Html\Minifier" /> + <preference for="Magento\Framework\Model\Entity\ScopeInterface" type="Magento\Framework\Model\Entity\Scope" /> + <preference for="Magento\Framework\ObjectManager\FactoryInterface" type="Magento\Framework\ObjectManager\Factory\Dynamic\Developer" /> + <preference for="Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface" type="Magento\Framework\Search\Adapter\Mysql\Filter\Preprocessor" /> + <preference for="Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface" type="Magento\Framework\Search\Adapter\Mysql\Field\Resolver" /> + <preference for="Magento\Framework\Search\Request\Aggregation\StatusInterface" type="Magento\Framework\Search\Request\Aggregation\Status" /> + <preference for="Magento\Framework\Search\Adapter\Mysql\Field\FieldInterface" type="Magento\Framework\Search\Adapter\Mysql\Field\Field"/> + <preference for="Magento\Framework\Search\Adapter\Aggregation\AggregationResolverInterface" type="Magento\Framework\Search\Adapter\Aggregation\AggregationResolver"/> + <preference for="Magento\Framework\App\RequestInterface" type="Magento\Framework\App\Request\Http" /> + <preference for="Magento\Framework\App\PlainTextRequestInterface" type="Magento\Framework\App\Request\Http" /> + <preference for="Magento\Framework\App\RequestContentInterface" type="Magento\Framework\App\Request\Http" /> + <preference for="Magento\Framework\App\Request\PathInfoProcessorInterface" type="Magento\Store\App\Request\PathInfoProcessor" /> + <preference for="Magento\Framework\App\ResponseInterface" type="Magento\Framework\App\Response\Http" /> + <preference for="Magento\Framework\App\RouterListInterface" type="Magento\Framework\App\RouterList" /> + <preference for="Magento\Framework\App\FrontControllerInterface" type="Magento\Framework\App\FrontController" /> + <preference for="Magento\Framework\App\CacheInterface" type="Magento\Framework\App\Cache\Proxy" /> + <preference for="Magento\Framework\App\Cache\StateInterface" type="Magento\Framework\App\Cache\State" /> + <preference for="Magento\Framework\App\Cache\TypeListInterface" type="Magento\Framework\App\Cache\TypeList" /> + <preference for="Magento\Framework\App\ObjectManager\ConfigWriterInterface" type="Magento\Framework\App\ObjectManager\ConfigWriter\Filesystem" /> + <preference for="Magento\Store\Model\StoreManagerInterface" type="Magento\Store\Model\StoreManager" /> + <preference for="Magento\Framework\View\DesignInterface" type="Magento\Theme\Model\View\Design\Proxy" /> + <preference for="Magento\Framework\View\Design\ThemeInterface" type="Magento\Theme\Model\Theme" /> + <preference for="Magento\Framework\View\Design\Theme\ResolverInterface" type="Magento\Theme\Model\Theme\Resolver" /> + <preference for="Magento\Framework\View\ConfigInterface" type="Magento\Framework\View\Config" /> + <preference for="Magento\Framework\View\Asset\Bundle\ConfigInterface" type="Magento\Framework\View\Asset\Bundle\Config" /> + <preference for="Magento\Framework\Locale\ListsInterface" type="Magento\Framework\Locale\TranslatedLists" /> + <preference for="Magento\Framework\Locale\AvailableLocalesInterface" type="Magento\Framework\Locale\Deployed\Codes" /> + <preference for="Magento\Framework\Locale\OptionInterface" type="Magento\Framework\Locale\Deployed\Options" /> + <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Proxy" /> + <preference for="Magento\Framework\Api\AttributeTypeResolverInterface" type="Magento\Framework\Reflection\AttributeTypeResolver" /> + <preference for="Magento\Framework\Api\Search\SearchResultInterface" type="Magento\Framework\Api\Search\SearchResult" /> + <preference for="Magento\Framework\Api\Search\SearchCriteriaInterface" type="Magento\Framework\Api\Search\SearchCriteria"/> + <preference for="Magento\Framework\Api\Search\DocumentInterface" type="Magento\Framework\Api\Search\Document" /> + <preference for="Magento\Framework\Api\Search\AggregationInterface" type="Magento\Framework\Search\Response\Aggregation" /> + <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" /> + <type name="Magento\Store\Model\Store"> + <arguments> + <argument name="currencyInstalled" xsi:type="string">system/currency/installed</argument> + </arguments> + </type> + <preference for="Magento\Framework\Api\ExtensionAttribute\JoinDataInterface" type="Magento\Framework\Api\ExtensionAttribute\JoinData" /> + <preference for="Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface" type="Magento\Framework\Api\ExtensionAttribute\JoinProcessor" /> + <preference for="Magento\Framework\Locale\ConfigInterface" type="Magento\Framework\Locale\Config" /> + <preference for="Magento\Framework\Notification\NotifierInterface" type="Magento\Framework\Notification\NotifierPool" /> + <preference for="Magento\Framework\UrlInterface" type="Magento\Framework\Url" /> + <preference for="Magento\Framework\Url\EncoderInterface" type="Magento\Framework\Url\Encoder" /> + <preference for="Magento\Framework\Url\DecoderInterface" type="Magento\Framework\Url\Decoder" /> + <preference for="Magento\Framework\Data\Collection\Db\FetchStrategyInterface" type="Magento\Framework\Data\Collection\Db\FetchStrategy\Query" /> + <preference for="Magento\Framework\Config\ScopeInterface" type="Magento\Framework\Config\Scope" /> + <preference for="Magento\Framework\Config\FileResolverInterface" type="Magento\Framework\App\Config\FileResolver" /> + <preference for="Magento\Framework\Config\CacheInterface" type="Magento\Framework\App\Cache\Type\Config" /> + <preference for="Magento\Framework\Config\ValidationStateInterface" type="Magento\Framework\App\Arguments\ValidationState" /> + <preference for="Magento\Framework\Module\ModuleListInterface" type="Magento\Framework\Module\ModuleList" /> + <preference for="Magento\Framework\Module\ModuleManagerInterface" type="Magento\Framework\Module\Manager" /> + <preference for="Magento\Framework\Component\ComponentRegistrarInterface" type="Magento\Framework\Component\ComponentRegistrar"/> + <preference for="Magento\Framework\Event\ConfigInterface" type="Magento\Framework\Event\Config" /> + <preference for="Magento\Framework\Event\InvokerInterface" type="Magento\Framework\Event\Invoker\InvokerDefault" /> + <preference for="Magento\Framework\Interception\PluginListInterface" type="Magento\Framework\Interception\PluginList\PluginList" /> + <preference for="Magento\Framework\Event\ManagerInterface" type="Magento\Framework\Event\Manager\Proxy" /> + <preference for="Magento\Framework\View\LayoutInterface" type="Magento\Framework\View\Layout" /> + <preference for="Magento\Framework\View\Layout\ProcessorInterface" type="Magento\Framework\View\Model\Layout\Merge" /> + <preference for="Magento\Framework\View\Layout\LayoutCacheKeyInterface" type="Magento\Framework\View\Model\Layout\CacheKey" /> + <preference for="Magento\Framework\View\Url\ConfigInterface" type="Magento\Framework\View\Url\Config" /> + <preference for="Magento\Framework\App\Route\ConfigInterface" type="Magento\Framework\App\Route\Config" /> + <preference for="Magento\Framework\App\ResourceConnection\ConfigInterface" type="Magento\Framework\App\ResourceConnection\Config\Proxy" /> + <preference for="Magento\Framework\Oauth\OauthInterface" type="Magento\Framework\Oauth\Oauth"/> + <preference for="Magento\Framework\View\Design\Theme\Domain\PhysicalInterface" type="Magento\Theme\Model\Theme\Domain\Physical" /> + <preference for="Magento\Framework\View\Design\Theme\Domain\VirtualInterface" type="Magento\Theme\Model\Theme\Domain\Virtual" /> + <preference for="Magento\Framework\View\Design\Theme\Domain\StagingInterface" type="Magento\Theme\Model\Theme\Domain\Staging" /> + <preference for="Magento\Framework\Json\EncoderInterface" type="Magento\Framework\Json\Encoder" /> + <preference for="Magento\Framework\Json\DecoderInterface" type="Magento\Framework\Json\Decoder" /> + <preference for="Magento\Framework\Message\ManagerInterface" type="Magento\Framework\Message\Manager" /> + <preference for="Magento\Framework\App\Config\ValueInterface" type="Magento\Framework\App\Config\Value" /> + <preference for="Magento\Framework\Interception\ChainInterface" type="Magento\Framework\Interception\Chain\Chain" /> + <preference for="Magento\Framework\Module\Output\ConfigInterface" type="Magento\Framework\Module\Output\Config" /> + <preference for="Magento\Framework\View\Design\Theme\CustomizationInterface" type="Magento\Framework\View\Design\Theme\Customization" /> + <preference for="Magento\Framework\View\Asset\ConfigInterface" type="Magento\Framework\View\Asset\Config" /> + <preference for="Magento\Framework\Image\Adapter\ConfigInterface" type="Magento\Framework\Image\Adapter\Config" /> + <preference for="Magento\Framework\Image\Adapter\UploadConfigInterface" type="Magento\Framework\Image\Adapter\Config" /> + <preference for="Magento\Framework\View\Design\Theme\Image\PathInterface" type="Magento\Theme\Model\Theme\Image\Path" /> + <preference for="Magento\Framework\Session\Config\ConfigInterface" type="Magento\Framework\Session\Config" /> + <preference for="Magento\Framework\Session\SidResolverInterface" type="Magento\Framework\Session\SidResolver\Proxy" /> + <preference for="Magento\Framework\Stdlib\Cookie\CookieScopeInterface" type="Magento\Framework\Stdlib\Cookie\CookieScope" /> + <preference for="Magento\Framework\Stdlib\Cookie\CookieReaderInterface" type="Magento\Framework\Stdlib\Cookie\PhpCookieReader" /> + <preference for="Magento\Framework\Stdlib\CookieManagerInterface" type="Magento\Framework\Stdlib\Cookie\PhpCookieManager" /> + <preference for="Magento\Framework\TranslateInterface" type="Magento\Framework\Translate" /> + <preference for="Magento\Framework\Config\ScopeListInterface" type="interceptionConfigScope" /> + <preference for="Magento\Framework\View\Design\Theme\Label\ListInterface" type="Magento\Theme\Model\ResourceModel\Theme\Collection" /> + <preference for="Magento\Framework\Mview\ConfigInterface" type="Magento\Framework\Mview\Config" /> + <preference for="Magento\Framework\Mview\ViewInterface" type="Magento\Framework\Mview\View" /> + <preference for="Magento\Framework\Mview\ProcessorInterface" type="Magento\Framework\Mview\Processor" /> + <preference for="Magento\Framework\Mview\View\CollectionInterface" type="Magento\Framework\Mview\View\Collection" /> + <preference for="Magento\Framework\Mview\View\SubscriptionInterface" type="Magento\Framework\Mview\View\Subscription" /> + <preference for="Magento\Framework\Mview\View\ChangelogInterface" type="Magento\Framework\Mview\View\Changelog" /> + <preference for="Magento\Framework\Api\MetadataServiceInterface" type="Magento\Framework\Api\DefaultMetadataService"/> + <preference for="Magento\Framework\Api\MetadataObjectInterface" type="Magento\Framework\Api\AttributeMetadata"/> + <preference for="Magento\Framework\Api\SearchCriteriaInterface" type="Magento\Framework\Api\SearchCriteria"/> + <preference for="Magento\Framework\App\Rss\UrlBuilderInterface" type="Magento\Framework\App\Rss\UrlBuilder"/> + <preference for="Magento\Framework\DB\LoggerInterface" type="Magento\Framework\DB\Logger\LoggerProxy"/> + <preference for="Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface" type="Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql"/> + <preference for="Magento\Framework\DB\QueryInterface" type="Magento\Framework\DB\Query"/> + <preference for="Magento\Framework\App\ProductMetadataInterface" type="Magento\Framework\App\ProductMetadata"/> + <preference for="Magento\Framework\Acl\Data\CacheInterface" type="Magento\Framework\Acl\Data\Cache" /> + <preference for="Magento\Framework\App\AreaInterface" type="Magento\Framework\App\Area" /> + <preference for="Magento\Framework\Setup\ModuleDataSetupInterface" type="Magento\Setup\Module\DataSetup" /> + <preference for="Magento\Framework\AuthorizationInterface" type="Magento\Framework\Authorization" /> + <preference for="Magento\Framework\Authorization\PolicyInterface" type="Magento\Framework\Authorization\Policy\DefaultPolicy" /> + <preference for="Magento\Framework\Authorization\RoleLocatorInterface" type="Magento\Framework\Authorization\RoleLocator\DefaultRoleLocator" /> + <preference for="Magento\Framework\Session\SessionManagerInterface" type="Magento\Framework\Session\Generic" /> + <preference for="Magento\Framework\App\Config\ScopeConfigInterface" type="Magento\Framework\App\Config" /> + <preference for="Magento\Framework\App\Config\ReinitableConfigInterface" type="Magento\Framework\App\ReinitableConfig" /> + <preference for="Magento\Framework\App\Config\MutableScopeConfigInterface" type="Magento\Framework\App\MutableScopeConfig" /> + <preference for="Magento\Framework\App\Config\Storage\WriterInterface" type="Magento\Framework\App\Config\Storage\Writer" /> + <preference for="Magento\Framework\Config\ConverterInterface" type="Magento\Framework\Config\Converter\Dom"/> + <preference for="Magento\Framework\App\DefaultPathInterface" type="Magento\Framework\App\DefaultPath\DefaultPath" /> + <preference for="Magento\Framework\Encryption\EncryptorInterface" type="Magento\Framework\Encryption\Encryptor" /> + <preference for="Magento\Framework\Filter\Encrypt\AdapterInterface" type="Magento\Framework\Filter\Encrypt\Basic" /> + <preference for="Magento\Framework\Cache\ConfigInterface" type="Magento\Framework\Cache\Config" /> + <preference for="Magento\Framework\View\Asset\MergeStrategyInterface" type="Magento\Framework\View\Asset\MergeStrategy\Direct" /> + <preference for="Magento\Framework\App\ViewInterface" type="Magento\Framework\App\View" /> + <preference for="Magento\Framework\Data\Collection\EntityFactoryInterface" type="Magento\Framework\Data\Collection\EntityFactory" /> + <preference for="Magento\Framework\Translate\InlineInterface" type="Magento\Framework\Translate\Inline" /> + <preference for="Magento\Framework\Session\ValidatorInterface" type="Magento\Framework\Session\Validator" /> + <preference for="Magento\Framework\Session\StorageInterface" type="Magento\Framework\Session\Storage" /> + <preference for="Magento\Framework\App\Request\DataPersistorInterface" type="Magento\Framework\App\Request\DataPersistor" /> + <preference for="Magento\Framework\Url\RouteParamsResolverInterface" type="Magento\Framework\Url\RouteParamsResolver" /> + <preference for="Magento\Framework\Url\RouteParamsPreprocessorInterface" type="Magento\Framework\Url\RouteParamsPreprocessorComposite" /> + <preference for="Magento\Framework\Url\ModifierInterface" type="Magento\Framework\Url\ModifierComposite" /> + <preference for="Magento\Framework\Url\QueryParamsResolverInterface" type="Magento\Framework\Url\QueryParamsResolver" /> + <preference for="Magento\Framework\Url\ScopeResolverInterface" type="Magento\Framework\Url\ScopeResolver" /> + <preference for="Magento\Framework\Url\SecurityInfoInterface" type="Magento\Framework\Url\SecurityInfo\Proxy" /> + <preference for="Magento\Framework\Locale\CurrencyInterface" type="Magento\Framework\Locale\Currency" /> + <preference for="Magento\Framework\CurrencyInterface" type="Magento\Framework\Currency" /> + <preference for="Magento\Framework\Locale\FormatInterface" type="Magento\Framework\Locale\Format" /> + <preference for="Magento\Framework\Locale\ResolverInterface" type="Magento\Framework\Locale\Resolver" /> + <preference for="Magento\Framework\Stdlib\DateTime\TimezoneInterface" type="Magento\Framework\Stdlib\DateTime\Timezone" /> + <preference for="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverterInterface" type="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverter" /> + <preference for="Magento\Framework\Communication\ConfigInterface" type="Magento\Framework\Communication\Config" /> + <preference for="Magento\Framework\Module\ResourceInterface" type="Magento\Framework\Module\ModuleResource" /> + <preference for="Magento\Framework\Pricing\Amount\AmountInterface" type="Magento\Framework\Pricing\Amount\Base" /> + <preference for="Magento\Framework\Api\SearchResultsInterface" type="Magento\Framework\Api\SearchResults" /> + <preference for="Magento\Framework\Api\AttributeInterface" type="Magento\Framework\Api\AttributeValue" /> + <preference for="Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface" type="Magento\Framework\Model\ResourceModel\Db\TransactionManager" /> + <preference for="Magento\Framework\Api\Data\ImageContentInterface" type="Magento\Framework\Api\ImageContent" /> + <preference for="Magento\Framework\Api\ImageContentValidatorInterface" type="Magento\Framework\Api\ImageContentValidator" /> + <preference for="Magento\Framework\Api\ImageProcessorInterface" type="Magento\Framework\Api\ImageProcessor" /> + <preference for="Magento\Framework\Code\Reader\ClassReaderInterface" type="Magento\Framework\Code\Reader\ClassReader" /> + <preference for="Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface" type="Magento\Framework\Stdlib\DateTime\DateTimeFormatter"/> + <preference for="Magento\Framework\Api\Search\SearchInterface" type="Magento\Framework\Search\Search"/> + <preference for="Magento\Framework\View\Design\FileResolution\Fallback\ResolverInterface" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple" /> + <preference for="Cm\RedisSession\Handler\ConfigInterface" type="Magento\Framework\Session\SaveHandler\Redis\Config"/> + <preference for="Cm\RedisSession\Handler\LoggerInterface" type="Magento\Framework\Session\SaveHandler\Redis\Logger"/> + <preference for="Magento\Framework\EntityManager\MapperInterface" type="Magento\Framework\EntityManager\CompositeMapper"/> + <preference for="Magento\Framework\Console\CommandListInterface" type="Magento\Framework\Console\CommandList"/> + <preference for="Magento\Framework\DataObject\IdentityGeneratorInterface" type="Magento\Framework\DataObject\IdentityService" /> + <preference for="Magento\Framework\Serialize\SerializerInterface" type="Magento\Framework\Serialize\Serializer\Json" /> + <preference for="Magento\Framework\App\Scope\ValidatorInterface" type="Magento\Framework\App\Scope\Validator"/> + <preference for="Magento\Framework\App\ScopeResolverInterface" type="Magento\Framework\App\ScopeResolver" /> + <preference for="Magento\Framework\App\ScopeInterface" type="Magento\Framework\App\ScopeDefault" /> + <preference for="Magento\Framework\View\Design\Theme\ListInterface" type="Magento\Framework\View\Design\Theme\ThemeList" /> + <preference for="Magento\Framework\View\Design\Theme\ThemeProviderInterface" type="Magento\Framework\View\Design\Theme\ThemeProvider" /> + <preference for="Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface" type="Magento\Framework\View\Asset\PreProcessor\ChainFactory"/> + <preference for="Magento\Framework\Css\PreProcessor\ErrorHandlerInterface" type="Magento\Framework\Css\PreProcessor\ErrorHandler" /> + <preference for="Magento\Framework\View\Asset\PreProcessor\Helper\SortInterface" type="Magento\Framework\View\Asset\PreProcessor\Helper\Sort"/> + <preference for="Magento\Framework\App\View\Deployment\Version\StorageInterface" type="Magento\Framework\App\View\Deployment\Version\Storage\File"/> + <preference for="Magento\Framework\View\Page\FaviconInterface" type="Magento\Theme\Model\Favicon\Favicon" /> + <preference for="Magento\Framework\View\Element\Message\InterpretationStrategyInterface" type="Magento\Framework\View\Element\Message\InterpretationMediator" /> + <preference for="Magento\Framework\App\FeedInterface" type="Magento\Framework\App\Feed" /> + <preference for="Magento\Framework\App\FeedFactoryInterface" type="Magento\Framework\App\FeedFactory" /> + <preference for="Magento\Framework\Indexer\Config\DependencyInfoProviderInterface" type="Magento\Framework\Indexer\Config\DependencyInfoProvider" /> + <preference for="Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface" type="Magento\Eav\Model\TypeLocator\ComplexType"/> + <preference for="Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaReaderInterface" type="Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DbSchemaReader" /> + <preference for="Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaWriterInterface" type="Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DbSchemaWriter" /> + <preference for="Magento\Framework\Setup\Declaration\Schema\SchemaConfigInterface" type="Magento\Framework\Setup\Declaration\Schema\SchemaConfig" /> + <preference for="Magento\Framework\Setup\Declaration\Schema\DataSavior\DumpAccessorInterface" type="Magento\Framework\Setup\Declaration\Schema\FileSystem\Csv" /> + <preference for="Magento\Framework\MessageQueue\PublisherInterface" type="Magento\Framework\MessageQueue\PublisherPool" /> + <preference for="Magento\Framework\MessageQueue\BulkPublisherInterface" type="Magento\Framework\MessageQueue\Bulk\PublisherPool" /> + <preference for="Magento\Framework\MessageQueue\MessageIdGeneratorInterface" type="Magento\Framework\MessageQueue\MessageIdGenerator" /> + <preference for="Magento\Framework\MessageQueue\Consumer\ConfigInterface" type="Magento\Framework\MessageQueue\Consumer\Config" /> + <preference for="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem\HandlerInterface" type="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem\Handler" /> + <preference for="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItemInterface" type="Magento\Framework\MessageQueue\Consumer\Config\ConsumerConfigItem" /> + <preference for="Magento\Framework\MessageQueue\Consumer\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Consumer\Config\CompositeValidator" /> + <preference for="Magento\Framework\MessageQueue\Consumer\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Consumer\Config\CompositeReader" /> + <preference for="Magento\Framework\Amqp\Topology\BindingInstallerInterface" type="Magento\Framework\Amqp\Topology\BindingInstaller" /> + <preference for="Magento\Framework\MessageQueue\Topology\ConfigInterface" type="Magento\Framework\MessageQueue\Topology\Config" /> + <preference for="Magento\Framework\MessageQueue\Topology\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Topology\Config\CompositeReader" /> + <preference for="Magento\Framework\MessageQueue\Topology\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Topology\Config\CompositeValidator" /> + <preference for="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface" type="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem" /> + <preference for="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface" type="Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\Binding" /> + <preference for="Magento\Framework\MessageQueue\Publisher\ConfigInterface" type="Magento\Framework\MessageQueue\Publisher\Config" /> + <preference for="Magento\Framework\MessageQueue\Publisher\Config\ReaderInterface" type="Magento\Framework\MessageQueue\Publisher\Config\CompositeReader" /> + <preference for="Magento\Framework\MessageQueue\Publisher\Config\ValidatorInterface" type="Magento\Framework\MessageQueue\Publisher\Config\CompositeValidator" /> + <preference for="Magento\Framework\MessageQueue\Publisher\Config\PublisherConnectionInterface" type="Magento\Framework\MessageQueue\Publisher\Config\PublisherConnection" /> + <preference for="Magento\Framework\MessageQueue\Publisher\Config\PublisherConfigItemInterface" type="Magento\Framework\MessageQueue\Publisher\Config\PublisherConfigItem" /> + <preference for="Magento\Framework\MessageQueue\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\ExchangeFactory" /> + <preference for="Magento\Framework\MessageQueue\Bulk\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\Bulk\ExchangeFactory" /> + <preference for="Magento\Framework\MessageQueue\QueueFactoryInterface" type="Magento\Framework\MessageQueue\QueueFactory" /> + <preference for="Magento\Framework\Search\Request\IndexScopeResolverInterface" type="Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver"/> + <preference for="Magento\Framework\HTTP\ClientInterface" type="Magento\Framework\HTTP\Client\Curl" /> + <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" /> + <type name="Magento\Framework\Acl\Data\Cache"> + <arguments> + <argument name="aclBuilder" xsi:type="object">Magento\Framework\Acl\Builder\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Logger\Handler\Base"> + <arguments> + <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> + </arguments> + </type> + <type name="Magento\Framework\Logger\Handler\System"> + <arguments> + <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> + </arguments> + </type> + <preference for="Magento\AsynchronousOperations\Model\ConfigInterface" type="Magento\WebapiAsync\Model\Config\Proxy" /> + <type name="Magento\Framework\Communication\Config\CompositeReader"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="asyncServiceReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\WebapiAsync\Code\Generator\Config\RemoteServiceReader\Communication</item> + <item name="sortOrder" xsi:type="string">0</item> + </item> + <item name="xmlReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\XmlReader</item> + <item name="sortOrder" xsi:type="string">10</item> + </item> + <item name="envReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\EnvReader</item> + <item name="sortOrder" xsi:type="string">20</item> + </item> + <item name="remoteServiceReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Code\Generator\Config\RemoteServiceReader\Communication</item> + <item name="sortOrder" xsi:type="string">5</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Logger\Monolog"> + <arguments> + <argument name="name" xsi:type="string">main</argument> + <argument name="handlers" xsi:type="array"> + <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item> + <item name="debug" xsi:type="object">Magento\Framework\Logger\Handler\Debug</item> + <item name="syslog" xsi:type="object">Magento\Framework\Logger\Handler\Syslog</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Logger\Handler\Syslog"> + <arguments> + <argument name="ident" xsi:type="string">Magento</argument> + </arguments> + </type> + <type name="Magento\Framework\Model\Context"> + <arguments> + <argument name="actionValidator" xsi:type="object">Magento\Framework\Model\ActionValidator\RemoveAction\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Api\FilterBuilder" shared="false" /> + <type name="Magento\Framework\Api\SearchCriteriaBuilder" shared="false" /> + <type name="Magento\Framework\View\Layout\Builder" shared="false" /> + <type name="Magento\Framework\View\Page\Builder" shared="false" /> + <type name="Magento\Framework\Message\Manager"> + <arguments> + <argument name="session" xsi:type="object">Magento\Framework\Message\Session\Proxy</argument> + <argument name="exceptionMessageFactory" xsi:type="object">Magento\Framework\Message\ExceptionMessageLookupFactory</argument> + </arguments> + </type> + <type name="Magento\Framework\View\BlockPool" shared="false" /> + <type name="Magento\Framework\App\Request\Http"> + <arguments> + <argument name="pathInfoProcessor" xsi:type="object">Magento\Backend\App\Request\PathInfoProcessor\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Response\Http"> + <arguments> + <argument name="sessionConfig" xsi:type="object">Magento\Framework\Session\Config\ConfigInterface\Proxy</argument> + </arguments> + </type> + <preference for="Magento\Framework\Session\SaveHandlerInterface" type="Magento\Framework\Session\SaveHandler" /> + <type name="Magento\Framework\Session\SaveHandlerFactory"> + <arguments> + <argument name="handlers" xsi:type="array"> + <item name="db" xsi:type="string">Magento\Framework\Session\SaveHandler\DbTable</item> + <item name="redis" xsi:type="string">Magento\Framework\Session\SaveHandler\Redis</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\FeedFactory"> + <arguments> + <argument name="formats" xsi:type="array"> + <item name="rss" xsi:type="string">Magento\Framework\App\Feed</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Session\SaveHandler\Redis"> + <arguments> + <argument name="config" xsi:type="object">Cm\RedisSession\Handler\ConfigInterface</argument> + <argument name="logger" xsi:type="object">Cm\RedisSession\Handler\LoggerInterface</argument> + </arguments> + </type> + <virtualType name="interceptionConfigScope" type="Magento\Framework\Config\Scope"> + <arguments> + <argument name="defaultScope" xsi:type="string">global</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\App\State"> + <arguments> + <argument name="mode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Asset\Source"> + <arguments> + <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Page\Config\Renderer"> + <arguments> + <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Arguments\ValidationState"> + <arguments> + <argument name="appMode" xsi:type="init_parameter">Magento\Framework\App\State::PARAM_MODE</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Cache\Frontend\Factory"> + <arguments> + <argument name="enforcedOptions" xsi:type="init_parameter">Magento\Framework\App\Cache\Frontend\Factory::PARAM_CACHE_FORCED_OPTIONS</argument> + <argument name="decorators" xsi:type="array"> + <item name="tag" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Framework\Cache\Frontend\Decorator\TagScope</item> + <item name="parameters" xsi:type="array"> + <item name="tag" xsi:type="string">MAGE</item> + </item> + </item> + <item name="logger" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Framework\Cache\Frontend\Decorator\Logger</item> + </item> + </argument> + <argument name="resource" xsi:type="object">Magento\Framework\App\ResourceConnection\Proxy</argument> + </arguments> + </type> + <type name="Magento\Backend\App\Area\FrontNameResolver"> + <arguments> + <argument name="defaultFrontName" xsi:type="init_parameter">Magento\Backend\Setup\ConfigOptionsList::CONFIG_PATH_BACKEND_FRONTNAME</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Cache\State"> + <arguments> + <argument name="banAll" xsi:type="init_parameter">Magento\Framework\App\Cache\State::PARAM_BAN_CACHE</argument> + </arguments> + </type> + <type name="Magento\Store\Model\StoreManager"> + <arguments> + <argument name="scopeCode" xsi:type="init_parameter">Magento\Store\Model\StoreManager::PARAM_RUN_CODE</argument> + <argument name="scopeType" xsi:type="init_parameter">Magento\Store\Model\StoreManager::PARAM_RUN_TYPE</argument> + </arguments> + </type> + <type name="Magento\Framework\Translate"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Translate</argument> + <argument name="locale" xsi:type="object">Magento\Framework\Locale\Resolver\Proxy</argument> + <argument name="translate" xsi:type="object">Magento\Framework\Translate\ResourceInterface\Proxy</argument> + <argument name="request" xsi:type="object">Magento\Framework\App\Request\Http\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Helper\Context"> + <arguments> + <argument name="translateInline" xsi:type="object">Magento\Framework\Translate\InlineInterface\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Data\Structure" shared="false" /> + <type name="Magento\Framework\View\Layout\Data\Structure" shared="false" /> + <type name="Magento\Theme\Model\View\Design"> + <arguments> + <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Acl" shared="false" /> + <type name="Magento\Framework\App\ObjectManager\ConfigLoader"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\ObjectManager\ConfigCache"> + <arguments> + <argument name="cacheFrontend" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + </arguments> + </type> + <type name="Magento\Framework\Cache\Config\Data"> + <arguments> + <argument name="cacheId" xsi:type="string">config_cache</argument> + <argument name="reader" xsi:type="object">Magento\Framework\Cache\Config\Reader\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Interception\Config\Config"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> + <argument name="cacheId" xsi:type="string">interception</argument> + </arguments> + </type> + <type name="Magento\Framework\Interception\Config\CacheManager"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + </arguments> + </type> + <type name="Magento\Framework\Interception\PluginList\PluginList"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + <argument name="reader" xsi:type="object">Magento\Framework\ObjectManager\Config\Reader\Dom\Proxy</argument> + <argument name="cacheId" xsi:type="string">plugin-list</argument> + <argument name="scopePriorityScheme" xsi:type="array"> + <item name="first" xsi:type="string">global</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\ResourceConnection"> + <arguments> + <argument name="connectionFactory" xsi:type="object">Magento\Framework\App\ResourceConnection\ConnectionFactory</argument> + </arguments> + </type> + <type name="Magento\Framework\App\ResourceConnection\Config"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\App\ResourceConnection\Config\Reader\Proxy</argument> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\ResourceConnection\Config\Reader"> + <arguments> + <argument name="fileResolver" xsi:type="object">Magento\Framework\App\Config\FileResolver\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Config\Scope"> + <arguments> + <argument name="defaultScope" xsi:type="string">primary</argument> + <argument name="areaList" xsi:type="object">Magento\Framework\App\AreaList\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Url"> + <arguments> + <argument name="session" xsi:type="object">Magento\Framework\Session\Generic\Proxy</argument> + <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + </arguments> + </type> + <virtualType name="layoutArgumentReaderInterpreter" type="Magento\Framework\Data\Argument\Interpreter\Composite"> + <arguments> + <argument name="interpreters" xsi:type="array"> + <item name="options" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item> + <item name="array" xsi:type="object">layoutArrayArgumentReaderInterpreterProxy</item> + <item name="boolean" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item> + <item name="number" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item> + <item name="string" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item> + <item name="null" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item> + <item name="object" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> + <item name="url" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> + <item name="helper" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item> + </argument> + <argument name="discriminator" xsi:type="const">Magento\Framework\View\Model\Layout\Merge::TYPE_ATTRIBUTE</argument> + </arguments> + </virtualType> + <virtualType name="layoutArgumentGeneratorInterpreterInternal" type="Magento\Framework\Data\Argument\Interpreter\Composite"> + <arguments> + <argument name="interpreters" xsi:type="array"> + <item name="options" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item> + <item name="array" xsi:type="object">layoutArrayArgumentGeneratorInterpreterProxy</item> + <item name="boolean" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item> + <item name="number" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item> + <item name="string" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item> + <item name="null" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item> + <item name="object" xsi:type="object">layoutObjectArgumentInterpreter</item> + <item name="url" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Url</item> + <item name="helper" xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\HelperMethod</item> + </argument> + <argument name="discriminator" xsi:type="const">Magento\Framework\View\Model\Layout\Merge::TYPE_ATTRIBUTE</argument> + </arguments> + </virtualType> + <virtualType name="layoutArgumentGeneratorInterpreter" type="Magento\Framework\View\Layout\Argument\Interpreter\Decorator\Updater"> + <arguments> + <argument name="subject" xsi:type="object">layoutArgumentGeneratorInterpreterInternal</argument> + </arguments> + </virtualType> + <virtualType name="layoutArrayArgumentReaderInterpreter" type="Magento\Framework\Data\Argument\Interpreter\ArrayType"> + <arguments> + <argument name="itemInterpreter" xsi:type="object">layoutArgumentReaderInterpreter</argument> + </arguments> + </virtualType> + <virtualType name="layoutArrayArgumentGeneratorInterpreter" type="Magento\Framework\Data\Argument\Interpreter\ArrayType"> + <arguments> + <argument name="itemInterpreter" xsi:type="object">layoutArgumentGeneratorInterpreterInternal</argument> + </arguments> + </virtualType> + <!-- + Array item can be of any type just like an argument, including array type itself, which creates circular dependency. + Proxy is used to resolve the circular dependency, so that array items undergo the same interpretation as arguments. + --> + <virtualType name="layoutArrayArgumentReaderInterpreterProxy" type="Magento\Framework\Data\Argument\InterpreterInterface\Proxy"> + <arguments> + <argument name="instanceName" xsi:type="string">layoutArrayArgumentReaderInterpreter</argument> + </arguments> + </virtualType> + <virtualType name="layoutArrayArgumentGeneratorInterpreterProxy" type="Magento\Framework\Data\Argument\InterpreterInterface\Proxy"> + <arguments> + <argument name="instanceName" xsi:type="string">layoutArrayArgumentGeneratorInterpreter</argument> + </arguments> + </virtualType> + <virtualType name="layoutObjectArgumentInterpreter" type="Magento\Framework\View\Layout\Argument\Interpreter\DataObject"> + <arguments> + <argument name="expectedClass" xsi:type="string">Magento\Framework\View\Element\Block\ArgumentInterface</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\Argument\Interpreter\NamedParams"> + <arguments> + <argument name="paramInterpreter" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</argument> + </arguments> + </type> + <virtualType name="containerRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> + <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\Reader\Container"> + <arguments> + <argument name="readerPool" xsi:type="object">containerRenderPool</argument> + </arguments> + </type> + <virtualType name="blockRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> + <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> + <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\Reader\Block"> + <arguments> + <argument name="readerPool" xsi:type="object">blockRenderPool</argument> + <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + <argument name="argumentInterpreter" xsi:type="object">layoutArgumentReaderInterpreter</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Layout\Reader\UiComponent"> + <arguments> + <argument name="readerPool" xsi:type="object">blockRenderPool</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Layout\ConfigCondition"> + <arguments> + <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + </arguments> + </type> + <virtualType name="bodyRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> + <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> + <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Page\Config\Reader\Body"> + <arguments> + <argument name="readerPool" xsi:type="object">bodyRenderPool</argument> + </arguments> + </type> + <virtualType name="commonRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="html" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Html</item> + <item name="head" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Head</item> + <item name="body" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Body</item> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> + <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> + <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout"> + <arguments> + <argument name="readerPool" xsi:type="object" shared="false">commonRenderPool</argument> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Layout</argument> + </arguments> + </type> + <virtualType name="genericLayoutRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="block" xsi:type="string">Magento\Framework\View\Layout\Reader\Block</item> + <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> + <item name="uiComponent" xsi:type="string">Magento\Framework\View\Layout\Reader\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Result\Layout"> + <arguments> + <argument name="layoutReaderPool" xsi:type="object">genericLayoutRenderPool</argument> + </arguments> + </type> + <virtualType name="pageConfigRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="html" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Html</item> + <item name="head" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Head</item> + <item name="body" xsi:type="string">Magento\Framework\View\Page\Config\Reader\Body</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\GeneratorPool"> + <arguments> + <argument name="generators" xsi:type="array"> + <item name="head" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Head</item> + <item name="body" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Body</item> + <item name="block" xsi:type="object">Magento\Framework\View\Layout\Generator\Block</item> + <item name="container" xsi:type="object">Magento\Framework\View\Layout\Generator\Container</item> + <item name="uiComponent" xsi:type="object">Magento\Framework\View\Layout\Generator\UiComponent</item> + </argument> + </arguments> + </type> + <virtualType name="pageLayoutGeneratorPool" type="Magento\Framework\View\Layout\GeneratorPool"> + <arguments> + <argument name="generators" xsi:type="array"> + <item name="head" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Head</item> + <item name="body" xsi:type="object">Magento\Framework\View\Page\Config\Generator\Body</item> + <item name="block" xsi:type="object">Magento\Framework\View\Layout\Generator\Block</item> + <item name="container" xsi:type="object">Magento\Framework\View\Layout\Generator\Container</item> + <item name="uiComponent" xsi:type="object">Magento\Framework\View\Layout\Generator\UiComponent</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Result\Page"> + <arguments> + <argument name="layoutReaderPool" xsi:type="object">pageConfigRenderPool</argument> + <argument name="generatorPool" xsi:type="object">pageLayoutGeneratorPool</argument> + <argument name="template" xsi:type="string">Magento_Theme::root.phtml</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Layout\Generator\Block"> + <arguments> + <argument name="argumentInterpreter" xsi:type="object">layoutArgumentGeneratorInterpreter</argument> + </arguments> + </type> + <type name="Magento\Framework\Mview\View"> + <arguments> + <argument name="state" xsi:type="object" shared="false">Magento\Indexer\Model\Mview\View\State</argument> + <argument name="changelog" xsi:type="object" shared="false">Magento\Framework\Mview\View\Changelog</argument> + </arguments> + </type> + <type name="Magento\Framework\Mview\Config"> + <arguments> + <argument name="configData" xsi:type="object">Magento\Framework\Mview\Config\Data\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Mview\Config\Data"> + <arguments> + <argument name="stateCollection" xsi:type="object" shared="false">Magento\Framework\Mview\View\State\CollectionInterface</argument> + </arguments> + </type> + <type name="Magento\Framework\App\View\Asset\Publisher" shared="false" /> + <type name="Magento\Framework\View\Asset\PreProcessor\FileNameResolver"> + <arguments> + <argument name="alternativeSources" xsi:type="array"> + <item name="css" xsi:type="object">AlternativeSourceProcessors</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\StaticResource"> + <arguments> + <argument name="response" xsi:type="object" shared="false">Magento\MediaStorage\Model\File\Storage\Response</argument> + <argument name="publisher" xsi:type="object">developerPublisher</argument> + </arguments> + </type> + <virtualType name="AlternativeSourceProcessors" type="Magento\Framework\View\Asset\PreProcessor\AlternativeSource"> + <arguments> + <argument name="filenameResolver" xsi:type="object">Magento\Framework\View\Asset\PreProcessor\MinificationFilenameResolver</argument> + <argument name="lockName" xsi:type="string">alternative-source-css</argument> + <argument name="lockerProcess" xsi:type="object">Magento\Framework\View\Asset\LockerProcess</argument> + <argument name="alternatives" xsi:type="array"> + <item name="less" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Framework\Css\PreProcessor\Adapter\Less\Processor</item> + </item> + </argument> + </arguments> + </virtualType> + <virtualType name="developerPublisher" type="Magento\Framework\App\View\Asset\Publisher"> + <arguments> + <argument name="materializationStrategyFactory" xsi:type="object">developerMaterialization</argument> + </arguments> + </virtualType> + <virtualType name="developerMaterialization" type="Magento\Framework\App\View\Asset\MaterializationStrategy\Factory"> + <arguments> + <argument name="strategiesList" xsi:type="array"> + <item name="view_preprocessed" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Symlink</item> + <item name="default" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Copy</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Design\FileResolution\Fallback\File"> + <arguments> + <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Design\FileResolution\Fallback\TemplateFile"> + <arguments> + <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Design\FileResolution\Fallback\LocaleFile"> + <arguments> + <argument name="resolver" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple</argument> + </arguments> + </type> + + <virtualType name="viewFileMinifiedFallbackResolver" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Minification"> + <arguments> + <argument name="fallback" xsi:type="object">viewFileFallbackResolver</argument> + </arguments> + </virtualType> + <virtualType name="viewFileFallbackResolver" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative"/> + <type name="Magento\Framework\View\Design\FileResolution\Fallback\StaticFile"> + <arguments> + <argument name="resolver" xsi:type="object">viewFileMinifiedFallbackResolver</argument> + </arguments> + </type> + <type name="Magento\Framework\Code\Generator"> + <arguments> + <argument name="generatedEntities" xsi:type="array"> + <item name="extensionInterfaceFactory" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceFactoryGenerator</item> + <item name="factory" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Factory</item> + <item name="proxy" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Proxy</item> + <item name="interceptor" xsi:type="string">\Magento\Framework\Interception\Code\Generator\Interceptor</item> + <item name="logger" xsi:type="string">\Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger</item> + <item name="mapper" xsi:type="string">\Magento\Framework\Api\Code\Generator\Mapper</item> + <item name="persistor" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Persistor</item> + <item name="repository" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Repository</item> + <item name="convertor" xsi:type="string">\Magento\Framework\ObjectManager\Code\Generator\Converter</item> + <item name="searchResults" xsi:type="string">\Magento\Framework\Api\Code\Generator\SearchResults</item> + <item name="extensionInterface" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator</item> + <item name="extension" xsi:type="string">\Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator</item> + <item name="remote" xsi:type="string">\Magento\Framework\MessageQueue\Code\Generator\RemoteServiceGenerator</item> + <item name="proxyDeferred" xsi:type="string">\Magento\Framework\Async\Code\Generator\ProxyDeferredGenerator</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\Cache\Frontend\Pool"> + <arguments> + <argument name="frontendSettings" xsi:type="array"> + <item name="page_cache" xsi:type="array"> + <item name="backend_options" xsi:type="array"> + <item name="cache_dir" xsi:type="string">page_cache</item> + </item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\Cache\Type\FrontendPool"> + <arguments> + <argument name="typeFrontendMap" xsi:type="array"> + <item name="full_page" xsi:type="string">page_cache</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Translate\Inline"> + <arguments> + <argument name="parser" xsi:type="object">Magento\Framework\Translate\Inline\ParserInterface\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Layout\ScheduledStructure" shared="false" /> + <type name="Magento\Framework\View\Page\Config\Structure" shared="false" /> + <type name="Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Container"> + <arguments> + <argument name="buckets" xsi:type="array"> + <item name="termBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Term</item> + <item name="rangeBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Range</item> + <item name="dynamicBucket" xsi:type="object">Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder\Dynamic</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Search\Dynamic\Algorithm\Repository"> + <arguments> + <argument name="algorithms" xsi:type="array"> + <item name="auto" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Auto</item> + <item name="manual" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Manual</item> + <item name="improved" xsi:type="string">Magento\Framework\Search\Dynamic\Algorithm\Improved</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\View\Model\Layout\Merge"> + <arguments> + <argument name="fileSource" xsi:type="object">Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy</argument> + <argument name="pageLayoutFileSource" xsi:type="object">pageLayoutFileCollectorAggregated</argument> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Layout</argument> + <argument name="layoutCacheKey" xsi:type="object">Magento\Framework\View\Layout\LayoutCacheKeyInterface</argument> + </arguments> + </type> + <type name="CSSmin"> + <arguments> + <argument name="raise_php_limits" xsi:type="boolean">false</argument> + </arguments> + </type> + <type name="Magento\Framework\App\DefaultPath\DefaultPath"> + <arguments> + <argument name="parts" xsi:type="array"> + <item name="module" xsi:type="string">core</item> + <item name="controller" xsi:type="string">index</item> + <item name="action" xsi:type="string">index</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Data\Collection\Db\FetchStrategy\Cache"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Collection</argument> + <argument name="cacheIdPrefix" xsi:type="string">collection_</argument> + <argument name="cacheLifetime" xsi:type="string">86400</argument> + </arguments> + </type> + <type name="Magento\Framework\Event\Config\Data"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\Event\Config\Reader\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Asset\Collection" shared="false" /> + <virtualType name="layoutFileSourceBase" type="Magento\Framework\View\File\Collector\Base"> + <arguments> + <argument name="subDir" xsi:type="string">layout</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceBase</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceBaseFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="layoutFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"> + <arguments> + <argument name="subDir" xsi:type="string">layout</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceTheme</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceThemeFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="layoutFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> + <arguments> + <argument name="subDir" xsi:type="string">layout/override/base</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceOverrideBase</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceOverrideBaseFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="layoutFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> + <arguments> + <argument name="subDir" xsi:type="string">layout/override/theme</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceOverrideTheme</argument> + </arguments> + </virtualType> + <virtualType name="layoutFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">layoutFileSourceOverrideThemeFiltered</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\File\Collector\Aggregated"> + <arguments> + <argument name="baseFiles" xsi:type="object">layoutFileSourceBaseSorted</argument> + <argument name="themeFiles" xsi:type="object">layoutFileSourceThemeSorted</argument> + <argument name="overrideBaseFiles" xsi:type="object">layoutFileSourceOverrideBaseSorted</argument> + <argument name="overrideThemeFiles" xsi:type="object">layoutFileSourceOverrideThemeSorted</argument> + </arguments> + </type> + <virtualType name="pageLayoutFileSourceBase" type="Magento\Framework\View\File\Collector\Base"> + <arguments> + <argument name="subDir" xsi:type="string">page_layout</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceBase</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceBaseFiltered</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"> + <arguments> + <argument name="subDir" xsi:type="string">page_layout</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceTheme</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceThemeFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageLayoutFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> + <arguments> + <argument name="subDir" xsi:type="string">page_layout/override/base</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideBase</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideBaseFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageLayoutFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> + <arguments> + <argument name="subDir" xsi:type="string">page_layout/override/theme</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideTheme</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageLayoutFileSourceOverrideThemeFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageLayoutFileCollectorAggregated" type="Magento\Framework\View\Layout\File\Collector\Aggregated"> + <arguments> + <argument name="baseFiles" xsi:type="object">pageLayoutFileSourceBaseSorted</argument> + <argument name="themeFiles" xsi:type="object">pageLayoutFileSourceThemeSorted</argument> + <argument name="overrideBaseFiles" xsi:type="object">pageLayoutFileSourceOverrideBaseSorted</argument> + <argument name="overrideThemeFiles" xsi:type="object">pageLayoutFileSourceOverrideThemeSorted</argument> + </arguments> + </virtualType> + + <virtualType name="pageFileSourceBase" type="Magento\Framework\View\File\Collector\Base"/> + <virtualType name="pageFileSourceBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceBase</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceBaseFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageFileSourceTheme" type="Magento\Framework\View\File\Collector\ThemeModular"/> + <virtualType name="pageFileSourceThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceTheme</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceThemeFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageFileSourceOverrideBase" type="Magento\Framework\View\File\Collector\Override\Base"> + <arguments> + <argument name="subDir" xsi:type="string">page/override</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceOverrideBaseFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceOverrideBase</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceOverrideBaseSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceOverrideBaseFiltered</argument> + </arguments> + </virtualType> + + <virtualType name="pageFileSourceOverrideTheme" type="Magento\Framework\View\File\Collector\Override\ThemeModular"> + <arguments> + <argument name="subDir" xsi:type="string">override/theme</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceOverrideThemeFiltered" type="Magento\Framework\View\File\Collector\Decorator\ModuleOutput"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceOverrideTheme</argument> + </arguments> + </virtualType> + <virtualType name="pageFileSourceOverrideThemeSorted" type="Magento\Framework\View\File\Collector\Decorator\ModuleDependency"> + <arguments> + <argument name="subject" xsi:type="object">pageFileSourceOverrideThemeFiltered</argument> + </arguments> + </virtualType> + <virtualType name="pageLayoutRenderPool" type="Magento\Framework\View\Layout\ReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="container" xsi:type="string">Magento\Framework\View\Layout\Reader\Container</item> + <item name="move" xsi:type="string">Magento\Framework\View\Layout\Reader\Move</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Page\Layout\Reader"> + <arguments> + <argument name="pageLayoutFileSource" xsi:type="object">pageLayoutFileCollectorAggregated</argument> + <argument name="reader" xsi:type="object">pageLayoutRenderPool</argument> + </arguments> + </type> + <type name="Magento\Framework\View\PageLayout\File\Collector\Aggregated"> + <arguments> + <argument name="baseFiles" xsi:type="object">pageFileSourceBaseSorted</argument> + <argument name="themeFiles" xsi:type="object">pageFileSourceThemeSorted</argument> + <argument name="overrideBaseFiles" xsi:type="object">pageFileSourceOverrideBaseSorted</argument> + <argument name="overrideThemeFiles" xsi:type="object">pageFileSourceOverrideThemeSorted</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Design\Theme\Image"> + <arguments> + <argument name="uploader" xsi:type="object">Magento\Framework\View\Design\Theme\Image\Uploader\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Config\Initial"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\App\Config\Initial\Reader\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Config\Initial\Reader"> + <arguments> + <argument name="converter" xsi:type="object">Magento\Framework\App\Config\Initial\Converter</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Route\Config"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\App\Route\Config\Reader\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Session\Validator"> + <arguments> + <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + <argument name="skippedUserAgentList" xsi:type="array"> + <item name="flash" xsi:type="string">Shockwave Flash</item> + <item name="flash_mac" xsi:type="string"><![CDATA[Adobe Flash Player\s{1,}\w{1,10}]]></item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\DataObject\Copy\Config"> + <arguments> + <argument name="dataStorage" xsi:type="object">Magento\Framework\DataObject\Copy\Config\Data\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\DataObject\Copy\Config\Reader"> + <arguments> + <argument name="fileName" xsi:type="string">fieldset.xml</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Framework\DataObject\Copy\Config\SchemaLocator</argument> + </arguments> + </type> + <type name="Magento\Framework\DataObject\Copy\Config\SchemaLocator"> + <arguments> + <argument name="schema" xsi:type="string">urn:magento:framework:DataObject/etc/fieldset.xsd</argument> + <argument name="perFileSchema" xsi:type="string">urn:magento:framework:DataObject/etc/fieldset_file.xsd</argument> + </arguments> + </type> + <type name="Magento\Framework\DataObject\Copy\Config\Data"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\DataObject\Copy\Config\Reader\Proxy</argument> + <argument name="cacheId" xsi:type="string">fieldset_config</argument> + </arguments> + </type> + <type name="Magento\Framework\Image"> + <arguments> + <argument name="adapter" xsi:type="object">Magento\Framework\Image\Adapter\Gd2</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Layout\PageType\Config\Reader"> + <arguments> + <argument name="fileName" xsi:type="string">page_types.xml</argument> + <argument name="converter" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Converter</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\SchemaLocator</argument> + <argument name="defaultScope" xsi:type="string">frontend</argument> + </arguments> + </type> + <virtualType name="Magento\Framework\View\Layout\PageType\Config\Data" type="Magento\Framework\Config\Data"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Reader</argument> + <argument name="cacheId" xsi:type="string">page_types_config</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Layout\PageType\Config"> + <arguments> + <argument name="dataStorage" xsi:type="object">Magento\Framework\View\Layout\PageType\Config\Data</argument> + </arguments> + </type> + <virtualType name="Magento\Framework\Message\Session\Storage" type="Magento\Framework\Session\Storage"> + <arguments> + <argument name="namespace" xsi:type="string">message</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\Message\Session"> + <arguments> + <argument name="storage" xsi:type="object">Magento\Framework\Message\Session\Storage</argument> + </arguments> + </type> + <type name="Magento\Framework\Url\ScopeResolver"> + <arguments> + <argument name="areaCode" xsi:type="string">frontend</argument> + </arguments> + </type> + + <type name="Magento\Framework\Module\ModuleList\Loader"> + <arguments> + <argument name="filesystemDriver" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument> + </arguments> + </type> + <type name="Magento\Framework\Module\Setup\MigrationData"> + <arguments> + <argument name="data" xsi:type="array"> + <item name="plain" xsi:type="string"><![CDATA[/^(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)::.*?$/sui]]></item> + <item name="wiki" xsi:type="string"><![CDATA[/{{(block|widget).*?(class|type)=\"(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?}}/sui]]></item> + <item name="xml" xsi:type="string"><![CDATA[/<block.*?class=\"(?P<alias>[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?>/sui]]></item> + <item name="serialized" xsi:type="string"><![CDATA[#(?P<string>s:\d+:"(?P<alias>[a-z]+[_a-z\d]*?/[a-z]+[_a-z\d]*?)")#sui]]></item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Webapi\Rest\Request\DeserializerFactory"> + <arguments> + <argument name="deserializers" xsi:type="array"> + <item name="application_json" xsi:type="array"> + <item name="type" xsi:type="string">application/json</item> + <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Json</item> + </item> + <item name="application_xml" xsi:type="array"> + <item name="type" xsi:type="string">application/xml</item> + <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> + </item> + <item name="application_xhtml_xml" xsi:type="array"> + <item name="type" xsi:type="string">application/xhtml+xml</item> + <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> + </item> + <item name="text_xml" xsi:type="array"> + <item name="type" xsi:type="string">text/xml</item> + <item name="model" xsi:type="string">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Validator\Factory"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + </arguments> + </type> + <type name="Magento\Server\Reflection" shared="false" /> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessor\Proxy</argument> + <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Url\Decoder"> + <arguments> + <argument name="urlBuilder" xsi:type="object">Magento\Framework\UrlInterface</argument> + </arguments> + </type> + <type name="Magento\Framework\Api\Search\SearchCriteriaBuilder" shared="false"/> + <type name="Magento\Framework\Api\Search\FilterGroupBuilder" shared="false"/> + <type name="Magento\Framework\Config\View"> + <arguments> + <argument name="fileName" xsi:type="string">view.xml</argument> + <argument name="converter" xsi:type="object">Magento\Framework\Config\Converter</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Framework\Config\SchemaLocator</argument> + <argument name="fileResolver" xsi:type="object">Magento\Framework\Config\FileResolver</argument> + </arguments> + </type> + <type name="Magento\Framework\DB\SelectFactory"> + <arguments> + <argument name="selectRenderer" xsi:type="object">Magento\Framework\DB\Select\RendererProxy</argument> + </arguments> + </type> + <type name="Magento\Framework\Data\Form\Filter\Date"> + <arguments> + <argument name="localeResolver" xsi:type="object">Magento\Framework\Locale\ResolverInterface</argument> + </arguments> + </type> + <type name="Magento\Framework\DB\Select\SelectRenderer"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="distinct" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\DistinctRenderer</item> + <item name="sort" xsi:type="string">100</item> + <item name="part" xsi:type="string">distinct</item> + </item> + <item name="columns" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\ColumnsRenderer</item> + <item name="sort" xsi:type="string">200</item> + <item name="part" xsi:type="string">columns</item> + </item> + <item name="union" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\UnionRenderer</item> + <item name="sort" xsi:type="string">300</item> + <item name="part" xsi:type="string">union</item> + </item> + <item name="from" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\FromRenderer</item> + <item name="sort" xsi:type="string">400</item> + <item name="part" xsi:type="string">from</item> + </item> + <item name="where" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\WhereRenderer</item> + <item name="sort" xsi:type="string">500</item> + <item name="part" xsi:type="string">where</item> + </item> + <item name="group" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\GroupRenderer</item> + <item name="sort" xsi:type="string">600</item> + <item name="part" xsi:type="string">group</item> + </item> + <item name="having" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\HavingRenderer</item> + <item name="sort" xsi:type="string">700</item> + <item name="part" xsi:type="string">having</item> + </item> + <item name="order" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\OrderRenderer</item> + <item name="sort" xsi:type="string">800</item> + <item name="part" xsi:type="string">order</item> + </item> + <item name="limit" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\LimitRenderer</item> + <item name="sort" xsi:type="string">900</item> + <item name="part" xsi:type="string">limitcount</item> + </item> + <item name="for_update" xsi:type="array"> + <item name="renderer" xsi:type="object">Magento\Framework\DB\Select\ForUpdateRenderer</item> + <item name="sort" xsi:type="string">1000</item> + <item name="part" xsi:type="string">forupdate</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\EntityManager\OperationPool"> + <arguments> + <argument name="operations" xsi:type="array"> + <item name="default" xsi:type="array"> + <item name="checkIfExists" xsi:type="string">Magento\Framework\EntityManager\Operation\CheckIfExists</item> + <item name="read" xsi:type="string">Magento\Framework\EntityManager\Operation\Read</item> + <item name="create" xsi:type="string">Magento\Framework\EntityManager\Operation\Create</item> + <item name="update" xsi:type="string">Magento\Framework\EntityManager\Operation\Update</item> + <item name="delete" xsi:type="string">Magento\Framework\EntityManager\Operation\Delete</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\Cache\FlushCacheByTags"> + <arguments> + <argument name="cacheList" xsi:type="array"> + <item name="block_html" xsi:type="const">Magento\Framework\App\Cache\Type\Block::TYPE_IDENTIFIER</item> + <item name="collections" xsi:type="const">Magento\Framework\App\Cache\Type\Collection::TYPE_IDENTIFIER</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\EntityManager\CompositeMapper"> + <arguments> + <argument name="mappers" xsi:type="array"> + <item name="mapper" xsi:type="object">Magento\Framework\EntityManager\Mapper</item> + </argument> + </arguments> + </type> + <preference for="Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor" /> + <type name="Magento\Framework\Api\SearchCriteria\CollectionProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="filters" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor</item> + <item name="sorting" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\SortingProcessor</item> + <item name="pagination" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\PaginationProcessor</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\DB\Select\QueryModifierFactory"> + <arguments> + <argument name="queryModifiers" xsi:type="array"> + <item name="in" xsi:type="string">Magento\Framework\DB\Select\InQueryModifier</item> + <item name="like" xsi:type="string">Magento\Framework\DB\Select\LikeQueryModifier</item> + <item name="composite" xsi:type="string">Magento\Framework\DB\Select\CompositeQueryModifier</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\DB\TemporaryTableService"> + <arguments> + <argument name="allowedIndexMethods" xsi:type="array"> + <item name="HASH" xsi:type="string">HASH</item> + <item name="BTREE" xsi:type="string">BTREE</item> + </argument> + <argument name="allowedEngines" xsi:type="array"> + <item name="INNODB" xsi:type="string">INNODB</item> + <item name="MEMORY" xsi:type="string">MEMORY</item> + <item name="MYISAM" xsi:type="string">MYISAM</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\DB\FieldDataConverter"> + <arguments> + <argument name="envBatchSize" xsi:type="init_parameter">Magento\Framework\DB\FieldDataConverter::BATCH_SIZE_VARIABLE_NAME</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Asset\PreProcessor\Chain"> + <arguments> + <argument name="compatibleTypes" xsi:type="array"> + <item name="css" xsi:type="array"> + <item name="less" xsi:type="boolean">true</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\View\Asset\PreProcessor\Pool"> + <arguments> + <argument name="defaultPreprocessor" xsi:type="string">Magento\Framework\View\Asset\PreProcessor\Passthrough</argument> + </arguments> + </type> + <type name="Magento\Framework\App\View\Deployment\Version\Storage\File"> + <arguments> + <argument name="directoryCode" xsi:type="const">Magento\Framework\App\Filesystem\DirectoryList::STATIC_VIEW</argument> + <argument name="fileName" xsi:type="string">deployed_version.txt</argument> + </arguments> + </type> + <type name="Magento\Framework\Locale\Resolver"> + <arguments> + <argument name="defaultLocalePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_LOCALE</argument> + <argument name="scopeType" xsi:type="const">Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT</argument> + </arguments> + </type> + <type name="Magento\Framework\View\Element\Message\Renderer\RenderersPool"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="escape_renderer" xsi:type="object">Magento\Framework\View\Element\Message\Renderer\EscapeRenderer</item> + <item name="block_renderer" xsi:type="object">Magento\Framework\View\Element\Message\Renderer\BlockRenderer</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <arguments> + <argument name="configurationsMap" xsi:type="array"> + <item name="default_message_identifier" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\EscapeRenderer::CODE</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\DB\Logger\LoggerProxy"> + <arguments> + <argument name="loggerAlias" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_OUTPUT</argument> + <argument name="logAllQueries" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_LOG_EVERYTHING</argument> + <argument name="logQueryTime" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_QUERY_TIME_THRESHOLD</argument> + <argument name="logCallStack" xsi:type="init_parameter">Magento\Framework\Config\ConfigOptionsListConstants::CONFIG_PATH_DB_LOGGER_INCLUDE_STACKTRACE</argument> + </arguments> + </type> + <type name="Magento\Framework\App\Config\MetadataConfigTypeProcessor"> + <arguments> + <argument name="configSource" xsi:type="object">Magento\Config\App\Config\Source\EnvironmentConfigSource</argument> + </arguments> + </type> + <type name="Magento\Framework\Message\ExceptionMessageFactoryPool"> + <arguments> + <argument name="defaultExceptionMessageFactory" xsi:type="object">Magento\Framework\Message\ExceptionMessageFactory</argument> + </arguments> + </type> + <type name="Magento\Framework\Mview\View\Subscription"> + <arguments> + <argument name="ignoredUpdateColumns" xsi:type="array"> + <item name="updated_at" xsi:type="string">updated_at</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Dto\ElementFactory"> + <arguments> + <argument name="typeFactories" xsi:type="array"> + <item name="table" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Table</item> + <item name="decimal" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> + <item name="float" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> + <item name="double" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Real</item> + <item name="smallint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> + <item name="tinyint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> + <item name="bigint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> + <item name="int" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Integer</item> + <item name="date" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Date</item> + <item name="timestamp" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Timestamp</item> + <item name="datetime" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Timestamp</item> + <item name="longtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\LongText</item> + <item name="mediumtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumText</item> + <item name="text" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Text</item> + <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> + <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> + <item name="blob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Blob</item> + <item name="mediumblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumBlob</item> + <item name="longblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\LongBlob</item> + <item name="boolean" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Boolean</item> + <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Unique</item> + <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Primary</item> + <item name="foreign" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Foreign</item> + <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Index</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Db\DefinitionAggregator"> + <arguments> + <argument name="definitionProcessors" xsi:type="array"> + <item name="boolean" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Boolean</item> + <item name="int" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> + <item name="smallint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> + <item name="tinyint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> + <item name="bigint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Integer</item> + <item name="decimal" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> + <item name="float" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> + <item name="double" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Real</item> + <item name="text" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="blob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="mediumblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="longblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="mediumtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="longtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Blob</item> + <item name="datetime" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Timestamp</item> + <item name="date" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Date</item> + <item name="timestamp" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Timestamp</item> + <item name="char" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="binary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Index</item> + <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> + <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> + <item name="constraint" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> + <item name="reference" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\ForeignKey</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Operations\AddColumn"> + <arguments> + <argument name="triggers" xsi:type="array"> + <item name="migrateDataFromSameTable" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DDL\Triggers\MigrateDataFrom</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Declaration\ReaderComposite"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="xml" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\FileSystem\XmlReader</item> + </argument> + </arguments> + </type> + <virtualType name="Magento\Framework\Setup\Declaration\Schema\Config\SchemaLocator" type="Magento\Framework\Config\SchemaLocator"> + <arguments> + <argument name="realPath" xsi:type="string">urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd</argument> + </arguments> + </virtualType> + <virtualType name="Magento\Framework\Setup\Declaration\Schema\FileSystem\XmlReader" type="Magento\Framework\Config\Reader\Filesystem"> + <arguments> + <argument name="fileResolver" xsi:type="object">Magento\Framework\Config\FileResolverByModule</argument> + <argument name="converter" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Config\Converter</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Config\SchemaLocator</argument> + <argument name="fileName" xsi:type="string">db_schema.xml</argument> + <argument name="idAttributes" xsi:type="array"> + <item name="/schema/table" xsi:type="string">name</item> + <item name="/schema/table/column" xsi:type="string">name</item> + <item name="/schema/table/constraint" xsi:type="string">referenceId</item> + <item name="/schema/table/index" xsi:type="string">referenceId</item> + <item name="/schema/table/index/column" xsi:type="string">name</item> + <item name="/schema/table/constraint/column" xsi:type="string">name</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Framework\Setup\Declaration\Schema\OperationsExecutor"> + <arguments> + <argument name="operations" xsi:type="array"> + <item name="recreate_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ReCreateTable</item> + <item name="create_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\CreateTable</item> + <item name="drop_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropTable</item> + <item name="drop_reference" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropReference</item> + <item name="modify_column" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ModifyColumn</item> + <item name="add_column" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\AddColumn</item> + <item name="drop_element" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\DropElement</item> + <item name="add_complex_element" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\AddComplexElement</item> + <item name="modify_table" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Operations\ModifyTable</item> + </argument> + <argument name="dataSaviorsCollection" xsi:type="array"> + <item name="table_savior" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\DataSavior\TableSavior</item> + <item name="column_savior" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\DataSavior\ColumnSavior</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Sharding"> + <arguments> + <argument name="resources" xsi:type="array"> + <item name="default" xsi:type="string">default</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationComposite"> + <arguments> + <argument name="rules" xsi:type="array"> + <item name="check_references" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\CheckReferenceColumnHasIndex</item> + <item name="real_types" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\RealTypes</item> + <item name="check_primary_key" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\PrimaryKeyCanBeCreated</item> + <item name="inconsistence_references" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\IncosistentReferenceDefinition</item> + <item name="auto_increment_validation" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Declaration\ValidationRules\AutoIncrementColumnValidation</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\SchemaListener"> + <arguments> + <argument name="definitionMappers" xsi:type="array"> + <item name="integer" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> + <item name="tinyint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> + <item name="smallint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> + <item name="mediumint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> + <item name="bigint" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\IntegerDefinition</item> + <item name="decimal" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> + <item name="float" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> + <item name="numeric" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\RealDefinition</item> + <item name="text" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="mediumtext" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="longtext" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="blob" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="mediumblob" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="longblog" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="varbinary" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="varchar" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="timestamp" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> + <item name="datetime" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> + <item name="date" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\DateDefinition</item> + <item name="boolean" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\BooleanDefinition</item> + </argument> + </arguments> + </type> + <virtualType name="\Magento\Framework\Setup\Patch\SchemaPatchReader" type="\Magento\Framework\Setup\Patch\PatchReader"> + <arguments> + <argument name="type" xsi:type="string">schema</argument> + </arguments> + </virtualType> + <virtualType name="\Magento\Framework\Setup\Patch\DataPatchReader" type="\Magento\Framework\Setup\Patch\PatchReader"> + <arguments> + <argument name="type" xsi:type="string">data</argument> + </arguments> + </virtualType> + <type name="\Magento\Framework\Setup\Patch\PatchApplier"> + <arguments> + <argument name="dataPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\DataPatchReader</argument> + <argument name="schemaPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\SchemaPatchReader</argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Patch\UpToDateData"> + <arguments> + <argument name="dataPatchReader" xsi:type="object">\Magento\Framework\Setup\Patch\DataPatchReader</argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Patch\UpToDateSchema"> + <arguments> + <argument name="schemaReader" xsi:type="object">\Magento\Framework\Setup\Patch\SchemaPatchReader</argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Config\CompositeReader"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="xmlReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Xml</item> + <item name="sortOrder" xsi:type="string">10</item> + </item> + <item name="envReader" xsi:type="array"> + <item name="reader" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Env</item> + <item name="sortOrder" xsi:type="string">20</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Config\Reader\Xml\CompositeConverter"> + <arguments> + <argument name="converters" xsi:type="array"> + <item name="topicConfig" xsi:type="array"> + <item name="converter" xsi:type="object">Magento\Framework\MessageQueue\Config\Reader\Xml\Converter\TopicConfig</item> + <item name="sortOrder" xsi:type="string">20</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Consumer\Config\Data"> + <arguments> + <argument name="reader" xsi:type="object">Magento\Framework\MessageQueue\Consumer\Config\CompositeReader</argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Consumer\Config\CompositeReader"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="xmlReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Consumer\Config\Xml\Reader</item> + <item name="envReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Consumer\Config\Env\Reader</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Consumer\Config\CompositeValidator"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="requiredFields" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Consumer\Config\Validator\RequiredFields</item> + <item name="fieldTypes" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Consumer\Config\Validator\FieldsTypes</item> + <item name="handlers" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Consumer\Config\Validator\Handlers</item> + <item name="consumerInstance" xsi:type="object" sortOrder="40">Magento\Framework\MessageQueue\Consumer\Config\Validator\ConsumerInstance</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Publisher\Config\CompositeValidator"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="connectionFormat" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Publisher\Config\Validator\Format</item> + <item name="enabledConnection" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Publisher\Config\Validator\EnabledConnection</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Publisher\Config\CompositeReader"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="asyncServiceReader" xsi:type="object" sortOrder="0">Magento\WebapiAsync\Code\Generator\Config\RemoteServiceReader\Publisher</item> + <item name="remoteServiceReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Publisher\Config\RemoteService\Reader</item> + <item name="xmlReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Publisher\Config\Xml\Reader</item> + <item name="envReader" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Publisher\Config\Env\Reader</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Topology\Config\CompositeValidator"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="format" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Topology\Config\Validator\Format</item> + <item name="fieldsTypes" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Topology\Config\Validator\FieldsTypes</item> + <item name="dependantFields" xsi:type="object" sortOrder="30">Magento\Framework\MessageQueue\Topology\Config\Validator\DependentFields</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Topology\Config\CompositeReader"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="remoteServiceReader" xsi:type="object" sortOrder="10">Magento\Framework\MessageQueue\Topology\Config\RemoteService\Reader</item> + <item name="xmlReader" xsi:type="object" sortOrder="20">Magento\Framework\MessageQueue\Topology\Config\Xml\Reader</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Amqp\Topology\BindingInstaller"> + <arguments> + <argument name="installers" xsi:type="array"> + <item name="queue" xsi:type="object">Magento\Framework\Amqp\Topology\BindingInstallerType\Queue</item> + <item name="exchange" xsi:type="object">Magento\Framework\Amqp\Topology\BindingInstallerType\Exchange</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Config\Reader\Env\Converter\Publisher"> + <arguments> + <argument name="connectionToExchangeMap" xsi:type="array"> + <item name="amqp" xsi:type="string">magento</item> + <item name="db" xsi:type="string">magento-db</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\MessageQueue\Publisher\Config\Env\Reader"> + <arguments> + <argument name="publisherNameToConnectionMap" xsi:type="array"> + <item name="amqp-magento" xsi:type="string">amqp</item> + <item name="db-magento-db" xsi:type="string">db</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Setup\Declaration\Schema\Operations\CreateTable"> + <arguments> + <argument name="triggers" xsi:type="array"> + <item name="migrateDataFromAnotherTable" xsi:type="object">Magento\Framework\Setup\Declaration\Schema\Db\MySQL\DDL\Triggers\MigrateDataFromAnotherTable</item> + </argument> + </arguments> + </type> + <!-- \Magento\Framework\MessageQueue\Bulk\PublisherPool is @api --> + <virtualType name="Magento\Framework\MessageQueue\Bulk\PublisherPool" type="Magento\Framework\MessageQueue\PublisherPool" /> + <type name="Magento\Framework\Session\Config"> + <arguments> + <argument name="scopeType" xsi:type="const">Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT</argument> + </arguments> + </type> + <virtualType name="CsrfRequestValidator" type="Magento\Framework\App\Request\CsrfValidator" /> + <virtualType name="RequestValidator" type="Magento\Framework\App\Request\CompositeValidator"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="csrf_validator" xsi:type="object">CsrfRequestValidator</item> + <item name="http_method_validator" xsi:type="object"> + Magento\Framework\App\Request\HttpMethodValidator + </item> + </argument> + </arguments> + </virtualType> + <preference for="Magento\Framework\App\Request\ValidatorInterface" type="RequestValidator" /> + <type name="Magento\Framework\App\Request\HttpMethodMap"> + <arguments> + <argument name="map" xsi:type="array"> + <item name="OPTIONS" xsi:type="string">\Magento\Framework\App\Action\HttpOptionsActionInterface</item> + <item name="GET" xsi:type="string">\Magento\Framework\App\Action\HttpGetActionInterface</item> + <item name="HEAD" xsi:type="string">\Magento\Framework\App\Action\HttpGetActionInterface</item> + <item name="POST" xsi:type="string">\Magento\Framework\App\Action\HttpPostActionInterface</item> + <item name="PUT" xsi:type="string">\Magento\Framework\App\Action\HttpPutActionInterface</item> + <item name="PATCH" xsi:type="string">\Magento\Framework\App\Action\HttpPatchActionInterface</item> + <item name="DELETE" xsi:type="string">\Magento\Framework\App\Action\HttpDeleteActionInterface</item> + <item name="CONNECT" xsi:type="string">\Magento\Framework\App\Action\HttpConnectActionInterface</item> + <item name="PROPFIND" xsi:type="string">\Magento\Framework\App\Action\HttpPropfindActionInterface</item> + <item name="TRACE" xsi:type="string">\Magento\Framework\App\Action\HttpTraceActionInterface</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\ScopeResolverPool"> + <arguments> + <argument name="scopeResolvers" xsi:type="array"> + <item name="default" xsi:type="object">Magento\Framework\App\ScopeResolver</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Cache\LockGuardedCacheLoader"> + <arguments> + <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> + <argument name="lockTimeout" xsi:type="number">10000</argument> + <argument name="delayTimeout" xsi:type="number">20</argument> + </arguments> + </type> + <preference for="Magento\Framework\HTTP\AsyncClientInterface" type="Magento\Framework\HTTP\AsyncClient\GuzzleAsyncClient" /> + <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompareInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompare"/> + <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillPutInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillPut"/> + <preference for="Magento\Framework\MessageQueue\PoisonPill\PoisonPillReadInterface" type="Magento\Framework\MessageQueue\PoisonPill\PoisonPillRead"/> + <preference for="Magento\Framework\MessageQueue\CallbackInvokerInterface" type="Magento\Framework\MessageQueue\CallbackInvoker"/> + <preference for="Magento\Framework\Mail\EmailMessageInterface" + type="Magento\Framework\Mail\EmailMessage" /> + <preference for="Magento\Framework\Mail\MimeMessageInterface" + type="Magento\Framework\Mail\MimeMessage" /> + <preference for="Magento\Framework\Mail\MimePartInterface" + type="Magento\Framework\Mail\MimePart" /> +</config> diff --git a/app/etc/registration_globlist.php b/app/etc/registration_globlist.php new file mode 100644 index 0000000000000..23caae00cb303 --- /dev/null +++ b/app/etc/registration_globlist.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Glob patterns relative to the project root directory, used by + * registration.php to generate a list of includes. + */ +return [ + 'app/code/*/*/cli_commands.php', + 'app/code/*/*/registration.php', + 'app/design/*/*/*/registration.php', + 'app/i18n/*/*/registration.php', + 'lib/internal/*/*/registration.php', + 'lib/internal/*/*/*/registration.php', + 'setup/src/*/*/registration.php' +]; diff --git a/app/etc/vendor_path.php b/app/etc/vendor_path.php new file mode 100644 index 0000000000000..b6af03ef32fa4 --- /dev/null +++ b/app/etc/vendor_path.php @@ -0,0 +1,5 @@ +<?php +/** + * Path to Composer vendor directory + */ +return './vendor'; diff --git a/generated/.htaccess b/generated/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/generated/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + diff --git a/pub/media/.htaccess b/pub/media/.htaccess new file mode 100644 index 0000000000000..d68d163d7a6b5 --- /dev/null +++ b/pub/media/.htaccess @@ -0,0 +1,134 @@ +Options -Indexes + +<IfModule mod_php5.c> +php_flag engine 0 +</IfModule> + +<IfModule mod_php7.c> +php_flag engine 0 +</IfModule> + +AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi +Options -ExecCGI + +<FilesMatch ".+\.(ph(p[3457]?|t|tml)|[aj]sp|p[ly]|sh|cgi|shtml?|html?)$"> +SetHandler default-handler +</FilesMatch> + +<IfModule mod_rewrite.c> + +############################################ +## enable rewrites + + Options +FollowSymLinks + RewriteEngine on + + ## you can put here your pub/media folder path relative to web root + #RewriteBase /magento/pub/media/ + +############################################ +## never rewrite for existing files + RewriteCond %{REQUEST_FILENAME} !-f + +############################################ +## rewrite everything else to get.php + + RewriteRule .* ../get.php [L] +</IfModule> + +############################################ +## setting MIME types + +# JavaScript +AddType application/javascript js jsonp +AddType application/json json + +# CSS +AddType text/css css + +# Images and icons +AddType image/x-icon ico +AddType image/gif gif +AddType image/png png +AddType image/jpeg jpg +AddType image/jpeg jpeg + +# SVG +AddType image/svg+xml svg + +# Fonts +AddType application/vnd.ms-fontobject eot +AddType application/x-font-ttf ttf +AddType application/x-font-otf otf +AddType application/x-font-woff woff +AddType application/font-woff2 woff2 + +# Flash +AddType application/x-shockwave-flash swf + +# Archives and exports +AddType application/zip gzip +AddType application/x-gzip gz gzip +AddType application/x-bzip2 bz2 +AddType text/csv csv +AddType application/xml xml + +<IfModule mod_headers.c> + + <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$> + Header append Cache-Control public + </FilesMatch> + + <FilesMatch .*\.(zip|gz|gzip|bz2|csv|xml)$> + Header append Cache-Control no-store + </FilesMatch> + +</IfModule> + +<IfModule mod_expires.c> + +############################################ +## Add default Expires header +## http://developer.yahoo.com/performance/rules.html#expires + + ExpiresActive On + + # Data + <FilesMatch \.(zip|gz|gzip|bz2|csv|xml)$> + ExpiresDefault "access plus 0 seconds" + </FilesMatch> + ExpiresByType text/xml "access plus 0 seconds" + ExpiresByType text/csv "access plus 0 seconds" + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/zip "access plus 0 seconds" + ExpiresByType application/x-gzip "access plus 0 seconds" + ExpiresByType application/x-bzip2 "access plus 0 seconds" + + # CSS, JavaScript + <FilesMatch \.(css|js)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType text/css "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + + # Favicon, images, flash + <FilesMatch \.(ico|gif|png|jpg|jpeg|swf|svg)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + + # Fonts + <FilesMatch \.(eot|ttf|otf|svg|woff|woff2)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType application/vnd.ms-fontobject "access plus 1 year" + ExpiresByType application/x-font-ttf "access plus 1 year" + ExpiresByType application/x-font-otf "access plus 1 year" + ExpiresByType application/x-font-woff "access plus 1 year" + ExpiresByType application/font-woff2 "access plus 1 year" + +</IfModule> diff --git a/pub/media/customer/.htaccess b/pub/media/customer/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/pub/media/customer/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + diff --git a/pub/media/downloadable/.htaccess b/pub/media/downloadable/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/pub/media/downloadable/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + diff --git a/pub/media/import/.htaccess b/pub/media/import/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/pub/media/import/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + diff --git a/pub/media/theme_customization/.htaccess b/pub/media/theme_customization/.htaccess new file mode 100644 index 0000000000000..2b93da6b4c079 --- /dev/null +++ b/pub/media/theme_customization/.htaccess @@ -0,0 +1,10 @@ +Options -Indexes +<Files ~ "\.xml$"> + <IfVersion < 2.4> + order allow,deny + deny from all + </IfVersion> + <IfVersion >= 2.4> + Require all denied + </IfVersion> +</Files> diff --git a/pub/static/.htaccess b/pub/static/.htaccess new file mode 100644 index 0000000000000..a5aa6fb0d5cfd --- /dev/null +++ b/pub/static/.htaccess @@ -0,0 +1,133 @@ +<IfModule mod_php5.c> +php_flag engine 0 +</IfModule> + +<IfModule mod_php7.c> +php_flag engine 0 +</IfModule> + +# To avoid situation when web server automatically adds extension to path +Options -MultiViews + +<IfModule mod_rewrite.c> + RewriteEngine On + + ## you can put here your pub/static folder path relative to web root + #RewriteBase /magento/pub/static/ + + # Remove signature of the static files that is used to overcome the browser cache + RewriteRule ^version.+?/(.+)$ $1 [L] + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-l + + RewriteRule .* ../static.php?resource=$0 [L] + # Detects if moxieplayer request with uri params and redirects to uri without params + <Files moxieplayer.swf> + RewriteCond %{QUERY_STRING} !^$ + RewriteRule ^(.*)$ %{REQUEST_URI}? [R=301,L] + </Files> +</IfModule> + +############################################ +## setting MIME types + +# JavaScript +AddType application/javascript js jsonp +AddType application/json json + +# HTML + +AddType text/html html + +# CSS +AddType text/css css + +# Images and icons +AddType image/x-icon ico +AddType image/gif gif +AddType image/png png +AddType image/jpeg jpg +AddType image/jpeg jpeg + +# SVG +AddType image/svg+xml svg + +# Fonts +AddType application/vnd.ms-fontobject eot +AddType application/x-font-ttf ttf +AddType application/x-font-otf otf +AddType application/x-font-woff woff +AddType application/font-woff2 woff2 + +# Flash +AddType application/x-shockwave-flash swf + +# Archives and exports +AddType application/zip gzip +AddType application/x-gzip gz gzip +AddType application/x-bzip2 bz2 +AddType text/csv csv +AddType application/xml xml + +<IfModule mod_headers.c> + + <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$> + Header append Cache-Control public + </FilesMatch> + + <FilesMatch .*\.(zip|gz|gzip|bz2|csv|xml)$> + Header append Cache-Control no-store + </FilesMatch> + +</IfModule> + +<IfModule mod_expires.c> + +############################################ +## Add default Expires header +## http://developer.yahoo.com/performance/rules.html#expires + + ExpiresActive On + + # Data + <FilesMatch \.(zip|gz|gzip|bz2|csv|xml)$> + ExpiresDefault "access plus 0 seconds" + </FilesMatch> + ExpiresByType text/xml "access plus 0 seconds" + ExpiresByType text/csv "access plus 0 seconds" + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/zip "access plus 0 seconds" + ExpiresByType application/x-gzip "access plus 0 seconds" + ExpiresByType application/x-bzip2 "access plus 0 seconds" + + # CSS, JavaScript, html + <FilesMatch \.(css|js|html|json)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType text/css "access plus 1 year" + ExpiresByType text/html "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType application/json "access plus 1 year" + + # Favicon, images, flash + <FilesMatch \.(ico|gif|png|jpg|jpeg|swf|svg)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + + # Fonts + <FilesMatch \.(eot|ttf|otf|svg|woff|woff2)$> + ExpiresDefault "access plus 1 year" + </FilesMatch> + ExpiresByType application/vnd.ms-fontobject "access plus 1 year" + ExpiresByType application/x-font-ttf "access plus 1 year" + ExpiresByType application/x-font-otf "access plus 1 year" + ExpiresByType application/x-font-woff "access plus 1 year" + ExpiresByType application/font-woff2 "access plus 1 year" + +</IfModule> diff --git a/var/.htaccess b/var/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/var/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + From 7da3d539b7402f5561be8fd679d61a5334ec0067 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Wed, 25 Sep 2019 14:20:32 +0530 Subject: [PATCH 074/734] corrected di.xml and Account.php files --- .../Block/Adminhtml/Order/Create/Form/Account.php | 14 ++++++-------- app/etc/di.xml | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index fc48e351b0e7e..aa5bd3702c6d1 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -165,16 +165,14 @@ public function getFormValues() { try { $customer = $this->customerRepository->getById($this->getCustomerId()); - } catch (\Exception $e) { - /** If customer does not exist do nothing. */ - } - $data = isset($customer) - ? $this->_extensibleDataObjectConverter->toFlatArray( + $data = $this->_extensibleDataObjectConverter->toFlatArray( $customer, [], - \Magento\Customer\Api\Data\CustomerInterface::class - ) - : []; + CustomerInterface::class + ); + } catch (\Exception $e) { + $data = []; + } foreach ($this->getQuote()->getData() as $key => $value) { if (strpos($key, 'customer_') === 0) { $data[substr($key, 9)] = $value; diff --git a/app/etc/di.xml b/app/etc/di.xml index 1a74fd9d7f840..882d1be623988 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> @@ -1777,4 +1778,7 @@ type="Magento\Framework\Mail\MimeMessage" /> <preference for="Magento\Framework\Mail\MimePartInterface" type="Magento\Framework\Mail\MimePart" /> + <type name="Magento\Framework\DB\Adapter\AdapterInterface"> + <plugin name="execute_commit_callbacks" type="Magento\Framework\Model\ExecuteCommitCallbacks" /> + </type> </config> From bad8f2b731a400a22bf47a8f748b4818ed2a7bdf Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Thu, 26 Sep 2019 14:06:56 -0400 Subject: [PATCH 075/734] rename tests (magento/magento2#22833: Short-term admin accounts) --- ...onTest.xml => AdminCreateNewUserWithInvalidExpirationTest.xml} | 0 ...tionTest.xml => AdminCreateNewUserWithValidExpirationTest.xml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/code/Magento/Security/Test/Mftf/Test/{AdminUserCreateNewUserWithInvalidExpirationTest.xml => AdminCreateNewUserWithInvalidExpirationTest.xml} (100%) rename app/code/Magento/Security/Test/Mftf/Test/{AdminUserCreateNewUserWithValidExpirationTest.xml => AdminCreateNewUserWithValidExpirationTest.xml} (100%) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml similarity index 100% rename from app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithInvalidExpirationTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml similarity index 100% rename from app/code/Magento/Security/Test/Mftf/Test/AdminUserCreateNewUserWithValidExpirationTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml From 581988a72634565f84e3bfe94377956777581080 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 27 Sep 2019 19:16:51 -0400 Subject: [PATCH 076/734] refactored tests to use more from User module (magento/magento2#22833: Short-term admin accounts) --- ...CreateNewUserWithExpirationActionGroup.xml | 31 ----------------- ...minFillInUserWithExpirationActionGroup.xml | 21 ++++++++++++ ...inSaveUserInvalidExpirationActionGroup.xml | 8 ++++- .../AdminSaveUserSuccessActionGroup.xml | 2 ++ .../Security/Test/Mftf/Data/UserData.xml | 34 ------------------- ...CreateNewUserWithInvalidExpirationTest.xml | 15 ++++---- ...inCreateNewUserWithValidExpirationTest.xml | 14 +++++--- ...llNewUserFormRequiredFieldsActionGroup.xml | 2 +- 8 files changed, 49 insertions(+), 78 deletions(-) delete mode 100644 app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml delete mode 100644 app/code/Magento/Security/Test/Mftf/Data/UserData.xml diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml deleted file mode 100644 index 5ebd9d723ed26..0000000000000 --- a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminCreateNewUserWithExpirationActionGroup.xml +++ /dev/null @@ -1,31 +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="AdminCreateNewUserWithExpirationActionGroup"> - <annotations> - <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided User with an expiration date and saves.</description> - </annotations> - <arguments> - <argument name="user" defaultValue="NewAdminUserWithValidExpiration"/> - </arguments> - - <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> - <waitForPageLoad stepKey="waitForUsersPage" /> - <fillField selector="{{AdminNewUserFormSection.username}}" userInput="{{user.username}}" stepKey="enterUserName" /> - <fillField selector="{{AdminNewUserFormSection.firstname}}" userInput="{{user.firstName}}" stepKey="enterFirstName" /> - <fillField selector="{{AdminNewUserFormSection.lastname}}" userInput="{{user.lastName}}" stepKey="enterLastName" /> - <fillField selector="{{AdminNewUserFormSection.email}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail" /> - <fillField selector="{{AdminNewUserFormSection.password}}" userInput="{{user.password}}" stepKey="enterPassword" /> - <fillField selector="{{AdminNewUserFormSection.passwordConfirmation}}" userInput="{{user.password}}" stepKey="confirmPassword" /> - <fillField selector="{{AdminNewUserFormSection.userExpiresField}}" userInput="{{user.expires_at}}" stepKey="enterUserExpiration" /> - <fillField selector="{{AdminNewUserFormSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> - <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> - <waitForPageLoad stepKey="waitForSaveTheUser" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml new file mode 100644 index 0000000000000..d1d5427506e40 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillInUserWithExpirationActionGroup" + extends="AdminFillNewUserFormRequiredFieldsActionGroup"> + <annotations> + <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided User details with a expiration date.</description> + </annotations> + <arguments> + <argument name="expires_at" type="string"/> + </arguments> + <fillField selector="{{AdminNewUserFormSection.userExpiresField}}" userInput="{{expires_at}}" + stepKey="fillExpireDate" after="fillPasswordConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml index 8fc890acebf84..02280ed809124 100644 --- a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml @@ -11,6 +11,12 @@ <annotations> <description>Error message for saving an admin user with an invalid expiration date.</description> </annotations> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='"Expiration date" must be later than the current date.' stepKey="seeErrorMessage"/> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='"Expiration date" must be later than the current date.' + stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml index 5c9b520a19a40..7aed1e07112cf 100644 --- a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml @@ -11,6 +11,8 @@ <annotations> <description>Success message for saving an admin user successfully.</description> </annotations> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> <see userInput="You saved the user." stepKey="seeSuccessMessage" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Data/UserData.xml b/app/code/Magento/Security/Test/Mftf/Data/UserData.xml deleted file mode 100644 index 43c75da1c37ee..0000000000000 --- a/app/code/Magento/Security/Test/Mftf/Data/UserData.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?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="NewAdminUserWithValidExpiration" type="user"> - <data key="username" unique="suffix">adminExpiresAt</data> - <data key="firstname">John</data> - <data key="lastname">Doe</data> - <data key="email" unique="prefix">admin@example.com</data> - <data key="password">123123q</data> - <data key="password_confirmation">123123q</data> - <data key="interface_local">en_US</data> - <data key="interface_local_label">English (United States)</data> - <data key="is_active">true</data> - <data key="is_active_label">Active</data> - <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> - <data key="role">Administrators</data> - <data key="expires_at">Sep 3, 2030 4:42:36 PM</data> - <array key="roles"> - <item>1</item> - </array> - </entity> - - <entity name="NewAdminUserWithInvalidExpiration" type="user" extends="NewAdminUserWithValidExpiration"> - <data key="expires_at">Sep 3, 2000 4:42:36 PM</data> - </entity> -</entities> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index 18c999a89b2b1..4fb14742f48d7 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -15,18 +15,21 @@ <description value="Create new user with invalid expiration date."/> <testCaseId value="" /> <severity value="CRITICAL"/> - <group value="security_userexpiration"/> + <group value="security_userexpire"/> </annotations> + <before> - <!-- Log in to Admin Panel --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> - <!-- Create new user with user expiration set --> - <actionGroup ref="AdminCreateNewUserWithExpirationActionGroup" stepKey="fillInNewUserWithInvalidExpiration"> - <argument name="user" value="NewAdminUserWithInvalidExpiration" /> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="-5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> - <actionGroup ref="AdminSaveUserInvalidExpirationActionGroup" stepKey="saveNewUserWithInvalidExpirationError" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index cfc94920a13b6..acfa0464a6045 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -15,18 +15,22 @@ <description value="Create new user with valid expiration date."/> <testCaseId value="" /> <severity value="CRITICAL"/> - <group value="security_userexpiration"/> + <group value="security_userexpire"/> </annotations> <before> - <!-- Log in to Admin Panel --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> - <!-- Create new user with user expiration set --> - <actionGroup ref="AdminCreateNewUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> - <argument name="user" value="NewAdminUserWithValidExpiration" /> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> + <!-- TODO: get proper date format to match above --> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess" /> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml index eb3ef37056b2f..15cb4e5319904 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml @@ -13,7 +13,7 @@ <description>Fills in the provided User details on the New User creation page.</description> </annotations> <arguments> - <argument name="user" type="entity"/> + <argument name="user" defaultValue="NewAdminUser"/> </arguments> <fillField selector="{{AdminNewUserFormSection.username}}" userInput="{{user.username}}" stepKey="fillUser"/> From f1858064cf19ecd7fd3662c3907d293e05d699d4 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 28 Sep 2019 15:06:33 -0400 Subject: [PATCH 077/734] added test to ensure timezones working correctly (magento/magento2#22833: Short-term admin accounts) --- .../Observer/AfterAdminUserSaveTest.php | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php index d33e181192927..ee2ae79b62cf8 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php @@ -38,6 +38,29 @@ public function testSaveNewUserExpiration() static::assertEquals($userExpiration->getExpiresAt(), $testDate); } + /** + * Save a new UserExpiration; used to validate that date conversion is working correctly. + * + * @magentoDataFixture Magento/User/_files/dummy_user.php + */ + public function testSaveNewUserExpirationInMinutes() + { + $adminUserNameFromFixture = 'dummy_username'; + $testDate = $this->getFutureDateInStoreTime('+2 minutes'); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt($testDate); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + } + /** * Remove the UserExpiration record * @@ -87,15 +110,16 @@ public function testChangeUserExpiration() } /** + * @param string $timeToAdd Amount of time to add * @return string * @throws \Exception */ - private function getFutureDateInStoreTime() + private function getFutureDateInStoreTime($timeToAdd = '+20 days') { /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface $locale */ $locale = Bootstrap::getObjectManager()->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); $testDate = new \DateTime(); - $testDate->modify('+20 days'); + $testDate->modify($timeToAdd); $storeDate = $locale->date($testDate); return $storeDate->format('Y-m-d H:i:s'); } From d8c8473ff10b29d37d40480180c04633475e800f Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 28 Sep 2019 15:25:30 -0400 Subject: [PATCH 078/734] use UTC to convert date in validator (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/UserExpiration/Validator.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index 6a1cd5e2ce038..a2211283e4cb3 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -16,19 +16,19 @@ */ class Validator extends AbstractValidator { - /**@var \Magento\Framework\Stdlib\DateTime\DateTime */ - private $dateTime; + + /**@var \Magento\Framework\Stdlib\DateTime\TimezoneInterface */ + private $timezone; /** * Validator constructor. * - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone */ public function __construct( - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone ) { - - $this->dateTime = $dateTime; + $this->timezone = $timezone; } /** @@ -45,8 +45,9 @@ public function isValid($value) $expiresAt = $value; $label = 'Expiration date'; if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { - $currentTime = $this->dateTime->gmtTimestamp(); - $expiresAt = $this->dateTime->gmtTimestamp($value); + $currentTime = $this->timezone->date()->getTimestamp(); + $utcExpiresAt = $this->timezone->convertConfigTimeToUtc($expiresAt); + $expiresAt = $this->timezone->date($utcExpiresAt)->getTimestamp(); if ($expiresAt < $currentTime) { $messages['expires_at'] = __('"%1" must be later than the current date.', $label); } From d5693fe45f94d1d3b92d51f756d3bc5147d6060e Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Sun, 29 Sep 2019 10:18:10 +0300 Subject: [PATCH 079/734] Update_Integration_Entity --- ...nClickEditIntegrationEntityActionGroup.xml | 15 +++++ .../AdminCreatesNewIntegrationActionGroup.xml | 23 +++++++ ...gateToCreateIntegrationPageActionGroup.xml | 18 +++++ ...dminSearchIntegrationInGridActionGroup.xml | 23 +++++++ ...ateCreatedIntegrationEntityActionGroup.xml | 27 ++++++++ ...sageCreateIntegrationEntityActionGroup.xml | 19 ++++++ ...atedIntegrationEntityInGridActionGroup.xml | 17 +++++ .../Mftf/Section/AdminIntegrationsSection.xml | 30 +++++++++ .../Test/AdminUpdateIntegrationEntityTest.xml | 67 +++++++++++++++++++ ...grationEntityWithIncorrectPasswordTest.xml | 60 +++++++++++++++++ .../TestCase/UpdateIntegrationEntityTest.xml | 2 + 11 files changed, 301 insertions(+) create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminClickEditIntegrationEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertUpdatedIntegrationEntityInGridActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminClickEditIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminClickEditIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..b41ac5a6cc145 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminClickEditIntegrationEntityActionGroup.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="AdminClickEditIntegrationEntityActionGroup"> + <click stepKey="clickEditButton" selector="{{IntegrationsGridSection.edit}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml new file mode 100644 index 0000000000000..533bbb6760573 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.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"> + + <!--Fill Required Fields --> + <actionGroup name="AdminCreatesNewIntegrationActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="password" type="string"/> + </arguments> + <fillField stepKey="fillNameField" selector="{{AddNewIntegrationSection.name}}" userInput="{{name}}"/> + <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.saveButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml new file mode 100644 index 0000000000000..f31102419b665 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml @@ -0,0 +1,18 @@ +<?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"> + + <!--Click the "Add New Integration" Button --> + + <actionGroup name="AdminNavigateToCreateIntegrationPageActionGroup"> + <click stepKey="clickAddNewIntegrationButton" selector="{{IntegrationsGridSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNIntegrationPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml new file mode 100644 index 0000000000000..6e0b7dc3eb9d5 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.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="AdminSearchIntegrationInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <!--Reset Search Filters --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <!--Fill Integration Name Field --> + <fillField selector="{{IntegrationsGridSection.name}}" userInput="{{name}}" stepKey="filterByName"/> + <!--Click "Search" Button --> + <click selector="{{IntegrationsGridSection.search}}" stepKey="doFilter"/> + <waitForPageLoad stepKey="waitForSitemapPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..0f385776fbb67 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml @@ -0,0 +1,27 @@ +<?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"> + + <!--Fill Required Fields --> + <actionGroup name="AdminUpdateCreatedIntegrationEntityActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="password" type="string"/> + <argument name="endpoint" type="string"/> + <argument name="linkUrl" type="string"/> + </arguments> + <fillField stepKey="fillNameField" selector="{{AddNewIntegrationSection.name}}" userInput="{{name}}"/> + <fillField stepKey="fillEndpiontField" selector="{{AddNewIntegrationSection.endpoint}}" userInput="{{endpoint}}"/> + <fillField stepKey="fillLinkUrlField" selector="{{AddNewIntegrationSection.linkUrl}}" userInput="{{linkUrl}}"/> + <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.save}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..e928149c7f08f --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminMessageCreateIntegrationEntityActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="The integration '{{name}}' has been saved."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{IntegrationsGridSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{IntegrationsGridSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertUpdatedIntegrationEntityInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertUpdatedIntegrationEntityInGridActionGroup.xml new file mode 100644 index 0000000000000..c825db4e94728 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertUpdatedIntegrationEntityInGridActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AssertUpdatedIntegrationEntityInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <see userInput="{{name}}" selector="{{IntegrationsGridSection.rowByIndex('1')}}" stepKey="seeIntegrationEntity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml new file mode 100644 index 0000000000000..1f0c39af31e92 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml @@ -0,0 +1,30 @@ +<?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="IntegrationsGridSection"> + <element name="add" type="button" selector=".add"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + <element name="name" type="input" selector="#integrationGrid_filter_name"/> + <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> + <element name="remove" type="button" selector=".delete"/> + <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> + <element name="edit" type="button" selector=".edit"/> + </section> + + <section name="AddNewIntegrationSection"> + <element name="name" type="input" selector="#integration_properties_name"/> + <element name="password" type="input" selector="#integration_properties_current_password"/> + <element name="saveButton" type="button" selector="#save-split-button-button"/> + <element name="endpoint" type="input" selector="#integration_properties_endpoint"/> + <element name="linkUrl" type="input" selector="#integration_properties_identity_link_url"/> + <element name="save" type="button" selector=".save"/> + </section> +</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml new file mode 100644 index 0000000000000..41e05a6bbe107 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -0,0 +1,67 @@ +<?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="AdminUpdateIntegrationEntityTest"> + <annotations> + <features value="Integration"/> + <stories value="System Integration"/> + <title value="Updating System Integration Entity"/> + <description value="Admin Updates Created Integration"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login As Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <!-- Click the "Add New Integration" button --> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <!-- Create New Integration --> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> + <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Find Created Integration In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <!-- Open Integration Edit Page --> + <actionGroup ref="AdminClickEditIntegrationEntityActionGroup" stepKey="clickEditButton"/> + <!-- Update Integration Entity --> + <actionGroup ref="AdminUpdateCreatedIntegrationEntityActionGroup" stepKey="updateIntegrationEntity"> + <argument name="name" value="Integration Updated"/> + <argument name="endpoint" value="https://endpoint-updated.com"/> + <argument name="linkUrl" value="https://testlink-updated.com"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The integration 'Integration Updated' has been saved."/> + <argument value="success" name="messageType"/> + </actionGroup> + <!-- Assert Updated Entity In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findDeletedIntegration"> + <argument name="name" value="Integration Updated"/> + </actionGroup> + <actionGroup ref="AssertUpdatedIntegrationEntityInGridActionGroup" stepKey="seeIntegrationEntity"> + <argument name="name" value="Integration Updated"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml new file mode 100644 index 0000000000000..941a0030f0261 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml @@ -0,0 +1,60 @@ +<?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="AdminUpdateIntegrationEntityWithIncorrectPasswordTest"> + <annotations> + <features value="Integration"/> + <stories value="System Integration"/> + <title value="Updating System Integration Entity with Incorrect Password"/> + <description value="Admin Updates Created Integration with Incorrect Password"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login As Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <!-- Click the "Add New Integration" button --> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <!-- Create New Integration --> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> + <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Find Created Integration In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <!-- Open Integration Edit Page --> + <actionGroup ref="AdminClickEditIntegrationEntityActionGroup" stepKey="clickEditButton"/> + <!-- Update Integration Entity --> + <actionGroup ref="AdminUpdateCreatedIntegrationEntityActionGroup" stepKey="updateIntegrationEntity"> + <argument name="name" value="Integration Updated"/> + <argument name="endpoint" value="https://endpoint-updated.com"/> + <argument name="linkUrl" value="https://testlink-updated.com"/> + <argument name="password" value="12345test"/> + </actionGroup> + <!-- Assert Error Message --> + <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeErrorMessage"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again."/> + <argument value="error" name="messageType"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/UpdateIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/UpdateIntegrationEntityTest.xml index 8ae10e1314b07..97b31c5d6502d 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/UpdateIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/UpdateIntegrationEntityTest.xml @@ -18,6 +18,7 @@ <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationSuccessSaveMessage" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationForm" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="UpdateIntegrationEntityTestVariation2"> <data name="tag" xsi:type="string">stable:no</data> @@ -41,6 +42,7 @@ <data name="integration/data/resource_access" xsi:type="string">Custom</data> <data name="integration/data/resources" xsi:type="string">Sales</data> <constraint name="Magento\Integration\Test\Constraint\AssertIncorrectUserPassword" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 0f91a9df6e3bb9e976df37ad4d2cf3d8fda85b9e Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 29 Sep 2019 12:23:21 -0400 Subject: [PATCH 080/734] add MFTF tests for logging in and fix timezone issue (magento/magento2#22833: Short-term admin accounts) --- .../Model/UserExpiration/Validator.php | 10 ++++- ...CreateNewUserWithInvalidExpirationTest.xml | 2 +- ...inCreateNewUserWithValidExpirationTest.xml | 4 +- ...minLoginAdminUserWithInvalidExpiration.xml | 37 ++++++++++++++++++ ...AdminLoginAdminUserWithValidExpiration.xml | 39 +++++++++++++++++++ 5 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index a2211283e4cb3..b132c122cbc34 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -20,15 +20,21 @@ class Validator extends AbstractValidator /**@var \Magento\Framework\Stdlib\DateTime\TimezoneInterface */ private $timezone; + /**@var \Magento\Framework\Stdlib\DateTime\DateTime */ + private $dateTime; + /** * Validator constructor. * * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime */ public function __construct( - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime ) { $this->timezone = $timezone; + $this->dateTime = $dateTime; } /** @@ -45,7 +51,7 @@ public function isValid($value) $expiresAt = $value; $label = 'Expiration date'; if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { - $currentTime = $this->timezone->date()->getTimestamp(); + $currentTime = $this->dateTime->gmtTimestamp(); $utcExpiresAt = $this->timezone->convertConfigTimeToUtc($expiresAt); $expiresAt = $this->timezone->date($utcExpiresAt)->getTimestamp(); if ($expiresAt < $currentTime) { diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index 4fb14742f48d7..6bcd7be784f88 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -15,7 +15,7 @@ <description value="Create new user with invalid expiration date."/> <testCaseId value="" /> <severity value="CRITICAL"/> - <group value="security_userexpire"/> + <group value="security"/> </annotations> <before> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index acfa0464a6045..f798865ab2279 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -15,7 +15,7 @@ <description value="Create new user with valid expiration date."/> <testCaseId value="" /> <severity value="CRITICAL"/> - <group value="security_userexpire"/> + <group value="security"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -29,8 +29,6 @@ <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> - - <!-- TODO: get proper date format to match above --> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml new file mode 100644 index 0000000000000..f207d57c41084 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminLoginAdminUserWithInvalidExpiration"> + <annotations> + <features value="Security"/> + <stories value="Try to login as a user with an invalid expiration date."/> + <title value="Try to login as a user with an invalid expiration date"/> + <description value="Try to login as a user with an invalid expiration date."/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+1 minute" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="logout" stepKey="logout"/> + <wait time="60" stepKey="waitForUserToExpire"/> + <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> + <argument name="adminUser" value="{$grabUsername}"/> + <argument name="adminPassword" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage" /> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml new file mode 100644 index 0000000000000..92c538982859e --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminLoginAdminUserWithValidExpiration"> + <annotations> + <features value="Security"/> + <stories value="Login as a user with a valid expiration date."/> + <title value="Login as a user with a valid expiration date"/> + <description value="Login as a user with a valid expiration date."/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> + <argument name="adminUser" value="{$grabUsername}"/> + <argument name="adminPassword" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> + + </test> +</tests> From 45d648f322cbdc9b7bd16b12ffd97c5677a400b3 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 29 Sep 2019 13:52:43 -0400 Subject: [PATCH 081/734] Update unit tests to handle new timezone dependency (magento/magento2#22833: Short-term admin accounts) --- .../Model/UserExpiration/ValidatorTest.php | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php index bde468c31ac24..b000ebbac96cb 100644 --- a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -27,29 +27,42 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase */ private $dateTimeMock; + /**@var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Stdlib\DateTime\TimezoneInterface */ + private $timezoneMock; + protected function setUp() { $objectManager = new ObjectManager($this); $this->dateTimeMock = $this->createPartialMock(\Magento\Framework\Stdlib\DateTime\DateTime::class, ['gmtTimestamp']); + $this->timezoneMock = + $this->createPartialMock( + \Magento\Framework\Stdlib\DateTime\Timezone::class, + ['date', 'convertConfigTimeToUtc'] + ); $this->validator = $objectManager->getObject( \Magento\Security\Model\UserExpiration\Validator::class, - ['dateTime' => $this->dateTimeMock] + ['dateTime' => $this->dateTimeMock, 'timezone' => $this->timezoneMock] ); } public function testWithPastDate() { - $currentTime = '1562447687'; - $expireTime = '1561324487'; - $testDate = new \DateTime(); - $testDate->modify('-10 days'); - $this->dateTimeMock->expects(static::exactly(2))->method('gmtTimestamp') - ->willReturnOnConsecutiveCalls( - $currentTime, - $expireTime - ); - static::assertFalse($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); + /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ + $dateObject = $this->createMock(\DateTime::class); + $this->timezoneMock->expects(static::once()) + ->method('date') + ->will(static::returnValue($dateObject)); + + $currentDate = new \DateTime(); + $currentDate = $currentDate->getTimestamp(); + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + + $this->dateTimeMock->expects(static::once())->method('gmtTimestamp')->willReturn($currentDate); + $this->timezoneMock->expects(static::once())->method('date')->willReturn($expireDate); + $dateObject->expects(static::once())->method('getTimestamp')->willReturn($expireDate->getTimestamp()); + static::assertFalse($this->validator->isValid($expireDate->format('Y-m-d H:i:s'))); static::assertContains( '"Expiration date" must be later than the current date.', $this->validator->getMessages() @@ -58,16 +71,20 @@ public function testWithPastDate() public function testWithFutureDate() { - $currentTime = '1562447687'; - $expireTime = '1563290841'; - $testDate = new \DateTime(); - $testDate->modify('+10 days'); - $this->dateTimeMock->expects(static::exactly(2))->method('gmtTimestamp') - ->willReturnOnConsecutiveCalls( - $currentTime, - $expireTime - ); - static::assertTrue($this->validator->isValid($testDate->format('Y-m-d H:i:s'))); + /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ + $dateObject = $this->createMock(\DateTime::class); + $this->timezoneMock->expects(static::once()) + ->method('date') + ->will(static::returnValue($dateObject)); + $currentDate = new \DateTime(); + $currentDate = $currentDate->getTimestamp(); + $expireDate = new \DateTime(); + $expireDate->modify('+10 days'); + + $this->dateTimeMock->expects(static::once())->method('gmtTimestamp')->willReturn($currentDate); + $this->timezoneMock->expects(static::once())->method('date')->willReturn($expireDate); + $dateObject->expects(static::once())->method('getTimestamp')->willReturn($expireDate->getTimestamp()); + static::assertTrue($this->validator->isValid($expireDate->format('Y-m-d H:i:s'))); static::assertEquals([], $this->validator->getMessages()); } } From 970510c6e1494cddfcdb756bc0262eaafe0b22f2 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 29 Sep 2019 16:51:01 -0400 Subject: [PATCH 082/734] add copyright and fix failing unrelated test (magento/magento2#22833: Short-term admin accounts) --- .../Test/Mftf/Test/AdminConfigurationPermissionTest.xml | 3 ++- .../Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml index 58e809ec45c4a..a079193e97a65 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml @@ -35,8 +35,9 @@ <waitForPageLoad time="30" stepKey="wait2"/> <seeInField selector="{{AdminEditUserSection.usernameTextField}}" userInput="$$noReportUser.username$$" stepKey="seeUsernameInField"/> <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillCurrentPassword"/> - <click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="clickUserRoleTab"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="clickUserRoleTab"/> <fillField selector="{{AdminEditUserSection.roleNameFilterTextField}}" userInput="$$noReportUserRole.rolename$$" stepKey="fillRoleNameSearch"/> <click selector="{{AdminEditUserSection.searchButton}}" stepKey="clickSearchButtonUserRole"/> <waitForPageLoad time="10" stepKey="wait3"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index f207d57c41084..e1e0a53606a57 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -1,4 +1,11 @@ <?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"> From 78dc3b5ebb2e7d77a444401c349eff195ad535ee Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 29 Sep 2019 17:22:04 -0400 Subject: [PATCH 083/734] add copyright (magento/magento2#22833: Short-term admin accounts) --- .../Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index 92c538982859e..f368a71d84d85 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -1,4 +1,11 @@ <?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"> From 171c4c9420fd071d6ca90924cca22a075e12f12f Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 30 Sep 2019 09:00:10 -0400 Subject: [PATCH 084/734] increase lead time for user expiration test (magento/magento2#22833: Short-term admin accounts) --- .../Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index e1e0a53606a57..ee8dfdb5cb085 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -25,7 +25,7 @@ </before> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+1 minute" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <generateDate date="+2 minute" format="M d, Y h:i:s" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> @@ -33,7 +33,7 @@ <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> <actionGroup ref="logout" stepKey="logout"/> - <wait time="60" stepKey="waitForUserToExpire"/> + <wait time="120" stepKey="waitForUserToExpire"/> <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> <argument name="adminUser" value="{$grabUsername}"/> <argument name="adminPassword" value="{$grabPassword}"/> From 89b8512cc209a67188feb60870bbd9c13a2f39fe Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 30 Sep 2019 15:10:39 -0400 Subject: [PATCH 085/734] fixes from code review (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/Plugin/AuthSession.php | 2 +- .../Model/ResourceModel/UserExpiration.php | 2 +- .../UserExpiration/Collection.php | 13 +++---- .../Magento/Security/Model/UserExpiration.php | 2 ++ .../Security/Model/UserExpirationManager.php | 12 ++++--- .../Observer/AdminUserAuthenticateBefore.php | 15 ++++---- .../Unit/Model/Plugin/AuthSessionTest.php | 20 ++++++++--- ...hp => AdminUserAuthenticateBeforeTest.php} | 34 ++++++++++++------- .../Magento/User/etc/db_schema_whitelist.json | 2 +- .../Model/UserExpirationManagerTest.php | 6 ++-- 10 files changed, 63 insertions(+), 45 deletions(-) rename app/code/Magento/Security/Test/Unit/Observer/{BeforeAdminUserAuthenticateTest.php => AdminUserAuthenticateBeforeTest.php} (79%) diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index 007f0871f70d1..910d0058e7021 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -72,7 +72,7 @@ public function __construct( public function aroundProlong(Session $session, \Closure $proceed) { if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus() || - $this->userExpirationManager->userIsExpired($session->getUser())) { + $this->userExpirationManager->isUserExpired($session->getUser()->getId())) { $session->destroy(); $this->addUserLogoutNotification(); return null; diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php index ec4a2bf7e42a0..71a331a178006 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -35,7 +35,7 @@ class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractD public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, - $connectionName = null + ?string $connectionName = null ) { parent::__construct($context, $connectionName); $this->timezone = $timezone; diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php index dee92c599f4de..2f2971bc90225 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php @@ -33,21 +33,18 @@ protected function _construct() /** * Filter for expired, active users. * - * @param string $now * @return $this */ - public function addActiveExpiredUsersFilter($now = null): Collection + public function addActiveExpiredUsersFilter(): Collection { - if ($now === null) { - $now = new \DateTime(); - $now->format('Y-m-d H:i:s'); - } + $currentTime = new \DateTime(); + $currentTime->format('Y-m-d H:i:s'); $this->getSelect()->joinLeft( ['user' => $this->getTable('admin_user')], 'main_table.user_id = user.user_id', ['is_active'] ); - $this->addFieldToFilter('expires_at', ['lt' => $now]) + $this->addFieldToFilter('expires_at', ['lt' => $currentTime]) ->addFieldToFilter('user.is_active', 1); return $this; @@ -59,7 +56,7 @@ public function addActiveExpiredUsersFilter($now = null): Collection * @param int[] $userIds * @return Collection */ - public function addUserIdsFilter($userIds = []): Collection + public function addUserIdsFilter(array $userIds = []): Collection { return $this->addFieldToFilter('main_table.user_id', ['in' => $userIds]); } diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index 0e70243f30117..08f60ade672b6 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -9,6 +9,8 @@ /** * Admin User Expiration model. + * @method string getUserId() + * @method \Magento\Security\Model\UserExpiration setUserId($userId) * @method string getExpiresAt() * @method \Magento\Security\Model\UserExpiration setExpiresAt($value) */ diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index aa3ac177bb914..9b26935c1baf2 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -10,7 +10,9 @@ use Magento\Security\Model\ResourceModel\UserExpiration\Collection as ExpiredUsersCollection; /** - * Class to handle admin user expirations. + * Class to handle admin user expirations. Temporary admin users can be created with an expiration + * date that, once past, will not allow them to login to the admin. A cron is run to periodically check for expired + * users and, if found, will deactivate them. * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class UserExpirationManager @@ -98,26 +100,26 @@ public function deactivateExpiredUsers(?array $userIds = null): void // delete expired records $expiredRecordIds = $expiredRecords->getAllIds(); - $expiredRecords->walk('delete'); // set user is_active to 0 $users = $this->userCollectionFactory->create() ->addFieldToFilter('main_table.user_id', ['in' => $expiredRecordIds]); $users->setDataToAll('is_active', 0)->save(); + $expiredRecords->walk('delete'); } /** * Check if the given user is expired. * - * @param \Magento\User\Model\User $user + * @param string $userId * @return bool */ - public function userIsExpired(\Magento\User\Model\User $user): bool + public function isUserExpired(string $userId): bool { $isExpired = false; /** @var \Magento\Security\Model\UserExpiration $expiredRecord */ $expiredRecord = $this->userExpirationCollectionFactory->create() - ->addExpiredRecordsForUserFilter($user->getId()) + ->addExpiredRecordsForUserFilter($userId) ->getFirstItem(); if ($expiredRecord && $expiredRecord->getId()) { $expiresAt = $this->dateTime->timestamp($expiredRecord->getExpiresAt()); diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index d40f25a023bbc..1ffdd1cd1d11e 100644 --- a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -25,22 +25,22 @@ class AdminUserAuthenticateBefore implements ObserverInterface private $userExpirationManager; /** - * @var \Magento\User\Model\User + * @var \Magento\User\Model\UserFactory */ - private $user; + private $userFactory; /** * AdminUserAuthenticateBefore constructor. * * @param UserExpirationManager $userExpirationManager - * @param \Magento\User\Model\User $user + * @param \Magento\User\Model\UserFactory $userFactory */ public function __construct( \Magento\Security\Model\UserExpirationManager $userExpirationManager, - \Magento\User\Model\User $user + \Magento\User\Model\UserFactory $userFactory ) { $this->userExpirationManager = $userExpirationManager; - $this->user = $user; + $this->userFactory = $userFactory; } /** @@ -53,10 +53,11 @@ public function __construct( public function execute(Observer $observer) { $username = $observer->getEvent()->getUsername(); + $user = $this->userFactory->create(); /** @var \Magento\User\Model\User $user */ - $user = $this->user->loadByUsername($username); + $user->loadByUsername($username); - if ($user->getId() && $this->userExpirationManager->userIsExpired($user)) { + if ($user->getId() && $this->userExpirationManager->isUserExpired($user->getId())) { $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); throw new AuthenticationException( __( diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index 19c845a66bd86..dc116d059867a 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -79,7 +79,7 @@ public function setUp() $this->userExpirationManagerMock = $this->createPartialMock( \Magento\Security\Model\UserExpirationManager::class, - ['userIsExpired'] + ['isUserExpired'] ); $this->userMock = $this->createMock(\Magento\User\Model\User::class); @@ -183,6 +183,7 @@ public function testAroundProlongSessionIsActiveUserIsExpired() return $result; }; + $adminUserId = '12345'; $this->currentSessionMock->expects($this->once()) ->method('isLoggedInStatus') ->willReturn(true); @@ -191,9 +192,13 @@ public function testAroundProlongSessionIsActiveUserIsExpired() ->method('getUser') ->willReturn($this->userMock); + $this->userMock->expects($this->once()) + ->method('getId') + ->willReturn($adminUserId); + $this->userExpirationManagerMock->expects($this->once()) - ->method('userIsExpired') - ->with($this->userMock) + ->method('isUserExpired') + ->with($adminUserId) ->willReturn(true); $this->authSessionMock->expects($this->once()) @@ -225,6 +230,7 @@ public function testAroundProlongSessionIsActive() return $result; }; + $adminUserId = '12345'; $this->currentSessionMock->expects($this->any()) ->method('isLoggedInStatus') ->willReturn(true); @@ -233,9 +239,13 @@ public function testAroundProlongSessionIsActive() ->method('getUser') ->willReturn($this->userMock); + $this->userMock->expects($this->once()) + ->method('getId') + ->willReturn($adminUserId); + $this->userExpirationManagerMock->expects($this->once()) - ->method('userIsExpired') - ->with($this->userMock) + ->method('isUserExpired') + ->with($adminUserId) ->willReturn(false); $this->adminSessionsManagerMock->expects($this->any()) diff --git a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php similarity index 79% rename from app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php rename to app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php index b3aafa7cdfbab..0ae58756d3f77 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/BeforeAdminUserAuthenticateTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -12,7 +12,7 @@ * * @package Magento\Security\Test\Unit\Observer */ -class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase +class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager @@ -29,6 +29,11 @@ class BeforeAdminUserAuthenticateTest extends \PHPUnit\Framework\TestCase */ private $userMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\UserFactory + */ + private $userFactoryMock; + /** * @var \Magento\Security\Observer\AdminUserAuthenticateBefore */ @@ -60,14 +65,15 @@ protected function setUp() $this->userExpirationManagerMock = $this->createPartialMock( \Magento\Security\Model\UserExpirationManager::class, - ['userIsExpired', 'deactivateExpiredUsers'] + ['isUserExpired', 'deactivateExpiredUsers'] ); + $this->userFactoryMock = $this->createPartialMock(\Magento\User\Model\UserFactory::class, ['create']); $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['loadByUsername', 'getId']); $this->observer = $this->objectManager->getObject( \Magento\Security\Observer\AdminUserAuthenticateBefore::class, [ 'userExpirationManager' => $this->userExpirationManagerMock, - 'user' => $this->userMock, + 'userFactory' => $this->userFactoryMock, ] ); $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); @@ -85,17 +91,18 @@ protected function setUp() */ public function testWithExpiredUser() { - $adminUserId = 123; + $adminUserId = '123'; $username = 'testuser'; $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); - $this->userMock->expects(static::once())->method('loadByUsername')->willReturn($this->userMock); + $this->userFactoryMock->expects(static::once())->method('create')->willReturn($this->userMock); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturnSelf(); $this->userExpirationManagerMock->expects(static::once()) - ->method('userIsExpired') - ->with($this->userMock) + ->method('isUserExpired') + ->with($adminUserId) ->willReturn(true); - $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($adminUserId); + $this->userMock->expects(static::exactly(3))->method('getId')->willReturn($adminUserId); $this->userExpirationManagerMock->expects(static::once()) ->method('deactivateExpiredUsers') ->with([$adminUserId]) @@ -105,15 +112,16 @@ public function testWithExpiredUser() public function testWithNonExpiredUser() { - $adminUserId = 123; + $adminUserId = '123'; $username = 'testuser'; $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); - $this->userMock->expects(static::once())->method('loadByUsername')->willReturn($this->userMock); - $this->userMock->expects(static::once())->method('getId')->willReturn($adminUserId); + $this->userFactoryMock->expects(static::once())->method('create')->willReturn($this->userMock); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturnSelf(); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($adminUserId); $this->userExpirationManagerMock->expects(static::once()) - ->method('userIsExpired') - ->with($this->userMock) + ->method('isUserExpired') + ->with($adminUserId) ->willReturn(false); $this->observer->execute($this->eventObserverMock); } diff --git a/app/code/Magento/User/etc/db_schema_whitelist.json b/app/code/Magento/User/etc/db_schema_whitelist.json index c214cae477cd7..2af77c0d8455b 100644 --- a/app/code/Magento/User/etc/db_schema_whitelist.json +++ b/app/code/Magento/User/etc/db_schema_whitelist.json @@ -42,4 +42,4 @@ "ADMIN_PASSWORDS_USER_ID_ADMIN_USER_USER_ID": true } } -} +} \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 64829566ea3b4..1d41771416c87 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -9,7 +9,7 @@ /** * Tests for \Magento\Security\Model\UserExpirationManager - * + * @magentoAppArea adminhtml * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase @@ -42,8 +42,6 @@ class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->objectManager->get(\Magento\Framework\Config\ScopeInterface::class) - ->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); $this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class); $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class); $this->adminSessionInfo = $this->objectManager->create(\Magento\Security\Model\AdminSessionInfo::class); @@ -59,7 +57,7 @@ public function testUserIsExpired() { $adminUserNameFromFixture = 'adminUserExpired'; $user = $this->loadUserByUsername($adminUserNameFromFixture); - static::assertTrue($this->userExpirationManager->userIsExpired($user)); + static::assertTrue($this->userExpirationManager->isUserExpired($user->getId())); } /** From bcef5909ac75be4762e08a8bedbcd7e9cce14863 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 30 Sep 2019 19:00:01 -0400 Subject: [PATCH 086/734] replace changes in User module with plugin for admin user form (magento/magento2#22833: Short-term admin accounts) --- .../Security/Model/Plugin/AdminUserForm.php | 71 +++++++++++++++++++ .../Magento/Security/etc/adminhtml/di.xml | 3 + .../Magento/User/Block/User/Edit/Tab/Main.php | 19 ----- 3 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 app/code/Magento/Security/Model/Plugin/AdminUserForm.php diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php new file mode 100644 index 0000000000000..2ce8cce115dca --- /dev/null +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\Plugin; + +/** + * Add the `expires_at` form field to the User main form. + * + * @package Magento\Security\Model\Plugin + */ +class AdminUserForm +{ + + /** + * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface + */ + private $localeDate; + + /** + * UserForm constructor. + * + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + */ + public function __construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate) + { + $this->localeDate = $localeDate; + } + + /** + * Add the `expires_at` field to the admin user edit form. + * @param \Magento\User\Block\User\Edit\Tab\Main $subject + * @param \Closure $proceed + * @return mixed + */ + public function aroundGetFormHtml( + \Magento\User\Block\User\Edit\Tab\Main $subject, + \Closure $proceed + ) { + /** @var \Magento\Framework\Data\Form $form */ + $form = $subject->getForm(); + if (is_object($form)) { + $dateFormat = $this->localeDate->getDateFormat( + \IntlDateFormatter::MEDIUM + ); + $timeFormat = $this->localeDate->getTimeFormat( + \IntlDateFormatter::MEDIUM + ); + $fieldset = $form->getElement('base_fieldset'); + $fieldset->addField( + 'expires_at', + 'date', + [ + 'name' => 'expires_at', + 'label' => __('Expiration Date'), + 'title' => __('Expiration Date'), + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, + 'class' => 'validate-date', + ] + ); + + $subject->setForm($form); + } + + return $proceed(); + } +} diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml index 8cf5b610ef3c5..388a1eac742a5 100644 --- a/app/code/Magento/Security/etc/adminhtml/di.xml +++ b/app/code/Magento/Security/etc/adminhtml/di.xml @@ -31,4 +31,7 @@ </argument> </arguments> </type> + <type name="Magento\User\Block\User\Edit\Tab\Main"> + <plugin name="user_expiration_user_form_field" type="Magento\Security\Model\Plugin\AdminUserForm"/> + </type> </config> diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index c92714ec1eb5a..27e00483733d0 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -170,25 +170,6 @@ protected function _prepareForm() ); } - $dateFormat = $this->_localeDate->getDateFormat( - \IntlDateFormatter::MEDIUM - ); - $timeFormat = $this->_localeDate->getTimeFormat( - \IntlDateFormatter::MEDIUM - ); - $baseFieldset->addField( - 'expires_at', - 'date', - [ - 'name' => 'expires_at', - 'label' => __('Expiration Date'), - 'title' => __('Expiration Date'), - 'date_format' => $dateFormat, - 'time_format' => $timeFormat, - 'class' => 'validate-date', - ] - ); - $baseFieldset->addField('user_roles', 'hidden', ['name' => 'user_roles', 'id' => '_user_roles']); $currentUserVerificationFieldset = $form->addFieldset( From 58511261b6ed15192b4014adc96e8a418df41e16 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Tue, 1 Oct 2019 16:24:42 +0530 Subject: [PATCH 087/734] Added Function and Integration --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 3 +++ .../Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 3f178ae02102a..e6e4f14854fc6 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -94,7 +94,10 @@ </annotations> <seeElement selector="{{AdminOrderFormAccountSection.requiredGroup}}" stepKey="seeCustomerGroupRequired"/> + <checkOption selector="{{AdminOrderFormItemsSection.emailRequiredToCreateOrder('1')}}" stepKey="selectEmailRequiredOption"/> <seeElement selector="{{AdminOrderFormAccountSection.requiredEmail}}" stepKey="seeEmailRequired"/> + <checkOption selector="{{AdminOrderFormItemsSection.emailRequiredToCreateOrder('0')}}" stepKey="unselectEmailRequiredOption"/> + <seeElement selector="{{AdminOrderFormAccountSection.noRequiredEmail}}" stepKey="seeNoEmailRequired"/> <clearField selector="{{AdminOrderFormAccountSection.email}}" stepKey="clearEmailField"/> <clearField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" stepKey="clearFirstNameField"/> <clearField selector="{{AdminOrderFormBillingAddressSection.LastName}}" stepKey="clearLastNameField"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 430211e52bfb5..f374b15ea2491 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -13,6 +13,7 @@ <element name="email" type="input" selector="#email"/> <element name="requiredGroup" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-group-id']"/> <element name="requiredEmail" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-email']"/> + <element name="noRequiredEmail" type="text" selector=".admin__field[data-ui-id='billing-address-fieldset-element-form-field-email']"/> <element name="defaultGeneral" type="text" selector="//*[contains(text(),'General')]" timeout="15"/> <element name="emailErrorMessage" type="text" selector="#email-error"/> </section> From 627273b5c49f9298b5094de9e62370bbbc85eb4f Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 1 Oct 2019 10:27:35 -0400 Subject: [PATCH 088/734] phpcs fixes (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/Plugin/AdminUserForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index 2ce8cce115dca..5eca5f3af3b4c 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -32,6 +32,7 @@ public function __construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface /** * Add the `expires_at` field to the admin user edit form. + * * @param \Magento\User\Block\User\Edit\Tab\Main $subject * @param \Closure $proceed * @return mixed From 7772d52ee9cbdd12346d8d2bc9583a4c05193884 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 4 Oct 2019 12:28:25 +0530 Subject: [PATCH 089/734] functional test changes --- .../Mftf/ActionGroup/AdminOrderActionGroup.xml | 15 ++++++--------- .../Mftf/Section/AdminOrderFormAccountSection.xml | 3 +-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index e6e4f14854fc6..f0f50e757e38f 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -92,12 +92,9 @@ <annotations> <description>Clears the Email, First Name, Last Name, Street Line 1, City, Postal Code and Phone fields when adding an Order and then verifies that they are required after attempting to Save.</description> </annotations> - + <seeElement selector="{{AdminOrderFormAccountSection.requiredGroup}}" stepKey="seeCustomerGroupRequired"/> - <checkOption selector="{{AdminOrderFormItemsSection.emailRequiredToCreateOrder('1')}}" stepKey="selectEmailRequiredOption"/> <seeElement selector="{{AdminOrderFormAccountSection.requiredEmail}}" stepKey="seeEmailRequired"/> - <checkOption selector="{{AdminOrderFormItemsSection.emailRequiredToCreateOrder('0')}}" stepKey="unselectEmailRequiredOption"/> - <seeElement selector="{{AdminOrderFormAccountSection.noRequiredEmail}}" stepKey="seeNoEmailRequired"/> <clearField selector="{{AdminOrderFormAccountSection.email}}" stepKey="clearEmailField"/> <clearField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" stepKey="clearFirstNameField"/> <clearField selector="{{AdminOrderFormBillingAddressSection.LastName}}" stepKey="clearLastNameField"/> @@ -184,7 +181,7 @@ <annotations> <description>EXTENDS: addConfigurableProductToOrder. Selects the provided Option to the Configurable Product.</description> </annotations> - + <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" userInput="{{option.label}}" stepKey="selectionConfigurableOption"/> </actionGroup> @@ -198,7 +195,7 @@ <argument name="option"/> <argument name="quantity" type="string"/> </arguments> - + <click selector="{{AdminOrderFormItemsSection.configure}}" stepKey="clickConfigure"/> <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> <wait time="2" stepKey="waitForOptionsToLoad"/> @@ -216,7 +213,7 @@ <argument name="product"/> <argument name="quantity" type="string" defaultValue="1"/> </arguments> - + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterBundle"/> <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchBundle"/> @@ -238,7 +235,7 @@ <arguments> <argument name="price" type="string"/> </arguments> - + <grabTextFrom selector="{{AdminOrderFormItemsSection.rowPrice('1')}}" stepKey="grabProductPriceFromGrid" after="clickOk"/> <assertEquals stepKey="assertProductPriceInGrid" message="Bundle product price in grid should be equal {{price}}" after="grabProductPriceFromGrid"> <expectedResult type="string">{{price}}</expectedResult> @@ -519,7 +516,7 @@ <argument name="product"/> <argument name="customer"/> </arguments> - + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> <waitForPageLoad stepKey="waitForNewOrderPageOpened"/> <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index f374b15ea2491..65d86c2940bab 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -12,8 +12,7 @@ <element name="group" type="select" selector="#group_id"/> <element name="email" type="input" selector="#email"/> <element name="requiredGroup" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-group-id']"/> - <element name="requiredEmail" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-email']"/> - <element name="noRequiredEmail" type="text" selector=".admin__field[data-ui-id='billing-address-fieldset-element-form-field-email']"/> + <element name="requiredEmail" type="text" selector=".admin__field[data-ui-id='billing-address-fieldset-element-form-field-email']"/> <element name="defaultGeneral" type="text" selector="//*[contains(text(),'General')]" timeout="15"/> <element name="emailErrorMessage" type="text" selector="#email-error"/> </section> From 6915dc8089a1403764e761c869a1e4130f636269 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 4 Oct 2019 16:12:42 +0530 Subject: [PATCH 090/734] Added Function and Integration email validation --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 1 - .../Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index f0f50e757e38f..3a4bb0b2a045e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -106,7 +106,6 @@ <clearField selector="{{AdminOrderFormBillingAddressSection.Phone}}" stepKey="clearPhoneField"/> <seeElement selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="seeShippingMethodNotSelected"/> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="trySubmitOrder"/> - <see selector="{{AdminOrderFormBillingAddressSection.emailError}}" userInput="This is a required field." stepKey="seeThatEmailIsRequired"/> <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" userInput="This is a required field." stepKey="seeFirstNameRequired"/> <see selector="{{AdminOrderFormBillingAddressSection.lastNameError}}" userInput="This is a required field." stepKey="seeLastNameRequired"/> <see selector="{{AdminOrderFormBillingAddressSection.streetAddressError}}" userInput="This is a required field." stepKey="seeStreetRequired"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml index 2d1a4d5a4cbae..7fe241c3a118d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml @@ -27,8 +27,6 @@ <element name="VatNumber" type="input" selector="#order-billing_address_vat_id" timeout="30"/> <element name="ValidateVatNumber" type="button" selector="#order-billing_address_vat_id + .actions>button.action-default" timeout="30"/> <element name="SaveAddress" type="checkbox" selector="#order-billing_address_save_in_address_book"/> - - <element name="emailError" type="text" selector="#email-error"/> <element name="firstNameError" type="text" selector="#order-billing_address_firstname-error"/> <element name="lastNameError" type="text" selector="#order-billing_address_lastname-error"/> <element name="streetAddressError" type="text" selector="#order-billing_address_street0-error"/> From 6b8e89e4b1541fdd0eb603a72bfcea1020d5467d Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 7 Oct 2019 20:23:23 -0400 Subject: [PATCH 091/734] fix invalid date input and add translations (magento/magento2#22833: Short-term admin accounts) --- .../Model/UserExpiration/Validator.php | 14 +++++++++----- .../Model/UserExpiration/ValidatorTest.php | 19 +++++++++++++++---- app/code/Magento/Security/i18n/en_US.csv | 2 ++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index b132c122cbc34..c71b2ecb849a2 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -51,11 +51,15 @@ public function isValid($value) $expiresAt = $value; $label = 'Expiration date'; if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { - $currentTime = $this->dateTime->gmtTimestamp(); - $utcExpiresAt = $this->timezone->convertConfigTimeToUtc($expiresAt); - $expiresAt = $this->timezone->date($utcExpiresAt)->getTimestamp(); - if ($expiresAt < $currentTime) { - $messages['expires_at'] = __('"%1" must be later than the current date.', $label); + if (strtotime($expiresAt)) { + $currentTime = $this->dateTime->gmtTimestamp(); + $utcExpiresAt = $this->timezone->convertConfigTimeToUtc($expiresAt); + $expiresAt = $this->timezone->date($utcExpiresAt)->getTimestamp(); + if ($expiresAt < $currentTime) { + $messages['expires_at'] = __('"%1" must be later than the current date.', $label); + } + } else { + $messages['expires_at'] = __('"%1" is not a valid date.', $label); } } $this->_addMessages($messages); diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php index b000ebbac96cb..1cc53e76ee969 100644 --- a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -46,13 +46,24 @@ protected function setUp() ); } + public function testWithInvalidDate() + { + $expireDate = 'invalid_date'; + + static::assertFalse($this->validator->isValid($expireDate)); + static::assertContains( + '"Expiration date" is not a valid date.', + $this->validator->getMessages() + ); + } + public function testWithPastDate() { /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ $dateObject = $this->createMock(\DateTime::class); $this->timezoneMock->expects(static::once()) - ->method('date') - ->will(static::returnValue($dateObject)); + ->method('date') + ->will(static::returnValue($dateObject)); $currentDate = new \DateTime(); $currentDate = $currentDate->getTimestamp(); @@ -74,8 +85,8 @@ public function testWithFutureDate() /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ $dateObject = $this->createMock(\DateTime::class); $this->timezoneMock->expects(static::once()) - ->method('date') - ->will(static::returnValue($dateObject)); + ->method('date') + ->will(static::returnValue($dateObject)); $currentDate = new \DateTime(); $currentDate = $currentDate->getTimestamp(); $expireDate = new \DateTime(); diff --git a/app/code/Magento/Security/i18n/en_US.csv b/app/code/Magento/Security/i18n/en_US.csv index 4329e0747d08e..3d5bb6e8b59d4 100644 --- a/app/code/Magento/Security/i18n/en_US.csv +++ b/app/code/Magento/Security/i18n/en_US.csv @@ -27,3 +27,5 @@ None,None "Min Time Between Password Reset Requests","Min Time Between Password Reset Requests" "Delay in minutes between password reset requests. Use 0 to disable.","Delay in minutes between password reset requests. Use 0 to disable." """%1"" must be later than the current date.","""%1"" must be later than the current date." +"User Expiration","User Expiration" +"""%1"" is not a valid date.","""%1"" is not a valid date." From 9e6f316cd1e9cc11310bb15225cf7b208ff6cfc1 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 8 Oct 2019 18:23:56 -0400 Subject: [PATCH 092/734] fix invalid date input in mftf test (magento/magento2#22833: Short-term admin accounts) --- .../Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index ee8dfdb5cb085..f05863b387b1a 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -17,7 +17,7 @@ <description value="Try to login as a user with an invalid expiration date."/> <testCaseId value=""/> <severity value="CRITICAL"/> - <group value="security"/> + <group value="debug"/> </annotations> <before> @@ -25,12 +25,14 @@ </before> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+2 minute" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <generateDate date="+2 minute" format="M d, Y h:i A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> <actionGroup ref="logout" stepKey="logout"/> <wait time="120" stepKey="waitForUserToExpire"/> From 20f2f0e052ece2df3815977ffb9dc9cc72d6616f Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@adobe.com> Date: Wed, 9 Oct 2019 09:54:49 -0500 Subject: [PATCH 093/734] Fixed test group name --- .../Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index f05863b387b1a..a752c484238fa 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -17,7 +17,7 @@ <description value="Try to login as a user with an invalid expiration date."/> <testCaseId value=""/> <severity value="CRITICAL"/> - <group value="debug"/> + <group value="security"/> </annotations> <before> From 65ae5b064415165f2df56435af392d4fa44d3339 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 11 Oct 2019 15:21:38 +0530 Subject: [PATCH 094/734] Integration test --- .../Adminhtml/Order/Create/Form/AccountTest.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index b75501911be6b..4174e3108c34e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -102,18 +102,6 @@ public function testGetFormWithCustomer() sprintf('Unexpected field "%s" in form.', $element->getId()) ); } - - self::assertContains( - '<option value="'.$customerGroup.'" selected="selected">Wholesale</option>', - $content, - 'The Customer Group specified for the chosen customer should be selected.' - ); - - self::assertContains( - 'value="'.$customer->getEmail().'"', - $content, - 'The Customer Email specified for the chosen customer should be input ' - ); } /** From 93fc0eeea9618ed1008a661ff691a08b81abd91e Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 11 Oct 2019 17:58:33 +0530 Subject: [PATCH 095/734] integration test fails --- .../Adminhtml/Order/Create/Form/AccountTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index 4174e3108c34e..b75501911be6b 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -102,6 +102,18 @@ public function testGetFormWithCustomer() sprintf('Unexpected field "%s" in form.', $element->getId()) ); } + + self::assertContains( + '<option value="'.$customerGroup.'" selected="selected">Wholesale</option>', + $content, + 'The Customer Group specified for the chosen customer should be selected.' + ); + + self::assertContains( + 'value="'.$customer->getEmail().'"', + $content, + 'The Customer Email specified for the chosen customer should be input ' + ); } /** From 1b1c12ed064f350a58a509421c4f7437be7f24eb Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 13 Oct 2019 18:51:39 -0400 Subject: [PATCH 096/734] fix dates in MFTF test; update admin user form plugin to set the user expiration value (#22833) --- .../Security/Model/Plugin/AdminUserForm.php | 38 +++++- .../Security/Observer/AfterAdminUserLoad.php | 62 --------- ...CreateNewUserWithInvalidExpirationTest.xml | 3 +- ...inCreateNewUserWithValidExpirationTest.xml | 14 +- ...minLoginAdminUserWithInvalidExpiration.xml | 2 +- ...AdminLoginAdminUserWithValidExpiration.xml | 3 +- .../Unit/Observer/AfterAdminUserLoadTest.php | 120 ------------------ app/code/Magento/Security/etc/events.xml | 3 - .../Observer/AfterAdminUserLoadTest.php | 46 ------- 9 files changed, 54 insertions(+), 237 deletions(-) delete mode 100644 app/code/Magento/Security/Observer/AfterAdminUserLoad.php delete mode 100644 app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index 5eca5f3af3b4c..d61de5d67adfa 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -7,6 +7,8 @@ namespace Magento\Security\Model\Plugin; +use Magento\Security\Model\UserExpiration; + /** * Add the `expires_at` form field to the User main form. * @@ -20,14 +22,31 @@ class AdminUserForm */ private $localeDate; + /** + * @var \Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResource; + + /** + * @var UserExpirationFactory + */ + private $userExpirationFactory; + /** * UserForm constructor. * * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + * @param UserExpiration $userExpiration + * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource */ - public function __construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate) - { + public function __construct( + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, + \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, + \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + ) { $this->localeDate = $localeDate; + $this->userExpirationResource = $userExpirationResource; + $this->userExpirationFactory = $userExpirationFactory; } /** @@ -51,6 +70,12 @@ public function aroundGetFormHtml( \IntlDateFormatter::MEDIUM ); $fieldset = $form->getElement('base_fieldset'); + $userIdField = $fieldset->getElements()->searchById('user_id'); + $userExpirationValue = null; + if ($userIdField) { + $userId = $userIdField->getValue(); + $userExpirationValue = $this->loadUserExpirationByUserId($userId); + } $fieldset->addField( 'expires_at', 'date', @@ -61,6 +86,7 @@ public function aroundGetFormHtml( 'date_format' => $dateFormat, 'time_format' => $timeFormat, 'class' => 'validate-date', + 'value' => $userExpirationValue, ] ); @@ -69,4 +95,12 @@ public function aroundGetFormHtml( return $proceed(); } + + private function loadUserExpirationByUserId($userId) + { + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->userExpirationFactory->create(); + $this->userExpirationResource->load($userExpiration, $userId); + return $userExpiration->getExpiresAt(); + } } diff --git a/app/code/Magento/Security/Observer/AfterAdminUserLoad.php b/app/code/Magento/Security/Observer/AfterAdminUserLoad.php deleted file mode 100644 index 133b26c2c01a2..0000000000000 --- a/app/code/Magento/Security/Observer/AfterAdminUserLoad.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Security\Observer; - -use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; - -/** - * Load UserExpiration on admin user. Only needed in the admin to load the expires_at when editing users. - */ -class AfterAdminUserLoad implements ObserverInterface -{ - /** - * @var \Magento\Security\Model\UserExpirationFactory - */ - private $userExpirationFactory; - - /** - * @var \Magento\Security\Model\ResourceModel\UserExpiration - */ - private $userExpirationResource; - - /** - * AfterAdminUserLoad constructor. - * - * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory - * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource - */ - public function __construct( - \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, - \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource - ) { - - $this->userExpirationFactory = $userExpirationFactory; - $this->userExpirationResource = $userExpirationResource; - } - - /** - * Set the user expiration date onto the user. - * - * @param Observer $observer - * @return void - */ - public function execute(Observer $observer) - { - /* @var $user \Magento\User\Model\User */ - $user = $observer->getEvent()->getObject(); - if ($user->getId()) { - /** @var \Magento\Security\Model\UserExpiration $userExpiration */ - $userExpiration = $this->userExpirationFactory->create(); - $this->userExpirationResource->load($userExpiration, $user->getId()); - if ($userExpiration->getExpiresAt()) { - $user->setExpiresAt($userExpiration->getExpiresAt()); - } - } - } -} diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index 6bcd7be784f88..b585a21dd743b 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -16,6 +16,7 @@ <testCaseId value="" /> <severity value="CRITICAL"/> <group value="security"/> + <group value="debug"/> </annotations> <before> @@ -26,7 +27,7 @@ </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="-5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <generateDate date="-5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index f798865ab2279..8a93000034bf7 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -16,6 +16,7 @@ <testCaseId value="" /> <severity value="CRITICAL"/> <group value="security"/> + <group value="debug"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -25,10 +26,21 @@ </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <generateDate date="+5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess" /> + + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + + <seeInField userInput="{$expiresDateTime}" selector="input#user_expires_at" stepKey="verifyUserExpirationInField"/> + + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <!-- TODO: delete created user --> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index a752c484238fa..c464633554f5c 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -25,7 +25,7 @@ </before> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+2 minute" format="M d, Y h:i A" stepKey="expiresDateTime"/> + <generateDate date="+2 minute" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index f368a71d84d85..f577790871fcd 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -18,6 +18,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="security"/> + <group value="debug"/> </annotations> <before> @@ -28,7 +29,7 @@ </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+5 day" format="M d, Y h:i:s" stepKey="expiresDateTime"/> + <generateDate date="+5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php deleted file mode 100644 index dfcbcad5780b8..0000000000000 --- a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserLoadTest.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Security\Test\Unit\Observer; - -/** - * Test class for \Magento\Security\Observer\AfterAdminUserLoad - */ -class AfterAdminUserLoadTest extends \PHPUnit\Framework\TestCase -{ - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationFactory - */ - private $userExpirationFactoryMock; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\ResourceModel\UserExpiration - */ - private $userExpirationResourceMock; - - /** - * @var \Magento\Security\Observer\AfterAdminUserLoad - */ - private $observer; - - /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager - */ - private $objectManager; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer - */ - private $eventObserverMock; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event - */ - private $eventMock; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User - */ - private $userMock; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration - */ - private $userExpirationMock; - - protected function setUp() - { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->userExpirationFactoryMock = $this->createMock(\Magento\Security\Model\UserExpirationFactory::class); - $this->userExpirationResourceMock = $this->createPartialMock( - \Magento\Security\Model\ResourceModel\UserExpiration::class, - ['load'] - ); - $this->observer = $this->objectManager->getObject( - \Magento\Security\Observer\AfterAdminUserLoad::class, - [ - 'userExpirationFactory' => $this->userExpirationFactoryMock, - 'userExpirationResource' => $this->userExpirationResourceMock, - ] - ); - - $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); - $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', 'setExpiresAt']); - $this->userExpirationMock = $this->createPartialMock( - \Magento\Security\Model\UserExpiration::class, - ['getExpiresAt'] - ); - } - - public function testWithExpiredUser() - { - $userId = '123'; - $testDate = new \DateTime(); - $testDate->modify('+10 days'); - $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); - $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); - $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); - $this->userExpirationFactoryMock->expects(static::once()) - ->method('create') - ->willReturn($this->userExpirationMock); - $this->userExpirationResourceMock->expects(static::once()) - ->method('load') - ->willReturn($this->userExpirationMock); - $this->userExpirationMock->expects(static::exactly(2)) - ->method('getExpiresAt') - ->willReturn($testDate->format('Y-m-d H:i:s')); - $this->userMock->expects(static::once()) - ->method('setExpiresAt') - ->willReturn($this->userMock); - $this->observer->execute($this->eventObserverMock); - } - - public function testWithNonExpiredUser() - { - $userId = '123'; - $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); - $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); - $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); - $this->userExpirationFactoryMock->expects(static::once())->method('create') - ->willReturn($this->userExpirationMock); - $this->userExpirationResourceMock->expects(static::once())->method('load') - ->willReturn($this->userExpirationMock); - $this->userExpirationMock->expects(static::once()) - ->method('getExpiresAt') - ->willReturn(null); - $this->observer->execute($this->eventObserverMock); - } -} diff --git a/app/code/Magento/Security/etc/events.xml b/app/code/Magento/Security/etc/events.xml index 9db9a67947ca8..f85cfc9387c48 100644 --- a/app/code/Magento/Security/etc/events.xml +++ b/app/code/Magento/Security/etc/events.xml @@ -10,9 +10,6 @@ <event name="admin_user_save_after"> <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserSave"/> </event> - <event name="admin_user_load_after"> - <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserLoad"/> - </event> <event name="admin_user_authenticate_before"> <observer name="check_user_expiration" instance="Magento\Security\Observer\AdminUserAuthenticateBefore"/> </event> diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php deleted file mode 100644 index fa552f04b9f2e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserLoadTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Security\Observer; - -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Test for \Magento\Security\Observer\AfterAdminUserLoad * - */ -class AfterAdminUserLoadTest extends \PHPUnit\Framework\TestCase -{ - - /** - * @magentoDataFixture Magento/Security/_files/expired_users.php - */ - public function testWithUserWithExpiration() - { - $adminUserNameFromFixture = 'adminUserExpired'; - /** @var \Magento\User\Model\User $user */ - $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername($adminUserNameFromFixture); - $userId = $user->getId(); - $loadedUser = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $loadedUser->load($userId); - static::assertNotNull($loadedUser->getExpiresAt()); - } - - /** - * @magentoDataFixture Magento/User/_files/dummy_user.php - */ - public function testWithNonExpiredUser() - { - $adminUserNameFromFixture = 'dummy_username'; - $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $user->loadByUsername($adminUserNameFromFixture); - $userId = $user->getId(); - $loadedUser = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); - $loadedUser->load($userId); - static::assertNull($loadedUser->getExpiresAt()); - } -} From 91e0604e0a3b95472dfeb8e0561afa0156c36056 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sun, 13 Oct 2019 18:58:54 -0400 Subject: [PATCH 097/734] remove test debug group (#22833) --- .../Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml | 1 - .../Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml | 3 --- .../Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml | 1 - 3 files changed, 5 deletions(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index b585a21dd743b..919d2523927fa 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -16,7 +16,6 @@ <testCaseId value="" /> <severity value="CRITICAL"/> <group value="security"/> - <group value="debug"/> </annotations> <before> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index 8a93000034bf7..a00872d848472 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -16,7 +16,6 @@ <testCaseId value="" /> <severity value="CRITICAL"/> <group value="security"/> - <group value="debug"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -35,12 +34,10 @@ <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> <argument name="user" value="NewAdminUser"/> </actionGroup> - <seeInField userInput="{$expiresDateTime}" selector="input#user_expires_at" stepKey="verifyUserExpirationInField"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> <argument name="user" value="NewAdminUser"/> </actionGroup> - <!-- TODO: delete created user --> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index f577790871fcd..c33cfb36d209a 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -18,7 +18,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="security"/> - <group value="debug"/> </annotations> <before> From 47dfddbd858a3ad5b12ebb709e12a2a1ff65633c Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 14 Oct 2019 15:36:57 -0400 Subject: [PATCH 098/734] delete users after tests in mftf (magento/magento2#22833: Short-term admin accounts) --- .../Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml | 5 +++++ .../Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index c464633554f5c..bdea845c81a56 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -42,5 +42,10 @@ </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage" /> + <!-- delete user --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser" /> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index c33cfb36d209a..12bba27f21269 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -42,5 +42,10 @@ </actionGroup> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> + <actionGroup ref="logout" stepKey="logoutAsUserWithValidExpiration"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> </test> </tests> From c6f9e6bbe269ff530a606acba5be70f398f082cf Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 14 Oct 2019 18:20:20 -0400 Subject: [PATCH 099/734] fix phpdocs (magento/magento2#22833: Short-term admin accounts) --- .../Magento/Security/Model/Plugin/AdminUserForm.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index d61de5d67adfa..f1777f182564a 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -28,7 +28,7 @@ class AdminUserForm private $userExpirationResource; /** - * @var UserExpirationFactory + * @var \Magento\Security\Model\UserExpirationFactory */ private $userExpirationFactory; @@ -36,7 +36,7 @@ class AdminUserForm * UserForm constructor. * * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param UserExpiration $userExpiration + * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource */ public function __construct( @@ -96,6 +96,12 @@ public function aroundGetFormHtml( return $proceed(); } + /** + * Loads a user expiration record by user ID. + * + * @param $userId string + * @return string + */ private function loadUserExpirationByUserId($userId) { /** @var \Magento\Security\Model\UserExpiration $userExpiration */ From df0c97c30036d6989e76c95132750afb479a69bf Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 15 Oct 2019 12:02:40 -0400 Subject: [PATCH 100/734] remove unused import and fix phpdocs (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/Plugin/AdminUserForm.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index f1777f182564a..d6276519578f9 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -7,8 +7,6 @@ namespace Magento\Security\Model\Plugin; -use Magento\Security\Model\UserExpiration; - /** * Add the `expires_at` form field to the User main form. * @@ -99,7 +97,7 @@ public function aroundGetFormHtml( /** * Loads a user expiration record by user ID. * - * @param $userId string + * @param string $userId * @return string */ private function loadUserExpirationByUserId($userId) From d5997ce775ab5ae8d39ebadbce91fbc7f83c13d9 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Wed, 16 Oct 2019 16:54:18 +0530 Subject: [PATCH 101/734] version checker test failed --- .../Block/Adminhtml/Order/Create/Form/Account.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 03915c0499367..07800cc4c10a0 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -163,16 +163,14 @@ public function getFormValues() { try { $customer = $this->customerRepository->getById($this->getCustomerId()); - } catch (\Exception $e) { - /** If customer does not exist do nothing. */ - } - $data = isset($customer) - ? $this->_extensibleDataObjectConverter->toFlatArray( + $data = $this->_extensibleDataObjectConverter->toFlatArray( $customer, [], - \Magento\Customer\Api\Data\CustomerInterface::class - ) - : []; + CustomerInterface::class + ); + } catch (\Exception $e) { + $data = []; + } foreach ($this->getQuote()->getData() as $key => $value) { if (strpos($key, 'customer_') === 0) { $data[substr($key, 9)] = $value; From cc11cf6d2e78f217575d1c22e0b1d4c027e539b2 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 17 Oct 2019 21:40:47 +0300 Subject: [PATCH 102/734] refactoring --- .../AdminSubmitIntegrationFormActionGroup.xml | 17 +++++++++++++++++ ...pdateCreatedIntegrationEntityActionGroup.xml | 2 -- .../Mftf/Section/AdminIntegrationsSection.xml | 12 ++++++------ ...tegrationEntityWithIncorrectPasswordTest.xml | 2 ++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitIntegrationFormActionGroup.xml diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitIntegrationFormActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitIntegrationFormActionGroup.xml new file mode 100644 index 0000000000000..316ff84d8d21c --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitIntegrationFormActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AdminSubmitIntegrationFormActionGroup"> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.save}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml index 0f385776fbb67..6635deff85e9a 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml @@ -21,7 +21,5 @@ <fillField stepKey="fillEndpiontField" selector="{{AddNewIntegrationSection.endpoint}}" userInput="{{endpoint}}"/> <fillField stepKey="fillLinkUrlField" selector="{{AddNewIntegrationSection.linkUrl}}" userInput="{{linkUrl}}"/> <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> - <!--Click the "Save" Button --> - <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.save}}"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml index 1f0c39af31e92..4af25b9be9714 100644 --- a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml @@ -9,22 +9,22 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="IntegrationsGridSection"> - <element name="add" type="button" selector=".add"/> + <element name="add" type="button" selector=".page-actions .add"/> <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> - <element name="name" type="input" selector="#integrationGrid_filter_name"/> + <element name="name" type="input" selector=".data-grid-filters #integrationGrid_filter_name"/> <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> - <element name="remove" type="button" selector=".delete"/> + <element name="remove" type="button" selector=".data-grid .delete"/> <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> - <element name="edit" type="button" selector=".edit"/> + <element name="edit" type="button" selector=".data-grid .edit"/> </section> <section name="AddNewIntegrationSection"> <element name="name" type="input" selector="#integration_properties_name"/> <element name="password" type="input" selector="#integration_properties_current_password"/> - <element name="saveButton" type="button" selector="#save-split-button-button"/> + <element name="saveButton" type="button" selector=".page-actions #save-split-button-button"/> <element name="endpoint" type="input" selector="#integration_properties_endpoint"/> <element name="linkUrl" type="input" selector="#integration_properties_identity_link_url"/> - <element name="save" type="button" selector=".save"/> + <element name="save" type="button" selector=".page-actions-buttons .save"/> </section> </sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml index 941a0030f0261..b8cc7bb19f7ea 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml @@ -51,6 +51,8 @@ <argument name="linkUrl" value="https://testlink-updated.com"/> <argument name="password" value="12345test"/> </actionGroup> + <!-- Submit The Form --> + <actionGroup ref="AdminSubmitIntegrationFormActionGroup" stepKey="submitTheForm"/> <!-- Assert Error Message --> <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeErrorMessage"> <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again."/> From ba791b2f60f87d10de4e2960ee4288276b04b44e Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 17 Oct 2019 21:46:58 +0300 Subject: [PATCH 103/734] refactoring --- .../AdminUpdateCreatedIntegrationEntityActionGroup.xml | 1 + .../Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml index 6635deff85e9a..0f40bd3093ed2 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml @@ -23,3 +23,4 @@ <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> </actionGroup> </actionGroups> +git \ No newline at end of file diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml index 41e05a6bbe107..589a765a215e5 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -51,6 +51,8 @@ <argument name="linkUrl" value="https://testlink-updated.com"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> + <!-- Submit The Form --> + <actionGroup ref="AdminSubmitIntegrationFormActionGroup" stepKey="submitTheForm"/> <!-- Assert Success Message --> <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> <argument name="message" value="The integration 'Integration Updated' has been saved."/> From 04fe2bf1d455923a2ab691a2d525281de416bec9 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 17 Oct 2019 21:50:31 +0300 Subject: [PATCH 104/734] deleting-mistakes --- .../AdminUpdateCreatedIntegrationEntityActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml index 0f40bd3093ed2..6635deff85e9a 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminUpdateCreatedIntegrationEntityActionGroup.xml @@ -23,4 +23,3 @@ <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> </actionGroup> </actionGroups> -git \ No newline at end of file From babc965cfaefc99601f580e1fb8cf8aab82f1788 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 19 Oct 2019 12:28:26 -0400 Subject: [PATCH 105/734] deactivate expired users on session prolong (#22833) --- .../Security/Model/Plugin/AuthSession.php | 8 ++- .../AdminNavigateWhileUserExpiredTest.xml | 53 +++++++++++++++++++ .../Unit/Model/Plugin/AuthSessionTest.php | 20 ++++--- .../Security/Model/Plugin/AuthSessionTest.php | 3 ++ 4 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index 910d0058e7021..05c44367caaee 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -71,8 +71,12 @@ public function __construct( */ public function aroundProlong(Session $session, \Closure $proceed) { - if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus() || - $this->userExpirationManager->isUserExpired($session->getUser()->getId())) { + if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus()) { + $session->destroy(); + $this->addUserLogoutNotification(); + return null; + } elseif ($this->userExpirationManager->isUserExpired($session->getUser()->getId())) { + $this->userExpirationManager->deactivateExpiredUsers([$session->getUser()->getId()]); $session->destroy(); $this->addUserLogoutNotification(); return null; diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml new file mode 100644 index 0000000000000..f8795454fdd16 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminNavigateWhileUserExpiredTest"> + <annotations> + <features value="Security"/> + <stories value="Navigate to an admin page after user expiration date passes."/> + <title value="Navigate to an admin page after user expiration date passes"/> + <description value="Navigate to an admin page after user expiration date passes."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create user --> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+2 minute" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="logout" stepKey="logout"/> + + <!-- Login as that user --> + <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> + <argument name="adminUser" value="{$grabUsername}"/> + <argument name="adminPassword" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> + <wait time="120" stepKey="waitForUserToExpire"/> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <!-- Confirm that user is logged out --> + <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> + + <!-- Delete created user --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index dc116d059867a..5bcda352c7af9 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -79,7 +79,7 @@ public function setUp() $this->userExpirationManagerMock = $this->createPartialMock( \Magento\Security\Model\UserExpirationManager::class, - ['isUserExpired'] + ['isUserExpired', 'deactivateExpiredUsers'] ); $this->userMock = $this->createMock(\Magento\User\Model\User::class); @@ -188,27 +188,31 @@ public function testAroundProlongSessionIsActiveUserIsExpired() ->method('isLoggedInStatus') ->willReturn(true); - $this->authSessionMock->expects($this->once()) + $this->authSessionMock->expects($this->exactly(2)) ->method('getUser') ->willReturn($this->userMock); - $this->userMock->expects($this->once()) + $this->userMock->expects($this->exactly(2)) ->method('getId') ->willReturn($adminUserId); + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('isAjax') + ->willReturn(false); + $this->userExpirationManagerMock->expects($this->once()) ->method('isUserExpired') ->with($adminUserId) ->willReturn(true); + $this->userExpirationManagerMock->expects($this->once()) + ->method('deactivateExpiredUsers') + ->with([$adminUserId]); + $this->authSessionMock->expects($this->once()) ->method('destroy'); - $this->requestMock->expects($this->once()) - ->method('getParam') - ->with('isAjax') - ->willReturn(false); - $this->adminSessionsManagerMock->expects($this->once()) ->method('getLogoutReasonMessage') ->willReturn($errorMessage); diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index 27b085312186e..598cb6daafae1 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -156,6 +156,7 @@ public function testProcessProlongWithExpiredUser() $expireDate = new \DateTime(); $expireDate->modify('-10 days'); + /** @var \Magento\User\Model\User $user */ $user = $this->objectManager->create(\Magento\User\Model\User::class); $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); $userExpirationFactory = $this->objectManager->create(\Magento\Security\Model\UserExpirationFactory::class); @@ -178,5 +179,7 @@ public function testProcessProlongWithExpiredUser() $this->adminSessionInfo->load($sessionId, 'session_id'); $this->authSession->prolong(); static::assertFalse($this->auth->isLoggedIn()); + $user->reload(); + static::assertFalse((bool)$user->getIsActive()); } } From 4c4149fdfc4d658a8bbb6649eb9f4b89828296d0 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 19 Oct 2019 12:59:15 -0400 Subject: [PATCH 106/734] add copyright (#22833) --- .../Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml index f8795454fdd16..c4603a88c56c4 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml @@ -1,4 +1,10 @@ <?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"> From 781684ab90d077ae838a5b3c833e1760c408c30a Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Tue, 22 Oct 2019 17:31:26 +0530 Subject: [PATCH 107/734] Integration test changes --- .../Block/Adminhtml/Order/Create/Form/Account.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index aa5bd3702c6d1..726a8e80c793b 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -165,14 +165,17 @@ public function getFormValues() { try { $customer = $this->customerRepository->getById($this->getCustomerId()); - $data = $this->_extensibleDataObjectConverter->toFlatArray( - $customer, - [], - CustomerInterface::class - ); } catch (\Exception $e) { - $data = []; + /** If customer does not exist do nothing. */ } + $data = isset($customer) + ? $this->_extensibleDataObjectConverter->toFlatArray( + $customer, + [], + \Magento\Customer\Api\Data\CustomerInterface::class + ) + : []; + foreach ($this->getQuote()->getData() as $key => $value) { if (strpos($key, 'customer_') === 0) { $data[substr($key, 9)] = $value; From 33a5d36e583c3d6a3a2a0cbb1fa592807ae930a6 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Fri, 25 Oct 2019 09:57:40 -0400 Subject: [PATCH 108/734] split out deactivateExpiredUsers into two methods suitable for calling without input parameters in cron (#22833) --- .../Security/Model/Plugin/AuthSession.php | 2 +- .../Security/Model/UserExpirationManager.php | 29 +++++++++++++++---- .../Observer/AdminUserAuthenticateBefore.php | 2 +- .../Unit/Model/Plugin/AuthSessionTest.php | 4 +-- .../AdminUserAuthenticateBeforeTest.php | 4 +-- .../Model/UserExpirationManagerTest.php | 4 +-- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index 05c44367caaee..6dc5e796d8950 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -76,7 +76,7 @@ public function aroundProlong(Session $session, \Closure $proceed) $this->addUserLogoutNotification(); return null; } elseif ($this->userExpirationManager->isUserExpired($session->getUser()->getId())) { - $this->userExpirationManager->deactivateExpiredUsers([$session->getUser()->getId()]); + $this->userExpirationManager->deactivateExpiredUsersById([$session->getUser()->getId()]); $session->destroy(); $this->addUserLogoutNotification(); return null; diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index 9b26935c1baf2..7043a559ca1d4 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -76,17 +76,34 @@ public function __construct( /** * Deactivate expired user accounts and invalidate their sessions. - * - * @param array|null $userIds */ - public function deactivateExpiredUsers(?array $userIds = null): void + public function deactivateExpiredUsers(): void { /** @var ExpiredUsersCollection $expiredRecords */ $expiredRecords = $this->userExpirationCollectionFactory->create()->addActiveExpiredUsersFilter(); - if ($userIds != null) { - $expiredRecords->addUserIdsFilter($userIds); - } + $this->processExpiredUsers($expiredRecords); + } + + /** + * Deactivate specific expired users. + * + * @param array $userIds + */ + public function deactivateExpiredUsersById(array $userIds): void + { + $expiredRecords = $this->userExpirationCollectionFactory->create() + ->addActiveExpiredUsersFilter() + ->addUserIdsFilter($userIds); + $this->processExpiredUsers($expiredRecords); + } + /** + * Deactivate expired user accounts and invalidate their sessions. + * + * @param ExpiredUsersCollection $expiredRecords + */ + private function processExpiredUsers(ExpiredUsersCollection $expiredRecords): void + { if ($expiredRecords->getSize() > 0) { // get all active sessions for the users and set them to logged out /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index 1ffdd1cd1d11e..61ce889e9722a 100644 --- a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -58,7 +58,7 @@ public function execute(Observer $observer) $user->loadByUsername($username); if ($user->getId() && $this->userExpirationManager->isUserExpired($user->getId())) { - $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); throw new AuthenticationException( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index 5bcda352c7af9..9870de8fc2cb4 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -79,7 +79,7 @@ public function setUp() $this->userExpirationManagerMock = $this->createPartialMock( \Magento\Security\Model\UserExpirationManager::class, - ['isUserExpired', 'deactivateExpiredUsers'] + ['isUserExpired', 'deactivateExpiredUsersById'] ); $this->userMock = $this->createMock(\Magento\User\Model\User::class); @@ -207,7 +207,7 @@ public function testAroundProlongSessionIsActiveUserIsExpired() ->willReturn(true); $this->userExpirationManagerMock->expects($this->once()) - ->method('deactivateExpiredUsers') + ->method('deactivateExpiredUsersById') ->with([$adminUserId]); $this->authSessionMock->expects($this->once()) diff --git a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php index 0ae58756d3f77..ecfd3e3759dd3 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -65,7 +65,7 @@ protected function setUp() $this->userExpirationManagerMock = $this->createPartialMock( \Magento\Security\Model\UserExpirationManager::class, - ['isUserExpired', 'deactivateExpiredUsers'] + ['isUserExpired', 'deactivateExpiredUsersById'] ); $this->userFactoryMock = $this->createPartialMock(\Magento\User\Model\UserFactory::class, ['create']); $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['loadByUsername', 'getId']); @@ -104,7 +104,7 @@ public function testWithExpiredUser() ->willReturn(true); $this->userMock->expects(static::exactly(3))->method('getId')->willReturn($adminUserId); $this->userExpirationManagerMock->expects(static::once()) - ->method('deactivateExpiredUsers') + ->method('deactivateExpiredUsersById') ->with([$adminUserId]) ->willReturn(null); $this->observer->execute($this->eventObserverMock); diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 1d41771416c87..9ab7287c414a6 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -71,7 +71,7 @@ public function testDeactivateExpiredUsersWithExpiredUser() $user = $this->loadUserByUsername($adminUsernameFromFixture); $sessionId = $this->authSession->getSessionId(); $this->expireUser($user); - $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); $this->adminSessionInfo->load($sessionId, 'session_id'); $user->reload(); $userExpirationModel = $this->loadExpiredUserModelByUser($user); @@ -90,7 +90,7 @@ public function testDeactivateExpiredUsersWithNonExpiredUser() $this->loginUser($adminUsernameFromFixture); $user = $this->loadUserByUsername($adminUsernameFromFixture); $sessionId = $this->authSession->getSessionId(); - $this->userExpirationManager->deactivateExpiredUsers([$user->getId()]); + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); $user->reload(); $userExpirationModel = $this->loadExpiredUserModelByUser($user); $this->adminSessionInfo->load($sessionId, 'session_id'); From c0e73a8b3c01473060bf55c9166421ca5b69056c Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Sat, 26 Oct 2019 13:32:23 +0100 Subject: [PATCH 109/734] Reduce severity of 'processing' state --- .../Indexer/Block/Backend/Grid/Column/Renderer/Status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php index d8a017ef5a4b4..144d44fe42880 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php @@ -27,7 +27,7 @@ public function render(\Magento\Framework\DataObject $row) $text = __('Ready'); break; case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING: - $class = 'grid-severity-major'; + $class = 'grid-severity-minor'; $text = __('Processing'); break; } From e39b01284294be28e760a5ca884ae90cca7f373f Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Sat, 26 Oct 2019 14:12:50 +0100 Subject: [PATCH 110/734] Add 'schedule status' column to admin indexer grid This is a copy of the 'schedule status' column available via command line. --- .../Grid/Column/Renderer/ScheduleStatus.php | 83 +++++++++++++++++++ .../layout/indexer_indexer_list_grid.xml | 9 ++ 2 files changed, 92 insertions(+) create mode 100644 app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php new file mode 100644 index 0000000000000..0a407527edb95 --- /dev/null +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; + +use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; +use Magento\Framework\Mview\View; +use Magento\Framework\Phrase; + +class ScheduleStatus extends AbstractRenderer +{ + /** + * @var \Magento\Framework\Mview\ViewInterface + */ + protected $viewModel; + + public function __construct( + \Magento\Backend\Block\Context $context, + View $viewModel, + array $data = [] + ) { + parent::__construct($context, $data); + $this->viewModel = $viewModel; + } + + /** + * Render indexer status + * + * @param \Magento\Framework\DataObject $row + * @return string + */ + public function render(\Magento\Framework\DataObject $row) + { + try { + if (!$row->getIsScheduled()) { + return ''; + } + + try { + $view = $this->viewModel->load($row->getIndexerId()); + } catch (\InvalidArgumentException $exception) { + // No view for this index. + return ''; + } + + $state = $view->getState()->loadByView($view->getId()); + $changelog = $view->getChangelog()->setViewId($view->getId()); + $currentVersionId = $changelog->getVersion(); + $count = count($changelog->getList($state->getVersionId(), $currentVersionId)); + + if ($count > 1000) { + $class = 'grid-severity-critical'; + } elseif ($count > 100) { + $class = 'grid-severity-major'; + } elseif ($count > 10) { + $class = 'grid-severity-minor'; + } else { + $class = 'grid-severity-notice'; + } + + if ($state->getStatus() !== 'idle') { + $class = 'grid-severity-minor'; + } + + $text = new Phrase( + "%status (%count in backlog)", + [ + 'status' => $state->getStatus(), + 'count' => $count, + ] + ); + + return '<span class="' . $class . '"><span>' . $text . '</span></span>'; + } catch (\Exception $exception) { + return '<span class="grid-severity-minor"><span>' . + htmlspecialchars( + get_class($exception) . ': ' . $exception->getMessage() + ) . '</span></span>'; + } + } +} diff --git a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml index f1099c4133bca..b6f9b0e53cba2 100644 --- a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml +++ b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml @@ -76,6 +76,15 @@ <argument name="column_css_class" xsi:type="string">indexer-status</argument> </arguments> </block> + <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.indexer.grid.columnSet.indexer_schedule_status" as="indexer_schedule_status"> + <arguments> + <argument name="header" xsi:type="string" translate="true">Schedule Status</argument> + <argument name="index" xsi:type="string">schedule_status</argument> + <argument name="renderer" xsi:type="string">Magento\Indexer\Block\Backend\Grid\Column\Renderer\ScheduleStatus</argument> + <argument name="sortable" xsi:type="string">0</argument> + <argument name="column_css_class" xsi:type="string">indexer-schedule-status</argument> + </arguments> + </block> <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.indexer.grid.columnSet.indexer_updated" as="indexer_updated"> <arguments> <argument name="header" xsi:type="string" translate="true">Updated</argument> From 1a1cba61332f3d51ff2840a766f1aecf29ce88a9 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Mon, 28 Oct 2019 16:25:23 +0000 Subject: [PATCH 111/734] Add comment with parameter annotations --- .../Block/Backend/Grid/Column/Renderer/ScheduleStatus.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 0a407527edb95..fb2679c8f4c24 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -16,6 +16,11 @@ class ScheduleStatus extends AbstractRenderer */ protected $viewModel; + /** + * @param \Magento\Backend\Block\Context $context + * @param \Magento\Framework\Mview\ViewInterface $viewModel + * @param array $data + */ public function __construct( \Magento\Backend\Block\Context $context, View $viewModel, From 7200593bb5fcebfe1d5fa8be91649c1c7e23698c Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Mon, 28 Oct 2019 16:28:15 +0000 Subject: [PATCH 112/734] Use Magento\Framework\Escaper not htmlspecialchars --- .../Backend/Grid/Column/Renderer/ScheduleStatus.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index fb2679c8f4c24..1aff83cdf4bbf 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -6,11 +6,17 @@ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; +use Magento\Framework\Escaper; use Magento\Framework\Mview\View; use Magento\Framework\Phrase; class ScheduleStatus extends AbstractRenderer { + /** + * @var \Magento\Framework\Escaper + */ + protected $escaper; + /** * @var \Magento\Framework\Mview\ViewInterface */ @@ -19,14 +25,17 @@ class ScheduleStatus extends AbstractRenderer /** * @param \Magento\Backend\Block\Context $context * @param \Magento\Framework\Mview\ViewInterface $viewModel + * @param \Magento\Framework\Escaper $escaper * @param array $data */ public function __construct( \Magento\Backend\Block\Context $context, + Escaper $escaper, View $viewModel, array $data = [] ) { parent::__construct($context, $data); + $this->escaper = $escaper; $this->viewModel = $viewModel; } @@ -80,7 +89,7 @@ public function render(\Magento\Framework\DataObject $row) return '<span class="' . $class . '"><span>' . $text . '</span></span>'; } catch (\Exception $exception) { return '<span class="grid-severity-minor"><span>' . - htmlspecialchars( + $this->escaper->escapeHtml( get_class($exception) . ': ' . $exception->getMessage() ) . '</span></span>'; } From b74b7a4984e9623adc92499ab530ab4103ded607 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Mon, 28 Oct 2019 16:30:13 +0000 Subject: [PATCH 113/734] Update unit test to match class change --- .../Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php b/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php index 705a9e3998ae8..7249f764a66a1 100644 --- a/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php @@ -49,7 +49,7 @@ public function renderDataProvider() ], 'set3' => [ [\Magento\Framework\Indexer\StateInterface::STATUS_WORKING], - ['class' => 'grid-severity-major', 'text' => 'Processing'] + ['class' => 'grid-severity-minor', 'text' => 'Processing'] ] ]; } From c1f8adb53a2a33339f61852bb6e1e5d505f082c9 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Wed, 30 Oct 2019 16:51:05 +0000 Subject: [PATCH 114/734] Correct parameter order in comment --- .../Block/Backend/Grid/Column/Renderer/ScheduleStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 1aff83cdf4bbf..712b17b91a0d7 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -24,8 +24,8 @@ class ScheduleStatus extends AbstractRenderer /** * @param \Magento\Backend\Block\Context $context - * @param \Magento\Framework\Mview\ViewInterface $viewModel * @param \Magento\Framework\Escaper $escaper + * @param \Magento\Framework\Mview\ViewInterface $viewModel * @param array $data */ public function __construct( From fbe87ab80084aa4f1803377c6c69c1fdb0779467 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 1 Nov 2019 12:13:46 +0530 Subject: [PATCH 115/734] static test failed exception fix --- .../Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 726a8e80c793b..01d7625b9e63c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -166,7 +166,7 @@ public function getFormValues() try { $customer = $this->customerRepository->getById($this->getCustomerId()); } catch (\Exception $e) { - /** If customer does not exist do nothing. */ + $data = []; } $data = isset($customer) ? $this->_extensibleDataObjectConverter->toFlatArray( From 93d698a4de0ba46094043cf895d87d271d67178e Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 1 Nov 2019 12:51:31 +0530 Subject: [PATCH 116/734] static test failed exception space fix --- .../Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 01d7625b9e63c..bffc51a11c5cb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -166,7 +166,7 @@ public function getFormValues() try { $customer = $this->customerRepository->getById($this->getCustomerId()); } catch (\Exception $e) { - $data = []; + $data = []; } $data = isset($customer) ? $this->_extensibleDataObjectConverter->toFlatArray( From f4692b2274d986489c8c096096957c28de65040a Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 1 Nov 2019 15:22:14 +0530 Subject: [PATCH 117/734] Semantic Version Checker --- app/code/Magento/Sales/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index 8d4ef50a2ebc5..3b20f6e43e721 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -47,5 +47,6 @@ "psr-4": { "Magento\\Sales\\": "" } - } + }, + "version": "102.0.4" } From 3e8369ece31b9241c7b1ad6c3bf47f48ac7e5cfc Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Fri, 1 Nov 2019 16:29:30 +0530 Subject: [PATCH 118/734] revert for composer json change --- app/code/Magento/Sales/composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index 3b20f6e43e721..8d4ef50a2ebc5 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -47,6 +47,5 @@ "psr-4": { "Magento\\Sales\\": "" } - }, - "version": "102.0.4" + } } From 6fe2ab2dbf043fad74c218cbd2a4d699843f573f Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Sat, 9 Nov 2019 11:59:42 +0530 Subject: [PATCH 119/734] git merge with 2.3-develop fix --- .../Sales/Block/Adminhtml/Order/Create/Form/Account.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 3b4fbac981f2e..6a62ba7fbf0fd 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -38,9 +38,7 @@ class Account extends AbstractForm * @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ protected $_extensibleDataObjectConverter; - private const XML_PATH_EMAIL_REQUIRED_CREATE_ORDER = 'customer/create_account/email_required_create_order'; - /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Model\Session\Quote $sessionQuote @@ -168,20 +166,19 @@ public function getFormValues() } catch (\Exception $e) { $data = []; } - - $data = isset($customer) + $data = isset($customer) ? $this->_extensibleDataObjectConverter->toFlatArray( $customer, [], \Magento\Customer\Api\Data\CustomerInterface::class ) : []; - - foreach ($this->getQuote()->getData() as $key => $value) { + foreach ($this->getQuote()->getData() as $key => $value) { if (strpos($key, 'customer_') === 0) { $data[substr($key, 9)] = $value; } } + if ($this->getQuote()->getCustomerEmail()) { $data['email'] = $this->getQuote()->getCustomerEmail(); } From 74cfd180853f41d9b6634267c82b7e51cc99ff56 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Wed, 20 Nov 2019 15:09:51 +0530 Subject: [PATCH 120/734] merge with 2.3 --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 9 +++++---- app/code/Magento/Sales/etc/config.xml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index bc4c7a1ab47cf..d1b3e67815098 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -2038,11 +2038,11 @@ protected function _validate() protected function _getNewCustomerEmail() { $email = $this->getData('account/email'); - + if ($email || $this->isEmailRequired()) { return $email; } - + return $this->generateEmail(); } @@ -2055,7 +2055,8 @@ private function isEmailRequired(): bool { return (bool)$this->_scopeConfig->getValue( self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->_session->getStore()->getId() ); } @@ -2075,7 +2076,7 @@ private function generateEmail(): string $account = $this->getData('account'); $account['email'] = $email; $this->setData('account', $account); - + return $email; } diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index cb921b8b0a1bc..1a881fbea2449 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -113,7 +113,7 @@ </dev> <customer> <create_account> - <email_required_create_order>0</email_required_create_order> + <email_required_create_order>1</email_required_create_order> </create_account> </customer> </default> From f96d993184b3aea5512a2c4976f459e712cc14f4 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 20 Nov 2019 12:20:33 +0200 Subject: [PATCH 121/734] magento/magento2#25540: Products are not displaying infront end after updating product via importing CSV. --- .../Model/Import/Product/Type/Bundle.php | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 81a47d72602b7..33a7d2efaa273 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -6,14 +6,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\BundleImportExport\Model\Import\Product\Type; -use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory; -use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; -use Magento\Framework\App\ObjectManager; use Magento\Bundle\Model\Product\Price as BundlePrice; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory; use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\Store\Model\StoreManagerInterface; @@ -26,6 +27,9 @@ */ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { + /** + * phpcs:disable Magento2.Commenting.ConstantsPHPDocFormatting + */ /** * Delimiter before product option value. @@ -62,6 +66,10 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst */ const SELECTION_PRICE_TYPE_PERCENT = 1; + /** + * phpcs:enable Magento2.Commenting.ConstantsPHPDocFormatting + */ + /** * Array of cached options. * @@ -133,7 +141,7 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst protected $_optionTypeMapping = [ 'dropdown' => 'select', 'radiobutton' => 'radio', - 'checkbox' => 'checkbox', + 'checkbox' => 'checkbox', 'multiselect' => 'multi', ]; @@ -543,7 +551,7 @@ protected function populateExistingSelections($existingOptions) ? $this->_bundleFieldMapping[$origKey] : $origKey; if ( - !isset($this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key]) + !isset($this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key]) ) { $this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key] = $existingSelection[$origKey]; @@ -616,6 +624,7 @@ protected function populateInsertOptionValues(array $optionIds): array if ($assoc['position'] == $this->_cachedOptions[$entityId][$key]['index'] && $assoc['parent_id'] == $entityId) { $option['parent_id'] = $entityId; + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $optionValues = array_merge( $optionValues, $this->populateOptionValueTemplate($option, $optionId) @@ -675,10 +684,7 @@ private function insertParentChildRelations() $childIds = []; foreach ($options as $option) { foreach ($option['selections'] as $selection) { - if (!isset($selection['parent_product_id'])) { - if (!isset($this->_cachedSkuToProducts[$selection['sku']])) { - continue; - } + if (isset($this->_cachedSkuToProducts[$selection['sku']])) { $childIds[] = $this->_cachedSkuToProducts[$selection['sku']]; } } @@ -735,17 +741,19 @@ protected function deleteOptionsAndSelections($productIds) $optionTable = $this->_resource->getTableName('catalog_product_bundle_option'); $optionValueTable = $this->_resource->getTableName('catalog_product_bundle_option_value'); $selectionTable = $this->_resource->getTableName('catalog_product_bundle_selection'); - $valuesIds = $this->connection->fetchAssoc($this->connection->select()->from( - ['bov' => $optionValueTable], - ['value_id'] - )->joinLeft( - ['bo' => $optionTable], - 'bo.option_id = bov.option_id', - ['option_id'] - )->where( - 'parent_id IN (?)', - $productIds - )); + $valuesIds = $this->connection->fetchAssoc( + $this->connection->select()->from( + ['bov' => $optionValueTable], + ['value_id'] + )->joinLeft( + ['bo' => $optionTable], + 'bo.option_id = bov.option_id', + ['option_id'] + )->where( + 'parent_id IN (?)', + $productIds + ) + ); $this->connection->delete( $optionValueTable, $this->connection->quoteInto('value_id IN (?)', array_keys($valuesIds)) From dcaa4a8079d8af00bcee5c1f4224fda71731ea5d Mon Sep 17 00:00:00 2001 From: mmularski <mmularczyk9@gmail.com> Date: Fri, 5 Jul 2019 13:40:43 +0200 Subject: [PATCH 122/734] Issue-709. Convert CreateAdminUserEntityTest to MFTF --- ...eateUserWithRoleAndIsActiveActionGroup.xml | 14 +++++++ ...teUserWithRoleAndIsInactiveActionGroup.xml | 14 +++++++ .../Magento/User/Test/Mftf/Data/UserData.xml | 30 ++++++++++++++ .../User/Test/Mftf/Data/UserRoleData.xml | 7 ++++ .../Mftf/Section/AdminNewUserFormSection.xml | 2 + .../Test/AdminCreateActiveUserEntityTest.xml | 40 +++++++++++++++++++ .../AdminCreateInactiveUserEntityTest.xml | 40 +++++++++++++++++++ 7 files changed, 147 insertions(+) create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml new file mode 100644 index 0000000000000..76f3691ddbdcb --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminCreateUserWithRoleAndIsActiveActionGroup" extends="AdminCreateUserActionGroup"> + <checkOption selector="{{AdminNewUserFormSection.userIsActive}}" stepKey="checkIsActive" after="confirmPassword"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml new file mode 100644 index 0000000000000..3c7db628e1378 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminCreateUserWithRoleAndIsInactiveActionGroup" extends="AdminCreateUserActionGroup"> + <checkOption selector="{{AdminNewUserFormSection.userInactive}}" stepKey="checkIsInactive" after="confirmPassword"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index 349935368dcf1..714f8f94c2ef3 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -135,6 +135,36 @@ <data key="username">admin_user_with_correct_password</data> <data key="password">123123123q</data> </entity> + <entity name="activeAdmin" type="user"> + <data key="username" unique="suffix">AdminUser</data> + <data key="firstname" unique="suffix">FirstName</data> + <data key="lastname" unique="suffix">LastName</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> + <entity name="inactiveAdmin" type="user"> + <data key="username" unique="suffix">AdminUser</data> + <data key="firstname" unique="suffix">FirstName</data> + <data key="lastname" unique="suffix">LastName</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> <!-- Since User delete action is performed via POST request we created this entity to be able to delete it. Please use "AdminDeleteUserViaCurlActionGroup". diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 96250f4e606ba..bd777d4ec98e6 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -15,6 +15,13 @@ <data key="access">1</data> </entity> + <entity name="roleDefaultAdministrator" type="role"> + <data key="name">Administrators</data> + <data key="rolename">Administrators</data> + <data key="scope">1</data> + <data key="access">1</data> + </entity> + <entity name="roleAdministrator" type="user_role"> <data key="name" unique="suffix">Administrator </data> <data key="rolename" unique="suffix">Administrator </data> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml index 9b030b216ce2c..c20ab4f6ce951 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -19,6 +19,8 @@ <element name="password" type="input" selector="#page_tabs_main_section_content input[name='password']"/> <element name="passwordConfirmation" type="input" selector="#page_tabs_main_section_content input[name='password_confirmation']"/> <element name="interfaceLocale" type="select" selector="#page_tabs_main_section_content select[name='interface_locale']"/> + <element name="userIsActive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='1']"/> + <element name="userInactive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='0']"/> <element name="currentPassword" type="input" selector="#page_tabs_main_section_content input[name='current_password']"/> <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml new file mode 100644 index 0000000000000..b05e7807e0562 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -0,0 +1,40 @@ +<?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="AdminCreateActiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create active admin user"/> + <description value="Admin user should be able to create active admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="User" value="activeAdmin"/> + <argument name="role" value="roleDefaultAdministrator"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToNewAdmin"> + <argument name="adminUser" value="activeAdmin"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="assertAdminIsInGrid"> + <argument name="user" value="activeAdmin"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutCreatedUser"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml new file mode 100644 index 0000000000000..e802b67eb8073 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -0,0 +1,40 @@ +<?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="AdminCreateInactiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create inactive admin user"/> + <description value="Admin user should be able to create inactive admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + + <actionGroup ref="AdminCreateUserWithRoleAndIsInactiveActionGroup" stepKey="createAdminUser"> + <argument name="User" value="inactiveAdmin"/> + <argument name="role" value="roleDefaultAdministrator"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="assertAdminIsInGrid"> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToNewAdmin"> + <argument name="adminUser" value="inactiveAdmin"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeUserErrorMessage" /> + </test> +</tests> From 2e9d1bb5c421264d62d7fbdf00c3cc266b298285 Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Mon, 25 Nov 2019 16:57:59 +0100 Subject: [PATCH 123/734] [BUGFIX] Make sure disabled products are removed from the Product Flat Table --- .../Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php index 7b2a6272c7052..8cafc82bf77d6 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php @@ -67,6 +67,7 @@ public function execute($ids) foreach ($idsBatches as $changedIds) { if ($tableExists) { $this->flatItemEraser->removeDeletedProducts($changedIds, $store->getId()); + $this->flatItemEraser->removeDisabledProducts($changedIds, $store->getId()); } if (!empty($changedIds)) { $this->_reindex($store->getId(), $changedIds); From c6d3c8e5161356efada9cf8278e355280efc3981 Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Mon, 25 Nov 2019 17:20:37 +0100 Subject: [PATCH 124/734] [BUGFIX] Solved invalid link field for Enterprise implementation --- .../Catalog/Model/Indexer/Product/Flat/Action/Eraser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php index c4d807667bfbc..ba209884fbb53 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php @@ -99,7 +99,7 @@ public function removeDisabledProducts(array &$ids, $storeId) ['status_global_attr' => $statusAttribute->getBackendTable()], ' status_global_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() . ' AND status_global_attr.store_id = ' . Store::DEFAULT_STORE_ID - . ' AND status_global_attr.' . $statusAttribute->getEntityIdField() . '=' + . ' AND status_global_attr.' . $metadata->getLinkField() . '=' . 'product_table.' . $metadata->getLinkField(), [] ); @@ -107,7 +107,7 @@ public function removeDisabledProducts(array &$ids, $storeId) ['status_attr' => $statusAttribute->getBackendTable()], ' status_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() . ' AND status_attr.store_id = ' . $storeId - . ' AND status_attr.' . $statusAttribute->getEntityIdField() . '=' + . ' AND status_attr.' . $metadata->getLinkField() . '=' . 'product_table.' . $metadata->getLinkField(), [] ); From b906d2b40fc3401667878408d8cb97615cf7350f Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Sat, 30 Nov 2019 14:43:55 +0000 Subject: [PATCH 125/734] Use interface constant not hard-coded string --- .../Block/Backend/Grid/Column/Renderer/ScheduleStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 712b17b91a0d7..0c8083d57bcd8 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -74,7 +74,7 @@ public function render(\Magento\Framework\DataObject $row) $class = 'grid-severity-notice'; } - if ($state->getStatus() !== 'idle') { + if ($state->getStatus() !== $state::STATUS_IDLE) { $class = 'grid-severity-minor'; } From 22418e9f34be7a4f6b5f1b6385c4ddd2c8176762 Mon Sep 17 00:00:00 2001 From: mmularski <mmularczyk9@gmail.com> Date: Wed, 10 Jul 2019 18:35:45 +0200 Subject: [PATCH 126/734] Issue-626. Convert DeleteTermEntityTest to MFTF --- ...AdminAssertTermAbsentInGridActionGroup.xml | 21 ++++++++ .../ActionGroup/CreateNewTermActionGroup.xml | 27 ++++++++++ .../ActionGroup/DeleteTermActionGroup.xml | 26 ++++++++++ ...tAssertTermAbsentInCheckoutActionGroup.xml | 31 ++++++++++++ ...ontProcessCheckoutToPaymentActionGroup.xml | 35 +++++++++++++ .../Test/Mftf/Data/TermData.xml | 20 ++++++++ .../Test/Mftf/Page/AdminEditTermPage.xml | 14 ++++++ .../Test/Mftf/Page/AdminNewTermPage.xml | 15 ++++++ .../Test/Mftf/Page/AdminTermsPage.xml | 12 +++++ .../Test/Mftf/Page/CheckoutPage.xml | 14 ++++++ .../Mftf/Section/AdminEditTermFormSection.xml | 15 ++++++ .../Mftf/Section/AdminNewTermFormSection.xml | 22 ++++++++ .../Section/AdminTermFormMessagesSection.xml | 15 ++++++ .../Mftf/Section/AdminTermGridSection.xml | 18 +++++++ .../StorefrontCheckoutAgreementsSection.xml | 16 ++++++ .../AdminDeleteActiveTextTermEntityTest.xml | 50 +++++++++++++++++++ 16 files changed, 351 insertions(+) create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml new file mode 100644 index 0000000000000..9e95f27c47287 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertTermAbsentInGridActionGroup"> + <arguments> + <argument name="termName" type="string"/> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <dontSee selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermAbsentInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml new file mode 100644 index 0000000000000..d420cc155a77c --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml @@ -0,0 +1,27 @@ +<?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="CreateNewTermActionGroup"> + <arguments> + <argument name="term"/> + </arguments> + <amOnPage url="{{AdminNewTermPage.url}}" stepKey="amOnNewTermPage"/> + <waitForPageLoad stepKey="waitForAdminNewTermPageLoad"/> + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{term.name}}" stepKey="fillFieldConditionName"/> + <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{term.isActive}}" stepKey="selectOptionIsActive"/> + <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{term.isHtml}}" stepKey="selectOptionIsHtml"/> + <selectOption selector="{{AdminNewTermFormSection.mode}}" userInput="{{term.mode}}" stepKey="selectOptionMode"/> + <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{term.storeView}}" stepKey="selectOptionStoreView" /> + <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{term.checkboxText}}" stepKey="fillFieldCheckboxText"/> + <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{term.content}}" stepKey="fillFieldContent"/> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml new file mode 100644 index 0000000000000..b88101ce88ef6 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml @@ -0,0 +1,26 @@ +<?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="DeleteTermActionGroup"> + <arguments> + <argument name="term" /> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForAdminTermsGridPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + <click selector="{{AdminEditTermFormSection.delete}}" stepKey="clickDeleteButton"/> + <click selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="clickDeleteOkButton"/> + <waitForPageLoad stepKey="waitForAdminTermsGridPageLoad2"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You deleted the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..7be17d8ca69d0 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml @@ -0,0 +1,31 @@ +<?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="StorefrontAssertTermAbsentInCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + <!--Check if agreement is absent on checkout--> + <dontSee selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + + <!--Checkout select Check/Money Order payment--> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentPageRendering"/> + <waitForPageLoad stepKey="waitForPaymentRendering"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml new file mode 100644 index 0000000000000..c40f24836c815 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml @@ -0,0 +1,35 @@ +<?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="StorefrontProcessCheckoutToPaymentActionGroup"> + <!--Go to Checkout--> + <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> + <wait time="5" stepKey="waitMinicartRendering"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + + <!--Process steps--> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForShippingMethods"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('')}}" stepKey="selectShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForTheNextButton"/> + <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForProcessShippingMethod"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml new file mode 100644 index 0000000000000..707aea18d0ca1 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml @@ -0,0 +1,20 @@ +<?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="activeTextTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Enabled</data> + <data key="isHtml">Text</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content" unique="suffix">TestMessage</data> + </entity> +</entities> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml new file mode 100644 index 0000000000000..3a13f7d7d5e60 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminEditTermPage" url="checkout/agreement/edit/id/{{termId}}/" area="admin" module="Magento_CheckoutAgreements" parameterized="true"> + <section name="AdminEditTermFormSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml new file mode 100644 index 0000000000000..51ec18a1d17e5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewTermPage" url="checkout/agreement/new" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminNewTermFormSection"/> + <section name="AdminTermFormMessagesSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml new file mode 100644 index 0000000000000..4fb1a9704d9d0 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminTermsPage" url="checkout/agreement/" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminTermGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml new file mode 100644 index 0000000000000..fdb75ab4bc4c2 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_CheckoutAgreements"> + <section name="StorefrontCheckoutAgreementsSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml new file mode 100644 index 0000000000000..734a75b49c03a --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml @@ -0,0 +1,15 @@ +<?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="AdminEditTermFormSection"> + <element name="delete" type="button" selector=".page-main-actions #delete"/> + <element name="acceptPopupButton" type="button" selector="button.action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml new file mode 100644 index 0000000000000..aeff29ef116fc --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml @@ -0,0 +1,22 @@ +<?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="AdminNewTermFormSection"> + <element name="save" type="button" selector=".page-main-actions #save"/> + <element name="conditionName" type="input" selector="#name"/> + <element name="isActive" type="select" selector="#is_active"/> + <element name="isHtml" type="select" selector="#is_html"/> + <element name="mode" type="select" selector="#mode"/> + <element name="storeView" type="multiselect" selector="#stores"/> + <element name="checkboxText" type="input" selector="#checkbox_text"/> + <element name="content" type="textarea" selector="#content"/> + <element name="contentHeight" type="input" selector="#content_height"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml new file mode 100644 index 0000000000000..a3a91855b0640 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml @@ -0,0 +1,15 @@ +<?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="AdminTermFormMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml new file mode 100644 index 0000000000000..326f9dcce4320 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml @@ -0,0 +1,18 @@ +<?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="AdminTermGridSection"> + <element name="searchButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"/> + <element name="resetButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[2]"/> + <element name="filterByTermName" type="input" selector="#agreementGrid_filter_name"/> + <element name="firstRowConditionName" type="text" selector=".data-grid>tbody>tr>td.col-name"/> + <element name="firstRowConditionId" type="text" selector=".data-grid>tbody>tr>td.col-id.col-agreement_id"/> + <element name="successMessage" type="text" selector=".message-success"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml new file mode 100644 index 0000000000000..cb3e98949c622 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.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="StorefrontCheckoutAgreementsSection"> + <element name="checkoutAgreementCheckbox" type="checkbox" selector="div.checkout-agreement.field.choice.required > input"/> + <element name="checkoutAgreementButton" type="button" selector="div.checkout-agreements-block > div > div > div > label > button > span"/> + <element name="checkoutAgreementErrorMessage" type="button" selector="div.checkout-agreement.field.choice.required > div.mage-error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml new file mode 100644 index 0000000000000..9b83cde365003 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml @@ -0,0 +1,50 @@ +<?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="AdminDeleteActiveTextTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Delete active text checkout agreement"/> + <description value="Admin should be able to delete active text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value=""/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <createData entity="SimpleTwo" stepKey="createdProduct"/> + <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminAssertTermAbsentInGridActionGroup" stepKey="assertTermAbsentInGrid"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> + <argument name="product" value="$$createdProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="StorefrontAssertTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> From 103c6729401e3d1198f5b34bb2e9ba50e5dc3305 Mon Sep 17 00:00:00 2001 From: mmularski <mmularczyk9@gmail.com> Date: Thu, 11 Jul 2019 07:31:49 +0200 Subject: [PATCH 127/734] Issue-627. Convert UpdateTermEntityTest to MFTF --- .../AdminAssertTermInGridActionGroup.xml | 21 ++++++++ .../ActionGroup/CreateNewTermActionGroup.xml | 27 ++++++++++ .../ActionGroup/DeleteTermActionGroup.xml | 26 +++++++++ ...tAssertTermAbsentInCheckoutActionGroup.xml | 31 +++++++++++ ...refrontAssertTermInCheckoutActionGroup.xml | 32 +++++++++++ ...ontProcessCheckoutToPaymentActionGroup.xml | 35 ++++++++++++ .../ActionGroup/UpdateTermActionGroup.xml | 32 +++++++++++ .../Test/Mftf/Data/TermData.xml | 47 ++++++++++++++++ .../Test/Mftf/Page/AdminEditTermPage.xml | 14 +++++ .../Test/Mftf/Page/AdminNewTermPage.xml | 15 ++++++ .../Test/Mftf/Page/AdminTermsPage.xml | 12 +++++ .../Test/Mftf/Page/CheckoutPage.xml | 14 +++++ .../Mftf/Section/AdminEditTermFormSection.xml | 15 ++++++ .../Mftf/Section/AdminNewTermFormSection.xml | 22 ++++++++ .../Section/AdminTermFormMessagesSection.xml | 15 ++++++ .../Mftf/Section/AdminTermGridSection.xml | 18 +++++++ .../StorefrontCheckoutAgreementsSection.xml | 16 ++++++ .../AdminUpdateDisabledHtmlTermEntityTest.xml | 54 +++++++++++++++++++ .../AdminUpdateDisabledTextTermEntityTest.xml | 54 +++++++++++++++++++ .../AdminUpdateEnabledTextTermEntityTest.xml | 54 +++++++++++++++++++ 20 files changed, 554 insertions(+) create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml new file mode 100644 index 0000000000000..9a855c6f8b5e9 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertTermInGridActionGroup"> + <arguments> + <argument name="termName" type="string"/> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml new file mode 100644 index 0000000000000..d420cc155a77c --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml @@ -0,0 +1,27 @@ +<?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="CreateNewTermActionGroup"> + <arguments> + <argument name="term"/> + </arguments> + <amOnPage url="{{AdminNewTermPage.url}}" stepKey="amOnNewTermPage"/> + <waitForPageLoad stepKey="waitForAdminNewTermPageLoad"/> + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{term.name}}" stepKey="fillFieldConditionName"/> + <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{term.isActive}}" stepKey="selectOptionIsActive"/> + <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{term.isHtml}}" stepKey="selectOptionIsHtml"/> + <selectOption selector="{{AdminNewTermFormSection.mode}}" userInput="{{term.mode}}" stepKey="selectOptionMode"/> + <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{term.storeView}}" stepKey="selectOptionStoreView" /> + <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{term.checkboxText}}" stepKey="fillFieldCheckboxText"/> + <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{term.content}}" stepKey="fillFieldContent"/> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml new file mode 100644 index 0000000000000..b88101ce88ef6 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml @@ -0,0 +1,26 @@ +<?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="DeleteTermActionGroup"> + <arguments> + <argument name="term" /> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForAdminTermsGridPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + <click selector="{{AdminEditTermFormSection.delete}}" stepKey="clickDeleteButton"/> + <click selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="clickDeleteOkButton"/> + <waitForPageLoad stepKey="waitForAdminTermsGridPageLoad2"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You deleted the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..7be17d8ca69d0 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml @@ -0,0 +1,31 @@ +<?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="StorefrontAssertTermAbsentInCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + <!--Check if agreement is absent on checkout--> + <dontSee selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + + <!--Checkout select Check/Money Order payment--> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentPageRendering"/> + <waitForPageLoad stepKey="waitForPaymentRendering"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..0cf745ce4e04f --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml @@ -0,0 +1,32 @@ +<?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="StorefrontAssertTermInCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + <!--Check if agreement is present on checkout and select it--> + <see selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + <selectOption selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementCheckbox}}" userInput="{{termCheckboxText}}" stepKey="checkAgreement"/> + + <!--Checkout select Check/Money Order payment--> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentPageRendering"/> + <waitForPageLoad stepKey="waitForPaymentRendering"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml new file mode 100644 index 0000000000000..c40f24836c815 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml @@ -0,0 +1,35 @@ +<?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="StorefrontProcessCheckoutToPaymentActionGroup"> + <!--Go to Checkout--> + <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> + <wait time="5" stepKey="waitMinicartRendering"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + + <!--Process steps--> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForShippingMethods"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('')}}" stepKey="selectShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForTheNextButton"/> + <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForProcessShippingMethod"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml new file mode 100644 index 0000000000000..d95729dfc27f8 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml @@ -0,0 +1,32 @@ +<?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="UpdateTermActionGroup"> + <arguments> + <argument name="term" /> + <argument name="updateTermData" /> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForAdminTermsGridLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{updateTermData.name}}" stepKey="fillFieldConditionName"/> + <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{updateTermData.isActive}}" stepKey="selectOptionIsActive"/> + <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{updateTermData.isHtml}}" stepKey="selectOptionIsHtml"/> + <selectOption selector="{{AdminNewTermFormSection.mode}}" userInput="{{updateTermData.mode}}" stepKey="selectOptionMode"/> + <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{updateTermData.storeView}}" stepKey="selectOptionStoreView" /> + <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{updateTermData.checkboxText}}" stepKey="fillFieldCheckboxText"/> + <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{updateTermData.content}}" stepKey="fillFieldContent"/> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml new file mode 100644 index 0000000000000..0172ffc771384 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml @@ -0,0 +1,47 @@ +<?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="activeTextTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Enabled</data> + <data key="isHtml">Text</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content" unique="suffix">TestMessage</data> + </entity> + <entity name="activeHtmlTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Enabled</data> + <data key="isHtml">HTML</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content"><html></data> + </entity> + <entity name="disabledTextTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Disabled</data> + <data key="isHtml">Text</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content" unique="suffix">TestMessage</data> + </entity> + <entity name="disabledHtmlTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Disabled</data> + <data key="isHtml">HTML</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content"><html></data> + </entity> +</entities> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml new file mode 100644 index 0000000000000..3a13f7d7d5e60 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminEditTermPage" url="checkout/agreement/edit/id/{{termId}}/" area="admin" module="Magento_CheckoutAgreements" parameterized="true"> + <section name="AdminEditTermFormSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml new file mode 100644 index 0000000000000..51ec18a1d17e5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewTermPage" url="checkout/agreement/new" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminNewTermFormSection"/> + <section name="AdminTermFormMessagesSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml new file mode 100644 index 0000000000000..4fb1a9704d9d0 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminTermsPage" url="checkout/agreement/" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminTermGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml new file mode 100644 index 0000000000000..fdb75ab4bc4c2 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_CheckoutAgreements"> + <section name="StorefrontCheckoutAgreementsSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml new file mode 100644 index 0000000000000..734a75b49c03a --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml @@ -0,0 +1,15 @@ +<?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="AdminEditTermFormSection"> + <element name="delete" type="button" selector=".page-main-actions #delete"/> + <element name="acceptPopupButton" type="button" selector="button.action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml new file mode 100644 index 0000000000000..aeff29ef116fc --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml @@ -0,0 +1,22 @@ +<?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="AdminNewTermFormSection"> + <element name="save" type="button" selector=".page-main-actions #save"/> + <element name="conditionName" type="input" selector="#name"/> + <element name="isActive" type="select" selector="#is_active"/> + <element name="isHtml" type="select" selector="#is_html"/> + <element name="mode" type="select" selector="#mode"/> + <element name="storeView" type="multiselect" selector="#stores"/> + <element name="checkboxText" type="input" selector="#checkbox_text"/> + <element name="content" type="textarea" selector="#content"/> + <element name="contentHeight" type="input" selector="#content_height"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml new file mode 100644 index 0000000000000..a3a91855b0640 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml @@ -0,0 +1,15 @@ +<?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="AdminTermFormMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml new file mode 100644 index 0000000000000..326f9dcce4320 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml @@ -0,0 +1,18 @@ +<?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="AdminTermGridSection"> + <element name="searchButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"/> + <element name="resetButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[2]"/> + <element name="filterByTermName" type="input" selector="#agreementGrid_filter_name"/> + <element name="firstRowConditionName" type="text" selector=".data-grid>tbody>tr>td.col-name"/> + <element name="firstRowConditionId" type="text" selector=".data-grid>tbody>tr>td.col-id.col-agreement_id"/> + <element name="successMessage" type="text" selector=".message-success"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml new file mode 100644 index 0000000000000..cb3e98949c622 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.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="StorefrontCheckoutAgreementsSection"> + <element name="checkoutAgreementCheckbox" type="checkbox" selector="div.checkout-agreement.field.choice.required > input"/> + <element name="checkoutAgreementButton" type="button" selector="div.checkout-agreements-block > div > div > div > label > button > span"/> + <element name="checkoutAgreementErrorMessage" type="button" selector="div.checkout-agreement.field.choice.required > div.mage-error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml new file mode 100644 index 0000000000000..29ed6c5194c38 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml @@ -0,0 +1,54 @@ +<?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="AdminUpdateDisabledHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update disabled HTML checkout agreement"/> + <description value="Admin should be able to update disabled HTML checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value=""/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <createData entity="SimpleTwo" stepKey="createdProduct"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> + <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <argument name="term" value="disabledHtmlTerm"/> + </actionGroup> + <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> + <argument name="term" value="disabledHtmlTerm"/> + <argument name="updateTermData" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> + <argument name="product" value="$$createdProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml new file mode 100644 index 0000000000000..d3d4e805e0ef7 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml @@ -0,0 +1,54 @@ +<?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="AdminUpdateDisabledTextTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update disabled Text checkout agreement"/> + <description value="Admin should be able to update disabled Text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value=""/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <createData entity="SimpleTwo" stepKey="createdProduct"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> + <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> + <argument name="term" value="activeHtmlTerm"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <argument name="term" value="disabledTextTerm"/> + </actionGroup> + <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> + <argument name="term" value="disabledTextTerm"/> + <argument name="updateTermData" value="activeHtmlTerm"/> + </actionGroup> + <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> + <argument name="product" value="$$createdProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeHtmlTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml new file mode 100644 index 0000000000000..5a761442a5992 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml @@ -0,0 +1,54 @@ +<?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="AdminUpdateEnabledTextTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update enabled Text checkout agreement"/> + <description value="Admin should be able to update enabled Text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value=""/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <createData entity="SimpleTwo" stepKey="createdProduct"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> + <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> + <argument name="term" value="disabledHtmlTerm"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> + <argument name="term" value="activeTextTerm"/> + <argument name="updateTermData" value="disabledHtmlTerm"/> + </actionGroup> + <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> + <argument name="product" value="$$createdProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="StorefrontAssertTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <argument name="termCheckboxText" value="{{disabledHtmlTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> From e6e4bb80e44266502f1236e299bacc9824679421 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Tue, 10 Dec 2019 18:54:58 +0000 Subject: [PATCH 128/734] Add class comments to appease linter --- .../Block/Backend/Grid/Column/Renderer/ScheduleStatus.php | 3 +++ .../Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php | 3 +++ .../Indexer/Block/Backend/Grid/Column/Renderer/Status.php | 3 +++ .../Indexer/Block/Backend/Grid/Column/Renderer/Updated.php | 3 +++ 4 files changed, 12 insertions(+) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 0c8083d57bcd8..7737077609b51 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -10,6 +10,9 @@ use Magento\Framework\Mview\View; use Magento\Framework\Phrase; +/** + * Renderer for 'Schedule Status' column in indexer grid + */ class ScheduleStatus extends AbstractRenderer { /** diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php index d5a749270a66f..adfe3dd5b346b 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Scheduled' column in indexer grid + */ class Scheduled extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php index 144d44fe42880..e8d6004b2727d 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Status' column in indexer grid + */ class Status extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php index 92ded9c1f0dc1..6c84ef0e628f7 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Updated' column in indexer grid + */ class Updated extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Datetime { /** From ed0cdb1293e0c98eb48ba11bdf39657641218b3f Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 12 Dec 2019 16:09:25 +0100 Subject: [PATCH 129/734] [WIP] Remove media gallery assets metadata when a directory removed --- .../Asset/Command/DeleteByDirectoryPath.php | 93 +++++++++++++++++++ .../Plugin/Wysiwyg/Images/Storage.php | 43 ++++++++- app/code/Magento/MediaGallery/etc/di.xml | 1 + .../DeleteByDirectoryPathInterface.php | 25 +++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php create mode 100644 app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php new file mode 100644 index 0000000000000..e9b22588928d5 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\Framework\Exception\LocalizedException; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; +use Psr\Log\LoggerInterface; + +/** + * Class DeleteByDirectoryPath + */ +class DeleteByDirectoryPath implements DeleteByDirectoryPathInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + private const MEDIA_GALLERY_ASSET_PATH = 'path'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * DeleteById constructor. + * + * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->logger = $logger; + } + + /** + * Delete media asset by path + * + * @param string $directoryPath + * + * @return void + * @throws CouldNotDeleteException + */ + public function execute(string $directoryPath): void + { + try { + $this->validateDirectoryPath($directoryPath); + + if (substr($directoryPath, -1) !== '/') { + $directoryPath .= '/'; + } + + /** @var AdapterInterface $connection */ + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); + $connection->delete($tableName, [self::MEDIA_GALLERY_ASSET_PATH . ' LIKE ?' => $directoryPath . '%']); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __( + 'Could not delete media assets by path %path: %error', + ['path' => $directoryPath, 'error' => $exception->getMessage()] + ); + throw new CouldNotDeleteException($message, $exception); + } + } + + /** + * Validate the directory path + * + * @param string $directoryPath + * @throws LocalizedException + */ + private function validateDirectoryPath(string $directoryPath): void + { + if (trim($directoryPath) === '') { + throw new LocalizedException(__('The directory path cannot be empty')); + } + } +} diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index ff0e1528e0597..1fcad5765e703 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -8,12 +8,12 @@ namespace Magento\MediaGallery\Plugin\Wysiwyg\Images; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; -use Magento\Framework\Exception\ValidatorException; use Psr\Log\LoggerInterface; /** @@ -31,6 +31,11 @@ class Storage */ private $deleteMediaAssetByPath; + /** + * @var DeleteByDirectoryPathInterface + */ + private $deleteMediAssetByDirectoryPath; + /** * @var Filesystem */ @@ -46,17 +51,20 @@ class Storage * * @param GetByPathInterface $getMediaAssetByPath * @param DeleteByPathInterface $deleteMediaAssetByPath + * @param DeleteByDirectoryPathInterface $deleteByDirectoryPath * @param Filesystem $filesystem * @param LoggerInterface $logger */ public function __construct( GetByPathInterface $getMediaAssetByPath, DeleteByPathInterface $deleteMediaAssetByPath, + DeleteByDirectoryPathInterface $deleteByDirectoryPath, Filesystem $filesystem, LoggerInterface $logger ) { $this->getMediaAssetByPath = $getMediaAssetByPath; $this->deleteMediaAssetByPath = $deleteMediaAssetByPath; + $this->deleteMediAssetByDirectoryPath = $deleteByDirectoryPath; $this->filesystem = $filesystem; $this->logger = $logger; } @@ -69,7 +77,6 @@ public function __construct( * @param string $target * * @return StorageSubject - * @throws ValidatorException * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -92,4 +99,36 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, return $result; } + + /** + * Delete media data after the folder delete action from Wysiwyg + * + * @param StorageSubject $subject + * @param null $result + * @param string $path + * + * @return null + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeleteDirectory(StorageSubject $subject, $result, $path) + { + if (!is_string($path)) { + return $result; + } + + $relativePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->getRelativePath($path); + + if (!$relativePath) { + return $result; + } + + try { + $this->deleteMediAssetByDirectoryPath->execute($relativePath); + } catch (\Exception $exception) { + $this->logger->critical($exception); + } + + return $result; + } } diff --git a/app/code/Magento/MediaGallery/etc/di.xml b/app/code/Magento/MediaGallery/etc/di.xml index 8c4a856852e1a..24ed42b2b06dd 100644 --- a/app/code/Magento/MediaGallery/etc/di.xml +++ b/app/code/Magento/MediaGallery/etc/di.xml @@ -13,6 +13,7 @@ <preference for="Magento\MediaGalleryApi\Model\Asset\Command\SaveInterface" type="Magento\MediaGallery\Model\Asset\Command\Save"/> <preference for="Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\GetByPath"/> <preference for="Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\DeleteByPath"/> + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface" type="Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath"/> <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\GetAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\GetAssetKeywords"/> <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords"/> diff --git a/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php new file mode 100644 index 0000000000000..e55f7cb714f77 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +/** + * A command represents the media gallery assets delete action. A media gallery asset is filtered by directory + * path value. + */ +interface DeleteByDirectoryPathInterface +{ + /** + * Delete media assets by directory path + * + * @param string $directoryPath + * + * @return void + */ + public function execute(string $directoryPath): void; +} From dd7674ee84e150867c26b8d9cb447d7ab0056a4e Mon Sep 17 00:00:00 2001 From: Nirav Patel <nirav.p@codilar.com> Date: Sun, 15 Dec 2019 19:31:42 +0530 Subject: [PATCH 130/734] fixed-html validation issue for some attribute --- .../view/base/web/js/swatch-renderer.js | 96 ++++++++++--------- .../templates/product/layered/renderer.phtml | 46 ++++----- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 894a4518f4de8..3b6c65adbb10d 100644 --- a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -63,12 +63,12 @@ define([ /** * Render tooltips by attributes (only to up). * Required element attributes: - * - option-type (integer, 0-3) - * - option-label (string) - * - option-tooltip-thumb - * - option-tooltip-value - * - thumb-width - * - thumb-height + * - data-option-type (integer, 0-3) + * - data-option-label (string) + * - data-option-tooltip-thumb + * - data-option-tooltip-value + * - data-thumb-width + * - data-thumb-height */ $.widget('mage.SwatchRendererTooltip', { options: { @@ -84,12 +84,12 @@ define([ $this = this.element, $element = $('.' + $widget.options.tooltipClass), timer, - type = parseInt($this.attr('option-type'), 10), - label = $this.attr('option-label'), - thumb = $this.attr('option-tooltip-thumb'), - value = $this.attr('option-tooltip-value'), - width = $this.attr('thumb-width'), - height = $this.attr('thumb-height'), + type = parseInt($this.data('option-type'), 10), + label = $this.data('option-label'), + thumb = $this.data('option-tooltip-thumb'), + value = $this.data('option-tooltip-value'), + width = $this.data('thumb-width'), + height = $this.data('thumb-height'), $image, $title, $corner; @@ -429,8 +429,8 @@ define([ // Create new control container.append( '<div class="' + classes.attributeClass + ' ' + item.code + '" ' + - 'attribute-code="' + item.code + '" ' + - 'attribute-id="' + item.id + '">' + + 'data-attribute-code="' + item.code + '" ' + + 'data-attribute-id="' + item.id + '">' + label + '<div aria-activedescendant="" ' + 'tabindex="0" ' + @@ -462,7 +462,7 @@ define([ if (showTooltip === 1) { // Connect Tooltip container - .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]') + .find('[data-option-type="1"], [data-option-type="2"], [data-option-type="0"], [data-option-type="3"]') .SwatchRendererTooltip(); } @@ -537,21 +537,22 @@ define([ ' aria-checked="false"' + ' aria-describedby="' + controlId + '"' + ' tabindex="0"' + - ' option-type="' + type + '"' + - ' option-id="' + id + '"' + - ' option-label="' + label + '"' + + ' data-option-type="' + type + '"' + + ' data-option-id="' + id + '"' + + ' data-option-label="' + label + '"' + ' aria-label="' + label + '"' + - ' option-tooltip-thumb="' + thumb + '"' + - ' option-tooltip-value="' + value + '"' + ' role="option"' + - ' thumb-width="' + width + '"' + - ' thumb-height="' + height + '"'; + ' data-thumb-width="' + width + '"' + + ' data-thumb-height="' + height + '"'; + + attr += thumb !== '' ? ' data-option-tooltip-thumb="' + thumb + '"' : ''; + attr += value !== '' ? ' data-option-tooltip-value="' + value + '"' : ''; swatchImageWidth = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.width : 30; swatchImageHeight = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.height : 20; if (!this.hasOwnProperty('products') || this.products.length <= 0) { - attr += ' option-empty="true"'; + attr += ' data-option-empty="true"'; } if (type === 0) { @@ -599,14 +600,14 @@ define([ html = '<select class="' + this.options.classes.selectClass + ' ' + config.code + '">' + - '<option value="0" option-id="0">' + chooseText + '</option>'; + '<option value="0" data-option-id="0">' + chooseText + '</option>'; $.each(config.options, function () { var label = this.label, - attr = ' value="' + this.id + '" option-id="' + this.id + '"'; + attr = ' value="' + this.id + '" data-option-id="' + this.id + '"'; if (!this.hasOwnProperty('products') || this.products.length <= 0) { - attr += ' option-empty="true"'; + attr += ' data-option-empty="true"'; } html += '<option ' + attr + '>' + label + '</option>'; @@ -721,7 +722,7 @@ define([ var $parent = $this.parents('.' + $widget.options.classes.attributeClass), $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper), $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass), - attributeId = $parent.attr('attribute-id'), + attributeId = $parent.data('attribute-id'), $input = $parent.find('.' + $widget.options.classes.attributeInput), checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']); @@ -736,14 +737,14 @@ define([ } if ($this.hasClass('selected')) { - $parent.removeAttr('option-selected').find('.selected').removeClass('selected'); + $parent.removeAttr('data-option-selected').find('.selected').removeClass('selected'); $input.val(''); $label.text(''); $this.attr('aria-checked', false); } else { - $parent.attr('option-selected', $this.attr('option-id')).find('.selected').removeClass('selected'); - $label.text($this.attr('option-label')); - $input.val($this.attr('option-id')); + $parent.attr('data-option-selected', $this.data('option-id')).find('.selected').removeClass('selected'); + $label.text($this.data('option-label')); + $input.val($this.data('option-id')); $input.attr('data-attr-name', this._getAttributeCodeById(attributeId)); $this.addClass('selected'); $widget._toggleCheckedAttributes($this, $wrapper); @@ -805,7 +806,7 @@ define([ */ _OnChange: function ($this, $widget) { var $parent = $this.parents('.' + $widget.options.classes.attributeClass), - attributeId = $parent.attr('attribute-id'), + attributeId = $parent.data('attribute-id'), $input = $parent.find('.' + $widget.options.classes.attributeInput); if ($widget.productForm.length > 0) { @@ -815,10 +816,10 @@ define([ } if ($this.val() > 0) { - $parent.attr('option-selected', $this.val()); + $parent.attr('data-option-selected', $this.val()); $input.val($this.val()); } else { - $parent.removeAttr('option-selected'); + $parent.removeAttr('data-option-selected'); $input.val(''); } @@ -845,8 +846,8 @@ define([ * @private */ _Rewind: function (controls) { - controls.find('div[option-id], option[option-id]').removeClass('disabled').removeAttr('disabled'); - controls.find('div[option-empty], option[option-empty]') + controls.find('div[data-option-id], option[data-option-id]').removeClass('disabled').removeAttr('disabled'); + controls.find('div[data-option-empty], option[data-option-empty]') .attr('disabled', true) .addClass('disabled') .attr('tabindex', '-1'); @@ -859,8 +860,8 @@ define([ */ _Rebuild: function () { var $widget = this, - controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[attribute-id]'), - selected = controls.filter('[option-selected]'); + controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[data-attribute-id]'), + selected = controls.filter('[data-option-selected]'); // Enable all options $widget._Rewind(controls); @@ -873,16 +874,16 @@ define([ // Disable not available options controls.each(function () { var $this = $(this), - id = $this.attr('attribute-id'), + id = $this.data('attribute-id'), products = $widget._CalcProducts(id); - if (selected.length === 1 && selected.first().attr('attribute-id') === id) { + if (selected.length === 1 && selected.first().data('attribute-id') === id) { return; } - $this.find('[option-id]').each(function () { + $this.find('[data-option-id]').each(function () { var $element = $(this), - option = $element.attr('option-id'); + option = $element.data('option-id'); if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option) || $element.hasClass('selected') || @@ -905,12 +906,13 @@ define([ */ _CalcProducts: function ($skipAttributeId) { var $widget = this, + selectedOptions = '.' + $widget.options.classes.attributeClass + '[data-option-selected]', products = []; // Generate intersection of products - $widget.element.find('.' + $widget.options.classes.attributeClass + '[option-selected]').each(function () { - var id = $(this).attr('attribute-id'), - option = $(this).attr('option-selected'); + $widget.element.find(selectedOptions).each(function () { + var id = $(this).data('attribute-id'), + option = $(this).attr('data-option-selected'); if ($skipAttributeId !== undefined && $skipAttributeId === id) { return; @@ -1322,7 +1324,7 @@ define([ _EmulateSelected: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) { var elem = this.element.find('.' + this.options.classes.attributeClass + - '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'), + '[data-attribute-code="' + attributeCode + '"] [data-option-id="' + optionId + '"]'), parentInput = elem.parent(); if (elem.hasClass('selected')) { @@ -1346,7 +1348,7 @@ define([ _EmulateSelectedByAttributeId: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeId, optionId) { var elem = this.element.find('.' + this.options.classes.attributeClass + - '[attribute-id="' + attributeId + '"] [option-id="' + optionId + '"]'), + '[data-attribute-id="' + attributeId + '"] [data-option-id="' + optionId + '"]'), parentInput = elem.parent(); if (elem.hasClass('selected')) { diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml index b22429eafc8e8..74fca7286528c 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml @@ -11,8 +11,8 @@ ?> <?php $swatchData = $block->getSwatchData(); ?> <div class="swatch-attribute swatch-layered <?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" - attribute-code="<?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" - attribute-id="<?= $block->escapeHtmlAttr($swatchData['attribute_id']) ?>"> + data-attribute-code="<?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" + data-attribute-id="<?= $block->escapeHtmlAttr($swatchData['attribute_id']) ?>"> <div class="swatch-attribute-options clearfix"> <?php foreach ($swatchData['options'] as $option => $label) : ?> <a href="<?= $block->escapeUrl($label['link']) ?>" @@ -24,11 +24,11 @@ ?> <div class="swatch-option <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="3" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="" + data-option-type="3" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="" ></div> <?php break; case '2': @@ -37,22 +37,22 @@ <?php $swatchImagePath = $block->getSwatchPath('swatch_image', $swatchData['swatches'][$option]['value']); ?> <div class="swatch-option image <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="2" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="<?= $block->escapeUrl($swatchThumbPath) ?>" - option-tooltip-value="" + data-option-type="2" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="<?= $block->escapeUrl($swatchThumbPath) ?>" + data-option-tooltip-value="" style="background: url(<?= $block->escapeUrl($swatchImagePath) ?>) no-repeat center; background-size: initial;"></div> <?php break; case '1': ?> <div class="swatch-option color <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="1" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="<?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?>" + data-option-type="1" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="<?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?>" style="background: <?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?> no-repeat center; background-size: initial;"></div> <?php break; case '0': @@ -60,11 +60,11 @@ ?> <div class="swatch-option text <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="0" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="" + data-option-type="0" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="" ><?= $block->escapeHtml($swatchData['swatches'][$option]['value']) ?></div> <?php break; } ?> @@ -77,7 +77,7 @@ <script> require(["jquery", "Magento_Swatches/js/swatch-renderer"], function ($) { $('.swatch-layered.<?= $block->escapeJs($swatchData['attribute_code']) ?>') - .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]') + .find('[data-option-type="1"], [data-option-type="2"], [data-option-type="0"], [data-option-type="3"]') .SwatchRendererTooltip(); }); </script> From 5a5091c906e27153ac9680e0db5d8261df3c024c Mon Sep 17 00:00:00 2001 From: Oleg Usik <o.usik@atwix.com> Date: Wed, 18 Dec 2019 17:14:01 +0200 Subject: [PATCH 131/734] Fixed 24990 link not to redirect to dashboard --- setup/src/Magento/Setup/Controller/Navigation.php | 13 ++++++++++++- setup/view/magento/setup/navigation/side-menu.phtml | 8 +++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 5ac0bbfe38c45..65bacebeed18a 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -10,6 +10,7 @@ use Zend\View\Model\JsonModel; use Zend\View\Model\ViewModel; use Magento\Setup\Model\Cron\Status; +use Magento\Setup\Model\ObjectManagerProvider; /** * Class Navigation @@ -32,14 +33,20 @@ class Navigation extends AbstractActionController */ protected $view; + /** + * @var ObjectManagerProvider $objectManagerProvider + */ + protected $objectManagerProvider; + /** * @param NavModel $navigation * @param Status $status */ - public function __construct(NavModel $navigation, Status $status) + public function __construct(NavModel $navigation, Status $status, ObjectManagerProvider $objectManagerProvider) { $this->navigation = $navigation; $this->status = $status; + $this->objectManagerProvider = $objectManagerProvider; $this->view = new ViewModel; $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); @@ -75,8 +82,12 @@ public function menuAction() */ public function sideMenuAction() { + /** @var \Magento\Backend\Model\UrlInterface $backendUrl */ + $backendUrl = $this->objectManagerProvider->get()->get(\Magento\Backend\Model\UrlInterface::class); + $this->view->setTemplate('/magento/setup/navigation/side-menu.phtml'); $this->view->setVariable('isInstaller', $this->navigation->getType() == NavModel::NAV_INSTALLER); + $this->view->setVariable('backendUrl', $backendUrl->getRouteUrl('adminhtml')); $this->view->setTerminal(true); return $this->view; } diff --git a/setup/view/magento/setup/navigation/side-menu.phtml b/setup/view/magento/setup/navigation/side-menu.phtml index f34a6c7c72e69..b731881e8bf4f 100644 --- a/setup/view/magento/setup/navigation/side-menu.phtml +++ b/setup/view/magento/setup/navigation/side-menu.phtml @@ -20,9 +20,11 @@ ng-show="<?= implode( '&&', $expressions) ?>" > <nav class="admin__menu" ng-controller="mainController"> - <span class="logo logo-static" data-edition="Community Edition"> - <img class="logo-img" src="<?= $this->basePath() ?>/pub/images/logo.svg" alt="Magento Admin Panel"> - </span> + <a href="<?= $this->backendUrl ?>" class="logo" data-edition="Community Edition"> + <span> + <img class="logo-img" src="<?= $this->basePath() ?>/pub/images/logo.svg" alt="Magento Admin Panel"> + </span> + </a> <ul id="nav" role="menubar"> <li class="item-home level-0" ng-class="{_active: $state.current.name === 'root.home'}"> <a href="" ui-sref="root.home"> From da9526f761ab9ede3202cd387015b94e91b75e89 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 26 Dec 2019 07:18:57 +0100 Subject: [PATCH 132/734] Refactoring and unit test coverage --- .../Asset/Command/DeleteByDirectoryPath.php | 9 +- .../Plugin/Wysiwyg/Images/Storage.php | 11 +- .../Command/DeleteByDirectoryPathTest.php | 105 ++++++++++++++++++ 3 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php index e9b22588928d5..c83ac23ddb0d7 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php @@ -60,9 +60,8 @@ public function execute(string $directoryPath): void try { $this->validateDirectoryPath($directoryPath); - if (substr($directoryPath, -1) !== '/') { - $directoryPath .= '/'; - } + // Make sure that the path has a trailing slash + $directoryPath = rtrim($directoryPath, '/') . '/'; /** @var AdapterInterface $connection */ $connection = $this->resourceConnection->getConnection(); @@ -86,8 +85,8 @@ public function execute(string $directoryPath): void */ private function validateDirectoryPath(string $directoryPath): void { - if (trim($directoryPath) === '') { - throw new LocalizedException(__('The directory path cannot be empty')); + if (!$directoryPath || trim($directoryPath) === '') { + throw new LocalizedException(__('Cannot remove assets, the directory path does not exist')); } } } diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index 1fcad5765e703..5fbe370e36a3c 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -118,16 +118,7 @@ public function afterDeleteDirectory(StorageSubject $subject, $result, $path) } $relativePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->getRelativePath($path); - - if (!$relativePath) { - return $result; - } - - try { - $this->deleteMediAssetByDirectoryPath->execute($relativePath); - } catch (\Exception $exception) { - $this->logger->critical($exception); - } + $this->deleteMediAssetByDirectoryPath->execute($relativePath); return $result; } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php new file mode 100644 index 0000000000000..ebc90cdc0470f --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Psr\Log\LoggerInterface; + +/** + * Test the DeleteByDirectoryPath command model + */ +class DeleteByDirectoryPathTest extends TestCase +{ + private const TABLE_NAME = 'media_gallery_asset'; + private const DIRECTORY_PATH = 'test-directory-path/'; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceConnection; + + /** + * @var DeleteByDirectoryPath + */ + private $deleteMediaAssetByDirectoryPath; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->logger = $this->createMock(LoggerInterface::class); + $this->resourceConnection = $this->createMock(ResourceConnection::class); + + $this->deleteMediaAssetByDirectoryPath = (new ObjectManager($this))->getObject( + DeleteByDirectoryPath::class, + [ + 'resourceConnection' => $this->resourceConnection, + 'logger' => $this->logger, + ] + ); + + $this->adapter = $this->createMock(AdapterInterface::class); + } + + /** + * Test delete media asset by path command + * + * @param string $directoryPath + * @throws CouldNotDeleteException + * @dataProvider directoryPathDataProvider + */ + public function testDeleteByDirectoryPath(string $directoryPath): void + { + if (!empty($directoryPath)) { + $this->resourceConnection->expects($this->once()) + ->method('getConnection') + ->willReturn($this->adapter); + $this->resourceConnection->expects($this->once()) + ->method('getTableName') + ->with(self::TABLE_NAME) + ->willReturn('prefix_' . self::TABLE_NAME); + $this->adapter->expects($this->once()) + ->method('delete') + ->with('prefix_' . self::TABLE_NAME, ['path LIKE ?' => self::DIRECTORY_PATH . '%']); + } else { + self::expectException('\Magento\Framework\Exception\CouldNotDeleteException'); + } + + $this->deleteMediaAssetByDirectoryPath->execute($directoryPath); + } + + /** + * Data provider for directory path + * + * @return array + */ + public function directoryPathDataProvider(): array + { + return [ + 'Existing path' => [self::DIRECTORY_PATH], + 'Empty path' => [''] + ]; + } +} From 145cb44e195c9d66c64c2b5f5d3e3f7371cdec15 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 30 Dec 2019 15:22:09 +0100 Subject: [PATCH 133/734] Added an extended class purpose description --- .../Model/Asset/Command/DeleteByDirectoryPath.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php index c83ac23ddb0d7..c985907673818 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php @@ -16,6 +16,8 @@ /** * Class DeleteByDirectoryPath + * + * Remove asset(s) that correspond the provided directory path */ class DeleteByDirectoryPath implements DeleteByDirectoryPathInterface { @@ -48,7 +50,7 @@ public function __construct( } /** - * Delete media asset by path + * Delete media asset(s) by path * * @param string $directoryPath * From 8679c9a0cad176f556fd6d4893dffd25fa24a88a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 30 Dec 2019 16:37:04 +0100 Subject: [PATCH 134/734] Changed DocBlock description of the parameter type --- app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index 5fbe370e36a3c..d741067228b09 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -104,7 +104,7 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, * Delete media data after the folder delete action from Wysiwyg * * @param StorageSubject $subject - * @param null $result + * @param mixed $result * @param string $path * * @return null From a025364608ed5baa643221b0513e378b25dbc393 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 3 Jan 2020 14:35:56 +0200 Subject: [PATCH 135/734] Create Sitemap Entity --- ...arketingCreateSitemapEntityActionGroup.xml | 23 ++++++++ ...ingNavigateToNewSitemapPageActionGroup.xml | 17 ++++++ ...AdminMarketingSearchSitemapActionGroup.xml | 23 ++++++++ .../AssertAdminSitemapInGridActionGroup.xml | 17 ++++++ .../AssertAdminSitemapMessageActionGroup.xml | 19 +++++++ .../AdminMarketingSiteMapGridSection.xml | 3 ++ .../AdminMarketingSiteMapNewSection.xml | 15 ++++++ .../AdminMarketingCreateSitemapEntityTest.xml | 54 +++++++++++++++++++ ...ketingCreateSitemapFailfFolderSaveTest.xml | 47 ++++++++++++++++ ...minMarketingCreateSitemapPathErrorTest.xml | 48 +++++++++++++++++ .../Test/TestCase/CreateSitemapEntityTest.xml | 6 +-- 11 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingCreateSitemapEntityActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingNavigateToNewSitemapPageActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSearchSitemapActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapInGridActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapNewSection.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingCreateSitemapEntityActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingCreateSitemapEntityActionGroup.xml new file mode 100644 index 0000000000000..9a332c87217d1 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingCreateSitemapEntityActionGroup.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"> + + <!--Fill Required Fields --> + <actionGroup name="AdminMarketingCreateSitemapEntityActionGroup"> + <arguments> + <argument name="filename" type="string"/> + <argument name="path" type="string"/> + </arguments> + <fillField stepKey="fillFilenameField" selector="{{NewSiteMapSection.filename}}" userInput="{{filename}}"/> + <fillField stepKey="fillPathField" selector="{{NewSiteMapSection.path}}" userInput="{{path}}"/> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{NewSiteMapSection.saveButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingNavigateToNewSitemapPageActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingNavigateToNewSitemapPageActionGroup.xml new file mode 100644 index 0000000000000..c72e43c74ae5c --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingNavigateToNewSitemapPageActionGroup.xml @@ -0,0 +1,17 @@ +<?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"> + + <!--Click "Add New Sitemap Button" --> + <actionGroup name="AdminMarketingNavigateToNewSitemapPageActionGroup"> + <click stepKey="clickAddNewSitemapButton" selector="{{AdminMarketingSiteMapGridSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSearchSitemapActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSearchSitemapActionGroup.xml new file mode 100644 index 0000000000000..76e0ddc58ea0d --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSearchSitemapActionGroup.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="AdminMarketingSearchSitemapActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <!--Reset Search Filters --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <!--Fill Sitemap Name Field --> + <fillField selector="{{AdminMarketingSiteMapGridSection.fileNameTextField}}" userInput="{{name}}" stepKey="filterByName"/> + <!--Click "Search" Button --> + <click selector="{{AdminMarketingSiteMapGridSection.searchButton}}" stepKey="doFilter"/> + <waitForPageLoad stepKey="waitForSitemapPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapInGridActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapInGridActionGroup.xml new file mode 100644 index 0000000000000..c844cb9a3ca76 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapInGridActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AssertAdminSitemapInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <see userInput="{{name}}" selector="{{AdminMarketingSiteMapGridSection.rowByIndex('1')}}" stepKey="seeSitemap"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml new file mode 100644 index 0000000000000..0180fe50f8cb4 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminSitemapMessageActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="You saved the sitemap."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{AdminMarketingSiteMapGridSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{AdminMarketingSiteMapGridSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml index 50c96ae6748ce..129712b28c8b2 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml @@ -9,6 +9,9 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMarketingSiteMapGridSection"> + <element name="add" type="button" selector=".page-actions-buttons .add"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> <element name="searchButton" type="button" selector=".admin__filter-actions [title='Search']"/> <element name="firstSearchResult" type="text" selector="#sitemapGrid_table>tbody>tr:nth-child(1)"/> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapNewSection.xml b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapNewSection.xml new file mode 100644 index 0000000000000..a7908417b7ac3 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapNewSection.xml @@ -0,0 +1,15 @@ +<?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="NewSiteMapSection"> + <element name="filename" type="input" selector="#sitemap_filename"/> + <element name="path" type="input" selector="#sitemap_path"/> + <element name="saveButton" type="button" selector=".page-actions .save"/> + </section> +</sections> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml new file mode 100644 index 0000000000000..91ac5c093b46b --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml @@ -0,0 +1,54 @@ +<?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="AdminMarketingCreateSitemapEntityTest"> + <annotations> + <features value="Sitemap"/> + <stories value="Admin Creates Sitemap Entity"/> + <title value="Sitemap Creation"/> + <description value="Sitemap Entity Creation"/> + <group value="sitemap"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--TEST BODY --> + <!--Navigate to Marketing->Sitemap Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSEOAndSearchSiteMap.dataUiId}}"/> + </actionGroup> + <!-- Navigate to New Sitemap Creation Page --> + <actionGroup ref="AdminMarketingNavigateToNewSitemapPageActionGroup" stepKey="navigateToAddNewSitemap"/> + <!-- Create Sitemap Entity --> + <actionGroup ref="AdminMarketingCreateSitemapEntityActionGroup" stepKey="createSitemap"> + <argument name="filename" value="sitemap.xml"/> + <argument name="path" value="/"/> + </actionGroup> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="You saved the sitemap."/> + <argument name="messageType" value="success"/> + </actionGroup> + <!-- Find Created Sitemap On Grid --> + <actionGroup ref="AdminMarketingSearchSitemapActionGroup" stepKey="findCreatedSitemapInGrid"> + <argument name="name" value="sitemap.xml"/> + </actionGroup> + <actionGroup ref="AssertAdminSitemapInGridActionGroup" stepKey="assertSitemapInGrid"> + <argument name="name" value="sitemap.xml"/> + </actionGroup> + <!--END TEST BODY --> + </test> +</tests> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml new file mode 100644 index 0000000000000..0f9fb7a51f12f --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml @@ -0,0 +1,47 @@ +<?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="AdminMarketingCreateSitemapFailfFolderSaveTest"> + <annotations> + <features value="Sitemap"/> + <stories value="Admin Creates Sitemap Entity Fail Folder Save"/> + <title value="Sitemap Creation Fail Folder Save"/> + <description value="Sitemap Entity Creation Fail Folder Save"/> + <group value="sitemap"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--TEST BODY --> + <!--Navigate to Marketing->Sitemap Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSEOAndSearchSiteMap.dataUiId}}"/> + </actionGroup> + <!-- Navigate to New Sitemap Creation Page --> + <actionGroup ref="AdminMarketingNavigateToNewSitemapPageActionGroup" stepKey="navigateToAddNewSitemap"/> + <!-- Create Sitemap Entity With Incorrect Folder--> + <actionGroup ref="AdminMarketingCreateSitemapEntityActionGroup" stepKey="createSitemap"> + <argument name="filename" value="sitemap.xml"/> + <argument name="path" value="%isolation%"/> + </actionGroup> + <!-- See Error Message--> + <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeErrorMessage"> + <argument name="message" value="Please create the specified folder "%isolation%" before saving the sitemap."/> + <argument value="error" name="messageType"/> + </actionGroup> + <!--END TEST BODY --> + </test> +</tests> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml new file mode 100644 index 0000000000000..d6fc1259e0699 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml @@ -0,0 +1,48 @@ +<?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="AdminMarketingCreateSitemapPathErrorTest"> + <annotations> + <features value="Sitemap"/> + <stories value="Admin Creates Sitemap Entity Path Error"/> + <title value="Sitemap Creation With Path Error"/> + <description value="Sitemap Entity Creation With Path Error"/> + <group value="sitemap"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--TEST BODY --> + + <!--Navigate to Marketing->Sitemap Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSEOAndSearchSiteMap.dataUiId}}"/> + </actionGroup> + <!-- Navigate to New Sitemap Creation Page --> + <actionGroup ref="AdminMarketingNavigateToNewSitemapPageActionGroup" stepKey="navigateToAddNewSitemap"/> + <!-- Create Sitemap Entity With Incorrect Path--> + <actionGroup ref="AdminMarketingCreateSitemapEntityActionGroup" stepKey="createSitemap"> + <argument name="filename" value="%isolation%"/> + <argument name="path" value="/"/> + </actionGroup> + <!-- See Error Message--> + <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeErrorMessage"> + <argument name="message" value="Path "/%isolation%" is not available and cannot be used."/> + <argument value="error" name="messageType"/> + </actionGroup> + <!--END TEST BODY --> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml index d2bf309e68902..f400dba3a380d 100644 --- a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml @@ -8,20 +8,20 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sitemap\Test\TestCase\CreateSitemapEntityTest" summary="Create Sitemap" ticketId="MAGETWO-23277"> <variation name="CreateSitemapEntityTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1,mftf_migrated:yes</data> <data name="sitemap/data/sitemap_filename" xsi:type="string">sitemap.xml</data> <data name="sitemap/data/sitemap_path" xsi:type="string">/</data> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapSuccessSaveMessage" /> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapInGrid" /> </variation> <variation name="CreateSitemapEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3,mftf_migrated:yes</data> <data name="sitemap/data/sitemap_filename" xsi:type="string">%isolation%</data> <data name="sitemap/data/sitemap_path" xsi:type="string">/</data> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapFailPathSaveMessage" /> </variation> <variation name="CreateSitemapEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3,,mftf_migrated:yes</data> <data name="sitemap/data/sitemap_filename" xsi:type="string">sitemap.xml</data> <data name="sitemap/data/sitemap_path" xsi:type="string">/%isolation%</data> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapFailFolderSaveMessage" /> From 252973f69865b4d845822e3959c902f0bc925121 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Mon, 6 Jan 2020 10:27:15 +0200 Subject: [PATCH 136/734] refactoring --- .../AssertAdminSitemapMessageActionGroup.xml | 19 ------------------- .../AdminMarketingSiteMapGridSection.xml | 1 - .../AdminMarketingCreateSitemapEntityTest.xml | 2 +- ...ketingCreateSitemapFailfFolderSaveTest.xml | 2 +- ...minMarketingCreateSitemapPathErrorTest.xml | 2 +- .../Test/TestCase/CreateSitemapEntityTest.xml | 2 +- 6 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml deleted file mode 100644 index 0180fe50f8cb4..0000000000000 --- a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapMessageActionGroup.xml +++ /dev/null @@ -1,19 +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="AssertAdminSitemapMessageActionGroup"> - <arguments> - <argument name="message" type="string" defaultValue="You saved the sitemap."/> - <argument name="messageType" type="string" defaultValue="success"/> - </arguments> - <waitForElementVisible selector="{{AdminMarketingSiteMapGridSection.messageByType(messageType)}}" stepKey="waitForMessage"/> - <see userInput="{{message}}" selector="{{AdminMarketingSiteMapGridSection.messageByType(messageType)}}" stepKey="verifyMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml index 129712b28c8b2..d315cc24e8f96 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml @@ -10,7 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMarketingSiteMapGridSection"> <element name="add" type="button" selector=".page-actions-buttons .add"/> - <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> <element name="searchButton" type="button" selector=".admin__filter-actions [title='Search']"/> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml index 91ac5c093b46b..daf3e58bca354 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml @@ -38,7 +38,7 @@ <argument name="path" value="/"/> </actionGroup> <!-- Assert Success Message --> - <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeSuccessMessage"> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> <argument name="message" value="You saved the sitemap."/> <argument name="messageType" value="success"/> </actionGroup> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml index 0f9fb7a51f12f..b0669a690370e 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml @@ -38,7 +38,7 @@ <argument name="path" value="%isolation%"/> </actionGroup> <!-- See Error Message--> - <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeErrorMessage"> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeErrorMessage"> <argument name="message" value="Please create the specified folder "%isolation%" before saving the sitemap."/> <argument value="error" name="messageType"/> </actionGroup> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml index d6fc1259e0699..6451080d62025 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml @@ -39,7 +39,7 @@ <argument name="path" value="/"/> </actionGroup> <!-- See Error Message--> - <actionGroup ref="AssertAdminSitemapMessageActionGroup" stepKey="seeErrorMessage"> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeErrorMessage"> <argument name="message" value="Path "/%isolation%" is not available and cannot be used."/> <argument value="error" name="messageType"/> </actionGroup> diff --git a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml index f400dba3a380d..f1da612aa6ca0 100644 --- a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/CreateSitemapEntityTest.xml @@ -21,7 +21,7 @@ <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapFailPathSaveMessage" /> </variation> <variation name="CreateSitemapEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S3,,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S3,mftf_migrated:yes</data> <data name="sitemap/data/sitemap_filename" xsi:type="string">sitemap.xml</data> <data name="sitemap/data/sitemap_path" xsi:type="string">/%isolation%</data> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapFailFolderSaveMessage" /> From 847e5bcc38c9175c5dba0a0eaa4aaf377c34ca59 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 7 Jan 2020 14:43:26 +0100 Subject: [PATCH 137/734] Errors handling refactoring and additional test coverage --- .../Asset/Command/DeleteByDirectoryPath.php | 10 +- .../Plugin/Wysiwyg/Images/Storage.php | 19 +- .../Test/Unit/Plugin/Images/StorageTest.php | 162 ++++++++++++++++++ 3 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php index c985907673818..a50095fb3d8e7 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php @@ -10,7 +10,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Exception\CouldNotDeleteException; -use Magento\Framework\Exception\LocalizedException; use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; use Psr\Log\LoggerInterface; @@ -55,13 +54,13 @@ public function __construct( * @param string $directoryPath * * @return void + * * @throws CouldNotDeleteException */ public function execute(string $directoryPath): void { + $this->validateDirectoryPath($directoryPath); try { - $this->validateDirectoryPath($directoryPath); - // Make sure that the path has a trailing slash $directoryPath = rtrim($directoryPath, '/') . '/'; @@ -83,12 +82,13 @@ public function execute(string $directoryPath): void * Validate the directory path * * @param string $directoryPath - * @throws LocalizedException + * + * @throws CouldNotDeleteException */ private function validateDirectoryPath(string $directoryPath): void { if (!$directoryPath || trim($directoryPath) === '') { - throw new LocalizedException(__('Cannot remove assets, the directory path does not exist')); + throw new CouldNotDeleteException(__('Cannot remove assets, the directory path does not exist')); } } } diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index d741067228b09..7a4aa219cdf29 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -8,6 +8,7 @@ namespace Magento\MediaGallery\Plugin\Wysiwyg\Images; +use Magento\Framework\Exception\CouldNotDeleteException; use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; @@ -15,6 +16,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\ValidatorException; /** * Ensures that metadata is removed from the database when a file is deleted and it is an image @@ -34,7 +36,7 @@ class Storage /** * @var DeleteByDirectoryPathInterface */ - private $deleteMediAssetByDirectoryPath; + private $deleteMediaAssetByDirectoryPath; /** * @var Filesystem @@ -64,7 +66,7 @@ public function __construct( ) { $this->getMediaAssetByPath = $getMediaAssetByPath; $this->deleteMediaAssetByPath = $deleteMediaAssetByPath; - $this->deleteMediAssetByDirectoryPath = $deleteByDirectoryPath; + $this->deleteMediaAssetByDirectoryPath = $deleteByDirectoryPath; $this->filesystem = $filesystem; $this->logger = $logger; } @@ -109,6 +111,7 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, * * @return null * + * @throws CouldNotDeleteException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterDeleteDirectory(StorageSubject $subject, $result, $path) @@ -117,8 +120,16 @@ public function afterDeleteDirectory(StorageSubject $subject, $result, $path) return $result; } - $relativePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->getRelativePath($path); - $this->deleteMediAssetByDirectoryPath->execute($relativePath); + try { + $mediaDirectoryRead = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $relativePath = $mediaDirectoryRead->getRelativePath($path); + if ($mediaDirectoryRead->isExist($relativePath) === false) { + throw new CouldNotDeleteException(__('Cannot remove assets, the provided path does not exist')); + } + $this->deleteMediaAssetByDirectoryPath->execute($relativePath); + } catch (ValidatorException $exception) { + $this->logger->critical($exception); + } return $result; } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php new file mode 100644 index 0000000000000..f57d6e9c7635b --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php @@ -0,0 +1,162 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Plugin\Images; + +use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; +use Magento\Framework\Exception\ValidatorException; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath; +use Magento\MediaGallery\Plugin\Wysiwyg\Images\Storage as StoragePlugin; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Test the DeleteByDirectoryPath command model + */ +class StorageTest extends TestCase +{ + private const NON_STRING_PATH = 2020; + private const NON_EXISTENT_PATH = 'non_existent'; + private const INVALID_PATH = '&&'; + private const VALID_PATH = 'test-directory-path/'; + + /** + * @var GetByPathInterface|MockObject + */ + private $getMediaAssetByPath; + + /** + * @var DeleteByPathInterface|MockObject + */ + private $deleteMediaAssetByPath; + + /** + * @var DeleteByDirectoryPathInterface|MockObject + */ + private $deleteMediaAssetByDirectoryPath; + + /** + * @var Filesystem|MockObject + */ + private $filesystem; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * @var StoragePlugin + */ + private $storagePlugin; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->logger = $this->createMock(LoggerInterface::class); + $this->getMediaAssetByPath = $this->createMock(GetByPathInterface::class); + $this->deleteMediaAssetByPath = $this->createMock(DeleteByPathInterface::class); + $this->deleteMediaAssetByDirectoryPath = $this->createMock(DeleteByDirectoryPath::class); + $this->filesystem = $this->createMock(Filesystem::class); + + $this->storagePlugin = (new ObjectManager($this))->getObject( + StoragePlugin::class, + [ + 'getMediaAssetByPath' => $this->getMediaAssetByPath, + 'deleteMediaAssetByPath' => $this->deleteMediaAssetByPath, + 'deleteByDirectoryPath' => $this->deleteMediaAssetByDirectoryPath, + 'filesystem' => $this->filesystem, + 'logger' => $this->logger + ] + ); + } + + /** + * @param string $path + * + * @dataProvider pathPathDataProvider + */ + public function testAfterDeleteDirectory(string $path): void + { + /** @var StorageSubject|MockObject $storageSubject */ + $storageSubject = $this->getMockBuilder(StorageSubject::class) + ->disableOriginalConstructor() + ->getMock(); + $directoryRead = $this->createMock(ReadInterface::class); + $this->filesystem->expects($this->any()) + ->method('getDirectoryRead') + ->willReturn($directoryRead); + + switch ($path) { + case self::NON_STRING_PATH: + $result = $this->storagePlugin->afterDeleteDirectory($storageSubject, null, (int)$path); + self::assertNull($result); + break; + case self::NON_EXISTENT_PATH: + $directoryRead->expects($this->once()) + ->method('getRelativePath') + ->with($path) + ->willReturn($path); + $directoryRead->expects($this->once()) + ->method('isExist') + ->with($path) + ->willReturn(false); + self::expectException('Magento\Framework\Exception\CouldNotDeleteException'); + $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); + break; + case self::INVALID_PATH: + $exception = new ValidatorException(__('Path cannot be used with directory')); + $directoryRead->expects($this->once()) + ->method('getRelativePath') + ->with($path) + ->willThrowException($exception); + $this->logger->expects($this->once()) + ->method('critical') + ->with($exception); + $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); + break; + case self::VALID_PATH: + $directoryRead->expects($this->once()) + ->method('getRelativePath') + ->with($path) + ->willReturn($path); + $directoryRead->expects($this->once()) + ->method('isExist') + ->with($path) + ->willReturn(true); + $this->deleteMediaAssetByDirectoryPath->expects($this->once()) + ->method('execute') + ->with($path); + $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); + break; + } + } + + /** + * Data provider for path + * + * @return array + */ + public function pathPathDataProvider(): array + { + return [ + 'Non string path' => [2020], + 'Non-existent path' => [self::NON_EXISTENT_PATH], + 'Invalid path' => [self::INVALID_PATH], + 'Existent path' => [self::VALID_PATH] + ]; + } +} From d488fd1023ccdafb48160ec62c160aafaf6575d0 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 8 Jan 2020 12:31:06 +0200 Subject: [PATCH 138/734] Adding Removing Action Group --- .../Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml index daf3e58bca354..7753b03716371 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml @@ -21,6 +21,9 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteCreatedSitemap"> + <argument name="filename" value="sitemap.xml"/> + </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> From be953c6f53e156f505398882718499f1dd2ec6c5 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 9 Jan 2020 11:13:12 +0200 Subject: [PATCH 139/734] Create Newsletter Template Entity --- ...ingCreateNewsletterTemplateActionGroup.xml | 29 +++++++++ ...ingDeleteNewsletterTemplateActionGroup.xml | 17 +++++ ...nNewsletterTemplateFromGridActionGroup.xml | 16 +++++ ...reateNewsletterTemplatePageActionGroup.xml | 17 +++++ ...rchNewsletterTemplateOnGridActionGroup.xml | 23 +++++++ ...tedNewsletterTemplateInGridActionGroup.xml | 22 +++++++ ...edNewsletterTemplateMessageActionGroup.xml | 19 ++++++ .../Test/Mftf/Data/NewsletterTemplateData.xml | 1 + .../AdminNewsletterGridMainActionsSection.xml | 20 ++++++ .../AdminNewsletterMainActionsSection.xml | 15 +++++ ...nMarketingCreateNewsletterTemplateTest.xml | 65 +++++++++++++++++++ .../CreateNewsletterTemplateEntityTest.xml | 1 + 12 files changed, 245 insertions(+) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml new file mode 100644 index 0000000000000..f7d0c59116af2 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml @@ -0,0 +1,29 @@ +<?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"> + <!--Create New Template --> + <actionGroup name="AdminMarketingCreateNewsletterTemplateActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + <argument name="senderName" type="string"/> + <argument name="senderEmail" type="string"/> + <argument name="templateContent" type="string"/> + </arguments> + <!--Filling All Required Fields--> + <fillField stepKey="fillTemplateNameField" selector="{{BasicFieldNewsletterSection.templateName}}" userInput="{{name}}"/> + <fillField stepKey="fillTemplateSubjectField" selector="{{BasicFieldNewsletterSection.templateSubject}}" userInput="{{subject}}"/> + <fillField stepKey="fillSenderNameField" selector="{{BasicFieldNewsletterSection.senderName}}" userInput="{{senderName}}"/> + <fillField stepKey="fillSenderEmailField" selector="{{BasicFieldNewsletterSection.senderEmail}}" userInput="{{senderEmail}}"/> + <fillField stepKey="fillTemplateContentField" selector="{{NewsletterWYSIWYGSection.TextArea}}" userInput="{{templateContent}}"/> + <!--Saving Created Template--> + <click stepKey="clickSaveTemplateButton" selector="{{AdminNewsletterMainActionsSection.saveTemplateButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml new file mode 100644 index 0000000000000..fd9c372388689 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml @@ -0,0 +1,17 @@ +<?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"> + <!--Delete Newsletter Template --> + <actionGroup name="AdminMarketingDeleteNewsletterTemplateActionGroup"> + <click stepKey="clickDeleteButton" selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}"/> + <click stepKey="confirmDelete" selector="{{AdminNewsletterMainActionsSection.confirmDelete}}"/> + <waitForPageLoad stepKey="waitForPageLoading"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml new file mode 100644 index 0000000000000..4e380fe63d251 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml @@ -0,0 +1,16 @@ +<?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"> + + <!--Open Newsletter Template --> + <actionGroup name="AdminMarketingOpenNewsletterTemplateFromGridActionGroup"> + <click stepKey="openTemplate" selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml new file mode 100644 index 0000000000000..c743bde60172a --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml @@ -0,0 +1,17 @@ +<?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"> + + <!--Click "Add New Template" button--> + <actionGroup name="AdminNavigateToCreateNewsletterTemplatePageActionGroup"> + <click stepKey="clickAddNewTemplateButton" selector="{{AdminNewsletterGridMainActionsSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml new file mode 100644 index 0000000000000..ac733a5c6ef62 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.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"> + + <!--Search Newsletter Template On Grid --> + <actionGroup name="AdminSearchNewsletterTemplateOnGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + </arguments> + <fillField userInput="{{name}}" selector="{{AdminNewsletterGridMainActionsSection.name}}" stepKey="filterName"/> + <fillField userInput="{{subject}}" selector="{{AdminNewsletterGridMainActionsSection.subject}}" stepKey="filterSubject"/> + <click stepKey="clickSearchButton" selector="{{AdminNewsletterGridMainActionsSection.search}}"/> + <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml new file mode 100644 index 0000000000000..495ce1b09047b --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Search Newsletter Template On Grid --> + <actionGroup name="AssertAdminCreatedNewsletterTemplateInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + </arguments> + + <see userInput="{{name}}" selector="{{AdminNewsletterGridMainActionsSection.newsletterGrid}}" stepKey="assertTemplatename"/> + <see userInput="{{subject}}" selector="{{AdminNewsletterGridMainActionsSection.newsletterGrid}}" stepKey="assertTemplateSubject"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml new file mode 100644 index 0000000000000..3261f33894756 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCreatedNewsletterTemplateMessageActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="The newsletter template has been saved."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml index fe2deed9a279f..0305ceb0efabe 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml @@ -13,5 +13,6 @@ <data key="subject">Test Newsletter Subject</data> <data key="senderName">Admin</data> <data key="senderEmail">admin@magento.com</data> + <data key="textAreaContent">Some Test Content</data> </entity> </entities> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml new file mode 100644 index 0000000000000..ed1b432f798a5 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml @@ -0,0 +1,20 @@ +<?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="AdminNewsletterGridMainActionsSection"> + <element name="newsletterGrid" type="text" selector="table.data-grid"/> + <element name="add" type="button" selector=".page-actions .page-actions-buttons .add"/> + <element name="name" type="input" selector="[id$='filter_code']"/> + <element name="subject" type="input" selector="[id$='filter_subject']"/> + <element name="search" type="button" selector=".action-default.scalable.action-secondary"/> + <element name="resetFilter" type="button" selector=".action-default.scalable.action-reset.action-tertiary"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml new file mode 100644 index 0000000000000..d824496f8e6b8 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml @@ -0,0 +1,15 @@ +<?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="AdminNewsletterMainActionsSection"> + <element name="saveTemplateButton" type="button" selector=".page-actions-inner .page-actions-buttons .save"/> + <element name="deleteTemplateButton" type="button" selector=".page-actions-inner .page-actions-buttons .delete"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml new file mode 100644 index 0000000000000..c5c18ad97cc11 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.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="AdminMarketingCreateNewsletterTemplateTest"> + <annotations> + <features value="Newsletter"/> + <stories value="Admin Creates Newsletter Template"/> + <title value="Newsletter Template Creation"/> + <description value="Newsletter Template Successfully Created"/> + <group value="newsletter"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <!-- Delete Created Newsletter Template --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplateInGrid"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + </actionGroup> + <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openTemplate"/> + <actionGroup ref="AdminMarketingDeleteNewsletterTemplateActionGroup" stepKey="deleteTemplate"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--TEST BODY --> + <!-- Navigate To MARKETING > Newsletter Template --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterTemplatePage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuMarketingCommunicationsNewsletterTemplate.dataUiId}}"/> + </actionGroup> + <!-- Navigate To Create Newsletter Template Page --> + <actionGroup ref="AdminNavigateToCreateNewsletterTemplatePageActionGroup" stepKey="navigateToCreateNewsletterPage"/> + <!-- Create Newsletter Template --> + <actionGroup ref="AdminMarketingCreateNewsletterTemplateActionGroup" stepKey="updateNewsletterTemplate"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + <argument name="senderName" value="{{_defaultNewsletter.senderName}}"/> + <argument name="senderEmail" value="{{_defaultNewsletter.senderEmail}}"/> + <argument name="templateContent" value="{{_defaultNewsletter.textAreaContent}}"/> + </actionGroup> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminCreatedNewsletterTemplateMessageActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The newsletter template has been saved."/> + </actionGroup> + <!-- Find Created Newsletter On grid --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + </actionGroup> + <!-- Assert Created Newsletter on Grid --> + <actionGroup ref="AssertAdminCreatedNewsletterTemplateInGridActionGroup" stepKey="assertNewsletterInGrid"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + </actionGroup> + <!--END TEST BODY --> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/CreateNewsletterTemplateEntityTest.xml b/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/CreateNewsletterTemplateEntityTest.xml index ce91de81459f1..27d2a1ba5b3f3 100644 --- a/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/CreateNewsletterTemplateEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/CreateNewsletterTemplateEntityTest.xml @@ -15,6 +15,7 @@ <data name="template/data/text" xsi:type="string">Some content %isolation%</data> <constraint name="Magento\Newsletter\Test\Constraint\AssertNewsletterSuccessCreateMessage" /> <constraint name="Magento\Newsletter\Test\Constraint\AssertNewsletterInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 4bb49dc7a16adfaec25d15f88352aa477ea7b5ae Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 9 Jan 2020 12:45:56 +0200 Subject: [PATCH 140/734] Update Newsletter Template --- ...ingCreateNewsletterTemplateActionGroup.xml | 29 +++++++ ...ingDeleteNewsletterTemplateActionGroup.xml | 17 ++++ ...nNewsletterTemplateFromGridActionGroup.xml | 16 ++++ ...reateNewsletterTemplatePageActionGroup.xml | 17 ++++ ...rchNewsletterTemplateOnGridActionGroup.xml | 23 ++++++ ...tedNewsletterTemplateInGridActionGroup.xml | 22 +++++ ...edNewsletterTemplateMessageActionGroup.xml | 19 +++++ .../Test/Mftf/Data/NewsletterTemplateData.xml | 8 ++ .../AdminNewsletterGridMainActionsSection.xml | 20 +++++ .../AdminNewsletterMainActionsSection.xml | 15 ++++ ...nMarketingNewsletterTemplateUpdateTest.xml | 82 +++++++++++++++++++ .../TestCase/UpdateNewsletterTemplateTest.xml | 1 + 12 files changed, 269 insertions(+) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml new file mode 100644 index 0000000000000..f7d0c59116af2 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingCreateNewsletterTemplateActionGroup.xml @@ -0,0 +1,29 @@ +<?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"> + <!--Create New Template --> + <actionGroup name="AdminMarketingCreateNewsletterTemplateActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + <argument name="senderName" type="string"/> + <argument name="senderEmail" type="string"/> + <argument name="templateContent" type="string"/> + </arguments> + <!--Filling All Required Fields--> + <fillField stepKey="fillTemplateNameField" selector="{{BasicFieldNewsletterSection.templateName}}" userInput="{{name}}"/> + <fillField stepKey="fillTemplateSubjectField" selector="{{BasicFieldNewsletterSection.templateSubject}}" userInput="{{subject}}"/> + <fillField stepKey="fillSenderNameField" selector="{{BasicFieldNewsletterSection.senderName}}" userInput="{{senderName}}"/> + <fillField stepKey="fillSenderEmailField" selector="{{BasicFieldNewsletterSection.senderEmail}}" userInput="{{senderEmail}}"/> + <fillField stepKey="fillTemplateContentField" selector="{{NewsletterWYSIWYGSection.TextArea}}" userInput="{{templateContent}}"/> + <!--Saving Created Template--> + <click stepKey="clickSaveTemplateButton" selector="{{AdminNewsletterMainActionsSection.saveTemplateButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml new file mode 100644 index 0000000000000..fd9c372388689 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml @@ -0,0 +1,17 @@ +<?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"> + <!--Delete Newsletter Template --> + <actionGroup name="AdminMarketingDeleteNewsletterTemplateActionGroup"> + <click stepKey="clickDeleteButton" selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}"/> + <click stepKey="confirmDelete" selector="{{AdminNewsletterMainActionsSection.confirmDelete}}"/> + <waitForPageLoad stepKey="waitForPageLoading"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml new file mode 100644 index 0000000000000..4e380fe63d251 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml @@ -0,0 +1,16 @@ +<?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"> + + <!--Open Newsletter Template --> + <actionGroup name="AdminMarketingOpenNewsletterTemplateFromGridActionGroup"> + <click stepKey="openTemplate" selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml new file mode 100644 index 0000000000000..c743bde60172a --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml @@ -0,0 +1,17 @@ +<?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"> + + <!--Click "Add New Template" button--> + <actionGroup name="AdminNavigateToCreateNewsletterTemplatePageActionGroup"> + <click stepKey="clickAddNewTemplateButton" selector="{{AdminNewsletterGridMainActionsSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.xml new file mode 100644 index 0000000000000..ac733a5c6ef62 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminSearchNewsletterTemplateOnGridActionGroup.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"> + + <!--Search Newsletter Template On Grid --> + <actionGroup name="AdminSearchNewsletterTemplateOnGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + </arguments> + <fillField userInput="{{name}}" selector="{{AdminNewsletterGridMainActionsSection.name}}" stepKey="filterName"/> + <fillField userInput="{{subject}}" selector="{{AdminNewsletterGridMainActionsSection.subject}}" stepKey="filterSubject"/> + <click stepKey="clickSearchButton" selector="{{AdminNewsletterGridMainActionsSection.search}}"/> + <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml new file mode 100644 index 0000000000000..495ce1b09047b --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateInGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Search Newsletter Template On Grid --> + <actionGroup name="AssertAdminCreatedNewsletterTemplateInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + </arguments> + + <see userInput="{{name}}" selector="{{AdminNewsletterGridMainActionsSection.newsletterGrid}}" stepKey="assertTemplatename"/> + <see userInput="{{subject}}" selector="{{AdminNewsletterGridMainActionsSection.newsletterGrid}}" stepKey="assertTemplateSubject"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml new file mode 100644 index 0000000000000..3261f33894756 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCreatedNewsletterTemplateMessageActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="The newsletter template has been saved."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml index fe2deed9a279f..8d3e87b17513d 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml @@ -13,5 +13,13 @@ <data key="subject">Test Newsletter Subject</data> <data key="senderName">Admin</data> <data key="senderEmail">admin@magento.com</data> + <data key="textAreaContent">Some Test Content</data> + </entity> + <entity name="updatedNewsletter" type="cms_page"> + <data key="name" unique="suffix">Updated Newsletter Template</data> + <data key="subject">Updated Newsletter Subject</data> + <data key="senderName">Admin</data> + <data key="senderEmail">admin@magento.com</data> + <data key="textAreaContent">Some Updated Test Content</data> </entity> </entities> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml new file mode 100644 index 0000000000000..ed1b432f798a5 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterGridMainActionsSection.xml @@ -0,0 +1,20 @@ +<?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="AdminNewsletterGridMainActionsSection"> + <element name="newsletterGrid" type="text" selector="table.data-grid"/> + <element name="add" type="button" selector=".page-actions .page-actions-buttons .add"/> + <element name="name" type="input" selector="[id$='filter_code']"/> + <element name="subject" type="input" selector="[id$='filter_subject']"/> + <element name="search" type="button" selector=".action-default.scalable.action-secondary"/> + <element name="resetFilter" type="button" selector=".action-default.scalable.action-reset.action-tertiary"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml new file mode 100644 index 0000000000000..d824496f8e6b8 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterMainActionsSection.xml @@ -0,0 +1,15 @@ +<?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="AdminNewsletterMainActionsSection"> + <element name="saveTemplateButton" type="button" selector=".page-actions-inner .page-actions-buttons .save"/> + <element name="deleteTemplateButton" type="button" selector=".page-actions-inner .page-actions-buttons .delete"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml new file mode 100644 index 0000000000000..1e7f6b7524d79 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml @@ -0,0 +1,82 @@ +<?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="AdminMarketingNewsletterTemplateUpdate"> + <annotations> + <features value="Newsletter"/> + <stories value="Newsletter Updating"/> + <title value="Newsletter Updating Test"/> + <description value="Admin should be able update created Newsletter Template"/> + <group value="reports"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To MARKETING > Newsletter Template --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterTemplatePage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuMarketingCommunicationsNewsletterTemplate.dataUiId}}"/> + </actionGroup> + <!-- Navigate To Create Newsletter Template Page --> + <actionGroup ref="AdminNavigateToCreateNewsletterTemplatePageActionGroup" stepKey="navigateTiCreateNewsletterTemplatePage"/> + <!-- Create Newsletter Template --> + <actionGroup ref="AdminMarketingCreateNewsletterTemplateActionGroup" stepKey="createNewsletterTemplate"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + <argument name="senderName" value="{{_defaultNewsletter.senderName}}"/> + <argument name="senderEmail" value="{{_defaultNewsletter.senderEmail}}"/> + <argument name="templateContent" value="{{_defaultNewsletter.textAreaContent}}"/> + </actionGroup> + <!-- Search Created Newsletter Template on Grid --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> + <argument name="name" value="{{_defaultNewsletter.name}}"/> + <argument name="subject" value="{{_defaultNewsletter.subject}}"/> + </actionGroup> + <!-- Open Created Newsletter Template --> + <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openCreatedNewsletterTemplate"/> + </before> + + <after> + <!-- Delete Created Newsletter Template --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplateInGrid"> + <argument name="name" value="{{updatedNewsletter.name}}"/> + <argument name="subject" value="{{updatedNewsletter.subject}}"/> + </actionGroup> + <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openTemplate"/> + <actionGroup ref="AdminMarketingDeleteNewsletterTemplateActionGroup" stepKey="deleteTemplate"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- TEST BODY --> + <!-- Update Created Newsletter Template --> + <actionGroup ref="AdminMarketingCreateNewsletterTemplateActionGroup" stepKey="updateNewsletterTemplate"> + <argument name="name" value="{{updatedNewsletter.name}}"/> + <argument name="subject" value="{{updatedNewsletter.subject}}"/> + <argument name="senderName" value="{{updatedNewsletter.senderName}}"/> + <argument name="senderEmail" value="{{updatedNewsletter.senderEmail}}"/> + <argument name="templateContent" value="{{updatedNewsletter.textAreaContent}}"/> + </actionGroup> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminCreatedNewsletterTemplateMessageActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="The newsletter template has been saved."/> + </actionGroup> + <!-- Find Created Newsletter on Grid --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> + <argument name="name" value="Updated Newsletter Template"/> + <argument name="subject" value="Updated Newsletter Subject"/> + </actionGroup> + <!-- Assert Created Newsletter on Grid --> + <actionGroup ref="AssertAdminCreatedNewsletterTemplateInGridActionGroup" stepKey="assertNewsletterInGrid"> + <argument name="name" value="{{updatedNewsletter.name}}"/> + <argument name="subject" value="{{updatedNewsletter.subject}}"/> + </actionGroup> + <!-- END TEST BODY --> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/UpdateNewsletterTemplateTest.xml b/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/UpdateNewsletterTemplateTest.xml index 456d9a91b1171..b6b40f43cc3e3 100644 --- a/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/UpdateNewsletterTemplateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Newsletter/Test/TestCase/UpdateNewsletterTemplateTest.xml @@ -16,6 +16,7 @@ <constraint name="Magento\Newsletter\Test\Constraint\AssertNewsletterSuccessCreateMessage" /> <constraint name="Magento\Newsletter\Test\Constraint\AssertNewsletterInGrid" /> <constraint name="Magento\Newsletter\Test\Constraint\AssertNewsletterForm" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 17ca06023089645d3d130a5fb4859fd2d4ec0c34 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 9 Jan 2020 16:16:32 +0200 Subject: [PATCH 141/734] Delete Sitemap Entity --- ...AdminMarketingSitemapSearchActionGroup.xml | 19 +++++++ ...sertAdminSitemapIsNotInGridActionGroup.xml | 17 +++++++ .../Sitemap/Test/Mftf/Data/SitemapData.xml | 4 ++ .../AdminMarketingDeleteSitemapEntityTest.xml | 49 +++++++++++++++++++ .../Test/TestCase/DeleteSitemapEntityTest.xml | 2 +- 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSitemapSearchActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapIsNotInGridActionGroup.xml create mode 100644 app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSitemapSearchActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSitemapSearchActionGroup.xml new file mode 100644 index 0000000000000..6784cf51848b3 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSitemapSearchActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminMarketingSitemapSearchActionGroup"> + <arguments> + <argument name="filename" type="string"/> + </arguments> + <click selector="{{AdminMarketingSiteMapGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField selector="{{AdminMarketingSiteMapGridSection.fileNameTextField}}" userInput="{{filename}}" stepKey="fillFileNameField"/> + <click selector="{{AdminMarketingSiteMapGridSection.searchButton}}" stepKey="clickSearchButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapIsNotInGridActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapIsNotInGridActionGroup.xml new file mode 100644 index 0000000000000..c8effb45eda7a --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertAdminSitemapIsNotInGridActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AssertAdminSitemapIsNotInGridActionGroup"> + <arguments> + <argument name="filename" type="string"/> + </arguments> + <dontSee userInput="{{filename}}" selector="{{AdminMarketingSiteMapGridSection.firstSearchResult}}" stepKey="verifyThatCorrectStoreGroupFound"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml b/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml index 0b5d5d3dcdefe..b952f829b1d96 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml @@ -12,4 +12,8 @@ <data key="filename">sitemap.xml</data> <data key="path">/</data> </entity> + <entity name="UniqueSitemapName"> + <data key="filename" unique="prefix">sitemap.xml</data> + <data key="path">/</data> + </entity> </entities> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml new file mode 100644 index 0000000000000..3ba13bb76b178 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml @@ -0,0 +1,49 @@ +<?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="AdminMarketingDeleteSitemapEntityTest"> + <annotations> + <features value="Sitemap"/> + <stories value="Admin Deletes Sitemap Entity Test"/> + <title value="Sitemap Deleting Test"/> + <description value="Admin Should Delete Sitemap Entity"/> + <group value="sitemap"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!--Navigate to Marketing->Sitemap Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSEOAndSearchSiteMap.dataUiId}}"/> + </actionGroup> + <!-- Navigate To New Sitemap Page --> + <actionGroup ref="AdminMarketingSiteMapNavigateNewActionGroup" stepKey="navigateToNewSitemapPage"/> + <!-- Create Sitemap Entity --> + <actionGroup ref="AdminMarketingSiteMapFillFormActionGroup" stepKey="createSitemap"> + <argument name="sitemap" value="UniqueSitemapName"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteCreatedSitemap"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> + <actionGroup ref="AssertSiteMapDeleteSuccessActionGroup" stepKey="assertSuccessMessage"/> + <actionGroup ref="AdminMarketingSitemapSearchActionGroup" stepKey="searchDeletedSitemap"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> + <actionGroup ref="AssertAdminSitemapIsNotInGridActionGroup" stepKey="dontSeeEntity"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/DeleteSitemapEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/DeleteSitemapEntityTest.xml index a520fa573e1b9..eeb000bd6a803 100644 --- a/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/DeleteSitemapEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/DeleteSitemapEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sitemap\Test\TestCase\DeleteSitemapEntityTest" summary="Delete Sitemap Entity" ticketId="MAGETWO-23296"> <variation name="DeleteSitemapEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="sitemap/dataset" xsi:type="string">default</data> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapSuccessDeleteMessage" /> <constraint name="Magento\Sitemap\Test\Constraint\AssertSitemapNotInGrid" /> From 7b4da97ba6dc502708c4851571c872e9ade9125e Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 9 Jan 2020 17:46:43 +0200 Subject: [PATCH 142/734] Refactoring --- ...edNewsletterTemplateMessageActionGroup.xml | 19 ------------------- ...nMarketingCreateNewsletterTemplateTest.xml | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml deleted file mode 100644 index 3261f33894756..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml +++ /dev/null @@ -1,19 +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="AssertAdminCreatedNewsletterTemplateMessageActionGroup"> - <arguments> - <argument name="message" type="string" defaultValue="The newsletter template has been saved."/> - <argument name="messageType" type="string" defaultValue="success"/> - </arguments> - <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="waitForMessage"/> - <see userInput="{{message}}" selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="verifyMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml index c5c18ad97cc11..2e6947e51df10 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml @@ -47,7 +47,7 @@ <argument name="templateContent" value="{{_defaultNewsletter.textAreaContent}}"/> </actionGroup> <!-- Assert Success Message --> - <actionGroup ref="AssertAdminCreatedNewsletterTemplateMessageActionGroup" stepKey="seeSuccessMessage"> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> <argument name="message" value="The newsletter template has been saved."/> </actionGroup> <!-- Find Created Newsletter On grid --> From b72972653fe19f852ec696f7f90292cf89ac840e Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 15 Jan 2020 15:27:28 +0200 Subject: [PATCH 143/734] Adding Form Assertion --- ...AdminNewsletterTemplateFormActionGroup.xml | 26 +++++++++++++++++++ ...nMarketingNewsletterTemplateUpdateTest.xml | 24 ++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminNewsletterTemplateFormActionGroup.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminNewsletterTemplateFormActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminNewsletterTemplateFormActionGroup.xml new file mode 100644 index 0000000000000..a5a3bd217d135 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminNewsletterTemplateFormActionGroup.xml @@ -0,0 +1,26 @@ +<?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="AssertAdminNewsletterTemplateFormActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="subject" type="string"/> + <argument name="senderName" type="string"/> + <argument name="senderEmail" type="string"/> + <argument name="templateContent" type="string"/> + </arguments> + + <seeInField stepKey="seeTemplateNameField" userInput="{{name}}" selector="{{BasicFieldNewsletterSection.templateName}}"/> + <seeInField stepKey="seeTemplateSubjectField" userInput="{{subject}}" selector="{{BasicFieldNewsletterSection.templateSubject}}"/> + <seeInField stepKey="seeTemplateSenderNameField" userInput="{{senderName}}" selector="{{BasicFieldNewsletterSection.senderName}}"/> + <seeInField stepKey="seeTemplateSenderEmailField" userInput="{{senderEmail}}" selector="{{BasicFieldNewsletterSection.senderEmail}}"/> + <seeInField stepKey="seeTemplateContentField" userInput="{{templateContent}}" selector="{{NewsletterWYSIWYGSection.TextArea}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml index 1e7f6b7524d79..b236dfee4ab6b 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml @@ -43,9 +43,12 @@ <!-- Open Created Newsletter Template --> <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openCreatedNewsletterTemplate"/> </before> - <after> - <!-- Delete Created Newsletter Template --> + <!-- Delete Updated Newsletter Template --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterGridPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuMarketingCommunicationsNewsletterTemplate.dataUiId}}"/> + </actionGroup> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplateInGrid"> <argument name="name" value="{{updatedNewsletter.name}}"/> <argument name="subject" value="{{updatedNewsletter.subject}}"/> @@ -69,14 +72,27 @@ </actionGroup> <!-- Find Created Newsletter on Grid --> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> - <argument name="name" value="Updated Newsletter Template"/> - <argument name="subject" value="Updated Newsletter Subject"/> + <argument name="name" value="{{updatedNewsletter.name}}"/> + <argument name="subject" value="{{updatedNewsletter.subject}}"/> </actionGroup> <!-- Assert Created Newsletter on Grid --> <actionGroup ref="AssertAdminCreatedNewsletterTemplateInGridActionGroup" stepKey="assertNewsletterInGrid"> <argument name="name" value="{{updatedNewsletter.name}}"/> <argument name="subject" value="{{updatedNewsletter.subject}}"/> </actionGroup> + <!-- Assert Newsletter Form --> + <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findUpdatedNewsletterTemplate"> + <argument name="name" value="Updated Newsletter Template"/> + <argument name="subject" value="Updated Newsletter Subject"/> + </actionGroup> + <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openTemplate"/> + <actionGroup ref="AssertAdminNewsletterTemplateFormActionGroup" stepKey="assertNewsletterForm"> + <argument name="name" value="{{updatedNewsletter.name}}"/> + <argument name="subject" value="{{updatedNewsletter.subject}}"/> + <argument name="senderName" value="{{updatedNewsletter.senderName}}"/> + <argument name="senderEmail" value="{{updatedNewsletter.senderEmail}}"/> + <argument name="templateContent" value="{{updatedNewsletter.textAreaContent}}"/> + </actionGroup> <!-- END TEST BODY --> </test> </tests> From adf9c65da195eab977dd5110dd3c0be1579491fa Mon Sep 17 00:00:00 2001 From: Stanislav Ilnytskyi <stailx1@gmail.com> Date: Thu, 16 Jan 2020 12:32:55 +0100 Subject: [PATCH 144/734] Update translate.js fix empty string value in front translations --- lib/web/mage/translate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/translate.js b/lib/web/mage/translate.js index 65ec33266aebd..aa4a8a55ce64d 100644 --- a/lib/web/mage/translate.js +++ b/lib/web/mage/translate.js @@ -38,7 +38,7 @@ define([ * @return {String} */ translate: function (text) { - return _data[text] ? _data[text] : text; + return _data.hasOwnProperty(text) ? _data[text] : text; } }; }()) From 268b1ba86dc24f6611f763a8e5030c662999aac0 Mon Sep 17 00:00:00 2001 From: Stanislav Ilnytskyi <stailx1@gmail.com> Date: Fri, 17 Jan 2020 13:41:52 +0100 Subject: [PATCH 145/734] Code review --- lib/web/mage/translate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/translate.js b/lib/web/mage/translate.js index aa4a8a55ce64d..16df4754d7061 100644 --- a/lib/web/mage/translate.js +++ b/lib/web/mage/translate.js @@ -38,7 +38,7 @@ define([ * @return {String} */ translate: function (text) { - return _data.hasOwnProperty(text) ? _data[text] : text; + return typeof _data[text] !== 'undefined' ? _data[text] : text; } }; }()) From ec609e33ba13a384c1f108da5ae95d894534b443 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Fri, 17 Jan 2020 16:27:17 +0200 Subject: [PATCH 146/734] Migrate ZF2 components to Laminas components --- .../Backend/App/Area/FrontNameResolver.php | 2 +- .../Unit/App/Area/FrontNameResolverTest.php | 4 +- .../CacheInvalidate/Model/PurgeCache.php | 4 +- .../CacheInvalidate/Model/SocketFactory.php | 4 +- .../Test/Unit/Model/PurgeCacheTest.php | 8 +- .../Test/Unit/Model/SocketFactoryTest.php | 2 +- .../Magento/Captcha/Model/DefaultModel.php | 10 +- .../Captcha/Model/ResourceModel/Log.php | 4 +- app/code/Magento/Captcha/composer.json | 6 +- .../Product/Frontend/Action/Synchronize.php | 4 +- .../Frontend/Action/SynchronizeTest.php | 4 +- .../Model/Cart/RequestQuantityProcessor.php | 2 +- .../Customer/Controller/Section/Load.php | 4 +- .../Test/Unit/Controller/Section/LoadTest.php | 4 +- .../Model/Url/DomainValidator.php | 2 +- .../Patch/Data/AddDownloadableHostsConfig.php | 2 +- app/code/Magento/Email/Model/Transport.php | 6 +- .../Test/Unit/Model/Oauth/ConsumerTest.php | 2 +- ...tionDuringMediaAssetInitializationTest.php | 2 +- .../GetByIdExceptionNoSuchEntityTest.php | 2 +- .../Command/GetByIdExceptionOnGetDataTest.php | 2 +- .../Asset/Command/GetByIdSuccessfulTest.php | 2 +- .../Unit/Model/File/Storage/ResponseTest.php | 2 +- .../App/FrontController/BuiltinPlugin.php | 2 +- .../Magento/PageCache/Model/Cache/Server.php | 4 +- .../Model/Controller/Result/BuiltinPlugin.php | 2 +- .../App/FrontController/BuiltinPluginTest.php | 2 +- .../Test/Unit/Model/Cache/ServerTest.php | 2 +- .../Controller/Result/BuiltinPluginTest.php | 2 +- .../Quote/Model/Quote/Item/Updater.php | 2 +- .../Product/Downloads/Collection.php | 2 +- .../Controller/Adminhtml/Feed/IndexTest.php | 2 +- .../Test/Unit/Controller/Feed/IndexTest.php | 2 +- .../Magento/Rss/Test/Unit/Model/RssTest.php | 2 +- .../Magento/Store/App/Response/Redirect.php | 8 +- app/code/Magento/Store/Model/Store.php | 2 +- .../Adminhtml/Design/Config/SaveTest.php | 4 +- .../Ui/Controller/Adminhtml/Index/Render.php | 12 +- .../Magento/Ui/Controller/Index/Render.php | 12 +- .../Controller/Adminhtml/Index/RenderTest.php | 4 +- .../Test/Unit/Controller/Index/RenderTest.php | 4 +- .../Test/Unit/Controller/RouterTest.php | 2 +- .../Webapi/Model/Config/ClassReflector.php | 18 +- app/code/Magento/Webapi/Model/Soap/Wsdl.php | 4 +- .../Model/Soap/Wsdl/ComplexTypeStrategy.php | 4 +- .../Webapi/Test/Unit/Controller/SoapTest.php | 2 +- .../Unit/Model/Config/ClassReflectorTest.php | 4 +- .../Soap/Wsdl/ComplexTypeStrategyTest.php | 2 +- composer.json | 64 +- composer.lock | 4751 +++++++++-------- .../Authentication/OauthHelper.php | 4 +- .../TestCase/Webapi/Adapter/Soap.php | 10 +- .../Magento/TestFramework/Request.php | 2 +- .../testsuite/Magento/Test/RequestTest.php | 2 +- .../App/Request/BackendValidatorTest.php | 2 +- .../Controller/Cards/DeleteActionTest.php | 2 +- .../Controller/Advanced/ResultTest.php | 2 +- .../Customer/Controller/AccountTest.php | 6 +- .../Controller/Adminhtml/IndexTest.php | 4 +- .../Magento/Developer/Helper/DataTest.php | 2 +- .../Magento/Framework/App/AreaTest.php | 2 +- .../App/Filesystem/CreatePdfFileTest.php | 2 +- .../App/Request/CsrfValidatorTest.php | 2 +- .../HeaderProvider/AbstractHeaderTestCase.php | 2 +- .../ParentClassWithNamespace.php | 8 +- .../SourceClassWithNamespace.php | 10 +- ...ceClassWithNamespaceInterceptor.php.sample | 6 +- .../SourceClassWithNamespaceProxy.php.sample | 6 +- .../_files/testFromClone/composer.json | 42 +- .../_files/testFromClone/composer.lock | 1040 ++-- .../testFromCreateProject/composer.lock | 1180 ++-- .../_files/testSkeleton/composer.lock | 1180 ++-- .../GraphQl/Config/GraphQlReaderTest.php | 2 +- .../Magento/Framework/HTTP/HeaderTest.php | 2 +- .../Stdlib/Cookie/CookieScopeTest.php | 2 +- .../testsuite/Magento/Framework/UrlTest.php | 2 +- .../Controller/GraphQlControllerTest.php | 4 +- .../Adminhtml/NewsletterTemplateTest.php | 4 +- .../Magento/Phpserver/PhpserverTest.php | 4 +- .../Adminhtml/Product/MassUpdateTest.php | 2 +- .../Magento/Setup/Controller/UrlCheckTest.php | 4 +- .../Model/ConfigOptionsListCollectorTest.php | 2 +- .../Setup/Model/ObjectManagerProviderTest.php | 2 +- .../Model/_files/testSkeleton/composer.lock | 680 +-- .../Magento/Sitemap/Model/SitemapTest.php | 2 +- .../Plugin/RequestPreprocessorTest.php | 2 +- .../Magento/Store/Model/StoreTest.php | 2 +- .../Ui/Controller/Index/RenderTest.php | 2 +- .../Integrity/Library/Injectable.php | 8 +- .../Test/Integrity/Library/InjectableTest.php | 14 +- .../Magento/Test/Integrity/ClassesTest.php | 2 +- .../Test/Integrity/Library/DependencyTest.php | 2 +- .../Api/ExtensibleInterfacesTest.php | 2 +- .../Magento/Test/Integrity/PublicCodeTest.php | 2 +- .../Test/Legacy/_files/obsolete_classes.php | 4 +- .../pre_composer_update_2.3.php | 4 +- lib/internal/Magento/Framework/App/Feed.php | 2 +- .../Magento/Framework/App/Request/Http.php | 2 +- .../Framework/App/Request/HttpMethodMap.php | 2 +- .../Framework/App/Response/HttpInterface.php | 2 +- .../App/Test/Unit/PageCache/KernelTest.php | 6 +- .../Code/Generator/ClassGenerator.php | 14 +- .../Code/Generator/CodeGeneratorInterface.php | 2 +- .../Code/Generator/EntityAbstract.php | 2 +- .../Generator/InterfaceMethodGenerator.php | 2 +- .../Framework/Code/Reader/ArgumentsReader.php | 6 +- .../Unit/Generator/ClassGeneratorTest.php | 24 +- .../Unit/Generator/TestAsset/ParentClass.php | 8 +- .../Unit/Generator/TestAsset/SourceClass.php | 12 +- .../Console/GenerationDirectoryAccess.php | 2 +- .../Framework/DB/Adapter/Pdo/Mysql.php | 2 +- .../Data/Test/Unit/Form/FormKeyTest.php | 2 +- .../Framework/Encryption/Helper/Security.php | 4 +- .../Test/Unit/Transfer/Adapter/HttpTest.php | 2 +- .../Framework/File/Transfer/Adapter/Http.php | 2 +- .../Magento/Framework/Filesystem/Glob.php | 10 +- .../Framework/HTTP/PhpEnvironment/Request.php | 12 +- .../HTTP/PhpEnvironment/Response.php | 4 +- .../Test/Unit/PhpEnvironment/RequestTest.php | 2 +- .../Test/Unit/PhpEnvironment/ResponseTest.php | 8 +- .../Magento/Framework/Mail/EmailMessage.php | 6 +- .../Magento/Framework/Mail/Message.php | 12 +- .../Magento/Framework/Mail/MimeInterface.php | 2 +- .../Magento/Framework/Mail/MimeMessage.php | 2 +- .../Magento/Framework/Mail/MimePart.php | 2 +- .../Magento/Framework/Mail/Transport.php | 4 +- .../Code/Generator/RemoteServiceGenerator.php | 4 +- .../Framework/Oauth/Helper/Request.php | 2 +- .../Code/Generator/Repository.php | 4 +- .../ExtensionAttributesProcessor.php | 2 +- .../Framework/Reflection/MethodsMap.php | 6 +- .../Framework/Reflection/NameFinder.php | 2 +- .../Reflection/Test/Unit/NameFinderTest.php | 4 +- .../Test/Unit/TypeProcessorTest.php | 2 +- .../Framework/Reflection/TypeProcessor.php | 14 +- .../Validator/CookieDomainValidator.php | 2 +- .../Magento/Framework/Stdlib/Parameters.php | 4 +- .../Framework/Url/Test/Unit/ValidatorTest.php | 4 +- .../Magento/Framework/Url/Validator.php | 4 +- .../Framework/Validator/AllowedProtocols.php | 2 +- .../Webapi/ServiceInputProcessor.php | 2 +- .../Webapi/ServiceOutputProcessor.php | 2 +- .../Magento/Framework/ZendEscaper.php | 2 +- lib/internal/Magento/Framework/composer.json | 18 +- setup/config/application.config.php | 4 +- setup/config/di.config.php | 4 +- setup/config/module.config.php | 2 +- setup/src/Magento/Setup/Application.php | 16 +- .../src/Magento/Setup/Console/CommandList.php | 2 +- .../Setup/Console/CompilerPreparation.php | 2 +- .../Magento/Setup/Controller/AddDatabase.php | 4 +- .../Setup/Controller/BackupActionItems.php | 16 +- .../Setup/Controller/CompleteBackup.php | 8 +- .../Setup/Controller/CreateAdminAccount.php | 4 +- .../Magento/Setup/Controller/CreateBackup.php | 4 +- .../Setup/Controller/CustomizeYourStore.php | 6 +- .../Magento/Setup/Controller/DataOption.php | 10 +- .../Setup/Controller/DatabaseCheck.php | 6 +- .../Setup/Controller/DependencyCheck.php | 6 +- .../Magento/Setup/Controller/Environment.php | 32 +- .../Setup/Controller/ExtensionGrid.php | 8 +- setup/src/Magento/Setup/Controller/Home.php | 6 +- setup/src/Magento/Setup/Controller/Index.php | 6 +- .../src/Magento/Setup/Controller/Install.php | 8 +- .../Setup/Controller/InstallExtensionGrid.php | 6 +- .../Setup/Controller/LandingInstaller.php | 4 +- .../Setup/Controller/LandingUpdater.php | 4 +- .../src/Magento/Setup/Controller/License.php | 4 +- .../Magento/Setup/Controller/Maintenance.php | 6 +- .../Magento/Setup/Controller/Marketplace.php | 10 +- .../Controller/MarketplaceCredentials.php | 4 +- .../Magento/Setup/Controller/ModuleGrid.php | 10 +- .../src/Magento/Setup/Controller/Modules.php | 6 +- .../Magento/Setup/Controller/Navigation.php | 6 +- .../Setup/Controller/OtherComponentsGrid.php | 10 +- .../Controller/ReadinessCheckInstaller.php | 4 +- .../Controller/ReadinessCheckUpdater.php | 4 +- .../Setup/Controller/SelectVersion.php | 8 +- .../src/Magento/Setup/Controller/Session.php | 24 +- .../Magento/Setup/Controller/StartUpdater.php | 8 +- .../src/Magento/Setup/Controller/Success.php | 4 +- .../Magento/Setup/Controller/SystemConfig.php | 4 +- .../Setup/Controller/UpdateExtensionGrid.php | 6 +- .../Setup/Controller/UpdaterSuccess.php | 4 +- .../src/Magento/Setup/Controller/UrlCheck.php | 6 +- .../Controller/ValidateAdminCredentials.php | 6 +- .../Setup/Controller/WebConfiguration.php | 4 +- .../Setup/Model/AdminAccountFactory.php | 2 +- .../Model/ConfigOptionsListCollector.php | 2 +- .../Magento/Setup/Model/Cron/JobFactory.php | 2 +- .../Magento/Setup/Model/InstallerFactory.php | 2 +- setup/src/Magento/Setup/Model/Navigation.php | 2 +- .../Setup/Model/ObjectManagerProvider.php | 2 +- .../src/Magento/Setup/Model/PackagesAuth.php | 10 +- .../Setup/Model/UpdaterTaskCreator.php | 2 +- setup/src/Magento/Setup/Module.php | 26 +- .../Setup/Module/ConnectionFactory.php | 2 +- .../Module/Di/Code/Reader/FileScanner.php | 4 +- .../Module/Di/Code/Scanner/PhpScanner.php | 2 +- .../Magento/Setup/Module/ResourceFactory.php | 2 +- .../Setup/Mvc/Bootstrap/InitParamListener.php | 28 +- .../Mvc/View/Http/InjectTemplateListener.php | 4 +- .../Test/Unit/Console/CommandListTest.php | 4 +- .../Unit/Console/CompilerPreparationTest.php | 2 +- .../Test/Unit/Controller/AddDatabaseTest.php | 2 +- .../Unit/Controller/BackupActionItemsTest.php | 16 +- .../Unit/Controller/CompleteBackupTest.php | 6 +- .../Controller/CreateAdminAccountTest.php | 2 +- .../Test/Unit/Controller/CreateBackupTest.php | 2 +- .../Controller/CustomizeYourStoreTest.php | 4 +- .../Test/Unit/Controller/DataOptionTest.php | 16 +- .../Test/Unit/Controller/EnvironmentTest.php | 48 +- .../Unit/Controller/ExtensionGridTest.php | 6 +- .../Setup/Test/Unit/Controller/IndexTest.php | 2 +- .../Controller/InstallExtensionGridTest.php | 4 +- .../Test/Unit/Controller/InstallTest.php | 24 +- .../Unit/Controller/LandingInstallerTest.php | 2 +- .../Unit/Controller/LandingUpdaterTest.php | 2 +- .../Test/Unit/Controller/LicenseTest.php | 4 +- .../Test/Unit/Controller/MaintenanceTest.php | 12 +- .../Test/Unit/Controller/MarketplaceTest.php | 16 +- .../Test/Unit/Controller/ModuleGridTest.php | 4 +- .../Test/Unit/Controller/ModulesTest.php | 4 +- .../Test/Unit/Controller/NavigationTest.php | 10 +- .../Controller/OtherComponentsGridTest.php | 6 +- .../ReadinessCheckInstallerTest.php | 4 +- .../Controller/ReadinessCheckUpdaterTest.php | 4 +- .../Unit/Controller/SelectVersionTest.php | 8 +- .../Test/Unit/Controller/SessionTest.php | 8 +- .../Test/Unit/Controller/StartUpdaterTest.php | 16 +- .../Test/Unit/Controller/SuccessTest.php | 2 +- .../Test/Unit/Controller/SystemConfigTest.php | 2 +- .../Controller/UpdateExtensionGridTest.php | 4 +- .../Unit/Controller/UpdaterSuccessTest.php | 2 +- .../Test/Unit/Controller/UrlCheckTest.php | 4 +- .../Unit/Controller/WebConfigurationTest.php | 2 +- .../Unit/Model/AdminAccountFactoryTest.php | 2 +- .../Test/Unit/Model/Cron/JobFactoryTest.php | 2 +- .../Test/Unit/Model/InstallerFactoryTest.php | 2 +- .../Setup/Test/Unit/Model/NavigationTest.php | 4 +- .../Unit/Model/ObjectManagerProviderTest.php | 2 +- .../Test/Unit/Model/PackagesAuthTest.php | 2 +- .../Unit/Module/ConnectionFactoryTest.php | 2 +- .../Test/Unit/Module/ResourceFactoryTest.php | 2 +- .../Mvc/Bootstrap/InitParamListenerTest.php | 52 +- .../LazyControllerAbstractFactory.php | 34 +- 246 files changed, 5263 insertions(+), 5012 deletions(-) diff --git a/app/code/Magento/Backend/App/Area/FrontNameResolver.php b/app/code/Magento/Backend/App/Area/FrontNameResolver.php index f03e97e32d2ab..6c586781f2d81 100644 --- a/app/code/Magento/Backend/App/Area/FrontNameResolver.php +++ b/app/code/Magento/Backend/App/Area/FrontNameResolver.php @@ -14,7 +14,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; -use Zend\Uri\Uri; +use Laminas\Uri\Uri; /** * Class to get area front name. diff --git a/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php b/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php index ba0b01d4055de..9483de741f169 100644 --- a/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php +++ b/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php @@ -29,7 +29,7 @@ class FrontNameResolverTest extends \PHPUnit\Framework\TestCase protected $scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Uri\Uri + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Uri\Uri */ protected $uri; @@ -51,7 +51,7 @@ protected function setUp() ->method('get') ->with(ConfigOptionsList::CONFIG_PATH_BACKEND_FRONTNAME) ->will($this->returnValue($this->_defaultFrontName)); - $this->uri = $this->createMock(\Zend\Uri\Uri::class); + $this->uri = $this->createMock(\Laminas\Uri\Uri::class); $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index b2aa0d000e9cf..a111414fd3074 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -110,8 +110,8 @@ private function splitTags($tagsPattern) /** * Send curl purge request to servers to invalidate cache by tags pattern * - * @param \Zend\Http\Client\Adapter\Socket $socketAdapter - * @param \Zend\Uri\Uri[] $servers + * @param \Laminas\Http\Client\Adapter\Socket $socketAdapter + * @param \Laminas\Uri\Uri[] $servers * @param string $formattedTagsChunk * @return bool Return true if successful; otherwise return false */ diff --git a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php index b69788bf829de..25b4228d9de5e 100644 --- a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php +++ b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php @@ -8,10 +8,10 @@ class SocketFactory { /** - * @return \Zend\Http\Client\Adapter\Socket + * @return \Laminas\Http\Client\Adapter\Socket */ public function create() { - return new \Zend\Http\Client\Adapter\Socket(); + return new \Laminas\Http\Client\Adapter\Socket(); } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php index c66e27ea41025..956c8802f597f 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php @@ -5,14 +5,14 @@ */ namespace Magento\CacheInvalidate\Test\Unit\Model; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; class PurgeCacheTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\CacheInvalidate\Model\PurgeCache */ protected $model; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Zend\Http\Client\Adapter\Socket */ + /** @var \PHPUnit_Framework_MockObject_MockObject | \Laminas\Http\Client\Adapter\Socket */ protected $socketAdapterMock; /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Cache\InvalidateLogger */ @@ -24,7 +24,7 @@ class PurgeCacheTest extends \PHPUnit\Framework\TestCase protected function setUp() { $socketFactoryMock = $this->createMock(\Magento\CacheInvalidate\Model\SocketFactory::class); - $this->socketAdapterMock = $this->createMock(\Zend\Http\Client\Adapter\Socket::class); + $this->socketAdapterMock = $this->createMock(\Laminas\Http\Client\Adapter\Socket::class); $this->socketAdapterMock->expects($this->once()) ->method('setOptions') ->with(['timeout' => 10]); @@ -113,7 +113,7 @@ public function testSendPurgeRequestWithException() ->method('getUris') ->willReturn($uris); $this->socketAdapterMock->method('connect') - ->willThrowException(new \Zend\Http\Client\Adapter\Exception\RuntimeException()); + ->willThrowException(new \Laminas\Http\Client\Adapter\Exception\RuntimeException()); $this->loggerMock->expects($this->never()) ->method('execute'); $this->loggerMock->expects($this->once()) diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php index c69c7f4d8543e..f72abc2760794 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php @@ -10,6 +10,6 @@ class SocketFactoryTest extends \PHPUnit\Framework\TestCase public function testCreate() { $factory = new \Magento\CacheInvalidate\Model\SocketFactory(); - $this->assertInstanceOf(\Zend\Http\Client\Adapter\Socket::class, $factory->create()); + $this->assertInstanceOf(\Laminas\Http\Client\Adapter\Socket::class, $factory->create()); } } diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php index bbbbfb0a36e08..32614be560893 100644 --- a/app/code/Magento/Captcha/Model/DefaultModel.php +++ b/app/code/Magento/Captcha/Model/DefaultModel.php @@ -11,14 +11,14 @@ use Magento\Framework\Math\Random; /** - * Implementation of \Zend\Captcha\Image + * Implementation of \Laminas\Captcha\Image * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * * @api * @since 100.0.2 */ -class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model\CaptchaInterface +class DefaultModel extends \Laminas\Captcha\Image implements \Magento\Captcha\Model\CaptchaInterface { /** * Key in session for captcha code @@ -51,7 +51,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model /** * Override default value to prevent a captcha cut off * @var int - * @see \Zend\Captcha\Image::$fsize + * @see \Laminas\Captcha\Image::$fsize * @since 100.2.0 */ protected $fsize = 22; @@ -99,7 +99,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model * @param ResourceModel\LogFactory $resLogFactory * @param string $formId * @param Random $randomMath - * @throws \Zend\Captcha\Exception\ExtensionNotLoadedException + * @throws \Laminas\Captcha\Exception\ExtensionNotLoadedException */ public function __construct( \Magento\Framework\Session\SessionManagerInterface $session, @@ -537,7 +537,7 @@ private function clearWord() /** * Override function to generate less curly captcha that will not cut off * - * @see \Zend\Captcha\Image::_randomSize() + * @see \Laminas\Captcha\Image::_randomSize() * @return int * @throws \Magento\Framework\Exception\LocalizedException * @since 100.2.0 diff --git a/app/code/Magento/Captcha/Model/ResourceModel/Log.php b/app/code/Magento/Captcha/Model/ResourceModel/Log.php index 95b7d3a5a0a03..30c20fdeb3956 100644 --- a/app/code/Magento/Captcha/Model/ResourceModel/Log.php +++ b/app/code/Magento/Captcha/Model/ResourceModel/Log.php @@ -79,7 +79,7 @@ public function logAttempt($login) 'count' => 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] + ['count' => new \Laminas\Db\Sql\Expression('count+1'), 'updated_at'] ); } $ip = $this->_remoteAddress->getRemoteAddress(); @@ -92,7 +92,7 @@ public function logAttempt($login) 'count' => 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] + ['count' => new \Laminas\Db\Sql\Expression('count+1'), 'updated_at'] ); } return $this; diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index 294961118e93a..c6bbcc8e91c3e 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -11,9 +11,9 @@ "magento/module-checkout": "*", "magento/module-customer": "*", "magento/module-store": "*", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php index 58f4b9a4bb51e..f6896fe6a7a99 100644 --- a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php +++ b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php @@ -71,8 +71,8 @@ public function execute() $this->synchronizer->syncActions($productsData, $typeId); } catch (\Exception $e) { $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php index bae370c7dd79f..4b7053765516d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php @@ -133,8 +133,8 @@ public function testExecuteActionException() $jsonObject->expects($this->once()) ->method('setStatusHeader') ->with( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); $jsonObject->expects($this->once()) diff --git a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php index 971b35c8f3e3d..d8597eb3640b3 100644 --- a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php +++ b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php @@ -34,7 +34,7 @@ public function __construct( */ public function process(array $cartData): array { - $filter = new \Zend\I18n\Filter\NumberParse($this->localeResolver->getLocale()); + $filter = new \Laminas\I18n\Filter\NumberParse($this->localeResolver->getLocale()); foreach ($cartData as $index => $data) { if (isset($data['qty'])) { diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index 37cd071b13623..6c3aa06b9f022 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -78,8 +78,8 @@ public function execute() $response = $this->sectionPool->getSectionsData($sectionNames, (bool)$forceNewSectionTimestamp); } catch (\Exception $e) { $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); $response = ['message' => $this->escaper->escapeHtml($e->getMessage())]; diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php index 5a7cf42be2c7e..8eca499f849ea 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php @@ -166,8 +166,8 @@ public function testExecuteWithThrowException() $this->resultJsonMock->expects($this->once()) ->method('setStatusHeader') ->with( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index cab7fb134ea33..68ab34c65a8d1 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -9,7 +9,7 @@ use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Validator\Ip as IpValidator; -use Zend\Uri\Uri as UriHandler; +use Laminas\Uri\Uri as UriHandler; /** * Class is responsible for checking if downloadable product link domain is allowed. diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 0e88bd166b604..642b1734310ea 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -14,7 +14,7 @@ use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; -use Zend\Uri\Uri as UriHandler; +use Laminas\Uri\Uri as UriHandler; use Magento\Framework\Url\ScopeResolverInterface; use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Setup\ModuleDataSetupInterface; diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index 79ceb56a8834d..a1e25c41f3bb5 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -13,12 +13,12 @@ use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Phrase; use Magento\Store\Model\ScopeInterface; -use Zend\Mail\Message; -use Zend\Mail\Transport\Sendmail; +use Laminas\Mail\Message; +use Laminas\Mail\Transport\Sendmail; /** * Class that responsible for filling some message data before transporting it. - * @see \Zend\Mail\Transport\Sendmail is used for transport + * @see \Laminas\Mail\Transport\Sendmail is used for transport */ class Transport implements TransportInterface { diff --git a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php index c6b7ce22fc39c..55c494e1ed2dd 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php @@ -6,7 +6,7 @@ namespace Magento\Integration\Test\Unit\Model\Oauth; use Magento\Framework\Url\Validator as UrlValidator; -use Zend\Validator\Uri as ZendUriValidator; +use Laminas\Validator\Uri as ZendUriValidator; use Magento\Integration\Model\Oauth\Consumer\Validator\KeyLength; /** diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php index 0d0bfc510b518..49a5421e623a5 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception during media asset initialization diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php index 0ca9b3a3ffc8a..75b69c441c6d7 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception thrown in case when there is no such entity diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php index a709c2d214bda..f76552487e0f7 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception during get media data diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php index c300d4f121bd2..c9e8416c53156 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command successful scenario diff --git a/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php b/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php index cdeb47d2b8490..7c0453fbf3da6 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php @@ -47,7 +47,7 @@ protected function setUp() public function testSendResponse(): void { $filePath = 'file_path'; - $headers = $this->getMockBuilder(\Zend\Http\Headers::class)->getMock(); + $headers = $this->getMockBuilder(\Laminas\Http\Headers::class)->getMock(); $this->response->setFilePath($filePath); $this->response->setHeaders($headers); $this->transferAdapter diff --git a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php index 37dcd1302b3a3..8fcecea2e23d2 100644 --- a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php @@ -90,7 +90,7 @@ public function aroundDispatch( protected function addDebugHeaders(ResponseHttp $result) { $cacheControlHeader = $result->getHeader('Cache-Control'); - if ($cacheControlHeader instanceof \Zend\Http\Header\HeaderInterface) { + if ($cacheControlHeader instanceof \Laminas\Http\Header\HeaderInterface) { $this->addDebugHeader($result, 'X-Magento-Cache-Control', $cacheControlHeader->getFieldValue()); } $this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'MISS', true); diff --git a/app/code/Magento/PageCache/Model/Cache/Server.php b/app/code/Magento/PageCache/Model/Cache/Server.php index 7f3a4af969d7e..3be54863f2dcd 100644 --- a/app/code/Magento/PageCache/Model/Cache/Server.php +++ b/app/code/Magento/PageCache/Model/Cache/Server.php @@ -9,8 +9,8 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Framework\App\RequestInterface; -use Zend\Uri\Uri; -use Zend\Uri\UriFactory; +use Laminas\Uri\Uri; +use Laminas\Uri\UriFactory; /** * Cache server model. diff --git a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php index aadae97009cac..20e0155aaf113 100644 --- a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php @@ -11,7 +11,7 @@ use Magento\Framework\Registry; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Response\Http as ResponseHttp; -use Zend\Http\Header\HeaderInterface as HttpHeaderInterface; +use Laminas\Http\Header\HeaderInterface as HttpHeaderInterface; use Magento\PageCache\Model\Cache\Type as CacheType; /** diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php index db0edfa6bd779..c9a887595b5a1 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php @@ -84,7 +84,7 @@ protected function setUp() */ public function testAroundDispatchProcessIfCacheMissed($state) { - $header = \Zend\Http\Header\GenericHeader::fromString('Cache-Control: no-cache'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Cache-Control: no-cache'); $this->configMock ->expects($this->once()) ->method('getType') diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php index a57effe1f31ad..57c4bb7107b13 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php @@ -7,7 +7,7 @@ use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use \Magento\PageCache\Model\Cache\Server; -use \Zend\Uri\UriFactory; +use \Laminas\Uri\UriFactory; class ServerTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php index fc4e056734939..cc431b94eb5bf 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php @@ -13,7 +13,7 @@ use Magento\Framework\Registry; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Response\Http as ResponseHttp; -use Zend\Http\Header\HeaderInterface as HttpHeaderInterface; +use Laminas\Http\Header\HeaderInterface as HttpHeaderInterface; use Magento\PageCache\Model\Cache\Type as CacheType; /** diff --git a/app/code/Magento/Quote/Model/Quote/Item/Updater.php b/app/code/Magento/Quote/Model/Quote/Item/Updater.php index 9865ae82ac3d6..410eb69e96ff5 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Updater.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Updater.php @@ -10,7 +10,7 @@ use Magento\Framework\DataObject\Factory as ObjectFactory; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item; -use Zend\Code\Exception\InvalidArgumentException; +use Laminas\Code\Exception\InvalidArgumentException; /** * Class Updater diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php index 2009cd3ff9d92..d194526858cde 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php @@ -104,7 +104,7 @@ public function addFieldToFilter($field, $condition = null) public function getSelectCountSql() { $countSelect = parent::getSelectCountSql(); - $countSelect->reset(\Zend\Db\Sql\Select::GROUP); + $countSelect->reset(\Laminas\Db\Sql\Select::GROUP); return $countSelect; } } diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php index a601f8fb2d1d7..3964cec18da8d 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php @@ -6,7 +6,7 @@ namespace Magento\Rss\Test\Unit\Controller\Adminhtml\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Zend\Feed\Writer\Exception\InvalidArgumentException; +use Laminas\Feed\Writer\Exception\InvalidArgumentException; /** * Class IndexTest diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php index 30415155d5f6e..3afd9f7833bba 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php @@ -6,7 +6,7 @@ namespace Magento\Rss\Test\Unit\Controller\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Zend\Feed\Writer\Exception\InvalidArgumentException; +use Laminas\Feed\Writer\Exception\InvalidArgumentException; /** * Class IndexTest diff --git a/app/code/Magento/Rss/Test/Unit/Model/RssTest.php b/app/code/Magento/Rss/Test/Unit/Model/RssTest.php index f2888e4296b40..75652a1f75221 100644 --- a/app/code/Magento/Rss/Test/Unit/Model/RssTest.php +++ b/app/code/Magento/Rss/Test/Unit/Model/RssTest.php @@ -43,7 +43,7 @@ class RssTest extends \PHPUnit\Framework\TestCase <link>http://magento.com/rss/link</link> <description><![CDATA[Feed Description]]></description> <pubDate>Sat, 22 Apr 2017 13:21:12 +0200</pubDate> - <generator>Zend\Feed</generator> + <generator>Laminas\Feed</generator> <docs>http://blogs.law.harvard.edu/tech/rss</docs> <item> <title><![CDATA[Feed 1 Title]]> diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index 178395ff6eb6a..da0c49aa1bc11 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -51,7 +51,7 @@ class Redirect implements \Magento\Framework\App\Response\RedirectInterface protected $_urlBuilder; /** - * @var \Zend\Uri\Uri|null + * @var \Laminas\Uri\Uri|null */ private $uri; @@ -64,7 +64,7 @@ class Redirect implements \Magento\Framework\App\Response\RedirectInterface * @param \Magento\Framework\Session\SessionManagerInterface $session * @param \Magento\Framework\Session\SidResolverInterface $sidResolver * @param \Magento\Framework\UrlInterface $urlBuilder - * @param \Zend\Uri\Uri|null $uri + * @param \Laminas\Uri\Uri|null $uri * @param bool $canUseSessionIdInParam */ public function __construct( @@ -74,7 +74,7 @@ public function __construct( \Magento\Framework\Session\SessionManagerInterface $session, \Magento\Framework\Session\SidResolverInterface $sidResolver, \Magento\Framework\UrlInterface $urlBuilder, - \Zend\Uri\Uri $uri = null, + \Laminas\Uri\Uri $uri = null, $canUseSessionIdInParam = true ) { $this->_canUseSessionIdInParam = $canUseSessionIdInParam; @@ -84,7 +84,7 @@ public function __construct( $this->_session = $session; $this->_sidResolver = $sidResolver; $this->_urlBuilder = $urlBuilder; - $this->uri = $uri ?: ObjectManager::getInstance()->get(\Zend\Uri\Uri::class); + $this->uri = $uri ?: ObjectManager::getInstance()->get(\Laminas\Uri\Uri::class); } /** diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 5eda6f4a9b57d..9d7add39394e3 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -18,7 +18,7 @@ use Magento\Framework\Url\ScopeInterface as UrlScopeInterface; use Magento\Framework\UrlInterface; use Magento\Store\Api\Data\StoreInterface; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; /** * Store model diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php index a193604a0d6da..21989a0e6c58e 100644 --- a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php +++ b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php @@ -37,7 +37,7 @@ class SaveTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject */ protected $context; - /** @var \Zend\Stdlib\Parameters|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Laminas\Stdlib\Parameters|\PHPUnit_Framework_MockObject_MockObject */ protected $fileParams; /** @var \Magento\Theme\Api\Data\DesignConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -83,7 +83,7 @@ public function setUp() '', false ); - $this->fileParams = $this->createMock(\Zend\Stdlib\Parameters::class); + $this->fileParams = $this->createMock(\Laminas\Stdlib\Parameters::class); $this->dataPersistor = $this->getMockBuilder(\Magento\Framework\App\Request\DataPersistorInterface::class) ->getMockForAbstractClass(); $this->controller = new Save( diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index b06c655939b1c..bbdbcc637a5a0 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -90,8 +90,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_403, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_403, + \Laminas\Http\AbstractMessage::VERSION_11, 'Forbidden' ); return $resultJson->setData([ @@ -108,8 +108,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); @@ -123,8 +123,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Ui/Controller/Index/Render.php b/app/code/Magento/Ui/Controller/Index/Render.php index faab203547064..f74123955ce23 100644 --- a/app/code/Magento/Ui/Controller/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Index/Render.php @@ -109,8 +109,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_403, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_403, + \Laminas\Http\AbstractMessage::VERSION_11, 'Forbidden' ); return $resultJson->setData( @@ -129,8 +129,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); @@ -144,8 +144,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php index 2bba8686490b6..7c4c373abad3b 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php @@ -12,8 +12,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Controller\Adminhtml\Index\Render; use Magento\Ui\Model\UiComponentTypeResolver; -use Zend\Http\AbstractMessage; -use Zend\Http\Response; +use Laminas\Http\AbstractMessage; +use Laminas\Http\Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php index 646cea81212f9..894ff354a96fe 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php @@ -12,8 +12,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Controller\Index\Render; use Magento\Ui\Model\UiComponentTypeResolver; -use Zend\Http\AbstractMessage; -use Zend\Http\Response; +use Laminas\Http\AbstractMessage; +use Laminas\Http\Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index c67f3f400b007..cd2725f218aae 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -11,7 +11,7 @@ use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Store\Model\Store; use PHPUnit\Framework\MockObject\MockObject; -use Zend\Stdlib\ParametersInterface; +use Laminas\Stdlib\ParametersInterface; /** * Test class for UrlRewrite Controller Router diff --git a/app/code/Magento/Webapi/Model/Config/ClassReflector.php b/app/code/Magento/Webapi/Model/Config/ClassReflector.php index b73e4e0afb585..af34a3852caab 100644 --- a/app/code/Magento/Webapi/Model/Config/ClassReflector.php +++ b/app/code/Magento/Webapi/Model/Config/ClassReflector.php @@ -5,7 +5,7 @@ */ namespace Magento\Webapi\Model\Config; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\MethodReflection; /** * Class reflector. @@ -64,8 +64,8 @@ public function __construct(\Magento\Framework\Reflection\TypeProcessor $typePro public function reflectClassMethods($className, $methods) { $data = []; - $classReflection = new \Zend\Code\Reflection\ClassReflection($className); - /** @var \Zend\Code\Reflection\MethodReflection $methodReflection */ + $classReflection = new \Laminas\Code\Reflection\ClassReflection($className); + /** @var \Laminas\Code\Reflection\MethodReflection $methodReflection */ foreach ($classReflection->getMethods() as $methodReflection) { $methodName = $methodReflection->getName(); if (in_array($methodName, $methods) || array_key_exists($methodName, $methods)) { @@ -78,14 +78,14 @@ public function reflectClassMethods($className, $methods) /** * Retrieve method interface and documentation description. * - * @param \Zend\Code\Reflection\MethodReflection $method + * @param \Laminas\Code\Reflection\MethodReflection $method * @return array * @throws \InvalidArgumentException */ - public function extractMethodData(\Zend\Code\Reflection\MethodReflection $method) + public function extractMethodData(\Laminas\Code\Reflection\MethodReflection $method) { $methodData = ['documentation' => $this->extractMethodDescription($method), 'interface' => []]; - /** @var \Zend\Code\Reflection\ParameterReflection $parameter */ + /** @var \Laminas\Code\Reflection\ParameterReflection $parameter */ foreach ($method->getParameters() as $parameter) { $parameterData = [ 'type' => $this->_typeProcessor->register($this->_typeProcessor->getParamType($parameter)), @@ -116,10 +116,10 @@ public function extractMethodData(\Zend\Code\Reflection\MethodReflection $method /** * Retrieve method full documentation description. * - * @param \Zend\Code\Reflection\MethodReflection $method + * @param \Laminas\Code\Reflection\MethodReflection $method * @return string */ - protected function extractMethodDescription(\Zend\Code\Reflection\MethodReflection $method) + protected function extractMethodDescription(\Laminas\Code\Reflection\MethodReflection $method) { $methodReflection = new MethodReflection( $method->getDeclaringClass()->getName(), @@ -144,7 +144,7 @@ protected function extractMethodDescription(\Zend\Code\Reflection\MethodReflecti */ public function extractClassDescription($className) { - $classReflection = new \Zend\Code\Reflection\ClassReflection($className); + $classReflection = new \Laminas\Code\Reflection\ClassReflection($className); $docBlock = $classReflection->getDocBlock(); if (!$docBlock) { return ''; diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl.php b/app/code/Magento/Webapi/Model/Soap/Wsdl.php index 2d0b310995215..870dfec1fc072 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl.php @@ -11,14 +11,14 @@ /** * Magento-specific WSDL builder. */ -class Wsdl extends \Zend\Soap\Wsdl +class Wsdl extends \Laminas\Soap\Wsdl { /** * Constructor. * Save URI for targetNamespace generation. * * @param string $name - * @param string|\Zend\Uri\Uri $uri + * @param string|\Laminas\Uri\Uri $uri * @param ComplexTypeStrategy $strategy */ public function __construct($name, $uri, ComplexTypeStrategy $strategy) diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php index 3884a0ef026e1..2087f9e5e3d6e 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php @@ -5,8 +5,8 @@ */ namespace Magento\Webapi\Model\Soap\Wsdl; -use Zend\Soap\Wsdl; -use Zend\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy; +use Laminas\Soap\Wsdl; +use Laminas\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy; /** * Magento-specific Complex type strategy for WSDL auto discovery. diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php index 9b42d3c9c3e3a..576353f3636a4 100644 --- a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php @@ -100,7 +100,7 @@ protected function setUp() $this->_responseMock ->expects($this->any()) ->method('getHeaders') - ->will($this->returnValue(new \Zend\Http\Headers())); + ->will($this->returnValue(new \Laminas\Http\Headers())); $appconfig = $this->createMock(\Magento\Framework\App\Config::class); $objectManagerHelper->setBackwardCompatibleProperty( diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php b/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php index b597b838a3512..8c77c65135a61 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php @@ -46,10 +46,10 @@ public function testReflectClassMethods() public function testExtractMethodData() { - $classReflection = new \Zend\Code\Reflection\ClassReflection( + $classReflection = new \Laminas\Code\Reflection\ClassReflection( \Magento\Webapi\Test\Unit\Model\Config\TestServiceForClassReflector::class ); - /** @var $methodReflection \Zend\Code\Reflection\MethodReflection */ + /** @var $methodReflection \Laminas\Code\Reflection\MethodReflection */ $methodReflection = $classReflection->getMethods()[0]; $methodData = $this->_classReflector->extractMethodData($methodReflection); $expectedResponse = $this->_getSampleReflectionData(); diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php b/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php index 93d65d545408d..4ff37b3c15b0d 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php @@ -7,7 +7,7 @@ use \Magento\Webapi\Model\Soap\Wsdl\ComplexTypeStrategy; -use Zend\Soap\Wsdl; +use Laminas\Soap\Wsdl; /** * Complex type strategy tests. diff --git a/composer.json b/composer.json index ab767fdac286d..0f70af3613dff 100644 --- a/composer.json +++ b/composer.json @@ -35,11 +35,41 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", + "guzzlehttp/guzzle": "^6.3.3", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-dependency-plugin": "^1.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^3.0.0", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.11.2", + "laminas/laminas-zendframework-bridge": "^1.0", "magento/composer": "~1.5.0", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", - "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", @@ -52,35 +82,7 @@ "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^3.0.0", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.11.2", - "guzzlehttp/guzzle": "^6.3.3" + "wikimedia/less.php": "~1.8.0" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", @@ -319,7 +321,7 @@ "Magento\\Framework\\": "lib/internal/Magento/Framework/", "Magento\\Setup\\": "setup/src/Magento/Setup/", "Magento\\": "app/code/Magento/", - "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" + "Laminas\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" }, "psr-0": { "": [ diff --git a/composer.lock b/composer.lock index b6d834610059a..5202b8e6c43d8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8d8e6b87c1f6ac98b3b7331eba9473f3", + "content-hash": "a418300ac80a4c8ddea59451a7e563c1", "packages": [ { "name": "braintree/braintree_php", @@ -949,3935 +949,4183 @@ "time": "2019-09-25T14:49:45+00:00" }, { - "name": "magento/composer", - "version": "1.5.0", + "name": "laminas/laminas-captcha", + "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/magento/composer.git", - "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd" + "url": "https://github.com/laminas/laminas-captcha.git", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/composer/zipball/ea12b95be5c0833b3d9497aaefa08816c19e5dcd", - "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/b88f650f3adf2d902ef56f6377cceb5cd87b9876", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876", "shasum": "" }, "require": { - "composer/composer": "^1.6", - "php": "~7.1.3||~7.2.0||~7.3.0", - "symfony/console": "~4.0.0 || ~4.1.0" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-captcha": "self.version" }, "require-dev": { - "phpunit/phpunit": "~7.0.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-recaptcha": "^3.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + }, + "suggest": { + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + } + }, "autoload": { "psr-4": { - "Magento\\Composer\\": "src" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0", - "AFL-3.0" + "BSD-3-Clause" ], - "description": "Magento composer library helps to instantiate Composer application and run composer commands.", - "time": "2019-07-29T19:52:05+00:00" + "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "captcha", + "laminas" + ], + "time": "2019-12-31T16:24:14+00:00" }, { - "name": "magento/magento-composer-installer", - "version": "0.1.13", + "name": "laminas/laminas-code", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/magento/magento-composer-installer.git", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/128784abc7a0d9e1fcc30c446533aa6f1db1f999", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.1" }, "replace": { - "magento-hackathon/magento-composer-installer": "*" + "zendframework/zend-code": "self.version" }, "require-dev": { - "composer/composer": "*@dev", - "firegento/phpcs": "dev-patch-1", - "mikey179/vfsstream": "*", - "phpunit/phpunit": "*", - "phpunit/phpunit-mock-objects": "dev-master", - "squizlabs/php_codesniffer": "1.4.7", - "symfony/process": "*" + "doctrine/annotations": "^1.0", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^1.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "phpunit/phpunit": "^7.5.15" }, - "type": "composer-plugin", + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" + }, + "type": "library", "extra": { - "composer-command-registry": [ - "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" - ], - "class": "MagentoHackathon\\Composer\\Magento\\Plugin" + "branch-alias": { + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" + } }, "autoload": { - "psr-0": { - "MagentoHackathon\\Composer\\Magento": "src/" + "psr-4": { + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Vinai Kopp", - "email": "vinai@netzarbeiter.com" - }, - { - "name": "Daniel Fahlke aka Flyingmana", - "email": "flyingmana@googlemail.com" - }, - { - "name": "Jörg Weller", - "email": "weller@flagbit.de" - }, - { - "name": "Karl Spies", - "email": "karl.spies@gmx.net" - }, - { - "name": "Tobias Vogt", - "email": "tobi@webguys.de" - }, - { - "name": "David Fuhr", - "email": "fuhr@flagbit.de" - } + "BSD-3-Clause" ], - "description": "Composer installer for Magento modules", - "homepage": "https://github.com/magento/magento-composer-installer", + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", "keywords": [ - "composer-installer", - "magento" + "code", + "laminas" ], - "time": "2017-12-29T16:45:24+00:00" + "time": "2019-12-31T16:28:14+00:00" }, { - "name": "magento/zendframework1", - "version": "1.14.2", + "name": "laminas/laminas-config", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/magento/zf1.git", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f" + "url": "https://github.com/laminas/laminas-config.git", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/8221062d42a198e431d183bbe672e5e1a2f98c5f", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/71ba6d5dd703196ce66b25abc4d772edb094dae1", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1", "shasum": "" }, "require": { - "php": ">=5.2.11" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-config": "self.version" }, "require-dev": { - "phpunit/dbunit": "1.3.*", - "phpunit/phpunit": "3.7.*" + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12.x-dev" + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" } }, "autoload": { - "psr-0": { - "Zend_": "library/" + "psr-4": { + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "library/" - ], "license": [ "BSD-3-Clause" ], - "description": "Magento Zend Framework 1", - "homepage": "http://framework.zend.com/", + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "homepage": "https://laminas.dev", "keywords": [ - "ZF1", - "framework" + "config", + "laminas" ], - "time": "2019-07-26T16:43:11+00:00" + "time": "2019-12-31T16:30:04+00:00" }, { - "name": "monolog/monolog", - "version": "1.25.2", + "name": "laminas/laminas-console", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" + "url": "https://github.com/laminas/laminas-console.git", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", - "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/478a6ceac3e31fb38d6314088abda8b239ee23a5", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "provide": { - "psr/log-implementation": "1.0.0" + "replace": { + "zendframework/zend-console": "self.version" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.23 || ^6.4.3" }, "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "description": "Build console applications using getopt syntax or routing, complete with prompts", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "logging", - "psr-3" + "console", + "laminas" ], - "time": "2019-11-13T10:00:05+00:00" + "time": "2019-12-31T16:31:45+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.99", + "name": "laminas/laminas-crypt", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "url": "https://github.com/laminas/laminas-crypt.git", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", "shasum": "" }, "require": { - "php": "^7" + "container-interop/container-interop": "~1.0", + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-crypt": "self.version" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Crypt\\": "src/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } + "BSD-3-Clause" ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "homepage": "https://laminas.dev", "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" + "crypt", + "laminas" ], - "time": "2018-07-02T15:55:56+00:00" + "time": "2019-12-31T16:33:11+00:00" }, { - "name": "paragonie/sodium_compat", - "version": "v1.12.1", + "name": "laminas/laminas-db", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "063cae9b3a7323579063e7037720f5b52b56c178" + "url": "https://github.com/laminas/laminas-db.git", + "reference": "dae817b9e0c724ef10cb7906ef8ef3fe68debeb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/063cae9b3a7323579063e7037720f5b52b56c178", - "reference": "063cae9b3a7323579063e7037720f5b52b56c178", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/dae817b9e0c724ef10cb7906ef8ef3fe68debeb7", + "reference": "dae817b9e0c724ef10cb7906ef8ef3fe68debeb7", "shasum": "" }, "require": { - "paragonie/random_compat": ">=1", - "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-db": "self.version" }, "require-dev": { - "phpunit/phpunit": "^3|^4|^5|^6|^7" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.25 || ^6.4.4" }, "suggest": { - "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", - "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9-dev", + "dev-develop": "2.10-dev" + }, + "laminas": { + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" + } + }, "autoload": { - "files": [ - "autoload.php" - ] + "psr-4": { + "Laminas\\Db\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com" + "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "db", + "laminas" + ], + "time": "2019-12-31T19:55:57+00:00" + }, + { + "name": "laminas/laminas-dependency-plugin", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-dependency-plugin.git", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-dependency-plugin/zipball/f269716dc584cd7b69e7f6e8ac1092d645ab56d5", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "composer/composer": "^1.9", + "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^8.4", + "roave/security-advisories": "dev-master", + "webimpress/coding-standard": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" }, - { - "name": "Frank Denis", - "email": "jedisct1@pureftpd.org" + "class": "Laminas\\DependencyPlugin\\DependencyRewriterPlugin" + }, + "autoload": { + "psr-4": { + "Laminas\\DependencyPlugin\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" ], - "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", - "keywords": [ - "Authentication", - "BLAKE2b", - "ChaCha20", - "ChaCha20-Poly1305", - "Chapoly", - "Curve25519", - "Ed25519", - "EdDSA", - "Edwards-curve Digital Signature Algorithm", - "Elliptic Curve Diffie-Hellman", - "Poly1305", - "Pure-PHP cryptography", - "RFC 7748", - "RFC 8032", - "Salpoly", - "Salsa20", - "X25519", - "XChaCha20-Poly1305", - "XSalsa20-Poly1305", - "Xchacha20", - "Xsalsa20", - "aead", - "cryptography", - "ecdh", - "elliptic curve", - "elliptic curve cryptography", - "encryption", - "libsodium", - "php", - "public-key cryptography", - "secret-key cryptography", - "side-channel resistant" - ], - "time": "2019-11-07T17:07:24+00:00" + "description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.", + "time": "2020-01-14T19:36:52+00:00" }, { - "name": "pelago/emogrifier", - "version": "v2.2.0", + "name": "laminas/laminas-di", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/MyIntervals/emogrifier.git", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" + "url": "https://github.com/laminas/laminas-di.git", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/239b22408a1f8eacda6fc2b838b5065c4cf1d88e", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", - "symfony/css-selector": "^3.4.0 || ^4.0.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^0.4.5 || ^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-di": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.2.0", - "phpmd/phpmd": "^2.6.0", - "phpunit/phpunit": "^4.8.0", - "squizlabs/php_codesniffer": "^3.3.2" + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" } }, "autoload": { "psr-4": { - "Pelago\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Oliver Klee", - "email": "github@oliverklee.de" - }, - { - "name": "Zoli Szabó", - "email": "zoli.szabo+github@gmail.com" - }, - { - "name": "John Reeve", - "email": "jreeve@pelagodesign.com" - }, - { - "name": "Jake Hotson", - "email": "jake@qzdesign.co.uk" - }, - { - "name": "Cameron Brooks" - }, - { - "name": "Jaime Prado" - } + "BSD-3-Clause" ], - "description": "Converts CSS styles into inline style attributes in your HTML code", - "homepage": "https://www.myintervals.com/emogrifier.php", + "homepage": "https://laminas.dev", "keywords": [ - "css", - "email", - "pre-processing" + "di", + "laminas" ], - "time": "2019-09-04T16:07:59+00:00" + "time": "2019-12-31T15:17:33+00:00" }, { - "name": "php-amqplib/php-amqplib", - "version": "v2.10.1", + "name": "laminas/laminas-diactoros", + "version": "1.8.7", "source": { "type": "git", - "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "1c2dce9d2822030d5dcfd50b709708830429c89a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/1c2dce9d2822030d5dcfd50b709708830429c89a", + "reference": "1c2dce9d2822030d5dcfd50b709708830429c89a", "shasum": "" }, "require": { - "ext-bcmath": "*", - "ext-sockets": "*", - "php": ">=5.6" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" }, "replace": { - "videlalvaro/php-amqplib": "self.version" + "zendframework/zend-diactoros": "self.version" }, "require-dev": { - "ext-curl": "*", - "nategood/httpful": "^0.2.20", - "phpunit/phpunit": "^5.7|^6.5|^7.0", - "squizlabs/php_codesniffer": "^2.5" + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-coding-standard": "~1.0", + "php-http/psr7-integration-tests": "dev-master", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev" + "dev-release-1.8": "1.8.x-dev" } }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php", + "src/functions/create_uploaded_file.legacy.php", + "src/functions/marshal_headers_from_sapi.legacy.php", + "src/functions/marshal_method_from_sapi.legacy.php", + "src/functions/marshal_protocol_version_from_sapi.legacy.php", + "src/functions/marshal_uri_from_sapi.legacy.php", + "src/functions/normalize_server.legacy.php", + "src/functions/normalize_uploaded_files.legacy.php", + "src/functions/parse_cookie_header.legacy.php" + ], "psr-4": { - "PhpAmqpLib\\": "PhpAmqpLib/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1-or-later" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Alvaro Videla", - "role": "Original Maintainer" - }, - { - "name": "John Kelly", - "email": "johnmkelly86@gmail.com", - "role": "Maintainer" - }, - { - "name": "Raúl Araya", - "email": "nubeiro@gmail.com", - "role": "Maintainer" - }, - { - "name": "Luke Bakken", - "email": "luke@bakken.io", - "role": "Maintainer" + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-7" + ], + "time": "2019-12-31T16:41:30+00:00" + }, + { + "name": "laminas/laminas-escaper", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/25f2a053eadfa92ddacb609dcbbc39362610da70", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70", + "shasum": "" + }, + "require": { + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-escaper": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Escaper\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" ], - "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", - "homepage": "https://github.com/php-amqplib/php-amqplib/", + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", "keywords": [ - "message", - "queue", - "rabbitmq" + "escaper", + "laminas" ], - "time": "2019-10-10T13:23:40+00:00" + "time": "2019-12-31T16:43:30+00:00" }, { - "name": "phpseclib/mcrypt_compat", - "version": "1.0.8", + "name": "laminas/laminas-eventmanager", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/phpseclib/mcrypt_compat.git", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" + "url": "https://github.com/laminas/laminas-eventmanager.git", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpseclib/phpseclib": ">=2.0.11 <3.0.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-eventmanager": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.7|^6.0" + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "suggest": { - "ext-openssl": "Will enable faster cryptographic operations" + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev", + "dev-develop": "3.3-dev" + } + }, "autoload": { - "files": [ - "lib/mcrypt.php" - ] + "psr-4": { + "Laminas\\EventManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "homepage": "http://phpseclib.sourceforge.net" - } + "BSD-3-Clause" ], - "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://laminas.dev", "keywords": [ - "cryptograpy", - "encryption", - "mcrypt" + "event", + "eventmanager", + "events", + "laminas" ], - "time": "2018-08-22T03:11:43+00:00" + "time": "2019-12-31T16:44:52+00:00" }, { - "name": "phpseclib/phpseclib", - "version": "2.0.23", + "name": "laminas/laminas-feed", + "version": "2.12.0", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099" + "url": "https://github.com/laminas/laminas-feed.git", + "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c78eb5058d5bb1a183133c36d4ba5b6675dfa099", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/64d25e18a6ea3db90c27fe2d6b95630daa1bf602", + "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-feed": "self.version" }, "require-dev": { - "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "sami/sami": "~2.0", - "squizlabs/php_codesniffer": "~2.0" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-message": "^1.0.1" }, "suggest": { - "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", - "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", - "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + } + }, "autoload": { - "files": [ - "phpseclib/bootstrap.php" - ], "psr-4": { - "phpseclib\\": "phpseclib/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" - }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" - }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" - }, - { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" - } + "BSD-3-Clause" ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", + "description": "provides functionality for consuming RSS and Atom feeds", + "homepage": "https://laminas.dev", "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" + "feed", + "laminas" ], - "time": "2019-09-17T03:41:22+00:00" + "time": "2019-12-31T16:46:54+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "laminas/laminas-filter", + "version": "2.9.2", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/laminas/laminas-filter.git", + "reference": "4d8c0c25e40836bd617335e744009c2c950c4ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/4d8c0c25e40836bd617335e744009c2c950c4ad5", + "reference": "4d8c0c25e40836bd617335e744009c2c950c4ad5", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "laminas/laminas-validator": "<2.10.1" + }, + "replace": { + "zendframework/zend-filter": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6", + "pear/archive_tar": "^1.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-factory": "^1.0" + }, + "suggest": { + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter", + "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Programmatically filter and normalize data and files", + "homepage": "https://laminas.dev", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "filter", + "laminas" ], - "time": "2017-02-14T16:28:37+00:00" + "time": "2019-12-31T16:54:29+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "laminas/laminas-form", + "version": "2.14.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/laminas/laminas-form.git", + "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/012aae01366cb8c8fb64e39a887363ef82f388dd", + "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-form": "self.version" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-recaptcha": "^3.0.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "phpunit/phpunit": "^5.7.23 || ^6.5.3" + }, + "suggest": { + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use laminas-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for laminas-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using laminas-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the laminas-form view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.14.x-dev", + "dev-develop": "2.15.x-dev" + }, + "laminas": { + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" - } + "Laminas\\Form\\": "src/" + }, + "files": [ + "autoload/formElementManagerPolyfill.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "homepage": "https://laminas.dev", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "form", + "laminas" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2019-12-31T16:56:34+00:00" }, { - "name": "psr/log", - "version": "1.1.2", + "name": "laminas/laminas-http", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "url": "https://github.com/laminas/laminas-http.git", + "reference": "d5b71b5b84329a13825e268e8bda1a225de0404b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d5b71b5b84329a13825e268e8bda1a225de0404b", + "reference": "d5b71b5b84329a13825e268e8bda1a225de0404b", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-http": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3" + }, + "suggest": { + "paragonie/certainty": "For automated management of cacert.pem" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "psr", - "psr-3" + "http", + "http client", + "laminas" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2019-12-31T17:02:20+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "laminas/laminas-hydrator", + "version": "2.4.2", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/4a0e81cf05f32edcace817f1f48cb4055f689d85", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85", "shasum": "" }, "require": { - "php": ">=5.6" + "laminas/laminas-stdlib": "^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-hydrator": "self.version" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + }, + "suggest": { + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", + "extra": { + "branch-alias": { + "dev-release-2.4": "2.4.x-dev" + }, + "laminas": { + "component": "Laminas\\Hydrator", + "config-provider": "Laminas\\Hydrator\\ConfigProvider" + } + }, "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "Laminas\\Hydrator\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } + "description": "Serialize objects to arrays, and vice versa", + "homepage": "https://laminas.dev", + "keywords": [ + "hydrator", + "laminas" ], - "description": "A polyfill for getallheaders.", - "time": "2019-03-08T08:55:37+00:00" + "time": "2019-12-31T17:06:38+00:00" }, { - "name": "ramsey/uuid", - "version": "3.8.0", + "name": "laminas/laminas-i18n", + "version": "2.9.2", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "url": "https://github.com/laminas/laminas-i18n.git", + "reference": "48883436c6fa1f9ef001af295e3a40003c5cfbf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/48883436c6fa1f9ef001af295e3a40003c5cfbf2", + "reference": "48883436c6fa1f9ef001af295e3a40003c5cfbf2", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, "replace": { - "rhumsaa/uuid": "self.version" + "zendframework/zend-i18n": "self.version" }, "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } + "BSD-3-Clause" ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", + "description": "Provide translations for your application, and filter and validate internationalized values", + "homepage": "https://laminas.dev", "keywords": [ - "guid", - "identifier", - "uuid" + "i18n", + "laminas" ], - "time": "2018-07-19T23:38:55+00:00" + "time": "2019-12-31T17:09:58+00:00" }, { - "name": "react/promise", - "version": "v2.7.1", + "name": "laminas/laminas-inputfilter", + "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + "url": "git@github.com:laminas/laminas-inputfilter.git", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/b29ce8f512c966468eee37ea4873ae5fb545d00a", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a", "shasum": "" }, "require": { - "php": ">=5.4.0" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-inputfilter": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", + "psr/http-message": "^1.0" + }, + "suggest": { + "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" + } + }, "autoload": { "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "Laminas\\InputFilter\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } + "BSD-3-Clause" ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "homepage": "https://laminas.dev", "keywords": [ - "promise", - "promises" + "inputfilter", + "laminas" ], - "time": "2019-01-07T21:25:54+00:00" + "time": "2019-12-31T17:11:54+00:00" }, { - "name": "seld/jsonlint", - "version": "1.7.2", + "name": "laminas/laminas-json", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "url": "https://github.com/laminas/laminas-json.git", + "reference": "db58425b7f0eba44a7539450cc926af80915951a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/db58425b7f0eba44a7539450cc926af80915951a", + "reference": "db58425b7f0eba44a7539450cc926af80915951a", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-json": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-xml": "^1.0.2", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "laminas/laminas-xml": "To support Laminas\\Json\\Json::fromXml() usage" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "JSON Linter", + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", "keywords": [ "json", - "linter", - "parser", - "validator" + "laminas" ], - "time": "2019-10-24T14:27:39+00:00" + "time": "2019-12-31T17:15:00+00:00" }, { - "name": "seld/phar-utils", - "version": "1.0.1", + "name": "laminas/laminas-loader", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + "url": "https://github.com/laminas/laminas-loader.git", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/5d01c2c237ae9e68bec262f339947e2ea18979bc", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc", "shasum": "" }, "require": { - "php": ">=5.3" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-loader": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { "psr-4": { - "Seld\\PharUtils\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } + "BSD-3-Clause" ], - "description": "PHAR file format utilities, for when PHP phars you up", + "description": "Autoloading and plugin loading strategies", + "homepage": "https://laminas.dev", "keywords": [ - "phra" + "laminas", + "loader" ], - "time": "2015-10-13T18:44:15+00:00" + "time": "2019-12-31T17:18:27+00:00" }, { - "name": "symfony/console", - "version": "v4.1.12", + "name": "laminas/laminas-log", + "version": "2.11.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02" + "url": "https://github.com/laminas/laminas-log.git", + "reference": "a702300d8639709b4f8f5f9617a62d1b973e5289" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e87c798f67dc9fceeb4f3d57847b52d945d1a02", - "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/a702300d8639709b4f8f5f9617a62d1b973e5289", + "reference": "a702300d8639709b4f8f5f9617a62d1b973e5289", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/log": "^1.0" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0.0" + }, + "replace": { + "zendframework/zend-log": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1", + "mikey179/vfsstream": "^1.6.7", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-mongo": "mongo extension to use Mongo writer", + "ext-mongodb": "mongodb extension to use MongoDB writer", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" + }, + "laminas": { + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Log\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "log", + "logging" ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2019-01-25T14:34:37+00:00" + "time": "2019-12-31T17:18:57+00:00" }, { - "name": "symfony/css-selector", - "version": "v4.3.8", + "name": "laminas/laminas-mail", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9" + "url": "https://github.com/laminas/laminas-mail.git", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/019fb670c1dff6be7fc91d3b88942bd0a5f68792", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792", "shasum": "" }, "require": { - "php": "^7.1.3" + "ext-iconv": "*", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "true/punycode": "^2.1" + }, + "replace": { + "zendframework/zend-mail": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4" + }, + "suggest": { + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Mail\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mail" ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2019-10-02T08:36:26+00:00" + "time": "2019-12-31T17:21:22+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v4.3.8", + "name": "laminas/laminas-math", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0df002fd4f500392eabd243c2947061a50937287" + "url": "https://github.com/laminas/laminas-math.git", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", - "reference": "0df002fd4f500392eabd243c2947061a50937287", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/8027b37e00accc43f28605c7d8fd081baed1f475", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/event-dispatcher-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "1.1" + "replace": { + "zendframework/zend-math": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "^3.4|^4.0", - "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0" + "fabpot/php-cs-fixer": "1.7.*", + "ircmaxell/random-lib": "~1.1", + "phpunit/phpunit": "~4.0" }, "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "ext-bcmath": "If using the bcmath functionality", + "ext-gmp": "If using the gmp functionality", + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Math\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "math" ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2019-11-03T09:04:05+00:00" + "time": "2019-12-31T17:24:15+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.7", + "name": "laminas/laminas-mime", + "version": "2.7.2", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" + "url": "https://github.com/laminas/laminas-mime.git", + "reference": "2dbace2c69542e5a251af3becb6d7209ac9fb42b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/2dbace2c69542e5a251af3becb6d7209ac9fb42b", + "reference": "2dbace2c69542e5a251af3becb6d7209ac9fb42b", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-mime": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6", + "phpunit/phpunit": "^5.7.21 || ^6.3" }, "suggest": { - "psr/event-dispatcher": "", - "symfony/event-dispatcher-implementation": "" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "branch-alias": { + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Mime\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", + "description": "Create and parse MIME messages and parts", + "homepage": "https://laminas.dev", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "laminas", + "mime" ], - "time": "2019-09-17T09:54:03+00:00" + "time": "2019-12-31T17:25:27+00:00" }, { - "name": "symfony/filesystem", - "version": "v4.3.8", + "name": "laminas/laminas-modulemanager", + "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" + "url": "https://github.com/laminas/laminas-modulemanager.git", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/92b1cde1aab5aef687b863face6dd5d9c6751c78", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-modulemanager": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" + }, + "suggest": { + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\ModuleManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Modular application system for laminas-mvc applications", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "modulemanager" ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" + "time": "2019-12-31T17:26:56+00:00" }, { - "name": "symfony/finder", - "version": "v4.3.8", + "name": "laminas/laminas-mvc", + "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" + "url": "https://github.com/laminas/laminas-mvc.git", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/7e7198b03556a57fb5fd3ed919d9e1cf71500642", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642", "shasum": "" }, "require": { - "php": "^7.1.3" + "container-interop/container-interop": "^1.1", + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-mvc": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "1.7.*", + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9", + "phpunit/phpunit": "^4.8.36", + "sebastian/comparator": "^1.2.4", + "sebastian/version": "^1.0.4" + }, + "suggest": { + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Laminas\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using laminas-servicemanager v3 and requiring the laminas-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Mvc\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mvc" ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2019-10-30T12:53:54+00:00" + "time": "2019-12-31T17:32:15+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.12.0", + "name": "laminas/laminas-psr7bridge", + "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + "url": "https://github.com/laminas/laminas-psr7bridge.git", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/14780ef1d40effd59d77ab29c6d439b2af42cdfa", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5", + "psr/http-message": "^1.0" }, - "suggest": { - "ext-ctype": "For best performance" + "replace": { + "zendframework/zend-psr7bridge": "self.version" + }, + "require-dev": { + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.0-dev", + "dev-develop": "1.1-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Psr7Bridge\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "http", + "laminas", + "psr", + "psr-7" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-12-31T17:38:47+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", + "name": "laminas/laminas-serializer", + "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + "url": "https://github.com/laminas/laminas-serializer.git", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/c1c9361f114271b0736db74e0083a919081af5e0", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-serializer": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "ext-mbstring": "For best performance" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Serializer\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", + "description": "Serialize and deserialize PHP structures to a variety of representations", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "laminas", + "serializer" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-12-31T17:42:11+00:00" }, { - "name": "symfony/process", - "version": "v4.3.8", + "name": "laminas/laminas-server", + "version": "2.8.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" + "url": "https://github.com/laminas/laminas-server.git", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/4aaca9174c40a2fab2e2aa77999da99f71bdd88e", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-server": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Server\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Create Reflection-based RPC servers", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "server" ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-12-31T17:43:03+00:00" }, { - "name": "tedivm/jshrink", - "version": "v1.3.3", + "name": "laminas/laminas-servicemanager", + "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/tedious/JShrink.git", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" + "url": "https://github.com/laminas/laminas-servicemanager.git", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/841abb656c6018afebeec1f355be438426d6a3dd", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd", "shasum": "" }, "require": { - "php": "^5.6|^7.0" + "container-interop/container-interop": "~1.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-servicemanager": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.8", - "php-coveralls/php-coveralls": "^1.1.0", - "phpunit/phpunit": "^6" + "athletic/athletic": "dev-master", + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-di": "Laminas\\Di component", + "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" + } + }, "autoload": { - "psr-0": { - "JShrink": "src/" + "psr-4": { + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Robert Hafner", - "email": "tedivm@tedivm.com" - } - ], - "description": "Javascript Minifier built in PHP", - "homepage": "http://github.com/tedious/JShrink", + "homepage": "https://laminas.dev", "keywords": [ - "javascript", - "minifier" + "laminas", + "servicemanager" ], - "time": "2019-06-28T18:11:46+00:00" + "time": "2019-12-31T17:44:16+00:00" }, { - "name": "true/punycode", - "version": "v2.1.1", + "name": "laminas/laminas-session", + "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/true/php-punycode.git", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" + "url": "https://github.com/laminas/laminas-session.git", + "reference": "60b5cc844e09627d4f1a2a547e13268f376ccb3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/60b5cc844e09627d4f1a2a547e13268f376ccb3d", + "reference": "60b5cc844e09627d4f1a2a547e13268f376ccb3d", "shasum": "" }, "require": { - "php": ">=5.3.0", - "symfony/polyfill-mbstring": "^1.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-session": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.7", - "squizlabs/php_codesniffer": "~2.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "mongodb/mongodb": "^1.0.1", + "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "mongodb/mongodb": "If you want to use the MongoDB session save handler" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" + } + }, "autoload": { "psr-4": { - "TrueBV\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Renan Gonçalves", - "email": "renan.saddam@gmail.com" - } + "BSD-3-Clause" ], - "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", - "homepage": "https://github.com/true/php-punycode", + "description": "Object-oriented interface to PHP sessions and storage", + "homepage": "https://laminas.dev", "keywords": [ - "idna", - "punycode" + "laminas", + "session" ], - "time": "2016-11-16T10:37:54+00:00" + "time": "2019-12-31T17:46:59+00:00" }, { - "name": "tubalmartin/cssmin", - "version": "v4.1.1", + "name": "laminas/laminas-soap", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" + "url": "https://github.com/laminas/laminas-soap.git", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", "shasum": "" }, "require": { - "ext-pcre": "*", - "php": ">=5.3.2" + "ext-soap": "*", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-soap": "self.version" }, "require-dev": { - "cogpowered/finediff": "0.3.*", - "phpunit/phpunit": "4.8.*" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4", + "phpunit/phpunit": "^5.7.21 || ^6.3" + }, + "suggest": { + "laminas/laminas-http": "Laminas\\Http component" }, - "bin": [ - "cssmin" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" + } + }, "autoload": { "psr-4": { - "tubalmartin\\CssMin\\": "src" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Túbal Martín", - "homepage": "http://tubalmartin.me/" - } - ], - "description": "A PHP port of the YUI CSS compressor", - "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", + "homepage": "https://laminas.dev", "keywords": [ - "compress", - "compressor", - "css", - "cssmin", - "minify", - "yui" + "laminas", + "soap" ], - "time": "2018-01-15T15:26:51+00:00" + "time": "2019-12-31T17:48:49+00:00" }, { - "name": "webonyx/graphql-php", - "version": "v0.13.8", + "name": "laminas/laminas-stdlib", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1||^8.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpbench/phpbench": "^0.14.0", - "phpstan/phpstan": "^0.11.4", - "phpstan/phpstan-phpunit": "^0.11.0", - "phpstan/phpstan-strict-rules": "^0.11.0", - "phpunit/phpcov": "^5.0", - "phpunit/phpunit": "^7.2", - "psr/http-message": "^1.0", - "react/promise": "2.*" + "replace": { + "zendframework/zend-stdlib": "self.version" }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, "autoload": { "psr-4": { - "GraphQL\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "api", - "graphql" + "laminas", + "stdlib" ], - "time": "2019-08-25T10:32:47+00:00" + "time": "2019-12-31T17:51:15+00:00" }, { - "name": "wikimedia/less.php", - "version": "1.8.2", + "name": "laminas/laminas-text", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/wikimedia/less.php.git", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266" + "url": "https://github.com/laminas/laminas-text.git", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/3601b5eacb06ed0a12f658df860cc0f9613cf4db", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db", "shasum": "" }, "require": { - "php": ">=7.2.9" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-text": "self.version" }, "require-dev": { - "phpunit/phpunit": "7.5.14" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, - "bin": [ - "bin/lessc" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" + } + }, "autoload": { - "psr-0": { - "Less": "lib/" - }, - "classmap": [ - "lessc.inc.php" - ] + "psr-4": { + "Laminas\\Text\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" - }, - { - "name": "Matt Agar", - "homepage": "https://github.com/agar" - }, - { - "name": "Martin Jantošovič", - "homepage": "https://github.com/Mordred" - } + "BSD-3-Clause" ], - "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", + "description": "Create FIGlets and text-based tables", + "homepage": "https://laminas.dev", "keywords": [ - "css", - "less", - "less.js", - "lesscss", - "php", - "stylesheet" + "laminas", + "text" ], - "time": "2019-11-06T18:30:11+00:00" + "time": "2019-12-31T17:54:52+00:00" }, { - "name": "zendframework/zend-captcha", - "version": "2.9.0", + "name": "laminas/laminas-uri", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3" + "url": "https://github.com/laminas/laminas-uri.git", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/6be8ce19622f359b048ce4faebf1aa1bca73a7ff", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "replace": { + "zendframework/zend-uri": "self.version" }, - "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://laminas.dev", "keywords": [ - "ZendFramework", - "captcha", - "zf" + "laminas", + "uri" ], - "time": "2019-06-18T09:32:52+00:00" + "time": "2019-12-31T17:56:00+00:00" }, { - "name": "zendframework/zend-code", - "version": "3.3.2", + "name": "laminas/laminas-validator", + "version": "2.12.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b" + "url": "https://github.com/laminas/laminas-validator.git", + "reference": "0813f234812d9fa9058b6da39eb13dedc90227db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/936fa7ad4d53897ea3e3eb41b5b760828246a20b", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/0813f234812d9fa9058b6da39eb13dedc90227db", + "reference": "0813f234812d9fa9058b6da39eb13dedc90227db", "shasum": "" }, "require": { - "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "doctrine/annotations": "^1.0", - "ext-phar": "*", - "phpunit/phpunit": "^7.5.15", - "zendframework/zend-coding-standard": "^1.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "replace": { + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "psr/http-message": "^1.0" }, "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3.x-dev", - "dev-develop": "3.4.x-dev" + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + }, + "laminas": { + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "homepage": "https://laminas.dev", "keywords": [ - "ZendFramework", - "code", - "zf" + "laminas", + "validator" ], - "time": "2019-08-31T14:14:34+00:00" + "time": "2019-12-31T17:57:44+00:00" }, { - "name": "zendframework/zend-config", - "version": "2.6.0", + "name": "laminas/laminas-view", + "version": "2.11.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "url": "https://github.com/laminas/laminas-view.git", + "reference": "f2f321d45f91742ab9417997d705073a34d60db7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/f2f321d45f91742ab9417997d705073a34d60db7", + "reference": "f2f321d45f91742ab9417997d705073a34d60db7", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1 || ^3.0", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "replace": { + "zendframework/zend-view": "self.version" + }, + "require-dev": { + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^5.7.15 || ^6.0.8" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "laminas-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with laminas-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, + "bin": [ + "bin/templatemap_generator.php" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "config", - "zf2" + "laminas", + "view" ], - "time": "2016-02-04T23:01:10+00:00" + "time": "2019-12-31T18:03:25+00:00" }, { - "name": "zendframework/zend-console", - "version": "2.8.0", + "name": "laminas/laminas-zendframework-bridge", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae" + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/95817ae78f73c48026972e350a2ecc31c6d9f9ae", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/0fb9675b84a1666ab45182b6c5b29956921e818d", + "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.2.1" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" + }, + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Build console applications using getopt syntax or routing, complete with prompts", + "description": "Alias legacy ZF class names to Laminas Project equivalents.", "keywords": [ "ZendFramework", - "console", + "autoloading", + "laminas", "zf" ], - "time": "2019-02-04T19:48:22+00:00" + "time": "2020-01-07T22:58:31+00:00" }, { - "name": "zendframework/zend-crypt", - "version": "2.6.0", + "name": "magento/composer", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" + "url": "https://github.com/magento/composer.git", + "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/magento/composer/zipball/ea12b95be5c0833b3d9497aaefa08816c19e5dcd", + "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "composer/composer": "^1.6", + "php": "~7.1.3||~7.2.0||~7.3.0", + "symfony/console": "~4.0.0 || ~4.1.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "phpunit/phpunit": "~7.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Magento\\Composer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-crypt", - "keywords": [ - "crypt", - "zf2" + "OSL-3.0", + "AFL-3.0" ], - "time": "2016-02-03T23:46:30+00:00" + "description": "Magento composer library helps to instantiate Composer application and run composer commands.", + "time": "2019-07-29T19:52:05+00:00" }, { - "name": "zendframework/zend-db", - "version": "2.10.0", + "name": "magento/magento-composer-installer", + "version": "0.1.13", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", - "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" + "url": "https://github.com/magento/magento-composer-installer.git", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", - "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "composer-plugin-api": "^1.0" }, - "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "replace": { + "magento-hackathon/magento-composer-installer": "*" }, - "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "require-dev": { + "composer/composer": "*@dev", + "firegento/phpcs": "dev-patch-1", + "mikey179/vfsstream": "*", + "phpunit/phpunit": "*", + "phpunit/phpunit-mock-objects": "dev-master", + "squizlabs/php_codesniffer": "1.4.7", + "symfony/process": "*" }, - "type": "library", + "type": "composer-plugin", "extra": { - "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "2.10-dev" - }, - "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" - } + "composer-command-registry": [ + "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" + ], + "class": "MagentoHackathon\\Composer\\Magento\\Plugin" }, "autoload": { - "psr-4": { - "Zend\\Db\\": "src/" + "psr-0": { + "MagentoHackathon\\Composer\\Magento": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "OSL-3.0" ], - "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "authors": [ + { + "name": "Vinai Kopp", + "email": "vinai@netzarbeiter.com" + }, + { + "name": "Daniel Fahlke aka Flyingmana", + "email": "flyingmana@googlemail.com" + }, + { + "name": "Jörg Weller", + "email": "weller@flagbit.de" + }, + { + "name": "Karl Spies", + "email": "karl.spies@gmx.net" + }, + { + "name": "Tobias Vogt", + "email": "tobi@webguys.de" + }, + { + "name": "David Fuhr", + "email": "fuhr@flagbit.de" + } + ], + "description": "Composer installer for Magento modules", + "homepage": "https://github.com/magento/magento-composer-installer", "keywords": [ - "ZendFramework", - "db", - "zf" + "composer-installer", + "magento" ], - "time": "2019-02-25T11:37:45+00:00" + "time": "2017-12-29T16:45:24+00:00" }, { - "name": "zendframework/zend-di", - "version": "2.6.1", + "name": "magento/zendframework1", + "version": "1.14.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" + "url": "https://github.com/magento/zf1.git", + "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/magento/zf1/zipball/8221062d42a198e431d183bbe672e5e1a2f98c5f", + "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.2.11" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/dbunit": "1.3.*", + "phpunit/phpunit": "3.7.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "1.12.x-dev" } }, "autoload": { - "psr-4": { - "Zend\\Di\\": "src/" + "psr-0": { + "Zend_": "library/" } }, "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "library/" + ], "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "description": "Magento Zend Framework 1", + "homepage": "http://framework.zend.com/", "keywords": [ - "di", - "zf2" + "ZF1", + "framework" ], - "time": "2016-04-25T20:58:11+00:00" + "time": "2019-07-26T16:43:11+00:00" }, { - "name": "zendframework/zend-diactoros", - "version": "1.8.7", + "name": "monolog/monolog", + "version": "1.25.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/a85e67b86e9b8520d07e6415fcbcb8391b44a75b", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0" + "php": ">=5.3.0", + "psr/log": "~1.0" }, "provide": { - "psr/http-message-implementation": "1.0" + "psr/log-implementation": "1.0.0" }, "require-dev": { - "ext-dom": "*", - "ext-libxml": "*", - "php-http/psr7-integration-tests": "dev-master", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { - "dev-release-1.8": "1.8.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" - ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], - "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", "keywords": [ - "http", - "psr", - "psr-7" + "log", + "logging", + "psr-3" ], - "time": "2019-08-06T17:53:53+00:00" + "time": "2019-11-13T10:00:05+00:00" }, { - "name": "zendframework/zend-escaper", - "version": "2.6.1", + "name": "paragonie/random_compat", + "version": "v9.99.99", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" - } + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, - "autoload": { - "psr-4": { - "Zend\\Escaper\\": "src/" - } + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, + "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", - "keywords": [ - "ZendFramework", - "escaper", - "zf" + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" ], - "time": "2019-09-05T20:03:20+00:00" + "time": "2018-07-02T15:55:56+00:00" }, { - "name": "zendframework/zend-eventmanager", - "version": "3.2.1", + "name": "paragonie/sodium_compat", + "version": "v1.12.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd" + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "063cae9b3a7323579063e7037720f5b52b56c178" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/063cae9b3a7323579063e7037720f5b52b56c178", + "reference": "063cae9b3a7323579063e7037720f5b52b56c178", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + "phpunit/phpunit": "^3|^4|^5|^6|^7" }, "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\EventManager\\": "src/" - } + "files": [ + "autoload.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "ISC" ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://github.com/zendframework/zend-eventmanager", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", "keywords": [ - "event", - "eventmanager", - "events", - "zf2" + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" ], - "time": "2018-04-25T15:33:34+00:00" + "time": "2019-11-07T17:07:24+00:00" }, { - "name": "zendframework/zend-feed", - "version": "2.12.0", + "name": "pelago/emogrifier", + "version": "v2.2.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733" + "url": "https://github.com/MyIntervals/emogrifier.git", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/d926c5af34b93a0121d5e2641af34ddb1533d733", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733", + "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^3.2.1" + "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", + "symfony/css-selector": "^3.4.0 || ^4.0.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "friendsofphp/php-cs-fixer": "^2.2.0", + "phpmd/phpmd": "^2.6.0", + "phpunit/phpunit": "^4.8.0", + "squizlabs/php_codesniffer": "^3.3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Pelago\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides functionality for consuming RSS and Atom feeds", + "authors": [ + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Zoli Szabó", + "email": "zoli.szabo+github@gmail.com" + }, + { + "name": "John Reeve", + "email": "jreeve@pelagodesign.com" + }, + { + "name": "Jake Hotson", + "email": "jake@qzdesign.co.uk" + }, + { + "name": "Cameron Brooks" + }, + { + "name": "Jaime Prado" + } + ], + "description": "Converts CSS styles into inline style attributes in your HTML code", + "homepage": "https://www.myintervals.com/emogrifier.php", "keywords": [ - "ZendFramework", - "feed", - "zf" + "css", + "email", + "pre-processing" ], - "time": "2019-03-05T20:08:49+00:00" + "time": "2019-09-04T16:07:59+00:00" }, { - "name": "zendframework/zend-filter", - "version": "2.9.2", + "name": "php-amqplib/php-amqplib", + "version": "v2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef" + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/d78f2cdde1c31975e18b2a0753381ed7b61118ef", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "ext-bcmath": "*", + "ext-sockets": "*", + "php": ">=5.6" }, - "conflict": { - "zendframework/zend-validator": "<2.10.1" + "replace": { + "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "pear/archive_tar": "^1.4.3", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" - }, - "suggest": { - "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "dev-master": "2.10-dev" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "PhpAmqpLib\\": "PhpAmqpLib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "LGPL-2.1-or-later" ], - "description": "Programmatically filter and normalize data and files", + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "John Kelly", + "email": "johnmkelly86@gmail.com", + "role": "Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" + } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", "keywords": [ - "ZendFramework", - "filter", - "zf" + "message", + "queue", + "rabbitmq" ], - "time": "2019-08-19T07:08:04+00:00" + "time": "2019-10-10T13:23:40+00:00" }, { - "name": "zendframework/zend-form", - "version": "2.14.3", + "name": "phpseclib/mcrypt_compat", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871" + "url": "https://github.com/phpseclib/mcrypt_compat.git", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/0b1616c59b1f3df194284e26f98c81ad0c377871", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871", + "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^3.2.1" + "php": ">=5.3.3", + "phpseclib/phpseclib": ">=2.0.11 <3.0.0" }, "require-dev": { - "doctrine/annotations": "~1.0", - "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "phpunit/phpunit": "^4.8.35|^5.7|^6.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "ext-openssl": "Will enable faster cryptographic operations" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.14.x-dev", - "dev-develop": "2.15.x-dev" - }, - "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" - } - }, "autoload": { - "psr-4": { - "Zend\\Form\\": "src/" - }, "files": [ - "autoload/formElementManagerPolyfill.php" + "lib/mcrypt.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "homepage": "http://phpseclib.sourceforge.net" + } + ], + "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", "keywords": [ - "ZendFramework", - "form", - "zf" + "cryptograpy", + "encryption", + "mcrypt" ], - "time": "2019-10-04T10:46:36+00:00" + "time": "2018-08-22T03:11:43+00:00" }, { - "name": "zendframework/zend-http", - "version": "2.10.0", + "name": "phpseclib/phpseclib", + "version": "2.0.23", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0" + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/4b4983178693a8fdda53b0bbee58552e2d2b1ac0", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c78eb5058d5bb1a183133c36d4ba5b6675dfa099", + "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "sami/sami": "~2.0", + "squizlabs/php_codesniffer": "~2.0" }, "suggest": { - "paragonie/certainty": "For automated management of cacert.pem" + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - } - }, "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { - "Zend\\Http\\": "src/" + "phpseclib\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", "keywords": [ - "ZendFramework", - "http", - "http client", - "zend", - "zf" + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" ], - "time": "2019-02-19T18:58:14+00:00" + "time": "2019-09-17T03:41:22+00:00" }, { - "name": "zendframework/zend-hydrator", - "version": "2.4.2", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/2bfc6845019e7b6d38b0ab5e55190244dc510285", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-release-2.4": "2.4.x-dev" - }, - "zf": { - "component": "Zend\\Hydrator", - "config-provider": "Zend\\Hydrator\\ConfigProvider" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize objects to arrays, and vice versa", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "ZendFramework", - "hydrator", - "zf" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "time": "2019-10-04T11:17:36+00:00" + "time": "2017-02-14T16:28:37+00:00" }, { - "name": "zendframework/zend-i18n", - "version": "2.9.2", + "name": "psr/http-message", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07" + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/e17a54b3aee333ab156958f570cde630acee8b07", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" - }, - "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provide translations for your application, and filter and validate internationalized values", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "ZendFramework", - "i18n", - "zf" + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" ], - "time": "2019-09-30T12:04:37+00:00" + "time": "2016-08-06T14:39:51+00:00" }, { - "name": "zendframework/zend-inputfilter", - "version": "2.10.1", + "name": "psr/log", + "version": "1.1.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e" + "url": "https://github.com/php-fig/log.git", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/1f44a2e9bc394a71638b43bc7024b572fa65410e", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e", + "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" - }, - "suggest": { - "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "ZendFramework", - "inputfilter", - "zf" + "log", + "psr", + "psr-3" ], - "time": "2019-08-28T19:45:32+00:00" + "time": "2019-11-01T11:05:21+00:00" }, { - "name": "zendframework/zend-json", - "version": "2.6.1", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", - "zendframework/zendxml": "^1.0.2" - }, - "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\Json\\": "src/" - } + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", - "keywords": [ - "json", - "zf2" + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } ], - "time": "2016-02-04T21:20:26+00:00" + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" }, { - "name": "zendframework/zend-loader", - "version": "2.6.1", + "name": "ramsey/uuid", + "version": "3.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", - "reference": "91da574d29b58547385b2298c020b257310898c6" + "url": "https://github.com/ramsey/uuid.git", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/91da574d29b58547385b2298c020b257310898c6", - "reference": "91da574d29b58547385b2298c020b257310898c6", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "paragonie/random_compat": "^1.0|^2.0|9.99.99", + "php": "^5.4 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "replace": { + "rhumsaa/uuid": "self.version" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "codeception/aspect-mock": "^1.0 | ~2.0.0", + "doctrine/annotations": "~1.2.0", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "mockery/mockery": "^0.9.9", + "moontoast/math": "^1.1", + "php-mock/php-mock-phpunit": "^0.3|^1.1", + "phpunit/phpunit": "^4.7|^5.0|^6.5", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "ext-ctype": "Provides support for PHP Ctype functions", + "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", + "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "branch-alias": { + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Ramsey\\Uuid\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Autoloading and plugin loading strategies", + "authors": [ + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + }, + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", "keywords": [ - "ZendFramework", - "loader", - "zf" + "guid", + "identifier", + "uuid" ], - "time": "2019-09-04T19:38:14+00:00" + "time": "2018-07-19T23:38:55+00:00" }, { - "name": "zendframework/zend-log", - "version": "2.11.0", + "name": "react/promise", + "version": "v2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", - "reference": "cb278772afdacb1924342248a069330977625ae6" + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/cb278772afdacb1924342248a069330977625ae6", - "reference": "cb278772afdacb1924342248a069330977625ae6", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" + "php": ">=5.4.0" }, "require-dev": { - "mikey179/vfsstream": "^1.6.7", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "ext-mongo": "mongo extension to use Mongo writer", - "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "phpunit/phpunit": "~4.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - }, - "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" - } - }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" - } + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "ZendFramework", - "log", - "logging", - "zf" + "promise", + "promises" ], - "time": "2019-08-23T21:28:18+00:00" + "time": "2019-01-07T21:25:54+00:00" }, { - "name": "zendframework/zend-mail", - "version": "2.10.0", + "name": "seld/jsonlint", + "version": "1.7.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": "^5.6 || ^7.0", - "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "php": "^5.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" - }, - "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, + "bin": [ + "bin/jsonlint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" - } - }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", "keywords": [ - "ZendFramework", - "mail", - "zf" + "json", + "linter", + "parser", + "validator" ], - "time": "2018-06-07T13:37:07+00:00" + "time": "2019-10-24T14:27:39+00:00" }, { - "name": "zendframework/zend-math", - "version": "2.7.1", + "name": "seld/phar-utils", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "ircmaxell/random-lib": "~1.1", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-bcmath": "If using the bcmath functionality", - "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Seld\\PharUtils\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } ], - "homepage": "https://github.com/zendframework/zend-math", + "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "math", - "zf2" + "phra" ], - "time": "2018-12-04T15:34:17+00:00" + "time": "2015-10-13T18:44:15+00:00" }, { - "name": "zendframework/zend-mime", - "version": "2.7.2", + "name": "symfony/console", + "version": "v4.1.12", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1" + "url": "https://github.com/symfony/console.git", + "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/c91e0350be53cc9d29be15563445eec3b269d7c1", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1", + "url": "https://api.github.com/repos/symfony/console/zipball/9e87c798f67dc9fceeb4f3d57847b52d945d1a02", + "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "4.1-dev" } }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" - } + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create and parse MIME messages and parts", - "keywords": [ - "ZendFramework", - "mime", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-16T19:30:37+00:00" + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2019-01-25T14:34:37+00:00" }, { - "name": "zendframework/zend-modulemanager", - "version": "2.8.4", + "name": "symfony/css-selector", + "version": "v4.3.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" + "url": "https://github.com/symfony/css-selector.git", + "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", + "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" - }, - "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "4.3-dev" } }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" - } + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Modular application system for zend-mvc applications", - "keywords": [ - "ZendFramework", - "modulemanager", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-28T13:29:38+00:00" + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2019-10-02T08:36:26+00:00" }, { - "name": "zendframework/zend-mvc", - "version": "2.7.15", + "name": "symfony/event-dispatcher", + "version": "v4.3.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0df002fd4f500392eabd243c2947061a50937287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", + "reference": "0df002fd4f500392eabd243c2947061a50937287", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "php": "^7.1.3", + "symfony/event-dispatcher-contracts": "^1.1" }, - "replace": { - "zendframework/zend-router": "^2.0" + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "1.1" }, "require-dev": { - "friendsofphp/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", - "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/http-foundation": "^3.4|^4.0", + "symfony/service-contracts": "^1.1", + "symfony/stopwatch": "~3.4|~4.0" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "4.3-dev" } }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { - "Zend\\Mvc\\": "src/" - } + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-mvc", - "keywords": [ - "mvc", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2018-05-03T13:13:41+00:00" + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2019-11-03T09:04:05+00:00" }, { - "name": "zendframework/zend-psr7bridge", - "version": "0.2.2", + "name": "symfony/event-dispatcher-contracts", + "version": "v1.1.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "shasum": "" }, "require": { - "php": ">=5.5", - "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "php": "^7.1.3" }, - "require-dev": { - "phpunit/phpunit": "^4.7", - "squizlabs/php_codesniffer": "^2.3" + "suggest": { + "psr/event-dispatcher": "", + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", "keywords": [ - "http", - "psr", - "psr-7" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "time": "2016-05-10T21:44:39+00:00" + "time": "2019-09-17T09:54:03+00:00" }, { - "name": "zendframework/zend-serializer", - "version": "2.9.1", + "name": "symfony/filesystem", + "version": "v4.3.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21" + "url": "https://github.com/symfony/filesystem.git", + "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "dev-master": "4.3-dev" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" - } + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize and deserialize PHP structures to a variety of representations", - "keywords": [ - "ZendFramework", - "serializer", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-19T08:06:30+00:00" + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2019-08-20T14:07:54+00:00" }, { - "name": "zendframework/zend-server", - "version": "2.8.1", + "name": "symfony/finder", + "version": "v4.3.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1" + "url": "https://github.com/symfony/finder.git", + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/d80c44700ebb92191dd9a3005316a6ab6637c0d1", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1", + "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "4.3-dev" } }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" - } + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create Reflection-based RPC servers", - "keywords": [ - "ZendFramework", - "server", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-16T18:27:05+00:00" + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2019-10-30T12:53:54+00:00" }, { - "name": "zendframework/zend-servicemanager", - "version": "2.7.11", + "name": "symfony/polyfill-ctype", + "version": "v1.12.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "athletic/athletic": "dev-master", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "php": ">=5.3.3" }, "suggest": { - "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" - } + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "servicemanager", - "zf2" + "compatibility", + "ctype", + "polyfill", + "portable" ], - "time": "2018-06-22T14:49:54+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { - "name": "zendframework/zend-session", - "version": "2.9.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.12.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "container-interop/container-interop": "^1.1", - "mongodb/mongodb": "^1.0.1", - "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "php": ">=5.3.3" }, "suggest": { - "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" - } + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Object-oriented interface to PHP sessions and storage", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "session", - "zf" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], - "time": "2019-10-28T19:40:43+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { - "name": "zendframework/zend-soap", - "version": "2.8.0", + "name": "symfony/process", + "version": "v4.3.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", - "reference": "8762d79efa220d82529c43ce08d70554146be645" + "url": "https://github.com/symfony/process.git", + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/8762d79efa220d82529c43ce08d70554146be645", - "reference": "8762d79efa220d82529c43ce08d70554146be645", + "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", "shasum": "" }, "require": { - "ext-soap": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" - }, - "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "4.3-dev" } }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" - } + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-soap", - "keywords": [ - "soap", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-04-30T16:45:35+00:00" + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2019-10-28T17:07:32+00:00" }, { - "name": "zendframework/zend-stdlib", - "version": "3.2.1", + "name": "tedivm/jshrink", + "version": "v1.3.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "66536006722aff9e62d1b331025089b7ec71c065" + "url": "https://github.com/tedious/JShrink.git", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", - "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6|^7.0" }, "require-dev": { - "phpbench/phpbench": "^0.13", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "friendsofphp/php-cs-fixer": "^2.8", + "php-coveralls/php-coveralls": "^1.1.0", + "phpunit/phpunit": "^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\Stdlib\\": "src/" + "psr-0": { + "JShrink": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "SPL extensions, array utilities, error handlers, and more", + "authors": [ + { + "name": "Robert Hafner", + "email": "tedivm@tedivm.com" + } + ], + "description": "Javascript Minifier built in PHP", + "homepage": "http://github.com/tedious/JShrink", "keywords": [ - "ZendFramework", - "stdlib", - "zf" + "javascript", + "minifier" ], - "time": "2018-08-28T21:34:05+00:00" + "time": "2019-06-28T18:11:46+00:00" }, { - "name": "zendframework/zend-text", - "version": "2.7.1", + "name": "true/punycode", + "version": "v2.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d" + "url": "https://github.com/true/php-punycode.git", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/41e32dafa4015e160e2f95a7039554385c71624d", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d", + "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.3.0", + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "phpunit/phpunit": "~4.7", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "TrueBV\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create FIGlets and text-based tables", + "authors": [ + { + "name": "Renan Gonçalves", + "email": "renan.saddam@gmail.com" + } + ], + "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", + "homepage": "https://github.com/true/php-punycode", "keywords": [ - "ZendFramework", - "text", - "zf" + "idna", + "punycode" ], - "time": "2019-10-16T20:36:27+00:00" + "time": "2016-11-16T10:37:54+00:00" }, { - "name": "zendframework/zend-uri", - "version": "2.7.1", + "name": "tubalmartin/cssmin", + "version": "v4.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083" + "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bfc4a5b9a309711e968d7c72afae4ac50c650083", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083", + "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "ext-pcre": "*", + "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "cogpowered/finediff": "0.3.*", + "phpunit/phpunit": "4.8.*" }, + "bin": [ + "cssmin" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "tubalmartin\\CssMin\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "authors": [ + { + "name": "Túbal Martín", + "homepage": "http://tubalmartin.me/" + } + ], + "description": "A PHP port of the YUI CSS compressor", + "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", "keywords": [ - "ZendFramework", - "uri", - "zf" + "compress", + "compressor", + "css", + "cssmin", + "minify", + "yui" ], - "time": "2019-10-07T13:35:33+00:00" + "time": "2018-01-15T15:26:51+00:00" }, { - "name": "zendframework/zend-validator", - "version": "2.12.2", + "name": "webonyx/graphql-php", + "version": "v0.13.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62" + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/fd24920c2afcf2a70d11f67c3457f8f509453a62", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.2.1" + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1||^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "doctrine/coding-standard": "^6.0", + "phpbench/phpbench": "^0.14.0", + "phpstan/phpstan": "^0.11.4", + "phpstan/phpstan-phpunit": "^0.11.0", + "phpstan/phpstan-strict-rules": "^0.11.0", + "phpunit/phpcov": "^5.0", + "phpunit/phpunit": "^7.2", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "react/promise": "2.*" }, "suggest": { - "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" - }, - "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" - } - }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "GraphQL\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", "keywords": [ - "ZendFramework", - "validator", - "zf" + "api", + "graphql" ], - "time": "2019-10-29T08:33:25+00:00" + "time": "2019-08-25T10:32:47+00:00" }, { - "name": "zendframework/zend-view", - "version": "2.11.3", + "name": "wikimedia/less.php", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5" + "url": "https://github.com/wikimedia/less.php.git", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e766457bd6ce13c5354e443bb949511b6904d7f5", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1 || ^3.0", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "php": ">=7.2.9" }, - "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "require-dev": { + "phpunit/phpunit": "7.5.14" }, "bin": [ - "bin/templatemap_generator.php" + "bin/lessc" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\View\\": "src/" - } + "psr-0": { + "Less": "lib/" + }, + "classmap": [ + "lessc.inc.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "Apache-2.0" ], - "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, + { + "name": "Matt Agar", + "homepage": "https://github.com/agar" + }, + { + "name": "Martin Jantošovič", + "homepage": "https://github.com/Mordred" + } + ], + "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", "keywords": [ - "ZendFramework", - "view", - "zf" + "css", + "less", + "less.js", + "lesscss", + "php", + "stylesheet" ], - "time": "2019-10-11T21:10:04+00:00" + "time": "2019-11-06T18:30:11+00:00" } ], "packages-dev": [ @@ -6486,6 +6734,7 @@ "selenium", "webdriver" ], + "abandoned": "php-webdriver/webdriver", "time": "2019-06-13T08:02:18+00:00" }, { diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php index 60dd6b57bda86..2d7fbae640ef1 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php @@ -10,7 +10,7 @@ use Magento\TestFramework\Authentication\Rest\OauthClient; use Magento\TestFramework\Helper\Bootstrap; use OAuth\Common\Consumer\Credentials; -use Zend\Stdlib\Exception\LogicException; +use Laminas\Stdlib\Exception\LogicException; use Magento\Integration\Model\Integration; class OauthHelper @@ -170,7 +170,7 @@ protected static function _rmRecursive($dir, $doSaveRoot = false) * * @param array $resources * @return \Magento\Integration\Model\Integration - * @throws \Zend\Stdlib\Exception\LogicException + * @throws \Laminas\Stdlib\Exception\LogicException */ protected static function _createIntegration($resources) { diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php index 8453edb071b3e..01c48c8410f5a 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php @@ -19,7 +19,7 @@ class Soap implements \Magento\TestFramework\TestCase\Webapi\AdapterInterface /** * SOAP client initialized with different WSDLs. * - * @var \Zend\Soap\Client[] + * @var \Laminas\Soap\Client[] */ protected $_soapClients = ['custom' => [], 'default' => []]; @@ -67,7 +67,7 @@ public function call($serviceInfo, $arguments = [], $storeCode = null, $integrat * * @param string $serviceInfo PHP service interface name, should include version if present * @param string|null $storeCode - * @return \Zend\Soap\Client + * @return \Laminas\Soap\Client */ protected function _getSoapClient($serviceInfo, $storeCode = null) { @@ -75,7 +75,7 @@ protected function _getSoapClient($serviceInfo, $storeCode = null) [$this->_getSoapServiceName($serviceInfo) . $this->_getSoapServiceVersion($serviceInfo)], $storeCode ); - /** @var \Zend\Soap\Client $soapClient */ + /** @var \Laminas\Soap\Client $soapClient */ $soapClient = null; if (isset($serviceInfo['soap']['token'])) { $token = $serviceInfo['soap']['token']; @@ -104,7 +104,7 @@ protected function _getSoapClient($serviceInfo, $storeCode = null) * * @param string $wsdlUrl * @param string $token Authentication token - * @return \Zend\Soap\Client + * @return \Laminas\Soap\Client */ public function instantiateSoapClient($wsdlUrl, $token = null) { @@ -113,7 +113,7 @@ public function instantiateSoapClient($wsdlUrl, $token = null) : \Magento\TestFramework\Authentication\OauthHelper::getApiAccessCredentials()['key']; $opts = ['http' => ['header' => "Authorization: Bearer " . $accessCredentials]]; $context = stream_context_create($opts); - $soapClient = new \Zend\Soap\Client($wsdlUrl); + $soapClient = new \Laminas\Soap\Client($wsdlUrl); $soapClient->setSoapVersion(SOAP_1_2); $soapClient->setStreamContext($context); if (TESTS_XDEBUG_ENABLED) { diff --git a/dev/tests/integration/framework/Magento/TestFramework/Request.php b/dev/tests/integration/framework/Magento/TestFramework/Request.php index 756badb0f333f..ede2f5a54bf05 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Request.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Request.php @@ -5,7 +5,7 @@ */ namespace Magento\TestFramework; -use \Zend\Stdlib\ParametersInterface; +use \Laminas\Stdlib\ParametersInterface; /** * HTTP request implementation that is used instead core one for testing diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php index a5c9a281c3ffd..6b853aebd41fa 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Test; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class RequestTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php index 21ffddf851ac4..68a9bc4d21656 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php @@ -27,7 +27,7 @@ use Magento\TestFramework\Bootstrap as TestBootstrap; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\Response\Http as HttpResponse; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\Backend\Model\UrlInterface as BackendUrl; use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php index eae831743f9cd..9257130cea121 100644 --- a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php @@ -9,7 +9,7 @@ use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\TestCase\AbstractController; use Magento\Vault\Model\CustomerTokenManagement; -use Zend\Http\Request; +use Laminas\Http\Request; /** * Class DeleteActionTest diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php index 32b7df03f922d..5bebfed06f322 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Test cases for catalog advanced search using mysql search engine. diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index df4acf3acca91..e12b3c4ed85a3 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -29,7 +29,7 @@ use Magento\TestFramework\Response; use Magento\Theme\Controller\Result\MessagePlugin; use PHPUnit\Framework\Constraint\StringContains; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -832,14 +832,14 @@ public function testConfirmationEmailWithSpecialCharacters(): void $message = $this->transportBuilderMock->getSentMessage(); $rawMessage = $message->getRawMessage(); - /** @var \Zend\Mime\Part $messageBodyPart */ + /** @var \Laminas\Mime\Part $messageBodyPart */ $messageBodyParts = $message->getBody()->getParts(); $messageBodyPart = reset($messageBodyParts); $messageEncoding = $messageBodyPart->getCharset(); $name = 'John Smith'; if (strtoupper($messageEncoding) !== 'ASCII') { - $name = \Zend\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); + $name = \Laminas\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); } $nameEmail = sprintf('%s <%s>', $name, $email); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index d76c520ade3b1..4dca86223ed96 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -398,7 +398,7 @@ public function testDeleteAction() $this->getRequest()->setParam('id', 1); $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); + $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_POST); $this->dispatch('backend/customer/index/delete'); $this->assertRedirect($this->stringContains('customer/index')); @@ -416,7 +416,7 @@ public function testNotExistingCustomerDeleteAction() $this->getRequest()->setParam('id', 2); $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); + $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_POST); $this->dispatch('backend/customer/index/delete'); $this->assertRedirect($this->stringContains('customer/index')); diff --git a/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php b/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php index f62773a68c144..e2c98eb4aef5f 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Developer\Helper; -use \Zend\Stdlib\Parameters; +use \Laminas\Stdlib\Parameters; class DataTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php index 64ff52ff4ec4d..c18b1ecfa4868 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\App; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class AreaTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php index 9ac778da91f29..d7b492bf5153c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -12,7 +12,7 @@ use Magento\Framework\App\Response\Http\FileFactory; use Magento\Framework\Filesystem; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Http\Header\ContentType; +use Laminas\Http\Header\ContentType; /** * Class CreatePdfFileTest diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php index 9246be52f41bf..7e5edf361e03a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php @@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\Framework\App\Response\Http as HttpResponse; use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php index 8183a5878ba85..98f56984a750d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php @@ -36,7 +36,7 @@ protected function assertHeaderPresent($name, $value) $this->interceptedResponse->sendResponse(); $header = $this->interceptedResponse->getHeader($name); - $this->assertTrue(is_subclass_of($header, \Zend\Http\Header\HeaderInterface::class, false)); + $this->assertTrue(is_subclass_of($header, \Laminas\Http\Header\HeaderInterface::class, false)); $this->assertSame( $value, $header->getFieldValue() diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php index 01e63126e5cde..27792e092d6b8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\GeneratorTest; -use Zend\Code\Generator\DocBlockGenerator; +use Laminas\Code\Generator\DocBlockGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class ParentClassWithNamespace /** * Public parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function publicParentMethod( /** * Protected parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -55,7 +55,7 @@ protected function _protectedParentMethod( /** * Private parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php index b9fc351ff64e6..0bc86f36a6357 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\GeneratorTest; -use Zend\Code\Generator\ClassGenerator; +use Laminas\Code\Generator\ClassGenerator; /** * phpcs:ignoreFile @@ -28,7 +28,7 @@ public function __construct($param1 = '', $param2 = '\\', $param3 = '\'') /** * Public child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -49,7 +49,7 @@ public function publicChildMethod( /** * Public child method with reference * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param array $array * @@ -62,7 +62,7 @@ public function publicMethodWithReference(ClassGenerator &$classGenerator, &$par /** * Protected child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -80,7 +80,7 @@ protected function _protectedChildMethod( /** * Private child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample index 83191f2d1b099..74c1522fa41f0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample @@ -18,7 +18,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicChildMethod(\Zend\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicChildMethod(\Laminas\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicChildMethod'); if (!$pluginInfo) { @@ -31,7 +31,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicMethodWithReference(\Zend\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) + public function publicMethodWithReference(\Laminas\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicMethodWithReference'); if (!$pluginInfo) { @@ -96,7 +96,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicParentMethod(\Zend\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicParentMethod(\Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicParentMethod'); if (!$pluginInfo) { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample index 359854f2d481c..42f766c786c0b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample @@ -93,7 +93,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicChildMethod(\Zend\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicChildMethod(\Laminas\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { return $this->_getSubject()->publicChildMethod($classGenerator, $param1, $param2, $param3, $array); } @@ -101,7 +101,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicMethodWithReference(\Zend\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) + public function publicMethodWithReference(\Laminas\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) { return $this->_getSubject()->publicMethodWithReference($classGenerator, $param1, $array); } @@ -141,7 +141,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicParentMethod(\Zend\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicParentMethod(\Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { return $this->_getSubject()->publicParentMethod($docBlockGenerator, $param1, $param2, $param3, $array); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json index ed9965622dc40..3e4e3c49a9eb3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json @@ -26,27 +26,27 @@ "phpseclib/phpseclib": "~0.3", "symfony/console": "~2.3", "tubalmartin/cssmin": "2.4.8-p6", - "zendframework/zend-code": "2.4.0", - "zendframework/zend-config": "2.4.0", - "zendframework/zend-console": "2.4.0", - "zendframework/zend-di": "2.4.0", - "zendframework/zend-eventmanager": "2.4.0", - "zendframework/zend-form": "2.4.0", - "zendframework/zend-http": "2.4.0", - "zendframework/zend-i18n": "2.4.0", - "zendframework/zend-json": "2.4.0", - "zendframework/zend-log": "2.4.0", - "zendframework/zend-modulemanager": "2.4.0", - "zendframework/zend-mvc": "2.4.0", - "zendframework/zend-serializer": "2.4.0", - "zendframework/zend-server": "2.4.0", - "zendframework/zend-servicemanager": "2.4.0", - "zendframework/zend-soap": "2.4.0", - "zendframework/zend-stdlib": "2.4.0", - "zendframework/zend-text": "2.4.0", - "zendframework/zend-uri": "2.4.0", - "zendframework/zend-validator": "2.4.0", - "zendframework/zend-view": "2.4.0" + "laminas/laminas-code": "2.4.0", + "laminas/laminas-config": "2.4.0", + "laminas/laminas-console": "2.4.0", + "laminas/laminas-di": "2.4.0", + "laminas/laminas-eventmanager": "2.4.0", + "laminas/laminas-form": "2.4.0", + "laminas/laminas-http": "2.4.0", + "laminas/laminas-i18n": "2.4.0", + "laminas/laminas-json": "2.4.0", + "laminas/laminas-log": "2.4.0", + "laminas/laminas-modulemanager": "2.4.0", + "laminas/laminas-mvc": "2.4.0", + "laminas/laminas-serializer": "2.4.0", + "laminas/laminas-server": "2.4.0", + "laminas/laminas-servicemanager": "2.4.0", + "laminas/laminas-soap": "2.4.0", + "laminas/laminas-stdlib": "2.4.0", + "laminas/laminas-text": "2.4.0", + "laminas/laminas-uri": "2.4.0", + "laminas/laminas-validator": "2.4.0", + "laminas/laminas-view": "2.4.0" }, "require-dev": { "fabpot/php-cs-fixer": "~1.2", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock index 094f899a2605b..4dd3612ccb020 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock @@ -2482,38 +2482,38 @@ "time": "2018-09-02T14:59:54+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -2524,7 +2524,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2540,33 +2540,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -2577,7 +2577,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2585,7 +2585,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -2593,36 +2593,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -2633,7 +2633,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2641,7 +2641,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -2649,33 +2649,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -2686,7 +2686,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2702,31 +2702,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -2737,14 +2737,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -2752,34 +2752,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -2788,13 +2788,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2810,24 +2810,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -2842,14 +2842,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -2857,16 +2857,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -2882,7 +2882,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -2904,7 +2904,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2912,7 +2912,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -2921,16 +2921,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -2939,7 +2939,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -2950,7 +2950,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2966,22 +2966,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -2998,14 +2998,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -3013,41 +3013,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -3058,7 +3058,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3074,41 +3074,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -3117,13 +3117,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3139,51 +3139,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -3192,13 +3192,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -3217,30 +3217,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -3254,7 +3254,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3272,37 +3272,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -3315,14 +3315,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -3330,44 +3330,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -3376,13 +3376,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3398,30 +3398,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -3433,13 +3433,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3455,16 +3455,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -3474,16 +3474,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -3494,7 +3494,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3502,7 +3502,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -3510,16 +3510,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -3528,7 +3528,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -3539,7 +3539,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3555,24 +3555,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -3580,21 +3580,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -3603,13 +3603,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3617,7 +3617,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -3626,16 +3626,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -3643,21 +3643,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -3666,13 +3666,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3688,16 +3688,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -3712,7 +3712,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -3723,14 +3723,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -3738,30 +3738,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -3772,7 +3772,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3780,7 +3780,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -3789,39 +3789,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -3832,7 +3832,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3840,7 +3840,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -3849,73 +3849,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -3929,14 +3929,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -3944,24 +3944,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -3976,15 +3976,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -3993,33 +3993,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -4028,13 +4028,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4050,27 +4050,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -4081,7 +4081,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4097,16 +4097,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -4118,12 +4118,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -4134,14 +4134,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -4149,43 +4149,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -4194,13 +4194,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4216,34 +4216,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -4254,14 +4254,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -4269,39 +4269,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -4313,14 +4313,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -4328,28 +4328,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -4360,7 +4360,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4376,27 +4376,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -4407,7 +4407,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4423,49 +4423,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -4474,13 +4474,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4488,7 +4488,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -4496,64 +4496,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -4567,7 +4567,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4575,7 +4575,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock index 4fb998ab77b34..8f575e8fd48c6 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock @@ -1870,13 +1870,13 @@ "symfony/console": "~4.1.0", "symfony/process": "~4.1.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0" }, "suggest": { "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" @@ -2335,28 +2335,28 @@ "symfony/event-dispatcher": "~4.1.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "conflict": { "gene/bluefoot": "*" @@ -3485,9 +3485,9 @@ "magento/module-customer": "102.0.*", "magento/module-store": "101.0.*", "php": "~7.1.3||~7.2.0", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "autoload": { @@ -9878,33 +9878,33 @@ "tubalmartin/cssmin": "4.1.1", "vertex/product-magento-module": "3.1.0", "webonyx/graphql-php": "^0.12.6", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "type": "metapackage", "license": [ @@ -12065,8 +12065,8 @@ "monolog/monolog": "^1.17.0", "php": "~7.1.3||~7.2.0", "psr/log": "~1.0", - "zendframework/zend-barcode": "^2.7.0", - "zendframework/zend-http": "^2.6.0" + "laminas/laminas-barcode": "^2.7.0", + "laminas/laminas-http": "^2.6.0" }, "suggest": { "magento/module-rma": "^101.1.0", @@ -12399,29 +12399,29 @@ "time": "2018-09-07T08:16:44+00:00" }, { - "name": "zendframework/zend-barcode", + "name": "laminas/laminas-barcode", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-barcode.git", + "url": "https://github.com/laminas/laminas-barcode.git", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", + "url": "https://api.github.com/repos/laminas/laminas-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6 || ^3.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6 || ^3.1", "zendframework/zendpdf": "^2.0.2" }, "suggest": { @@ -12436,7 +12436,7 @@ }, "autoload": { "psr-4": { - "Zend\\Barcode\\": "src/" + "Laminas\\Barcode\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12452,38 +12452,38 @@ "time": "2017-12-11T15:30:02+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -12494,7 +12494,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12510,33 +12510,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -12547,7 +12547,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12555,7 +12555,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -12563,36 +12563,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -12603,7 +12603,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12611,7 +12611,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -12619,33 +12619,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -12656,7 +12656,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12672,31 +12672,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -12707,14 +12707,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -12722,34 +12722,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -12758,13 +12758,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12780,24 +12780,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -12812,14 +12812,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -12827,16 +12827,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -12852,7 +12852,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -12874,7 +12874,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12882,7 +12882,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -12891,16 +12891,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -12909,7 +12909,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -12920,7 +12920,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12936,22 +12936,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -12968,14 +12968,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -12983,41 +12983,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -13028,7 +13028,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13044,41 +13044,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -13087,13 +13087,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13109,51 +13109,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -13162,13 +13162,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -13187,30 +13187,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -13224,7 +13224,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13242,37 +13242,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -13285,14 +13285,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -13300,44 +13300,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -13346,13 +13346,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13368,30 +13368,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -13403,13 +13403,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13425,16 +13425,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -13444,16 +13444,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -13464,7 +13464,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13472,7 +13472,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -13480,16 +13480,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -13498,7 +13498,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -13509,7 +13509,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13525,24 +13525,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -13550,21 +13550,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -13573,13 +13573,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13587,7 +13587,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -13596,16 +13596,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -13613,21 +13613,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -13636,13 +13636,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13658,16 +13658,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -13682,7 +13682,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -13693,14 +13693,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -13708,30 +13708,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -13742,7 +13742,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13750,7 +13750,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -13759,39 +13759,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -13802,7 +13802,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13810,7 +13810,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -13819,73 +13819,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -13899,14 +13899,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -13914,24 +13914,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -13946,15 +13946,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -13963,33 +13963,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -13998,13 +13998,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14020,27 +14020,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14051,7 +14051,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14067,16 +14067,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -14088,12 +14088,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -14104,14 +14104,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -14119,43 +14119,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -14164,13 +14164,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14186,34 +14186,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -14224,14 +14224,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -14239,39 +14239,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -14283,14 +14283,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -14298,28 +14298,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -14330,7 +14330,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14346,27 +14346,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14377,7 +14377,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14393,49 +14393,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -14444,13 +14444,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14458,7 +14458,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -14466,64 +14466,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -14537,7 +14537,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14545,7 +14545,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock index 36a98e6cd9596..a3e60ad227bf7 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock @@ -1870,13 +1870,13 @@ "symfony/console": "~4.1.0", "symfony/process": "~4.1.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0" }, "suggest": { "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" @@ -2335,28 +2335,28 @@ "symfony/event-dispatcher": "~4.1.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "conflict": { "gene/bluefoot": "*" @@ -3485,9 +3485,9 @@ "magento/module-customer": "102.0.*", "magento/module-store": "101.0.*", "php": "~7.1.3||~7.2.0", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "autoload": { @@ -9878,33 +9878,33 @@ "tubalmartin/cssmin": "4.1.1", "vertex/product-magento-module": "3.1.0", "webonyx/graphql-php": "^0.12.6", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "type": "metapackage", "license": [ @@ -12065,8 +12065,8 @@ "monolog/monolog": "^1.17.0", "php": "~7.1.3||~7.2.0", "psr/log": "~1.0", - "zendframework/zend-barcode": "^2.7.0", - "zendframework/zend-http": "^2.6.0" + "laminas/laminas-barcode": "^2.7.0", + "laminas/laminas-http": "^2.6.0" }, "suggest": { "magento/module-rma": "^101.1.0", @@ -12399,29 +12399,29 @@ "time": "2018-09-07T08:16:44+00:00" }, { - "name": "zendframework/zend-barcode", + "name": "laminas/laminas-barcode", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-barcode.git", + "url": "https://github.com/laminas/laminas-barcode.git", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", + "url": "https://api.github.com/repos/laminas/laminas-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6 || ^3.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6 || ^3.1", "zendframework/zendpdf": "^2.0.2" }, "suggest": { @@ -12436,7 +12436,7 @@ }, "autoload": { "psr-4": { - "Zend\\Barcode\\": "src/" + "Laminas\\Barcode\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12452,38 +12452,38 @@ "time": "2017-12-11T15:30:02+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -12494,7 +12494,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12510,33 +12510,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -12547,7 +12547,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12555,7 +12555,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -12563,36 +12563,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -12603,7 +12603,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12611,7 +12611,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -12619,33 +12619,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -12656,7 +12656,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12672,31 +12672,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -12707,14 +12707,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -12722,34 +12722,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -12758,13 +12758,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12780,24 +12780,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -12812,14 +12812,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -12827,16 +12827,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -12852,7 +12852,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -12874,7 +12874,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12882,7 +12882,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -12891,16 +12891,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -12909,7 +12909,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -12920,7 +12920,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12936,22 +12936,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -12968,14 +12968,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -12983,41 +12983,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -13028,7 +13028,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13044,41 +13044,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -13087,13 +13087,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13109,51 +13109,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -13162,13 +13162,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -13187,30 +13187,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -13224,7 +13224,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13242,37 +13242,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -13285,14 +13285,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -13300,44 +13300,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -13346,13 +13346,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13368,30 +13368,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -13403,13 +13403,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13425,16 +13425,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -13444,16 +13444,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -13464,7 +13464,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13472,7 +13472,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -13480,16 +13480,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -13498,7 +13498,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -13509,7 +13509,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13525,24 +13525,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -13550,21 +13550,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -13573,13 +13573,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13587,7 +13587,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -13596,16 +13596,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -13613,21 +13613,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -13636,13 +13636,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13658,16 +13658,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -13682,7 +13682,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -13693,14 +13693,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -13708,30 +13708,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -13742,7 +13742,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13750,7 +13750,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -13759,39 +13759,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -13802,7 +13802,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13810,7 +13810,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -13819,73 +13819,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -13899,14 +13899,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -13914,24 +13914,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -13946,15 +13946,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -13963,33 +13963,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -13998,13 +13998,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14020,27 +14020,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14051,7 +14051,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14067,16 +14067,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -14088,12 +14088,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -14104,14 +14104,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -14119,43 +14119,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -14164,13 +14164,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14186,34 +14186,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -14224,14 +14224,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -14239,39 +14239,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -14283,14 +14283,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -14298,28 +14298,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -14330,7 +14330,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14346,27 +14346,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14377,7 +14377,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14393,49 +14393,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -14444,13 +14444,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14458,7 +14458,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -14466,64 +14466,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -14537,7 +14537,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14545,7 +14545,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 2ffce44b32cfe..1e72485a9859f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -183,7 +183,7 @@ enumValues(includeDeprecated: true) { $request->setPathInfo('/graphql'); $request->setMethod('POST'); $request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $request->setHeaders($headers); diff --git a/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php index 6c6b0b4aafba9..1bd182c20b290 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\HTTP; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class HeaderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php b/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php index 9dee98fcb9121..2b5cb27e84dcb 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php @@ -9,7 +9,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Test CookieScope diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index db830d228201c..773c833627572 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\TestFramework\Helper\Bootstrap; /** diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php index 2fd388d9db3f5..527e471ff3e39 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php @@ -98,7 +98,7 @@ public function testDispatch() : void $this->request->setPathInfo('/graphql'); $this->request->setMethod('POST'); $this->request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $this->request->setHeaders($headers); $response = $this->graphql->dispatch($this->request); @@ -241,7 +241,7 @@ public function testError() : void $this->request->setPathInfo('/graphql'); $this->request->setMethod('POST'); $this->request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $this->request->setHeaders($headers); $response = $this->graphql->dispatch($this->request); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php index ae57703f9e8e2..06450d2a1d187 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php @@ -36,7 +36,7 @@ protected function setUp() 'text' => 'Template Content', 'form_key' => $this->formKey, ]; - $this->getRequest()->setPostValue($post)->setMethod(\Zend\Http\Request::METHOD_POST); + $this->getRequest()->setPostValue($post)->setMethod(\Laminas\Http\Request::METHOD_POST); $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Newsletter\Model\Template::class ); @@ -141,7 +141,7 @@ public function testSaveActionTemplateWithGetAndVerifyRedirect() // Loading by code, since ID will vary. template_code is not actually used to load anywhere else. $this->model->load('some_unique_code', 'template_code'); - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_GET)->setParam('id', $this->model->getId()); + $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_GET)->setParam('id', $this->model->getId()); $this->dispatch('backend/newsletter/template/save'); $this->assertEquals(404, $this->getResponse()->getStatusCode()); diff --git a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php index b11991fc03255..237bb0aa118bc 100644 --- a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php @@ -22,7 +22,7 @@ class PhpserverTest extends \PHPUnit\Framework\TestCase private static $serverPid; /** - * @var \Zend\Http\Client + * @var \Laminas\Http\Client */ private $httpClient; @@ -53,7 +53,7 @@ private function getUrl($url) public function setUp() { - $this->httpClient = new \Zend\Http\Client(null, ['timeout' => 10]); + $this->httpClient = new \Laminas\Http\Client(null, ['timeout' => 10]); } public function testServerHasPid() diff --git a/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php index d6664d1fbb7ac..60d5d8cd5c4f4 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php @@ -13,7 +13,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\UrlInterface; use Magento\Review\Model\ResourceModel\Review\CollectionFactory; -use Zend\Http\Request; +use Laminas\Http\Request; /** * Test Mass Update action. diff --git a/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php index c5c54b8c1dd3b..80796c0294958 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Stdlib\RequestInterface as Request; -use Zend\View\Model\JsonModel; +use Laminas\Stdlib\RequestInterface as Request; +use Laminas\View\Model\JsonModel; class UrlCheckTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php index 455d6436f724d..c7868d90af7a4 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php @@ -39,7 +39,7 @@ public function testCollectOptionsLists() ] ); - $serviceLocator = $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class); + $serviceLocator = $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class); $serviceLocator->expects($this->once()) ->method('get') diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php index a80da16be67eb..d66329761336b 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject; use Symfony\Component\Console\Application; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Tests ObjectManagerProvider diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock index 48fa6d0d0cd34..ddd1ed9684ed3 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock @@ -4057,26 +4057,26 @@ "oyejorge/less.php": "1.7.0.3", "php": "~5.5.0|~5.6.0|~7.0.0", "tubalmartin/cssmin": "2.4.8-p4", - "zendframework/zend-code": "2.3.1", - "zendframework/zend-config": "2.3.1", - "zendframework/zend-console": "2.3.1", - "zendframework/zend-di": "2.3.1", - "zendframework/zend-eventmanager": "2.3.1", - "zendframework/zend-form": "2.3.1", - "zendframework/zend-http": "2.3.1", - "zendframework/zend-json": "2.3.1", - "zendframework/zend-log": "2.3.1", - "zendframework/zend-modulemanager": "2.3.1", - "zendframework/zend-mvc": "2.3.1", - "zendframework/zend-serializer": "2.3.1", - "zendframework/zend-server": "2.3.1", - "zendframework/zend-servicemanager": "2.3.1", - "zendframework/zend-soap": "2.3.1", - "zendframework/zend-stdlib": "2.3.1", - "zendframework/zend-text": "2.3.1", - "zendframework/zend-uri": "2.3.1", - "zendframework/zend-validator": "2.3.1", - "zendframework/zend-view": "2.3.1" + "laminas/laminas-code": "2.3.1", + "laminas/laminas-config": "2.3.1", + "laminas/laminas-console": "2.3.1", + "laminas/laminas-di": "2.3.1", + "laminas/laminas-eventmanager": "2.3.1", + "laminas/laminas-form": "2.3.1", + "laminas/laminas-http": "2.3.1", + "laminas/laminas-json": "2.3.1", + "laminas/laminas-log": "2.3.1", + "laminas/laminas-modulemanager": "2.3.1", + "laminas/laminas-mvc": "2.3.1", + "laminas/laminas-serializer": "2.3.1", + "laminas/laminas-server": "2.3.1", + "laminas/laminas-servicemanager": "2.3.1", + "laminas/laminas-soap": "2.3.1", + "laminas/laminas-stdlib": "2.3.1", + "laminas/laminas-text": "2.3.1", + "laminas/laminas-uri": "2.3.1", + "laminas/laminas-validator": "2.3.1", + "laminas/laminas-view": "2.3.1" }, "require-dev": { "ext-ctype": "*", @@ -4696,33 +4696,33 @@ "time": "2014-09-22 08:08:50" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/a64e90c9ee8c939335ee7e21e39e3342c0e54526", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/a64e90c9ee8c939335ee7e21e39e3342c0e54526", "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version" + "laminas/laminas-eventmanager": "self.version" }, "require-dev": { "doctrine/common": ">=2.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "suggest": { "doctrine/common": "Doctrine\\Common >=2.1 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -4733,7 +4733,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4741,7 +4741,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -4749,37 +4749,37 @@ "time": "2014-04-15 14:47:18" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "698c139707380b29fd09791ec1c21b837e9a3f15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/698c139707380b29fd09791ec1c21b837e9a3f15", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/698c139707380b29fd09791ec1c21b837e9a3f15", "reference": "698c139707380b29fd09791ec1c21b837e9a3f15", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -4790,7 +4790,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4798,7 +4798,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -4806,22 +4806,22 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/4253efd75a022d97ef326eac38a06c8eebb48a37", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/4253efd75a022d97ef326eac38a06c8eebb48a37", "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -4829,8 +4829,8 @@ "satooshi/php-coveralls": "dev-master" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -4841,14 +4841,14 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-console", + "homepage": "https://github.com/laminas/laminas-console", "keywords": [ "console", "zf2" @@ -4856,32 +4856,32 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/7502db10f8023bfd4e860ce83c9cdeda0db42c31", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/7502db10f8023bfd4e860ce83c9cdeda0db42c31", "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-code": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-code": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -4892,14 +4892,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -4907,16 +4907,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", "shasum": "" }, @@ -4937,14 +4937,14 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-escaper", + "homepage": "https://github.com/laminas/laminas-escaper", "keywords": [ "escaper", "zf2" @@ -4952,22 +4952,22 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/ec158e89d0290f988a29ccd6bf179b561efbb702", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ec158e89d0290f988a29ccd6bf179b561efbb702", "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -4983,7 +4983,7 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4998,36 +4998,36 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/307fe694659e08ffd710c70e4db8bc60187bcc84", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/307fe694659e08ffd710c70e4db8bc60187bcc84", "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-crypt": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-crypt": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-crypt": "Zend\\Crypt component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component for UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component for UriNormalize filter" }, "type": "library", "extra": { @@ -5038,7 +5038,7 @@ }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5046,7 +5046,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed data filters", - "homepage": "https://github.com/zendframework/zend-filter", + "homepage": "https://github.com/laminas/laminas-filter", "keywords": [ "filter", "zf2" @@ -5054,48 +5054,48 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/d7a1f5bc4626b1df990391502a868b28c37ba65d", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/d7a1f5bc4626b1df990391502a868b28c37ba65d", "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-inputfilter": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-inputfilter": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-captcha": "self.version", - "zendframework/zend-code": "self.version", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-validator": "self.version", - "zendframework/zend-view": "self.version", - "zendframework/zendservice-recaptcha": "*" + "laminas/laminas-captcha": "self.version", + "laminas/laminas-code": "self.version", + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-validator": "self.version", + "laminas/laminas-view": "self.version", + "laminas/laminas-recaptcha": "*" }, "suggest": { - "zendframework/zend-captcha": "Zend\\Captcha component", - "zendframework/zend-code": "Zend\\Code component", - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-captcha": "Laminas\\Captcha component", + "laminas/laminas-code": "Laminas\\Code component", + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -5106,14 +5106,14 @@ }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-form", + "homepage": "https://github.com/laminas/laminas-form", "keywords": [ "form", "zf2" @@ -5121,25 +5121,25 @@ "time": "2014-04-15 14:36:41" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/869ce7bdf60727e14d85c305d2948fbe831c3534", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/869ce7bdf60727e14d85c305d2948fbe831c3534", "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-loader": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-uri": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-loader": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-uri": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5155,7 +5155,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5163,7 +5163,7 @@ "BSD-3-Clause" ], "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", - "homepage": "https://github.com/zendframework/zend-http", + "homepage": "https://github.com/laminas/laminas-http", "keywords": [ "http", "zf2" @@ -5171,33 +5171,33 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "abca740015a856d03542f5b6c535b8874f84b622" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/abca740015a856d03542f5b6c535b8874f84b622", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/abca740015a856d03542f5b6c535b8874f84b622", "reference": "abca740015a856d03542f5b6c535b8874f84b622", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-filter": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-filter": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "To support plugin manager support" + "laminas/laminas-servicemanager": "To support plugin manager support" }, "type": "library", "extra": { @@ -5208,7 +5208,7 @@ }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5223,34 +5223,34 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/5284687fc3aeab27961d2e17ada08973ae6daafe", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/5284687fc3aeab27961d2e17ada08973ae6daafe", "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-http": "self.version", - "zendframework/zend-server": "self.version" + "laminas/laminas-http": "self.version", + "laminas/laminas-server": "self.version" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-server": "Zend\\Server component", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-server": "Laminas\\Server component", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -5261,7 +5261,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5269,7 +5269,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -5277,16 +5277,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/5e0bd7e28c644078685f525cf8ae03d9a01ae292", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/5e0bd7e28c644078685f525cf8ae03d9a01ae292", "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292", "shasum": "" }, @@ -5307,14 +5307,14 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-loader", + "homepage": "https://github.com/laminas/laminas-loader", "keywords": [ "loader", "zf2" @@ -5322,41 +5322,41 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "217611433f5cb56d4420a1db8f164e5db85d815d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/217611433f5cb56d4420a1db8f164e5db85d815d", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/217611433f5cb56d4420a1db8f164e5db85d815d", "reference": "217611433f5cb56d4420a1db8f164e5db85d815d", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-console": "self.version", - "zendframework/zend-db": "self.version", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-mail": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-console": "self.version", + "laminas/laminas-db": "self.version", + "laminas/laminas-escaper": "self.version", + "laminas/laminas-mail": "self.version", + "laminas/laminas-validator": "self.version" }, "suggest": { "ext-mongo": "*", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML formatter", - "zendframework/zend-mail": "Zend\\Mail component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML formatter", + "laminas/laminas-mail": "Laminas\\Mail component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -5367,7 +5367,7 @@ }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5375,7 +5375,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -5384,16 +5384,16 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/63225fcebb196fc6e20094f5f01e9354779ec31e", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/63225fcebb196fc6e20094f5f01e9354779ec31e", "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e", "shasum": "" }, @@ -5408,8 +5408,8 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable", - "zendframework/zend-servicemanager": ">= current version, if using the BigInteger::factory functionality" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable", + "laminas/laminas-servicemanager": ">= current version, if using the BigInteger::factory functionality" }, "type": "library", "extra": { @@ -5420,14 +5420,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -5435,40 +5435,40 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/d4591b958e40b8f5ae8110d9b203331437aa19f2", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/d4591b958e40b8f5ae8110d9b203331437aa19f2", "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-config": "self.version", - "zendframework/zend-console": "self.version", - "zendframework/zend-loader": "self.version", - "zendframework/zend-mvc": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-config": "self.version", + "laminas/laminas-console": "self.version", + "laminas/laminas-loader": "self.version", + "laminas/laminas-mvc": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -5479,7 +5479,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5494,70 +5494,70 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-form": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-form": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-authentication": "self.version", - "zendframework/zend-console": "self.version", - "zendframework/zend-di": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-form": "self.version", - "zendframework/zend-http": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-inputfilter": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-log": "self.version", - "zendframework/zend-modulemanager": "self.version", - "zendframework/zend-serializer": "self.version", - "zendframework/zend-session": "self.version", - "zendframework/zend-text": "self.version", - "zendframework/zend-uri": "self.version", - "zendframework/zend-validator": "self.version", + "laminas/laminas-authentication": "self.version", + "laminas/laminas-console": "self.version", + "laminas/laminas-di": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-form": "self.version", + "laminas/laminas-http": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-inputfilter": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-log": "self.version", + "laminas/laminas-modulemanager": "self.version", + "laminas/laminas-serializer": "self.version", + "laminas/laminas-session": "self.version", + "laminas/laminas-text": "self.version", + "laminas/laminas-uri": "self.version", + "laminas/laminas-validator": "self.version", "zendframework/zend-version": "self.version", - "zendframework/zend-view": "self.version" + "laminas/laminas-view": "self.version" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-form": "Zend\\Form component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-stdlib": "Zend\\Stdlib component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-form": "Laminas\\Form component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-stdlib": "Laminas\\Stdlib component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", "zendframework/zend-version": "Zend\\Version component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -5568,14 +5568,14 @@ }, "autoload": { "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -5583,33 +5583,33 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-json": "self.version", - "zendframework/zend-math": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-json": "self.version", + "laminas/laminas-math": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "To support plugin manager support" + "laminas/laminas-servicemanager": "To support plugin manager support" }, "type": "library", "extra": { @@ -5620,7 +5620,7 @@ }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5628,7 +5628,7 @@ "BSD-3-Clause" ], "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", - "homepage": "https://github.com/zendframework/zend-serializer", + "homepage": "https://github.com/laminas/laminas-serializer", "keywords": [ "serializer", "zf2" @@ -5636,23 +5636,23 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-code": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-code": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5668,14 +5668,14 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-server", + "homepage": "https://github.com/laminas/laminas-server", "keywords": [ "server", "zf2" @@ -5683,16 +5683,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/7a428b595a1fcd4c2a8026ee5d5f89a56036f682", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/7a428b595a1fcd4c2a8026ee5d5f89a56036f682", "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682", "shasum": "" }, @@ -5703,10 +5703,10 @@ "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-di": "self.version" + "laminas/laminas-di": "self.version" }, "suggest": { - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -5717,7 +5717,7 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5732,33 +5732,33 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "fab9f5309be04cacf01af54f694aed5102592c5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/fab9f5309be04cacf01af54f694aed5102592c5c", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/fab9f5309be04cacf01af54f694aed5102592c5c", "reference": "fab9f5309be04cacf01af54f694aed5102592c5c", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-server": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-server": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-uri": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-http": "self.version" + "laminas/laminas-http": "self.version" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -5769,14 +5769,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -5784,16 +5784,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/6079302963d57f36a9ba92ed3f38b992997aa78d", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/6079302963d57f36a9ba92ed3f38b992997aa78d", "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d", "shasum": "" }, @@ -5804,16 +5804,16 @@ "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-serializer": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-serializer": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -5824,14 +5824,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -5839,23 +5839,23 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5871,14 +5871,14 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-text", + "homepage": "https://github.com/laminas/laminas-text", "keywords": [ "text", "zf2" @@ -5886,23 +5886,23 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "0de6ba8c166a235588783ff8e88a19b364630d89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/0de6ba8c166a235588783ff8e88a19b364630d89", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/0de6ba8c166a235588783ff8e88a19b364630d89", "reference": "0de6ba8c166a235588783ff8e88a19b364630d89", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-escaper": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5918,7 +5918,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5926,7 +5926,7 @@ "BSD-3-Clause" ], "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", - "homepage": "https://github.com/zendframework/zend-uri", + "homepage": "https://github.com/laminas/laminas-uri", "keywords": [ "uri", "zf2" @@ -5934,44 +5934,44 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/a5f97f6c3a27a27b1235f724ad0048715459f013", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/a5f97f6c3a27a27b1235f724ad0048715459f013", "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-db": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-math": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-session": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-db": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-math": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-session": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages as well as to use the various Date validators", - "zendframework/zend-math": "Zend\\Math component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages as well as to use the various Date validators", + "laminas/laminas-math": "Laminas\\Math component", "zendframework/zend-resources": "Translations of validator messages", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -5982,7 +5982,7 @@ }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5990,7 +5990,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -5998,57 +5998,57 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e308d498fa7851f26bd639bfe3ebbfba397c47bc", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/e308d498fa7851f26bd639bfe3ebbfba397c47bc", "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-loader": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-loader": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-authentication": "self.version", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-feed": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-http": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-mvc": "self.version", - "zendframework/zend-navigation": "self.version", - "zendframework/zend-paginator": "self.version", - "zendframework/zend-permissions-acl": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-authentication": "self.version", + "laminas/laminas-escaper": "self.version", + "laminas/laminas-feed": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-http": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-mvc": "self.version", + "laminas/laminas-navigation": "self.version", + "laminas/laminas-paginator": "self.version", + "laminas/laminas-permissions-acl": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "type": "library", "extra": { @@ -6059,7 +6059,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6067,7 +6067,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php index 73863e0915c66..7f4a8df2f3c6f 100644 --- a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php @@ -14,7 +14,7 @@ use Magento\Sitemap\Model\Sitemap; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Request; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use PHPUnit\Framework\TestCase; /** diff --git a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php index a1a99ecd32b89..b4d1f94b74ffe 100644 --- a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php @@ -11,7 +11,7 @@ use Magento\Framework\App\Config\Value; use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\Response; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Tests \Magento\Store\App\FrontController\Plugin\RequestPreprocessor. diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php index 00de5544d8fb7..5477c8007be04 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php @@ -11,7 +11,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\UrlInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php b/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php index c63a6fe75f5fc..5873c0e1da53e 100644 --- a/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php +++ b/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php @@ -8,7 +8,7 @@ namespace Magento\Ui\Controller\Index; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Http\Headers; +use Laminas\Http\Headers; /** * Test component rendering on storefront. diff --git a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php index f6ea0c393260a..c4b69cc5150ba 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php +++ b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php @@ -5,9 +5,9 @@ */ namespace Magento\TestFramework\Integrity\Library; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\FileReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\FileReflection; +use Laminas\Code\Reflection\ParameterReflection; /** */ @@ -28,7 +28,7 @@ public function getDependencies(FileReflection $fileReflection) foreach ($fileReflection->getClasses() as $class) { /** @var ClassReflection $class */ foreach ($class->getMethods() as $method) { - /** @var \Zend\Code\Reflection\MethodReflection $method */ + /** @var \Laminas\Code\Reflection\MethodReflection $method */ if ($method->getDeclaringClass()->getName() != $class->getName()) { continue; } diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php index 4e5bb8d9d0aa9..856ba1f80ac68 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php @@ -17,7 +17,7 @@ class InjectableTest extends \PHPUnit\Framework\TestCase protected $injectable; /** - * @var \Zend\Code\Reflection\FileReflection + * @var \Laminas\Code\Reflection\FileReflection */ protected $fileReflection; @@ -38,23 +38,23 @@ public function setUp() { $this->injectable = new Injectable(); $this->fileReflection = $this->getMockBuilder( - \Zend\Code\Reflection\FileReflection::class + \Laminas\Code\Reflection\FileReflection::class )->disableOriginalConstructor()->getMock(); $classReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $methodReflection = $this->getMockBuilder( - \Zend\Code\Reflection\MethodReflection::class + \Laminas\Code\Reflection\MethodReflection::class )->disableOriginalConstructor()->getMock(); $this->parameterReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ParameterReflection::class + \Laminas\Code\Reflection\ParameterReflection::class )->disableOriginalConstructor()->getMock(); $this->declaredClass = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $methodReflection->expects( @@ -98,7 +98,7 @@ public function setUp() public function testGetDependencies() { $classReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $classReflection->expects( diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index b5a4e41b63279..c5447ef716eb2 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -501,7 +501,7 @@ private function removeSpecialCases(array $badClasses, string $file, string $con } // Remove usage of classes that have been declared as "use" or "include" - // Also deals with case like: "use \Zend\Code\Scanner\FileScanner, Magento\Tools\Di\Compiler\Log\Log;" + // Also deals with case like: "use \Laminas\Code\Scanner\FileScanner, Magento\Tools\Di\Compiler\Log\Log;" // (continued) where there is a comma separating two different classes. if (preg_match('/use\s.*[\\n]?.*' . str_replace('\\', '\\\\', $badClass) . '[\,\;]/', $contents)) { unset($badClasses[array_search($badClass, $badClasses)]); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php index a4baacbe4d169..b2c117960352a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php @@ -11,7 +11,7 @@ use Magento\TestFramework\Integrity\Library\Injectable; use Magento\TestFramework\Integrity\Library\PhpParser\ParserFactory; use Magento\TestFramework\Integrity\Library\PhpParser\Tokens; -use Zend\Code\Reflection\FileReflection; +use Laminas\Code\Reflection\FileReflection; /** * Test check if Magento library components contain incorrect dependencies to application layer diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php index 571ed4afbb6ce..23c72593c9fc8 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php @@ -170,7 +170,7 @@ function ($filename) { if (preg_match('/' . $extensibleClassPattern . '/', $fileContent) && !preg_match('/' . $abstractExtensibleClassPattern . '/', $fileContent) ) { - $fileReflection = new \Zend\Code\Reflection\FileReflection($filename, true); + $fileReflection = new \Laminas\Code\Reflection\FileReflection($filename, true); foreach ($fileReflection->getClasses() as $classReflection) { if ($classReflection->isSubclassOf(self::EXTENSIBLE_DATA_INTERFACE)) { $methodsToCheck = ['setExtensionAttributes', 'getExtensionAttributes']; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php index 6dd9d2f2bddfd..849a57911a77e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php @@ -167,7 +167,7 @@ public function publicPHPTypesDataProvider() * Retrieve list of classes and interfaces declared in the file * * @param string $file - * @return \Zend\Code\Scanner\ClassScanner[] + * @return \Laminas\Code\Scanner\ClassScanner[] */ private function getDeclaredClassesAndInterfaces($file) { diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index af785c28db414..f386aab71a57c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4238,8 +4238,8 @@ 'Magento\Elasticsearch\Test\Unit\Model\SearchAdapter\ConnectionManagerTest', 'Magento\Elasticsearch\Test\Unit\SearchAdapter\ConnectionManagerTest' ], - ['Zend_Feed', 'Zend\Feed'], - ['Zend_Uri', 'Zend\Uri\Uri'], + ['Zend_Feed', 'Laminas\Feed'], + ['Zend_Uri', 'Laminas\Uri\Uri'], ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], ['Magento\Wishlist\Setup\Patch\Schema\AddProductIdConstraint'], diff --git a/dev/tools/UpgradeScripts/pre_composer_update_2.3.php b/dev/tools/UpgradeScripts/pre_composer_update_2.3.php index 665af637443be..e6f1ddb31c4a3 100644 --- a/dev/tools/UpgradeScripts/pre_composer_update_2.3.php +++ b/dev/tools/UpgradeScripts/pre_composer_update_2.3.php @@ -17,7 +17,7 @@ Steps included: - Require new version of the metapackage - Update "require-dev" section - - Add "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section + - Add "Laminas\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section - Update Magento/Updater if it's installed - Update name, version, and description fields in the root composer.json @@ -248,7 +248,7 @@ runComposer('remove --dev sjparkinson/static-review fabpot/php-cs-fixer --no-update'); output('\nAdding "Zend\\\\Mvc\\\\Controller\\\\": "setup/src/Zend/Mvc/Controller/" to "autoload": "psr-4"'); - $composerData['autoload']['psr-4']['Zend\\Mvc\\Controller\\'] = 'setup/src/Zend/Mvc/Controller/'; + $composerData['autoload']['psr-4']['Laminas\\Mvc\\Controller\\'] = 'setup/src/Zend/Mvc/Controller/'; if (preg_match('/^magento\/project\-(community|enterprise)\-edition$/', $composerData['name'])) { output('\nUpdating project name, version, and description'); diff --git a/lib/internal/Magento/Framework/App/Feed.php b/lib/internal/Magento/Framework/App/Feed.php index 1154749a75b94..c504f463f68dc 100644 --- a/lib/internal/Magento/Framework/App/Feed.php +++ b/lib/internal/Magento/Framework/App/Feed.php @@ -7,7 +7,7 @@ namespace Magento\Framework\App; -use Zend\Feed\Writer\FeedFactory; +use Laminas\Feed\Writer\FeedFactory; /** * Default XML feed class diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 4e709ed276954..fbcf20b9f4f99 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -106,7 +106,7 @@ class Http extends Request implements RequestContentInterface, RequestSafetyInte * @param ConfigInterface $routeConfig * @param PathInfoProcessorInterface $pathInfoProcessor * @param ObjectManagerInterface $objectManager - * @param \Zend\Uri\UriInterface|string|null $uri + * @param \Laminas\Uri\UriInterface|string|null $uri * @param array $directFrontNames * @param PathInfo|null $pathInfoService */ diff --git a/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php b/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php index 4e7216954b0d4..ddc3a03416c68 100644 --- a/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php +++ b/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php @@ -59,7 +59,7 @@ private function processMap(array $map): array * * @return string[] * - * @see \Zend\Http\Request Has list of methods as METHOD_* constants. + * @see \Laminas\Http\Request Has list of methods as METHOD_* constants. */ public function getMap(): array { diff --git a/lib/internal/Magento/Framework/App/Response/HttpInterface.php b/lib/internal/Magento/Framework/App/Response/HttpInterface.php index 08b1257f73abe..17825aeb88d65 100644 --- a/lib/internal/Magento/Framework/App/Response/HttpInterface.php +++ b/lib/internal/Magento/Framework/App/Response/HttpInterface.php @@ -48,7 +48,7 @@ public function setHeader($name, $value, $replace = false); * If header with specified name was not found returns false. * * @param string $name - * @return \Zend\Http\Header\HeaderInterface|bool + * @return \Laminas\Http\Header\HeaderInterface|bool * @since 100.2.0 */ public function getHeader($name); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php index db200f962f5b5..81df376a5d42f 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php @@ -52,7 +52,7 @@ class KernelTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $headersMock = $this->createMock(\Zend\Http\Headers::class); + $headersMock = $this->createMock(\Laminas\Http\Headers::class); $this->cacheMock = $this->createMock(\Magento\Framework\App\PageCache\Cache::class); $this->fullPageCacheMock = $this->createMock(\Magento\PageCache\Model\Cache\Type::class); $this->contextMock = $this->createMock(\Magento\Framework\App\Http\Context::class); @@ -204,7 +204,7 @@ function ($value) { } ); - $cacheControlHeader = \Zend\Http\Header\CacheControl::fromString( + $cacheControlHeader = \Laminas\Http\Header\CacheControl::fromString( 'Cache-Control: public, max-age=100, s-maxage=100' ); @@ -261,7 +261,7 @@ public function testProcessSaveCacheDataProvider() */ public function testProcessNotSaveCache($cacheControlHeader, $httpCode, $isGet, $overrideHeaders) { - $header = \Zend\Http\Header\CacheControl::fromString("Cache-Control: $cacheControlHeader"); + $header = \Laminas\Http\Header\CacheControl::fromString("Cache-Control: $cacheControlHeader"); $this->responseMock->expects( $this->once() )->method( diff --git a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php index 0a48945e55c2d..397d2c09fbf47 100644 --- a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php @@ -5,10 +5,10 @@ */ namespace Magento\Framework\Code\Generator; -use Zend\Code\Generator\MethodGenerator; -use Zend\Code\Generator\PropertyGenerator; +use Laminas\Code\Generator\MethodGenerator; +use Laminas\Code\Generator\PropertyGenerator; -class ClassGenerator extends \Zend\Code\Generator\ClassGenerator implements +class ClassGenerator extends \Laminas\Code\Generator\ClassGenerator implements \Magento\Framework\Code\Generator\CodeGeneratorInterface { /** @@ -86,7 +86,7 @@ protected function _setDataToObject($object, array $data, array $map) */ public function setClassDocBlock(array $docBlock) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $docBlock, $this->_docBlockOptions); @@ -115,7 +115,7 @@ public function addMethods(array $methods) ) { $parametersArray = []; foreach ($methodOptions['parameters'] as $parameterOptions) { - $parameterObject = new \Zend\Code\Generator\ParameterGenerator(); + $parameterObject = new \Laminas\Code\Generator\ParameterGenerator(); $this->_setDataToObject($parameterObject, $parameterOptions, $this->_parameterOptions); $parametersArray[] = $parameterObject; } @@ -124,7 +124,7 @@ public function addMethods(array $methods) } if (isset($methodOptions['docblock']) && is_array($methodOptions['docblock'])) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $methodOptions['docblock'], $this->_docBlockOptions); @@ -172,7 +172,7 @@ public function addProperties(array $properties) if (isset($propertyOptions['docblock'])) { $docBlock = $propertyOptions['docblock']; if (is_array($docBlock)) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $docBlock, $this->_docBlockOptions); $propertyObject->setDocBlock($docBlockObject); diff --git a/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php b/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php index 81ada6a1ee369..59fc5522a12e2 100644 --- a/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php +++ b/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php @@ -9,7 +9,7 @@ * Interface \Magento\Framework\Code\Generator\CodeGeneratorInterface * */ -interface CodeGeneratorInterface extends \Zend\Code\Generator\GeneratorInterface +interface CodeGeneratorInterface extends \Laminas\Code\Generator\GeneratorInterface { /** * Set class name. diff --git a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php index 3efe110ccf193..78360b9b7b1ad 100644 --- a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php +++ b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\Generator; -use Zend\Code\Generator\ValueGenerator; +use Laminas\Code\Generator\ValueGenerator; abstract class EntityAbstract { diff --git a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php index d73c94eb89e00..ddcabdcf85724 100644 --- a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php @@ -8,7 +8,7 @@ /** * Interface method generator. */ -class InterfaceMethodGenerator extends \Zend\Code\Generator\MethodGenerator +class InterfaceMethodGenerator extends \Laminas\Code\Generator\MethodGenerator { /** * {@inheritdoc} diff --git a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php index d2e6c01997b30..08f93ec514b55 100644 --- a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php +++ b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php @@ -54,7 +54,7 @@ public function getConstructorArguments(\ReflectionClass $class, $groupByPositio return $output; } - $constructor = new \Zend\Code\Reflection\MethodReflection($class->getName(), '__construct'); + $constructor = new \Laminas\Code\Reflection\MethodReflection($class->getName(), '__construct'); foreach ($constructor->getParameters() as $parameter) { $name = $parameter->getName(); $position = $parameter->getPosition(); @@ -90,10 +90,10 @@ public function getConstructorArguments(\ReflectionClass $class, $groupByPositio * Process argument type. * * @param \ReflectionClass $class - * @param \Zend\Code\Reflection\ParameterReflection $parameter + * @param \Laminas\Code\Reflection\ParameterReflection $parameter * @return string */ - private function processType(\ReflectionClass $class, \Zend\Code\Reflection\ParameterReflection $parameter) + private function processType(\ReflectionClass $class, \Laminas\Code\Reflection\ParameterReflection $parameter) { if ($parameter->getClass()) { return NamespaceResolver::NS_SEPARATOR . $parameter->getClass()->getName(); diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php index 79d94b4d25a3d..bdcd834527c91 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php @@ -138,11 +138,11 @@ public function testSetClassDocBlock() /** * @param array $expectedDocBlock - * @param \Zend\Code\Generator\DocBlockGenerator $actualDocBlock + * @param \Laminas\Code\Generator\DocBlockGenerator $actualDocBlock */ protected function _assertDocBlockData( array $expectedDocBlock, - \Zend\Code\Generator\DocBlockGenerator $actualDocBlock + \Laminas\Code\Generator\DocBlockGenerator $actualDocBlock ) { // assert plain string data foreach ($expectedDocBlock as $propertyName => $propertyData) { @@ -156,7 +156,7 @@ protected function _assertDocBlockData( $expectedTagsData = $expectedDocBlock['tags']; $actualTags = $actualDocBlock->getTags(); $this->assertSameSize($expectedTagsData, $actualTags); - /** @var $actualTag \Zend\Code\Generator\DocBlock\Tag */ + /** @var $actualTag \Laminas\Code\Generator\DocBlock\Tag */ foreach ($actualTags as $actualTag) { $tagName = $actualTag->getName(); $this->assertArrayHasKey($tagName, $expectedTagsData); @@ -173,7 +173,7 @@ public function testAddMethods() $this->assertSameSize($this->_methodData, $actualMethods); - /** @var $method \Zend\Code\Generator\MethodGenerator */ + /** @var $method \Laminas\Code\Generator\MethodGenerator */ foreach ($actualMethods as $methodName => $method) { $this->assertArrayHasKey($methodName, $this->_methodData); $expectedMethodData = $this->_methodData[$methodName]; @@ -196,7 +196,7 @@ public function testAddMethods() foreach ($expectedMethodData['parameters'] as $parameterData) { $parameterName = $parameterData['name']; $this->assertArrayHasKey($parameterName, $actualParameters); - /** @var $actualParameter \Zend\Code\Generator\ParameterGenerator */ + /** @var $actualParameter \Laminas\Code\Generator\ParameterGenerator */ $actualParameter = $actualParameters[$parameterName]; $this->assertEquals($parameterName, $actualParameter->getName()); @@ -210,7 +210,7 @@ public function testAddMethods() // assert default value if (isset($parameterData['defaultValue'])) { - /** @var $actualDefaultValue \Zend\Code\Generator\ValueGenerator */ + /** @var $actualDefaultValue \Laminas\Code\Generator\ValueGenerator */ $actualDefaultValue = $actualParameter->getDefaultValue(); $this->assertEquals($parameterData['defaultValue'], $actualDefaultValue->getValue()); } @@ -242,11 +242,11 @@ protected function _assertFlag($flagType, array $expectedData, $actualObject) /** * @param array $expectedData - * @param \Zend\Code\Generator\AbstractMemberGenerator $actualObject + * @param \Laminas\Code\Generator\AbstractMemberGenerator $actualObject */ protected function _assertVisibility( array $expectedData, - \Zend\Code\Generator\AbstractMemberGenerator $actualObject + \Laminas\Code\Generator\AbstractMemberGenerator $actualObject ) { $expectedVisibility = isset($expectedData['visibility']) ? $expectedData['visibility'] : 'public'; $this->assertEquals($expectedVisibility, $actualObject->getVisibility()); @@ -260,7 +260,7 @@ protected function _assertVisibility( */ public function testAddMethodFromGenerator() { - $invalidMethod = new \Zend\Code\Generator\MethodGenerator(); + $invalidMethod = new \Laminas\Code\Generator\MethodGenerator(); $this->_model->addMethodFromGenerator($invalidMethod); } @@ -271,7 +271,7 @@ public function testAddProperties() $this->assertSameSize($this->_propertyData, $actualProperties); - /** @var $property \Zend\Code\Generator\PropertyGenerator */ + /** @var $property \Laminas\Code\Generator\PropertyGenerator */ foreach ($actualProperties as $propertyName => $property) { $this->assertArrayHasKey($propertyName, $this->_propertyData); $expectedPropertyData = $this->_propertyData[$propertyName]; @@ -287,7 +287,7 @@ public function testAddProperties() // assert default value if (isset($expectedPropertyData['defaultValue'])) { - /** @var $actualDefaultValue \Zend\Code\Generator\ValueGenerator */ + /** @var $actualDefaultValue \Laminas\Code\Generator\ValueGenerator */ $actualDefaultValue = $property->getDefaultValue(); $this->assertEquals($expectedPropertyData['defaultValue'], $actualDefaultValue->getValue()); } @@ -308,7 +308,7 @@ public function testAddProperties() */ public function testAddPropertyFromGenerator() { - $invalidProperty = new \Zend\Code\Generator\PropertyGenerator(); + $invalidProperty = new \Laminas\Code\Generator\PropertyGenerator(); $this->_model->addPropertyFromGenerator($invalidProperty); } diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php index 4565620a7557e..c5a7cd84686c9 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator\TestAsset; -use Zend\Code\Generator\DocBlockGenerator; +use Laminas\Code\Generator\DocBlockGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class ParentClass /** * Public parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function publicParentMethod( /** * Protected parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -55,7 +55,7 @@ protected function _protectedParentMethod( /** * Private parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php index 5ba3031a2ae4d..08f781c01756a 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator\TestAsset; -use Zend\Code\Generator\ClassGenerator; +use Laminas\Code\Generator\ClassGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class SourceClass extends ParentClass /** * Public child constructor * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function __construct( /** * Public child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -58,7 +58,7 @@ public function publicChildMethod( /** * Public child method with reference * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param array $array * * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -70,7 +70,7 @@ public function publicMethodWithReference(ClassGenerator &$classGenerator, array /** * Protected child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -88,7 +88,7 @@ protected function _protectedChildMethod( /** * Private child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php index b8978c9ef7181..9ea37b0d0f6bd 100644 --- a/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php +++ b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php @@ -9,7 +9,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\WriteFactory; use Magento\Framework\Filesystem\DriverPool; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; use Magento\Setup\Mvc\Bootstrap\InitParamListener; /** diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 058bf18b16d3c..d42715fee9923 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -1957,7 +1957,7 @@ public function insertOnDuplicate($table, array $data, array $fields = []) $field = $this->quoteIdentifier($k); if ($v instanceof \Zend_Db_Expr) { $value = $v->__toString(); - } elseif ($v instanceof \Zend\Db\Sql\Expression) { + } elseif ($v instanceof \Laminas\Db\Sql\Expression) { $value = $v->getExpression(); } elseif (is_string($v)) { $value = sprintf('VALUES(%s)', $this->quoteIdentifier($v)); diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php index eb642278694c0..ab1b1e9cc65f5 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php @@ -26,7 +26,7 @@ class FormKeyTest extends \PHPUnit\Framework\TestCase protected $sessionMock; /** - * @var \Zend\Escaper\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Escaper\Escaper|\PHPUnit_Framework_MockObject_MockObject */ protected $escaperMock; diff --git a/lib/internal/Magento/Framework/Encryption/Helper/Security.php b/lib/internal/Magento/Framework/Encryption/Helper/Security.php index 63884b5c7fb3e..0320468b35f02 100644 --- a/lib/internal/Magento/Framework/Encryption/Helper/Security.php +++ b/lib/internal/Magento/Framework/Encryption/Helper/Security.php @@ -6,10 +6,10 @@ namespace Magento\Framework\Encryption\Helper; -use Zend\Crypt\Utils; +use Laminas\Crypt\Utils; /** - * Class implements compareString from Zend\Crypt + * Class implements compareString from Laminas\Crypt * * @api */ diff --git a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php index 023c4cc4ddba6..65fde6f27fd8a 100644 --- a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php +++ b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php @@ -90,7 +90,7 @@ public function testSendWithOptions(): void $file = __DIR__ . '/../../_files/javascript.js'; $contentType = 'content/type'; - $headers = $this->getMockBuilder(\Zend\Http\Headers::class)->getMock(); + $headers = $this->getMockBuilder(\Laminas\Http\Headers::class)->getMock(); $this->response->expects($this->atLeastOnce()) ->method('setHeader') ->withConsecutive(['Content-length', filesize($file)], ['Content-Type', $contentType]); diff --git a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php index cd42c8d04b477..07fc73eacf282 100644 --- a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php +++ b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php @@ -10,7 +10,7 @@ use Magento\Framework\File\Mime; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\ObjectManager; -use Zend\Http\Headers; +use Laminas\Http\Headers; /** * File adapter to send the file to the client. diff --git a/lib/internal/Magento/Framework/Filesystem/Glob.php b/lib/internal/Magento/Framework/Filesystem/Glob.php index 415bb5494c2e8..a73fa1a371da6 100644 --- a/lib/internal/Magento/Framework/Filesystem/Glob.php +++ b/lib/internal/Magento/Framework/Filesystem/Glob.php @@ -5,13 +5,13 @@ */ namespace Magento\Framework\Filesystem; -use Zend\Stdlib\Glob as ZendGlob; -use Zend\Stdlib\Exception\RuntimeException as ZendRuntimeException; +use Laminas\Stdlib\Glob as LaminasGlob; +use Laminas\Stdlib\Exception\RuntimeException as ZendRuntimeException; /** - * Wrapper for Zend\Stdlib\Glob + * Wrapper for Laminas\Stdlib\Glob */ -class Glob extends ZendGlob +class Glob extends LaminasGlob { /** * Find pathnames matching a pattern. @@ -24,7 +24,7 @@ class Glob extends ZendGlob public static function glob($pattern, $flags = 0, $forceFallback = false) { try { - $result = ZendGlob::glob($pattern, $flags, $forceFallback); + $result = LaminasGlob::glob($pattern, $flags, $forceFallback); } catch (ZendRuntimeException $e) { $result = []; } diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php index 3ecf360f36894..13d6e7b72d89f 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php @@ -7,11 +7,11 @@ use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; use Magento\Framework\Stdlib\StringUtils; -use Zend\Http\Header\HeaderInterface; -use Zend\Stdlib\Parameters; -use Zend\Stdlib\ParametersInterface; -use Zend\Uri\UriFactory; -use Zend\Uri\UriInterface; +use Laminas\Http\Header\HeaderInterface; +use Laminas\Stdlib\Parameters; +use Laminas\Stdlib\ParametersInterface; +use Laminas\Uri\UriFactory; +use Laminas\Uri\UriInterface; /** * HTTP Request for current PHP environment. @@ -19,7 +19,7 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ -class Request extends \Zend\Http\PhpEnvironment\Request +class Request extends \Laminas\Http\PhpEnvironment\Request { /**#@+ * Protocols diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index dc3e63fcc7df8..27ca4ae58948b 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -10,7 +10,7 @@ /** * Base HTTP response object */ -class Response extends \Zend\Http\PhpEnvironment\Response implements \Magento\Framework\App\Response\HttpInterface +class Response extends \Laminas\Http\PhpEnvironment\Response implements \Magento\Framework\App\Response\HttpInterface { /** * Flag; is this response a redirect? @@ -198,7 +198,7 @@ public function sendHeaders() $status = $this->renderStatusLine(); header($status); - /** @var \Zend\Http\Header\HeaderInterface $header */ + /** @var \Laminas\Http\Header\HeaderInterface $header */ foreach ($this->getHeaders() as $header) { header($header->toString(), false); } diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php index 6bd8a977f2a2c..addd4ba93576e 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php @@ -7,7 +7,7 @@ use \Magento\Framework\HTTP\PhpEnvironment\Request; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class RequestTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php index ba6746e75ce34..7ba897ee21cbe 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php @@ -12,7 +12,7 @@ class ResponseTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\HTTP\PhpEnvironment\Response */ protected $response; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\Headers */ + /** @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\Headers */ protected $headers; protected function setUp() @@ -22,7 +22,7 @@ protected function setUp() ['getHeaders', 'send', 'clearHeader'] ); $this->headers = $this->createPartialMock( - \Zend\Http\Headers::class, + \Laminas\Http\Headers::class, ['has', 'get', 'current', 'removeHeader'] ); } @@ -117,7 +117,7 @@ public function testClearHeaderIfHeaderExistsAndWasFound() $this->headers->addHeaderLine('Header-name: header-value'); - $header = \Zend\Http\Header\GenericHeader::fromString('Header-name: header-value'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Header-name: header-value'); $this->headers ->expects($this->once()) @@ -152,7 +152,7 @@ public function testClearHeaderAndHeaderNotExists() $this->headers->addHeaderLine('Header-name: header-value'); - $header = \Zend\Http\Header\GenericHeader::fromString('Header-name: header-value'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Header-name: header-value'); $this->headers ->expects($this->once()) diff --git a/lib/internal/Magento/Framework/Mail/EmailMessage.php b/lib/internal/Magento/Framework/Mail/EmailMessage.php index 02c75977cd093..0b8a1da8a4b85 100644 --- a/lib/internal/Magento/Framework/Mail/EmailMessage.php +++ b/lib/internal/Magento/Framework/Mail/EmailMessage.php @@ -8,9 +8,9 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Zend\Mail\Address as ZendAddress; -use Zend\Mail\AddressList; -use Zend\Mime\Message as ZendMimeMessage; +use Laminas\Mail\Address as ZendAddress; +use Laminas\Mail\AddressList; +use Laminas\Mime\Message as ZendMimeMessage; /** * Email message diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 0e4d79aac9331..1d572e29fecc1 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -5,8 +5,8 @@ */ namespace Magento\Framework\Mail; -use Zend\Mime\Mime; -use Zend\Mime\Part; +use Laminas\Mime\Mime; +use Laminas\Mime\Part; /** * Class Message for email transportation @@ -17,7 +17,7 @@ class Message implements MailMessageInterface { /** - * @var \Zend\Mail\Message + * @var \Laminas\Mail\Message */ protected $zendMessage; @@ -35,7 +35,7 @@ class Message implements MailMessageInterface */ public function __construct($charset = 'utf-8') { - $this->zendMessage = new \Zend\Mail\Message(); + $this->zendMessage = new \Laminas\Mail\Message(); $this->zendMessage->setEncoding($charset); } @@ -164,7 +164,7 @@ public function getRawMessage() * * @param string $body * @param string $messageType - * @return \Zend\Mime\Message + * @return \Laminas\Mime\Message */ private function createMimeFromString($body, $messageType) { @@ -172,7 +172,7 @@ private function createMimeFromString($body, $messageType) $part->setCharset($this->zendMessage->getEncoding()); $part->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE); $part->setType($messageType); - $mimeMessage = new \Zend\Mime\Message(); + $mimeMessage = new \Laminas\Mime\Message(); $mimeMessage->addPart($part); return $mimeMessage; } diff --git a/lib/internal/Magento/Framework/Mail/MimeInterface.php b/lib/internal/Magento/Framework/Mail/MimeInterface.php index 026dd188d1685..a7910e195a160 100644 --- a/lib/internal/Magento/Framework/Mail/MimeInterface.php +++ b/lib/internal/Magento/Framework/Mail/MimeInterface.php @@ -9,7 +9,7 @@ /** * Interface MimeInterface used providing constants * - * @see \Zend\Mime\Mime + * @see \Laminas\Mime\Mime */ interface MimeInterface { diff --git a/lib/internal/Magento/Framework/Mail/MimeMessage.php b/lib/internal/Magento/Framework/Mail/MimeMessage.php index 4d783dafd1d7a..6c293fd957e35 100644 --- a/lib/internal/Magento/Framework/Mail/MimeMessage.php +++ b/lib/internal/Magento/Framework/Mail/MimeMessage.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Mail; -use Zend\Mime\Message as ZendMimeMessage; +use Laminas\Mime\Message as ZendMimeMessage; /** * Class MimeMessage diff --git a/lib/internal/Magento/Framework/Mail/MimePart.php b/lib/internal/Magento/Framework/Mail/MimePart.php index a43ed4b36e072..7cb0e9fbf0097 100644 --- a/lib/internal/Magento/Framework/Mail/MimePart.php +++ b/lib/internal/Magento/Framework/Mail/MimePart.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Zend\Mime\Part as ZendMimePart; +use Laminas\Mime\Part as ZendMimePart; /** * @inheritDoc diff --git a/lib/internal/Magento/Framework/Mail/Transport.php b/lib/internal/Magento/Framework/Mail/Transport.php index 8a7ace17cc9a0..44e8242c965a1 100644 --- a/lib/internal/Magento/Framework/Mail/Transport.php +++ b/lib/internal/Magento/Framework/Mail/Transport.php @@ -7,8 +7,8 @@ use Magento\Framework\Exception\MailException; use Magento\Framework\Phrase; -use Zend\Mail\Message as ZendMessage; -use Zend\Mail\Transport\Sendmail; +use Laminas\Mail\Message as ZendMessage; +use Laminas\Mail\Transport\Sendmail; class Transport implements \Magento\Framework\Mail\TransportInterface { diff --git a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php index 0cd62963c547c..74053d58f0503 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php +++ b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php @@ -11,7 +11,7 @@ use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; use Magento\Framework\MessageQueue\Code\Generator\Config\RemoteServiceReader\Communication as RemoteServiceReader; use Magento\Framework\Reflection\MethodsMap as ServiceMethodsMap; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\MethodReflection; /** * Code generator for remote services. @@ -131,7 +131,7 @@ protected function _getClassMethods() $sourceMethodParameters = $methodReflection->getParameters(); $methodParameters = []; $topicParameters = []; - /** @var \Zend\Code\Reflection\ParameterReflection $methodParameter */ + /** @var \Laminas\Code\Reflection\ParameterReflection $methodParameter */ foreach ($sourceMethodParameters as $methodParameter) { $parameterName = $methodParameter->getName(); $parameter = [ diff --git a/lib/internal/Magento/Framework/Oauth/Helper/Request.php b/lib/internal/Magento/Framework/Oauth/Helper/Request.php index 548c4a5990efe..d5f2fa45e6843 100644 --- a/lib/internal/Magento/Framework/Oauth/Helper/Request.php +++ b/lib/internal/Magento/Framework/Oauth/Helper/Request.php @@ -6,7 +6,7 @@ namespace Magento\Framework\Oauth\Helper; use Magento\Framework\App\RequestInterface; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; class Request { diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php index be484f074342d..aeca85a6e9ae0 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php @@ -11,8 +11,8 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\InputException; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; /** * Class Repository diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php index aa978e7f337cc..fb22cf6872b9e 100644 --- a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php @@ -11,7 +11,7 @@ use Magento\Framework\AuthorizationInterface; use Magento\Framework\Phrase; use Magento\Framework\Api\ExtensionAttributesInterface; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\MethodReflection; /** * Processes extension attributes and produces an array for the data. diff --git a/lib/internal/Magento/Framework/Reflection/MethodsMap.php b/lib/internal/Magento/Framework/Reflection/MethodsMap.php index 6b0ddfbfc2127..57347c62e4244 100644 --- a/lib/internal/Magento/Framework/Reflection/MethodsMap.php +++ b/lib/internal/Magento/Framework/Reflection/MethodsMap.php @@ -7,9 +7,9 @@ namespace Magento\Framework\Reflection; use Magento\Framework\Serialize\SerializerInterface; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; use Magento\Framework\App\Cache\Type\Reflection as ReflectionCache; /** diff --git a/lib/internal/Magento/Framework/Reflection/NameFinder.php b/lib/internal/Magento/Framework/Reflection/NameFinder.php index 5cd4ab744bb1b..81eb4782c4c98 100644 --- a/lib/internal/Magento/Framework/Reflection/NameFinder.php +++ b/lib/internal/Magento/Framework/Reflection/NameFinder.php @@ -6,7 +6,7 @@ namespace Magento\Framework\Reflection; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; class NameFinder { diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index e4c0294c0cfb5..a0ae3b4841d56 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -6,7 +6,7 @@ // @codingStandardsIgnoreStart namespace Magento\Framework\Reflection\Test\Unit; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * NameFinder Unit Test @@ -64,7 +64,7 @@ public function testGetSetterMethodNameWrongCamelCasedAttribute() */ public function testFindAccessorMethodName() { - $reflectionClass = $this->createMock(\Zend\Code\Reflection\ClassReflection::class); + $reflectionClass = $this->createMock(\Laminas\Code\Reflection\ClassReflection::class); $reflectionClass->expects($this->atLeastOnce())->method('hasMethod')->willReturn(false); $reflectionClass->expects($this->atLeastOnce())->method('getName')->willReturn('className'); diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php index 1a8702c0e1c5b..0a1473e7d3d8e 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php @@ -15,7 +15,7 @@ use Magento\Framework\Reflection\Test\Unit\Fixture\UseClasses\SampleTwo\SampleFour; use Magento\Framework\Reflection\Test\Unit\Fixture\UseSample; use Magento\Framework\Reflection\TypeProcessor; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php index 9571fa53547ab..65b485f79aff5 100644 --- a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php @@ -8,12 +8,12 @@ use Magento\Framework\Exception\SerializationException; use Magento\Framework\Phrase; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\DocBlock\Tag\ParamTag; -use Zend\Code\Reflection\DocBlock\Tag\ReturnTag; -use Zend\Code\Reflection\DocBlockReflection; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\DocBlock\Tag\ParamTag; +use Laminas\Code\Reflection\DocBlock\Tag\ReturnTag; +use Laminas\Code\Reflection\DocBlockReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; /** * Type processor of config reader properties @@ -310,7 +310,7 @@ public function getExceptions($methodReflection) if ($methodDocBlock->hasTag('throws')) { $throwsTypes = $methodDocBlock->getTags('throws'); if (is_array($throwsTypes)) { - /** @var $throwsType \Zend\Code\Reflection\DocBlock\Tag\ThrowsTag */ + /** @var $throwsType \Laminas\Code\Reflection\DocBlock\Tag\ThrowsTag */ foreach ($throwsTypes as $throwsType) { $exceptions = array_merge($exceptions, $throwsType->getTypes()); } diff --git a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php index 7d286eca4ed3f..f4092622e0b01 100644 --- a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php +++ b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php @@ -19,7 +19,7 @@ public function isValid($value) return false; } - $validator = new \Zend\Validator\Hostname(\Zend\Validator\Hostname::ALLOW_ALL); + $validator = new \Laminas\Validator\Hostname(\Laminas\Validator\Hostname::ALLOW_ALL); if (!empty($value) && !$validator->isValid($value)) { $this->_addMessages($validator->getMessages()); diff --git a/lib/internal/Magento/Framework/Stdlib/Parameters.php b/lib/internal/Magento/Framework/Stdlib/Parameters.php index dcefaf85d390e..e5743355527c8 100644 --- a/lib/internal/Magento/Framework/Stdlib/Parameters.php +++ b/lib/internal/Magento/Framework/Stdlib/Parameters.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Stdlib; -use Zend\Stdlib\Parameters as ZendParameters; +use Laminas\Stdlib\Parameters as ZendParameters; /** * Class Parameters @@ -100,7 +100,7 @@ public function get($name, $default = null) * * @param string $name * @param mixed $value - * @return \Zend\Stdlib\Parameters + * @return \Laminas\Stdlib\Parameters */ public function set($name, $value) { diff --git a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php index 843ee8b66e710..171430df906fe 100644 --- a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php +++ b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php @@ -12,7 +12,7 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Url\Validator */ protected $object; - /** @var \Zend\Validator\Uri */ + /** @var \Laminas\Validator\Uri */ protected $zendValidator; /** @var string[] */ @@ -22,7 +22,7 @@ protected function setUp() { $objectManager = new ObjectManager($this); - $this->zendValidator = $this->createMock(\Zend\Validator\Uri::class); + $this->zendValidator = $this->createMock(\Laminas\Validator\Uri::class); $this->object = $objectManager->getObject( \Magento\Framework\Url\Validator::class, ['validator' => $this->zendValidator] diff --git a/lib/internal/Magento/Framework/Url/Validator.php b/lib/internal/Magento/Framework/Url/Validator.php index 489e8c502179d..c85853bf48fd2 100644 --- a/lib/internal/Magento/Framework/Url/Validator.php +++ b/lib/internal/Magento/Framework/Url/Validator.php @@ -20,14 +20,14 @@ class Validator extends \Zend_Validate_Abstract /**#@-*/ /** - * @var \Zend\Validator\Uri + * @var \Laminas\Validator\Uri */ private $validator; /** * Object constructor */ - public function __construct(\Zend\Validator\Uri $validator) + public function __construct(\Laminas\Validator\Uri $validator) { // set translated message template $this->setMessage((string)new \Magento\Framework\Phrase("Invalid URL '%value%'."), self::INVALID_URL); diff --git a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php index a25346d673408..c1473097d7262 100644 --- a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php +++ b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php @@ -7,7 +7,7 @@ */ namespace Magento\Framework\Validator; -use \Zend\Uri\Uri; +use \Laminas\Uri\Uri; /** * Check is URI starts from allowed protocol diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index f93d7efda5c8a..902e67bf015b7 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -21,7 +21,7 @@ use Magento\Framework\Reflection\TypeProcessor; use Magento\Framework\Webapi\Exception as WebapiException; use Magento\Framework\Webapi\CustomAttribute\PreprocessorInterface; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * Deserialize arguments from API requests. diff --git a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php index 224421d6561c8..25eacb00c23ae 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php @@ -11,7 +11,7 @@ use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Reflection\MethodsMap; use Magento\Framework\Reflection\TypeProcessor; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * Data object converter diff --git a/lib/internal/Magento/Framework/ZendEscaper.php b/lib/internal/Magento/Framework/ZendEscaper.php index 37182ab97d49d..6edd84d7c87f4 100644 --- a/lib/internal/Magento/Framework/ZendEscaper.php +++ b/lib/internal/Magento/Framework/ZendEscaper.php @@ -8,6 +8,6 @@ /** * Magento wrapper for Zend's Escaper class */ -class ZendEscaper extends \Zend\Escaper\Escaper +class ZendEscaper extends \Laminas\Escaper\Escaper { } diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index dfbfb5a25debe..3689cdccd3e2a 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -30,15 +30,15 @@ "symfony/console": "~4.1.0", "symfony/process": "~4.1.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", "guzzlehttp/guzzle": "^6.3.3" }, "archive": { diff --git a/setup/config/application.config.php b/setup/config/application.config.php index e7efd12023df4..a293e20219b27 100644 --- a/setup/config/application.config.php +++ b/setup/config/application.config.php @@ -5,8 +5,8 @@ */ use Magento\Setup\Mvc\Bootstrap\InitParamListener; -use Zend\Mvc\Service\DiAbstractServiceFactoryFactory; -use Zend\ServiceManager\Di\DiAbstractServiceFactory; +use Laminas\Mvc\Service\DiAbstractServiceFactoryFactory; +use Laminas\ServiceManager\Di\DiAbstractServiceFactory; return [ 'modules' => [ diff --git a/setup/config/di.config.php b/setup/config/di.config.php index fbdebe7a96e18..b9f2ebe2fa4e0 100644 --- a/setup/config/di.config.php +++ b/setup/config/di.config.php @@ -8,8 +8,8 @@ 'di' => [ 'instance' => [ 'preference' => [ - \Zend\EventManager\EventManagerInterface::class => 'EventManager', - \Zend\ServiceManager\ServiceLocatorInterface::class => \Zend\ServiceManager\ServiceManager::class, + \Laminas\EventManager\EventManagerInterface::class => 'EventManager', + \Laminas\ServiceManager\ServiceLocatorInterface::class => \Laminas\ServiceManager\ServiceManager::class, \Magento\Framework\DB\LoggerInterface::class => \Magento\Framework\DB\Logger\Quiet::class, \Magento\Framework\Locale\ConfigInterface::class => \Magento\Framework\Locale\Config::class, \Magento\Framework\Filesystem\DriverInterface::class => diff --git a/setup/config/module.config.php b/setup/config/module.config.php index 7816c8e3599f3..4e6b89ee70d91 100644 --- a/setup/config/module.config.php +++ b/setup/config/module.config.php @@ -37,7 +37,7 @@ ], 'controllers' => [ 'abstract_factories' => [ - \Zend\Mvc\Controller\LazyControllerAbstractFactory::class, + \Laminas\Mvc\Controller\LazyControllerAbstractFactory::class, ], ], ]; diff --git a/setup/src/Magento/Setup/Application.php b/setup/src/Magento/Setup/Application.php index 4366727e080c8..2c34cf00aa66c 100644 --- a/setup/src/Magento/Setup/Application.php +++ b/setup/src/Magento/Setup/Application.php @@ -5,19 +5,19 @@ */ namespace Magento\Setup; -use Zend\Mvc\Application as ZendApplication; -use Zend\Mvc\Service\ServiceManagerConfig; -use Zend\ServiceManager\ServiceManager; +use Laminas\Mvc\Application as ZendApplication; +use Laminas\Mvc\Service\ServiceManagerConfig; +use Laminas\ServiceManager\ServiceManager; /** - * This class is wrapper on \Zend\Mvc\Application and allows to do more customization like services loading, which + * This class is wrapper on \Laminas\Mvc\Application and allows to do more customization like services loading, which * cannot be loaded via configuration. */ class Application { /** - * Creates \Zend\Mvc\Application and bootstrap it. - * This method is similar to \Zend\Mvc\Application::init but allows to load + * Creates \Laminas\Mvc\Application and bootstrap it. + * This method is similar to \Laminas\Mvc\Application::init but allows to load * Magento specific services. * * @param array $configuration @@ -52,8 +52,8 @@ public function bootstrap(array $configuration) } /** - * Uses \Zend\ServiceManager\ServiceManager::get method to load different kind of services. - * Some services cannot be loaded via configuration like \Zend\ServiceManager\Di\DiAbstractServiceFactory and + * Uses \Laminas\ServiceManager\ServiceManager::get method to load different kind of services. + * Some services cannot be loaded via configuration like \Laminas\ServiceManager\Di\DiAbstractServiceFactory and * should be initialized via corresponding factory. * * @param ServiceManager $serviceManager diff --git a/setup/src/Magento/Setup/Console/CommandList.php b/setup/src/Magento/Setup/Console/CommandList.php index f0dad6f4a7452..338330ef91599 100644 --- a/setup/src/Magento/Setup/Console/CommandList.php +++ b/setup/src/Magento/Setup/Console/CommandList.php @@ -7,7 +7,7 @@ namespace Magento\Setup\Console; use Magento\Setup\Console\Command\TablesWhitelistGenerateCommand; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; /** * Class CommandList contains predefined list of commands for Setup. diff --git a/setup/src/Magento/Setup/Console/CompilerPreparation.php b/setup/src/Magento/Setup/Console/CompilerPreparation.php index c39c721b61716..c83aa48636393 100644 --- a/setup/src/Magento/Setup/Console/CompilerPreparation.php +++ b/setup/src/Magento/Setup/Console/CompilerPreparation.php @@ -15,7 +15,7 @@ use Magento\Setup\Console\Command\DiCompileCommand; use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Symfony\Component\Console\Input\ArgvInput; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; /** * Class prepares folders for code generation diff --git a/setup/src/Magento/Setup/Controller/AddDatabase.php b/setup/src/Magento/Setup/Controller/AddDatabase.php index 8d36eece58e22..7002f8e7f64a4 100644 --- a/setup/src/Magento/Setup/Controller/AddDatabase.php +++ b/setup/src/Magento/Setup/Controller/AddDatabase.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class AddDatabase extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/BackupActionItems.php b/setup/src/Magento/Setup/Controller/BackupActionItems.php index a00492e001f8c..a79d9a566dab0 100644 --- a/setup/src/Magento/Setup/Controller/BackupActionItems.php +++ b/setup/src/Magento/Setup/Controller/BackupActionItems.php @@ -9,9 +9,9 @@ use Magento\Framework\Backup\Factory; use Magento\Framework\Backup\Filesystem; use Magento\Framework\Setup\BackupRollback; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; class BackupActionItems extends AbstractActionController { @@ -63,20 +63,20 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel; + $view = new \Laminas\View\Model\ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } /** * Checks disk space availability * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function checkAction() { @@ -114,7 +114,7 @@ public function checkAction() /** * Takes backup for code, media or DB * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function createAction() { diff --git a/setup/src/Magento/Setup/Controller/CompleteBackup.php b/setup/src/Magento/Setup/Controller/CompleteBackup.php index e0e45a208cdf5..158e0b724fd8f 100644 --- a/setup/src/Magento/Setup/Controller/CompleteBackup.php +++ b/setup/src/Magento/Setup/Controller/CompleteBackup.php @@ -6,9 +6,9 @@ namespace Magento\Setup\Controller; use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; class CompleteBackup extends AbstractActionController { @@ -19,7 +19,7 @@ public function indexAction() { $view = new ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } diff --git a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php index 9c20312b23dca..d06407796ff9b 100644 --- a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php +++ b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class CreateAdminAccount extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/CreateBackup.php b/setup/src/Magento/Setup/Controller/CreateBackup.php index 9987cfd13bcf4..97c6f0deef188 100644 --- a/setup/src/Magento/Setup/Controller/CreateBackup.php +++ b/setup/src/Magento/Setup/Controller/CreateBackup.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class CreateBackup extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php index 83c96ed9d43ef..cc987e8339008 100644 --- a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php +++ b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php @@ -9,9 +9,9 @@ use Magento\Framework\Module\FullModuleList; use Magento\Framework\Setup\Lists; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; -use Zend\View\Model\JsonModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +use Laminas\View\Model\JsonModel; class CustomizeYourStore extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/DataOption.php b/setup/src/Magento/Setup/Controller/DataOption.php index 88ebf291016d9..549358f6e3cf2 100644 --- a/setup/src/Magento/Setup/Controller/DataOption.php +++ b/setup/src/Magento/Setup/Controller/DataOption.php @@ -7,10 +7,10 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\UninstallCollector; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** * Controller of data option selection @@ -35,7 +35,7 @@ public function __construct(UninstallCollector $uninstallCollector) /** * Shows data option page * - * @return ViewModel|\Zend\Http\Response + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/DatabaseCheck.php b/setup/src/Magento/Setup/Controller/DatabaseCheck.php index 4b88a8732d2c7..cf6c6ae1b4409 100644 --- a/setup/src/Magento/Setup/Controller/DatabaseCheck.php +++ b/setup/src/Magento/Setup/Controller/DatabaseCheck.php @@ -7,9 +7,9 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Setup\Validator\DbValidator; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Class DatabaseCheck diff --git a/setup/src/Magento/Setup/Controller/DependencyCheck.php b/setup/src/Magento/Setup/Controller/DependencyCheck.php index e0e1050fa628d..44f205ace3925 100644 --- a/setup/src/Magento/Setup/Controller/DependencyCheck.php +++ b/setup/src/Magento/Setup/Controller/DependencyCheck.php @@ -10,9 +10,9 @@ use Magento\Setup\Model\DependencyReadinessCheck; use Magento\Setup\Model\ModuleStatusFactory; use Magento\Setup\Model\UninstallDependencyCheck; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Class DependencyCheck diff --git a/setup/src/Magento/Setup/Controller/Environment.php b/setup/src/Magento/Setup/Controller/Environment.php index c6c7073e02951..2a330bd1453a8 100644 --- a/setup/src/Magento/Setup/Controller/Environment.php +++ b/setup/src/Magento/Setup/Controller/Environment.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Setup\Model\Cron\ReadinessCheck; -use Zend\Mvc\Controller\AbstractActionController; +use Laminas\Mvc\Controller\AbstractActionController; /** * Class Environment @@ -66,20 +66,20 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function indexAction() { - $view = new \Zend\View\Model\JsonModel([]); + $view = new \Laminas\View\Model\JsonModel([]); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } /** * Verifies php version * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpVersionAction() { @@ -91,13 +91,13 @@ public function phpVersionAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_VERSION_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Checks PHP settings * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpSettingsAction() { @@ -109,13 +109,13 @@ public function phpSettingsAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_SETTINGS_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies php verifications * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpExtensionsAction() { @@ -127,7 +127,7 @@ public function phpExtensionsAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_EXTENSIONS_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** @@ -155,7 +155,7 @@ private function getPhpChecksInfo($type) /** * Verifies file permissions * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function filePermissionsAction() { @@ -183,13 +183,13 @@ public function filePermissionsAction() ], ]; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies updater application exists * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function updaterApplicationAction() { @@ -201,13 +201,13 @@ public function updaterApplicationAction() $data = [ 'responseType' => $responseType ]; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies Setup and Updater Cron status * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function cronScriptAction() { @@ -232,6 +232,6 @@ public function cronScriptAction() $updaterCheck['notice']; } $data['responseType'] = $responseType; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } } diff --git a/setup/src/Magento/Setup/Controller/ExtensionGrid.php b/setup/src/Magento/Setup/Controller/ExtensionGrid.php index 48c63eafcf140..cbe9340ffd8f8 100644 --- a/setup/src/Magento/Setup/Controller/ExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/ExtensionGrid.php @@ -7,9 +7,9 @@ use Magento\Setup\Model\PackagesAuth; use Magento\Setup\Model\PackagesData; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Grid; /** @@ -50,7 +50,7 @@ public function __construct( /** * Index page action * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Home.php b/setup/src/Magento/Setup/Controller/Home.php index 4b0f4ef7917bd..a9b45af731b81 100644 --- a/setup/src/Magento/Setup/Controller/Home.php +++ b/setup/src/Magento/Setup/Controller/Home.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller of homepage of setup @@ -15,7 +15,7 @@ class Home extends AbstractActionController { /** - * @return ViewModel|\Zend\Http\Response + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Index.php b/setup/src/Magento/Setup/Controller/Index.php index ea2fadd94f65e..347ef5738add3 100644 --- a/setup/src/Magento/Setup/Controller/Index.php +++ b/setup/src/Magento/Setup/Controller/Index.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Main controller of the Setup Wizard @@ -15,7 +15,7 @@ class Index extends AbstractActionController { /** - * @return ViewModel|\Zend\Http\Response + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Install.php b/setup/src/Magento/Setup/Controller/Install.php index ceb37e7d7b6e0..a47c0e375500f 100644 --- a/setup/src/Magento/Setup/Controller/Install.php +++ b/setup/src/Magento/Setup/Controller/Install.php @@ -14,10 +14,10 @@ use Magento\Setup\Model\InstallerFactory; use Magento\Setup\Model\RequestDataConverter; use Magento\Setup\Model\WebLogger; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** * Install controller diff --git a/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php b/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php index 5589bb963aa3b..b6bed4f3db1f1 100644 --- a/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php @@ -6,9 +6,9 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\PackagesData; /** diff --git a/setup/src/Magento/Setup/Controller/LandingInstaller.php b/setup/src/Magento/Setup/Controller/LandingInstaller.php index 22eea503721be..8aee28df15137 100644 --- a/setup/src/Magento/Setup/Controller/LandingInstaller.php +++ b/setup/src/Magento/Setup/Controller/LandingInstaller.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller for Setup Landing page diff --git a/setup/src/Magento/Setup/Controller/LandingUpdater.php b/setup/src/Magento/Setup/Controller/LandingUpdater.php index 9c6ef98dc6dd1..b3728e404f8c1 100644 --- a/setup/src/Magento/Setup/Controller/LandingUpdater.php +++ b/setup/src/Magento/Setup/Controller/LandingUpdater.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller for Updater Landing page diff --git a/setup/src/Magento/Setup/Controller/License.php b/setup/src/Magento/Setup/Controller/License.php index 84caaebe6c5eb..69778a4bca908 100644 --- a/setup/src/Magento/Setup/Controller/License.php +++ b/setup/src/Magento/Setup/Controller/License.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\License as LicenseModel; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Class LicenseController diff --git a/setup/src/Magento/Setup/Controller/Maintenance.php b/setup/src/Magento/Setup/Controller/Maintenance.php index c3038c1171766..d95b23453e2c9 100644 --- a/setup/src/Magento/Setup/Controller/Maintenance.php +++ b/setup/src/Magento/Setup/Controller/Maintenance.php @@ -6,9 +6,9 @@ namespace Magento\Setup\Controller; use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\Json\Json; class Maintenance extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/Marketplace.php b/setup/src/Magento/Setup/Controller/Marketplace.php index 8b3f19167a8da..99e935baa9169 100644 --- a/setup/src/Magento/Setup/Controller/Marketplace.php +++ b/setup/src/Magento/Setup/Controller/Marketplace.php @@ -5,10 +5,10 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; -use Zend\Json\Json; -use Zend\View\Model\JsonModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +use Laminas\Json\Json; +use Laminas\View\Model\JsonModel; use Magento\Setup\Model\PackagesData; use Magento\Setup\Model\PackagesAuth; @@ -43,7 +43,7 @@ public function indexAction() { $view = new ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } diff --git a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php index c2329d09d6021..189acebc2a5f8 100644 --- a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php +++ b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class MarketplaceCredentials extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/ModuleGrid.php b/setup/src/Magento/Setup/Controller/ModuleGrid.php index 1c1da63ea0017..f01e7bfbac11f 100644 --- a/setup/src/Magento/Setup/Controller/ModuleGrid.php +++ b/setup/src/Magento/Setup/Controller/ModuleGrid.php @@ -11,7 +11,7 @@ /** * Controller for module grid tasks */ -class ModuleGrid extends \Zend\Mvc\Controller\AbstractActionController +class ModuleGrid extends \Laminas\Mvc\Controller\AbstractActionController { /** * Module grid @@ -32,11 +32,11 @@ public function __construct( /** * Index page action * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTerminal(true); return $view; } @@ -44,14 +44,14 @@ public function indexAction() /** * Get Components info action * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel * @throws \RuntimeException */ public function modulesAction() { $moduleList = $this->gridModule->getList(); - return new \Zend\View\Model\JsonModel( + return new \Laminas\View\Model\JsonModel( [ 'success' => true, 'modules' => $moduleList, diff --git a/setup/src/Magento/Setup/Controller/Modules.php b/setup/src/Magento/Setup/Controller/Modules.php index 2d67e0dc65814..4264e8f986641 100644 --- a/setup/src/Magento/Setup/Controller/Modules.php +++ b/setup/src/Magento/Setup/Controller/Modules.php @@ -7,9 +7,9 @@ use Magento\Setup\Model\ModuleStatus; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\Json\Json; class Modules extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 5ac0bbfe38c45..8d7145ccb9751 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -6,9 +6,9 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\Navigation as NavModel; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Cron\Status; /** diff --git a/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php b/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php index 284f0d2aee296..edae5c5903090 100644 --- a/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php +++ b/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php @@ -7,8 +7,8 @@ namespace Magento\Setup\Controller; use Magento\Composer\InfoCommand; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Controller for other components grid on select version page @@ -40,13 +40,13 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel; + $view = new \Laminas\View\Model\ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php index 6c29ebda3bae5..26bcb8dd2f34d 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class ReadinessCheckInstaller extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php index 91e30ef06e703..c272e64a4ef62 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class ReadinessCheckUpdater extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/SelectVersion.php b/setup/src/Magento/Setup/Controller/SelectVersion.php index cfac31432feba..613a1504d77e6 100644 --- a/setup/src/Magento/Setup/Controller/SelectVersion.php +++ b/setup/src/Magento/Setup/Controller/SelectVersion.php @@ -8,9 +8,9 @@ use Magento\Composer\InfoCommand; use Magento\Setup\Model\SystemPackage; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** * Controller for selecting version @@ -32,7 +32,7 @@ public function __construct( } /** - * @return ViewModel|\Zend\Http\Response + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Session.php b/setup/src/Magento/Setup/Controller/Session.php index c9caa5a8de792..76f6f2e859abc 100644 --- a/setup/src/Magento/Setup/Controller/Session.php +++ b/setup/src/Magento/Setup/Controller/Session.php @@ -8,10 +8,10 @@ /** * Sets up session for setup/index.php/session/prolong or redirects to error page */ -class Session extends \Zend\Mvc\Controller\AbstractActionController +class Session extends \Laminas\Mvc\Controller\AbstractActionController { /** - * @var \Zend\ServiceManager\ServiceManager + * @var \Laminas\ServiceManager\ServiceManager */ private $serviceManager; @@ -21,11 +21,11 @@ class Session extends \Zend\Mvc\Controller\AbstractActionController private $objectManagerProvider; /** - * @param \Zend\ServiceManager\ServiceManager $serviceManager + * @param \Laminas\ServiceManager\ServiceManager $serviceManager * @param \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider */ public function __construct( - \Zend\ServiceManager\ServiceManager $serviceManager, + \Laminas\ServiceManager\ServiceManager $serviceManager, \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider ) { $this->serviceManager = $serviceManager; @@ -35,13 +35,13 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel|\Zend\Http\Response + * @return \Laminas\View\Model\ViewModel|\Laminas\Http\Response */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } @@ -78,23 +78,23 @@ public function prolongAction() ); } $session->prolong(); - return new \Zend\View\Model\JsonModel(['success' => true]); + return new \Laminas\View\Model\JsonModel(['success' => true]); } } catch (\Exception $e) { } - return new \Zend\View\Model\JsonModel(['success' => false]); + return new \Laminas\View\Model\JsonModel(['success' => false]); } /** * Unlogin action, return 401 error page * - * @return \Zend\View\Model\ViewModel|\Zend\Http\Response + * @return \Laminas\View\Model\ViewModel|\Laminas\Http\Response */ public function unloginAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTemplate('/error/401.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_401); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_401); return $view; } } diff --git a/setup/src/Magento/Setup/Controller/StartUpdater.php b/setup/src/Magento/Setup/Controller/StartUpdater.php index ff19f8b4c0734..fb4d8ae03b9ef 100644 --- a/setup/src/Magento/Setup/Controller/StartUpdater.php +++ b/setup/src/Magento/Setup/Controller/StartUpdater.php @@ -7,10 +7,10 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\UpdaterTaskCreator; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; +use Laminas\Json\Json; /** * Controller for updater tasks diff --git a/setup/src/Magento/Setup/Controller/Success.php b/setup/src/Magento/Setup/Controller/Success.php index c792aadefe4f0..4df88c5891071 100644 --- a/setup/src/Magento/Setup/Controller/Success.php +++ b/setup/src/Magento/Setup/Controller/Success.php @@ -7,8 +7,8 @@ use Magento\Framework\Module\ModuleList; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class Success extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/SystemConfig.php b/setup/src/Magento/Setup/Controller/SystemConfig.php index e089adaddf40f..f189de9fee95a 100644 --- a/setup/src/Magento/Setup/Controller/SystemConfig.php +++ b/setup/src/Magento/Setup/Controller/SystemConfig.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class SystemConfig extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php b/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php index b5d342cf69745..55ff91a04cdf4 100644 --- a/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php @@ -5,9 +5,9 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Grid; /** diff --git a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php index 37a662b959faa..4a4a5ce7f665f 100644 --- a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php +++ b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class UpdaterSuccess extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/UrlCheck.php b/setup/src/Magento/Setup/Controller/UrlCheck.php index 6a209e0c18304..af7d3738be1e2 100644 --- a/setup/src/Magento/Setup/Controller/UrlCheck.php +++ b/setup/src/Magento/Setup/Controller/UrlCheck.php @@ -5,9 +5,9 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\Json\Json; use Magento\Framework\Validator\Url as UrlValidator; class UrlCheck extends AbstractActionController diff --git a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php index ce5da002a372d..03faef928cdaf 100644 --- a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php +++ b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php @@ -8,9 +8,9 @@ use Magento\Setup\Model\Installer; use Magento\Setup\Model\RequestDataConverter; use Magento\Setup\Validator\AdminCredentialsValidator; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Controller for admin credentials validation diff --git a/setup/src/Magento/Setup/Controller/WebConfiguration.php b/setup/src/Magento/Setup/Controller/WebConfiguration.php index 6dded9f4071ce..68800f0f7404c 100644 --- a/setup/src/Magento/Setup/Controller/WebConfiguration.php +++ b/setup/src/Magento/Setup/Controller/WebConfiguration.php @@ -7,8 +7,8 @@ use Magento\Framework\App\SetupInfo; use Magento\Framework\Config\ConfigOptionsListConstants; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; class WebConfiguration extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Model/AdminAccountFactory.php b/setup/src/Magento/Setup/Model/AdminAccountFactory.php index 5f31bae30d3a9..86687fd28c23e 100644 --- a/setup/src/Magento/Setup/Model/AdminAccountFactory.php +++ b/setup/src/Magento/Setup/Model/AdminAccountFactory.php @@ -7,7 +7,7 @@ namespace Magento\Setup\Model; use Magento\Setup\Module\Setup; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\DB\Adapter\AdapterInterface; diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php index cd2442c215bb3..191d37f00a132 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php @@ -9,7 +9,7 @@ use Magento\Framework\Filesystem; use Magento\Framework\Module\FullModuleList; use Magento\Framework\Setup\ConfigOptionsListInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Collects all ConfigOptionsList class in modules and setup diff --git a/setup/src/Magento/Setup/Model/Cron/JobFactory.php b/setup/src/Magento/Setup/Model/Cron/JobFactory.php index a29bf389254f7..26cdc7aa3de80 100644 --- a/setup/src/Magento/Setup/Model/Cron/JobFactory.php +++ b/setup/src/Magento/Setup/Model/Cron/JobFactory.php @@ -11,7 +11,7 @@ use Magento\Setup\Console\Command\ModuleDisableCommand; use Magento\Setup\Console\Command\ModuleEnableCommand; use Magento\Setup\Console\Command\UpgradeCommand; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Console\Command\MaintenanceDisableCommand; use Magento\Setup\Console\Command\MaintenanceEnableCommand; diff --git a/setup/src/Magento/Setup/Model/InstallerFactory.php b/setup/src/Magento/Setup/Model/InstallerFactory.php index 0fb933dd46cb4..aaa33bd1a5ae4 100644 --- a/setup/src/Magento/Setup/Model/InstallerFactory.php +++ b/setup/src/Magento/Setup/Model/InstallerFactory.php @@ -6,7 +6,7 @@ namespace Magento\Setup\Model; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Module\ResourceFactory; use Magento\Framework\App\ErrorHandler; use Magento\Framework\Setup\LoggerInterface; diff --git a/setup/src/Magento/Setup/Model/Navigation.php b/setup/src/Magento/Setup/Model/Navigation.php index 9882059be0f7e..1c5cee2303c2a 100644 --- a/setup/src/Magento/Setup/Model/Navigation.php +++ b/setup/src/Magento/Setup/Model/Navigation.php @@ -6,7 +6,7 @@ namespace Magento\Setup\Model; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\App\DeploymentConfig; class Navigation diff --git a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php index 79216c8ec89b5..d89976552ab33 100644 --- a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php +++ b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Application; use Magento\Framework\Console\CommandListInterface; use Magento\Framework\ObjectManagerInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Mvc\Bootstrap\InitParamListener; /** diff --git a/setup/src/Magento/Setup/Model/PackagesAuth.php b/setup/src/Magento/Setup/Model/PackagesAuth.php index 5a29f9953d51b..502952db1aa16 100644 --- a/setup/src/Magento/Setup/Model/PackagesAuth.php +++ b/setup/src/Magento/Setup/Model/PackagesAuth.php @@ -7,7 +7,7 @@ namespace Magento\Setup\Model; use Magento\Framework\App\Filesystem\DirectoryList; -use Zend\View\Model\JsonModel; +use Laminas\View\Model\JsonModel; /** * Class PackagesAuth, checks, saves and removes auth details related to packages. @@ -30,7 +30,7 @@ class PackagesAuth /**#@-*/ /** - * @var \Zend\ServiceManager\ServiceLocatorInterface + * @var \Laminas\ServiceManager\ServiceLocatorInterface */ protected $serviceLocator; @@ -55,14 +55,14 @@ class PackagesAuth private $serializer; /** - * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator + * @param \Laminas\ServiceManager\ServiceLocatorInterface $serviceLocator * @param \Magento\Framework\HTTP\Client\Curl $curl * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @throws \RuntimeException */ public function __construct( - \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, + \Laminas\ServiceManager\ServiceLocatorInterface $serviceLocator, \Magento\Framework\HTTP\Client\Curl $curl, \Magento\Framework\Filesystem $filesystem, \Magento\Framework\Serialize\Serializer\Json $serializer = null @@ -198,7 +198,7 @@ public function saveAuthJson($username, $password) ] ] ]; - $json = new \Zend\View\Model\JsonModel($authContent); + $json = new \Laminas\View\Model\JsonModel($authContent); $json->setOption('prettyPrint', true); $jsonContent = $json->serialize(); diff --git a/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php b/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php index e3c598a4aa57e..c80717fe7c857 100644 --- a/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php +++ b/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Setup\Model\Cron\JobComponentUninstall; -use Zend\Json\Json; +use Laminas\Json\Json; /** * Validates payloads for updater tasks diff --git a/setup/src/Magento/Setup/Module.php b/setup/src/Magento/Setup/Module.php index a2c4b1b774b9b..635d954dcda84 100644 --- a/setup/src/Magento/Setup/Module.php +++ b/setup/src/Magento/Setup/Module.php @@ -8,11 +8,11 @@ use Magento\Framework\App\Response\HeaderProvider\XssProtection; use Magento\Setup\Mvc\View\Http\InjectTemplateListener; -use Zend\EventManager\EventInterface; -use Zend\ModuleManager\Feature\BootstrapListenerInterface; -use Zend\ModuleManager\Feature\ConfigProviderInterface; -use Zend\Mvc\ModuleRouteListener; -use Zend\Mvc\MvcEvent; +use Laminas\EventManager\EventInterface; +use Laminas\ModuleManager\Feature\BootstrapListenerInterface; +use Laminas\ModuleManager\Feature\ConfigProviderInterface; +use Laminas\Mvc\ModuleRouteListener; +use Laminas\Mvc\MvcEvent; class Module implements BootstrapListenerInterface, @@ -23,28 +23,28 @@ class Module implements */ public function onBootstrap(EventInterface $e) { - /** @var \Zend\Mvc\MvcEvent $e */ - /** @var \Zend\Mvc\Application $application */ + /** @var \Laminas\Mvc\MvcEvent $e */ + /** @var \Laminas\Mvc\Application $application */ $application = $e->getApplication(); - /** @var \Zend\EventManager\EventManager $events */ + /** @var \Laminas\EventManager\EventManager $events */ $events = $application->getEventManager(); - /** @var \Zend\EventManager\SharedEventManager $sharedEvents */ + /** @var \Laminas\EventManager\SharedEventManager $sharedEvents */ $sharedEvents = $events->getSharedManager(); $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($events); - // Override Zend\Mvc\View\Http\InjectTemplateListener + // Override Laminas\Mvc\View\Http\InjectTemplateListener // to process templates by Vendor/Module $injectTemplateListener = new InjectTemplateListener(); $sharedEvents->attach( - \Zend\Stdlib\DispatchableInterface::class, + \Laminas\Stdlib\DispatchableInterface::class, MvcEvent::EVENT_DISPATCH, [$injectTemplateListener, 'injectTemplate'], -89 ); $response = $e->getResponse(); - if ($response instanceof \Zend\Http\Response) { + if ($response instanceof \Laminas\Http\Response) { $headers = $response->getHeaders(); if ($headers) { $headers->addHeaderLine('Cache-Control', 'no-cache, no-store, must-revalidate'); @@ -52,7 +52,7 @@ public function onBootstrap(EventInterface $e) $headers->addHeaderLine('Expires', '1970-01-01'); $headers->addHeaderLine('X-Frame-Options: SAMEORIGIN'); $headers->addHeaderLine('X-Content-Type-Options: nosniff'); - /** @var \Zend\Http\Header\UserAgent $userAgentHeader */ + /** @var \Laminas\Http\Header\UserAgent $userAgentHeader */ $userAgentHeader = $e->getRequest()->getHeader('User-Agent'); $xssHeaderValue = $userAgentHeader && $userAgentHeader->getFieldValue() && strpos($userAgentHeader->getFieldValue(), XssProtection::IE_8_USER_AGENT) === false diff --git a/setup/src/Magento/Setup/Module/ConnectionFactory.php b/setup/src/Magento/Setup/Module/ConnectionFactory.php index ccb1f819bb0a9..5f50d5efdf381 100644 --- a/setup/src/Magento/Setup/Module/ConnectionFactory.php +++ b/setup/src/Magento/Setup/Module/ConnectionFactory.php @@ -6,7 +6,7 @@ namespace Magento\Setup\Module; use Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Connection adapter factory diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php index be52fc3d24dfd..8c80f339a3a70 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php @@ -9,7 +9,7 @@ /** * @SuppressWarnings(PHPMD) */ -class FileScanner extends \Zend\Code\Scanner\FileScanner +class FileScanner extends \Laminas\Code\Scanner\FileScanner { /** * @var int @@ -26,7 +26,7 @@ protected function scan() } if (!$this->tokens) { - throw new \Zend\Code\Exception\RuntimeException('No tokens were provided'); + throw new \Laminas\Code\Exception\RuntimeException('No tokens were provided'); } /** diff --git a/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php index 1cd242acbe50b..6f8976b552f41 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php @@ -145,7 +145,7 @@ protected function _fetchMissingExtensionAttributesClasses($reflectionClass, $fi $entityType = ucfirst(\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::ENTITY_TYPE); if ($reflectionClass->hasMethod($methodName) && $reflectionClass->isInterface()) { $returnType = $this->typeProcessor->getGetterReturnType( - (new \Zend\Code\Reflection\ClassReflection($reflectionClass->getName()))->getMethod($methodName) + (new \Laminas\Code\Reflection\ClassReflection($reflectionClass->getName()))->getMethod($methodName) ); $missingClassName = $returnType['type']; if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) { diff --git a/setup/src/Magento/Setup/Module/ResourceFactory.php b/setup/src/Magento/Setup/Module/ResourceFactory.php index 7d26226cd6929..e29e6a8726c4b 100644 --- a/setup/src/Magento/Setup/Module/ResourceFactory.php +++ b/setup/src/Magento/Setup/Module/ResourceFactory.php @@ -7,7 +7,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Setup\Module\Setup\ResourceConfig; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; class ResourceFactory { diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index 709be46eac42b..f57fdd4c66962 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -12,16 +12,16 @@ use Magento\Framework\App\State; use Magento\Framework\Filesystem; use Magento\Framework\Shell\ComplexParameter; -use Zend\Console\Request; -use Zend\EventManager\EventManagerInterface; -use Zend\EventManager\ListenerAggregateInterface; -use Zend\Mvc\Application; -use Zend\Mvc\MvcEvent; -use Zend\Router\Http\RouteMatch; -use Zend\ServiceManager\FactoryInterface; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\Stdlib\RequestInterface; -use Zend\Uri\UriInterface; +use Laminas\Console\Request; +use Laminas\EventManager\EventManagerInterface; +use Laminas\EventManager\ListenerAggregateInterface; +use Laminas\Mvc\Application; +use Laminas\Mvc\MvcEvent; +use Laminas\Router\Http\RouteMatch; +use Laminas\ServiceManager\FactoryInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\Stdlib\RequestInterface; +use Laminas\Uri\UriInterface; /** * A listener that injects relevant Magento initialization parameters and initializes filesystem @@ -37,7 +37,7 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface const BOOTSTRAP_PARAM = 'magento-init-params'; /** - * @var \Zend\Stdlib\CallbackHandler[] + * @var \Laminas\Stdlib\CallbackHandler[] */ private $listeners = []; @@ -114,8 +114,8 @@ public function onBootstrap(MvcEvent $e) /** * Check if user logged-in and has permissions * - * @param \Zend\Mvc\MvcEvent $event - * @return false|\Zend\Http\Response + * @param \Laminas\Mvc\MvcEvent $event + * @return false|\Laminas\Http\Response * * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Setup\Exception @@ -162,7 +162,7 @@ public function authPreDispatch($event) !$adminSession->isAllowed('Magento_Backend::setup_wizard') ) { $adminSession->destroy(); - /** @var \Zend\Http\Response $response */ + /** @var \Laminas\Http\Response $response */ $response = $event->getResponse(); $baseUrl = Http::getDistroBaseUrlPath($_SERVER); $response->getHeaders()->addHeaderLine('Location', $baseUrl . 'index.php/session/unlogin'); diff --git a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php index 5d8d903c558d6..2b4b2af1bc84f 100644 --- a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php +++ b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Mvc\View\Http; -use Zend\Mvc\MvcEvent; -use Zend\Mvc\View\Http\InjectTemplateListener as ZendInjectTemplateListener; +use Laminas\Mvc\MvcEvent; +use Laminas\Mvc\View\Http\InjectTemplateListener as ZendInjectTemplateListener; class InjectTemplateListener extends ZendInjectTemplateListener { diff --git a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php index 3f3e06ac9d732..d63461975f21e 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php @@ -16,13 +16,13 @@ class CommandListTest extends \PHPUnit\Framework\TestCase private $commandList; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceManager + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\ServiceManager\ServiceManager */ private $serviceManager; public function setUp() { - $this->serviceManager = $this->createMock(\Zend\ServiceManager\ServiceManager::class); + $this->serviceManager = $this->createMock(\Laminas\ServiceManager\ServiceManager::class); $this->commandList = new CommandList($this->serviceManager); } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php b/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php index 7bd11c64f93ac..212759374847d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php @@ -11,7 +11,7 @@ use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\Filesystem\Driver\File; use Symfony\Component\Console\Input\ArgvInput; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; use Magento\Setup\Console\CompilerPreparation; use PHPUnit_Framework_MockObject_MockObject as Mock; diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php index 6e8306965c6ec..ec346fa6353d9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller AddDatabase */ $controller = new AddDatabase(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php index f6004d808ec6f..93912c3062097 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php @@ -67,11 +67,11 @@ public function setUp() $this->filesystem ); - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->any())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); @@ -91,7 +91,7 @@ public function testCheckAction() $this->directoryList->expects($this->once())->method('getPath')->willReturn(__DIR__); $this->filesystem->expects($this->once())->method('validateAvailableDiscSpace'); $jsonModel = $this->controller->checkAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -106,7 +106,7 @@ public function testCheckActionWithError() $this->throwException(new \Exception("Test error message")) ); $jsonModel = $this->controller->checkAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -118,7 +118,7 @@ public function testCreateAction() { $this->backupRollback->expects($this->once())->method('dbBackup')->willReturn('backup/path/'); $jsonModel = $this->controller->createAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -129,6 +129,6 @@ public function testCreateAction() public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php index ef3290785875f..755552e86de79 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php @@ -25,10 +25,10 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertSame('/error/404.phtml', $viewModel->getTemplate()); $this->assertSame( - \Zend\Http\Response::STATUS_CODE_404, + \Laminas\Http\Response::STATUS_CODE_404, $this->controller->getResponse()->getStatusCode() ); } @@ -36,7 +36,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/complete-backup/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php index b8df922fcc93c..c33e56333cf8d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php @@ -14,7 +14,7 @@ public function testIndexAction() { $controller = new CreateAdminAccount(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php index fd3b36b25525c..eb7d7cee9a2c1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller CreateBackup */ $controller = new CreateBackup(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php index bb4e0c8b9291b..e7b5df737eee5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php @@ -71,7 +71,7 @@ public function testIndexAction($expected, $withSampleData) $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); @@ -109,7 +109,7 @@ public function indexActionDataProvider() public function testDefaultTimeZoneAction() { $jsonModel = $this->controller->defaultTimeZoneAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $this->assertArrayHasKey('defaultTimeZone', $jsonModel->getVariables()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php index 89831dd1471f5..b30a23a92c607 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php @@ -16,17 +16,17 @@ class DataOptionTest extends \PHPUnit\Framework\TestCase private $uninstallCollector; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\PhpEnvironment\Request + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\PhpEnvironment\Request */ private $request; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\PhpEnvironment\Response + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\PhpEnvironment\Response */ private $response; /** - * @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject */ private $mvcEvent; @@ -37,14 +37,14 @@ class DataOptionTest extends \PHPUnit\Framework\TestCase public function setUp() { - $this->request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $this->response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $this->request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $this->response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $this->uninstallCollector = $this->createMock(\Magento\Setup\Model\UninstallCollector::class); $this->controller = new DataOption($this->uninstallCollector); - $this->mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $this->mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $this->mvcEvent->expects($this->any()) ->method('setRequest') ->with($this->request) @@ -64,7 +64,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php index 6050122d78521..825905aa5099b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php @@ -10,7 +10,7 @@ use Magento\Setup\Controller\ReadinessCheckUpdater; use Magento\Setup\Controller\ResponseTypeInterface; use PHPUnit\Framework\MockObject\MockObject; -use Zend\View\Model\JsonModel; +use Laminas\View\Model\JsonModel; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -58,9 +58,9 @@ public function setUp() public function testFilePermissionsInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -72,9 +72,9 @@ public function testFilePermissionsInstaller() public function testPhpVersionActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -87,9 +87,9 @@ public function testPhpVersionActionInstaller() public function testPhpVersionActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -108,9 +108,9 @@ public function testPhpVersionActionUpdater() public function testPhpSettingsActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -123,9 +123,9 @@ public function testPhpSettingsActionInstaller() public function testPhpSettingsActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -144,9 +144,9 @@ public function testPhpSettingsActionUpdater() public function testPhpExtensionsActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -159,9 +159,9 @@ public function testPhpExtensionsActionInstaller() public function testPhpExtensionsActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -291,7 +291,7 @@ public function testCronScriptActionBothNotice() public function testIndexAction() { $model = $this->environment->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $model); } /** @@ -306,7 +306,7 @@ protected function getMvcEventMock( MockObject $response, MockObject $routeMatch ) { - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->once())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php index febcbd1f8dbd4..a97a8d96b34d2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php @@ -97,7 +97,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -119,7 +119,7 @@ public function testExtensionsAction() ); $jsonModel = $this->controller->extensionsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -147,7 +147,7 @@ public function testSyncAction() ); $jsonModel = $this->controller->syncAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php index f258d5d98d107..e39c535fe9980 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller Index */ $controller = new Index(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertFalse($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php index 9eba4413d0cda..a92988f060724 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php @@ -41,7 +41,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - static::assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + static::assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); } /** @@ -56,7 +56,7 @@ public function testExtensionsAction($extensions) ->willReturn($extensions); $jsonModel = $this->controller->extensionsAction(); - static::assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + static::assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); static::assertArrayHasKey('success', $variables); static::assertArrayHasKey('extensions', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php index d0fd62adc42b4..e68ff822e4f1a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php @@ -75,7 +75,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -86,7 +86,7 @@ public function testStartAction() $this->installer->expects($this->exactly(2))->method('getInstallInfo'); $this->deploymentConfig->expects($this->once())->method('isAvailable')->willReturn(false); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('key', $variables); $this->assertArrayHasKey('success', $variables); @@ -101,7 +101,7 @@ public function testStartActionPriorInstallException() $this->installer->expects($this->never())->method('getInstallInfo'); $this->deploymentConfig->expects($this->once())->method('isAvailable')->willReturn(true); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('messages', $variables); @@ -126,7 +126,7 @@ public function testStartActionWithSampleDataError() $this->installer->method('install'); $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -145,7 +145,7 @@ public function testProgressAction() $progress->expects($this->once())->method('getRatio')->willReturn($numValue); $this->webLogger->expects($this->once())->method('get')->willReturn($consoleMessages); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('progress', $variables); $this->assertArrayHasKey('success', $variables); @@ -162,7 +162,7 @@ public function testProgressActionWithError() $this->progressFactory->expects($this->once())->method('createFromLog') ->will($this->throwException(new \LogicException($e))); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -180,7 +180,7 @@ public function testProgressActionWithSampleDataError() $this->progressFactory->expects($this->once())->method('createFromLog')->willReturn($progress); $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -193,7 +193,7 @@ public function testProgressActionNoInstallLogFile() { $this->webLogger->expects($this->once())->method('logfileExists')->willReturn(false); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -204,11 +204,11 @@ public function testProgressActionNoInstallLogFile() public function testDispatch() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->once())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php index d337270dd938b..7db7d30b8b6fc 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php @@ -32,7 +32,7 @@ public function testIndexAction() $controller = new LandingInstaller($productMetadataMock); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertEquals('/magento/setup/landing.phtml', $viewModel->getTemplate()); $variables = $viewModel->getVariables(); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php index 1e75b36334bb9..3ad28a24bb734 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php @@ -32,7 +32,7 @@ public function testIndexAction() $controller = new LandingUpdater($productMetadataMock); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertEquals('/magento/setup/landing.phtml', $viewModel->getTemplate()); $variables = $viewModel->getVariables(); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php index b496051c947ca..367be5f7b8381 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php @@ -30,7 +30,7 @@ public function testIndexActionWithLicense() { $this->licenseModel->expects($this->once())->method('getContents')->willReturn('some license string'); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertArrayHasKey('license', $viewModel->getVariables()); } @@ -38,7 +38,7 @@ public function testIndexActionNoLicense() { $this->licenseModel->expects($this->once())->method('getContents')->willReturn(false); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertArrayHasKey('message', $viewModel->getVariables()); $this->assertEquals('error/404', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php index f4c27ee890785..b938b1e04a81b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php @@ -29,11 +29,11 @@ public function setUp() $this->maintenanceMode = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); $this->controller = new Maintenance($this->maintenanceMode); - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->any())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); @@ -51,7 +51,7 @@ public function testIndexAction() { $this->maintenanceMode->expects($this->once())->method('set'); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -63,7 +63,7 @@ public function testIndexActionWithExceptions() $this->throwException(new \Exception("Test error message")) ); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php index dbdd6003f69c9..91a9bfcfdc296 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php @@ -45,7 +45,7 @@ public function testSaveAuthJsonAction() ->method('saveAuthJson') ->willReturn(true); $jsonModel = $this->controller->saveAuthJsonAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -59,7 +59,7 @@ public function testSaveAuthJsonActionWithError() ->will($this->throwException(new \Exception)); $this->packagesAuth->expects($this->never())->method('saveAuthJson'); $jsonModel = $this->controller->saveAuthJsonAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -77,7 +77,7 @@ public function testCheckAuthAction() ->method('checkCredentials') ->will($this->returnValue(json_encode(['success' => true]))); $jsonModel = $this->controller->checkAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -90,7 +90,7 @@ public function testCheckAuthActionWithError() ->method('getAuthJsonData') ->will($this->throwException(new \Exception)); $jsonModel = $this->controller->checkAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -105,7 +105,7 @@ public function testRemoveCredentialsAction() ->will($this->returnValue(true)); $jsonModel = $this->controller->removeCredentialsAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -118,7 +118,7 @@ public function testRemoveCredentialsWithError() ->method('removeCredentials') ->will($this->throwException(new \Exception)); $jsonModel = $this->controller->removeCredentialsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -128,13 +128,13 @@ public function testRemoveCredentialsWithError() public function testPopupAuthAction() { $viewModel = $this->controller->popupAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php index 2c25aac37d578..25bf754676c23 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php @@ -40,7 +40,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -72,7 +72,7 @@ public function testModulesAction() ->willReturn($moduleList); $jsonModel = $this->controller->modulesAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php index 35a463090a806..243feebbc2655 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php @@ -56,7 +56,7 @@ public function testIndexAction(array $expected) $this->modules->expects($this->once())->method('getAllModules')->willReturn($expected['modules']); $this->status->expects($this->once())->method('checkConstraints')->willReturn([]); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -74,7 +74,7 @@ public function testIndexActionWithError(array $expected) ->method('checkConstraints') ->willReturn(['ModuleA', 'ModuleB']); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('error', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php index a8a3962793d51..595892a71ac98 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php @@ -46,14 +46,14 @@ public function testIndexAction() $this->navigationModel->expects($this->once())->method('getData')->willReturn('some data'); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $viewModel); $this->assertArrayHasKey('nav', $viewModel->getVariables()); } public function testMenuActionUpdater() { $viewModel = $this->controller->menuAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -64,7 +64,7 @@ public function testMenuActionUpdater() public function testMenuActionInstaller() { $viewModel = $this->controller->menuAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -76,7 +76,7 @@ public function testHeaderBarInstaller() { $this->navigationModel->expects($this->once())->method('getType')->willReturn(NavModel::NAV_INSTALLER); $viewModel = $this->controller->headerBarAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -88,7 +88,7 @@ public function testHeaderBarUpdater() { $this->navigationModel->expects($this->once())->method('getType')->willReturn(NavModel::NAV_UPDATER); $viewModel = $this->controller->headerBarAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php index 095c8907bf43d..feb8d1abd2493 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php @@ -69,7 +69,7 @@ public function testComponentsAction() ] ]); $jsonModel = $this->controller->componentsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -109,7 +109,7 @@ public function testComponentsActionWithError() ->method('getInstalledMagentoPackages') ->will($this->throwException(new \Exception("Test error message"))); $jsonModel = $this->controller->componentsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -118,6 +118,6 @@ public function testComponentsActionWithError() public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php index 81e687564b857..71bdc74730bc3 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php @@ -23,7 +23,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('actionFrom', $variables); @@ -33,7 +33,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/readiness-check/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php index a5f3d25e73421..c184cd2f52465 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php @@ -23,7 +23,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('actionFrom', $variables); @@ -33,7 +33,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/readiness-check/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php index 85e060f684d07..3d9dee65c9acc 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php @@ -34,7 +34,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -50,7 +50,7 @@ public function testSystemPackageAction() ] ]); $jsonModel = $this->controller->systemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -62,7 +62,7 @@ public function testSystemPackageActionActionWithError() ->method('getPackageVersions') ->will($this->throwException(new \Exception("Test error message"))); $jsonModel = $this->controller->systemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -80,7 +80,7 @@ public function testInstalledSystemPackageAction() ] ]); $jsonModel = $this->controller->installedSystemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php index 216013ebfc8d9..ecef8409cb0a5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php @@ -21,7 +21,7 @@ class SessionTest extends \PHPUnit\Framework\TestCase private $objectManagerProvider; /** - * @var \Zend\ServiceManager\ServiceManager + * @var \Laminas\ServiceManager\ServiceManager */ private $serviceManager; @@ -33,7 +33,7 @@ public function setUp() $this->createPartialMock(\Magento\Setup\Model\ObjectManagerProvider::class, ['get']); $this->objectManager = $objectManager; $this->objectManagerProvider = $objectManagerProvider; - $this->serviceManager = $this->createPartialMock(\Zend\ServiceManager\ServiceManager::class, ['get']); + $this->serviceManager = $this->createPartialMock(\Laminas\ServiceManager\ServiceManager::class, ['get']); } /** @@ -91,7 +91,7 @@ public function testIndexAction() /** @var $controller Session */ $controller = new Session($this->serviceManager, $this->objectManagerProvider); $viewModel = $controller->unloginAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); } /** @@ -116,6 +116,6 @@ public function testProlongActionWithExistingSession() ->method('get') ->will($this->returnValue($sessionMock)); $controller = new Session($this->serviceManager, $this->objectManagerProvider); - $this->assertEquals(new \Zend\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); + $this->assertEquals(new \Laminas\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php index 7833cf113578a..7daa5fc052d5b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php @@ -21,17 +21,17 @@ class StartUpdaterTest extends \PHPUnit\Framework\TestCase private $controller; /** - * @var \Zend\Http\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Http\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject */ private $request; /** - * @var \Zend\Http\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Http\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject */ private $response; /** - * @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject */ private $mvcEvent; @@ -54,10 +54,10 @@ public function setUp() $this->updaterTaskCreator, $this->payloadValidator ); - $this->request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $this->response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); - $this->mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $this->request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $this->response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); + $this->mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $this->mvcEvent->expects($this->any()) ->method('setRequest') ->with($this->request) @@ -77,7 +77,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php index 1e6b224dbe8d7..91027d76fe627 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php @@ -24,7 +24,7 @@ public function testIndexAction() $controller = new Success($moduleList, $objectManagerProvider); $sampleDataState->expects($this->once())->method('hasError'); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php index 4c93ba0bfd838..29a6d1a370cb8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php @@ -18,7 +18,7 @@ public function testIndexAction() /** @var $controller SystemConfig */ $controller = new SystemConfig(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php index 52f2c2d236541..8a5286af19a06 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php @@ -8,8 +8,8 @@ use Magento\Setup\Controller\UpdateExtensionGrid; use Magento\Setup\Model\Grid\Extension; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** * Class UpdateExtensionGridTest diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php index 3c8997f96dc3d..80f8a54165001 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php @@ -18,7 +18,7 @@ public function testIndexAction() /** @var $controller UpdaterSuccess */ $controller = new UpdaterSuccess($maintenanceMode); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php index d07e5fe53e8db..4aee55c4dbc55 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Test\Unit\Controller; use Magento\Setup\Controller\UrlCheck; -use Zend\Stdlib\RequestInterface; -use Zend\View\Model\JsonModel; +use Laminas\Stdlib\RequestInterface; +use Laminas\View\Model\JsonModel; use Magento\Framework\Validator\Url as UrlValidator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php index 0222e86f958fe..9d921184e57e5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php @@ -16,7 +16,7 @@ public function testIndexAction() $controller = new WebConfiguration(); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertArrayHasKey('autoBaseUrl', $viewModel->getVariables()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php index 1bea7b65c72ca..38eba7c4bde72 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php @@ -13,7 +13,7 @@ class AdminAccountFactoryTest extends \PHPUnit\Framework\TestCase public function testCreate() { $serviceLocatorMock = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, ['get']); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, ['get']); $serviceLocatorMock ->expects($this->once()) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php index 1f8a3fea16da2..88ad666ded388 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php @@ -29,7 +29,7 @@ class JobFactoryTest extends \PHPUnit\Framework\TestCase public function setUp() { $serviceManager = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, [], '', false); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, [], '', false); $status = $this->createMock(\Magento\Setup\Model\Cron\Status::class); $status->expects($this->once())->method('getStatusFilePath')->willReturn('path_a'); $status->expects($this->once())->method('getLogFilePath')->willReturn('path_b'); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php index 0ef30b9ab4d6f..bf9f2072411b1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php @@ -12,7 +12,7 @@ use Magento\Setup\Model\DeclarationInstaller; use Magento\Setup\Model\InstallerFactory; use Magento\Setup\Module\ResourceFactory; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php b/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php index a0089e44067df..ded3ebd696a67 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php @@ -11,7 +11,7 @@ class NavigationTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceLocatorInterface + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\ServiceManager\ServiceLocatorInterface */ private $serviceLocatorMock; @@ -28,7 +28,7 @@ class NavigationTest extends \PHPUnit\Framework\TestCase public function setUp() { $this->serviceLocatorMock = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, ['get']); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, ['get']); $this->serviceLocatorMock ->expects($this->exactly(2)) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php index 552453c4a185c..1081ff3888eed 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php @@ -8,7 +8,7 @@ use Magento\Setup\Model\ObjectManagerProvider; use Magento\Setup\Model\Bootstrap; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\App\ObjectManagerFactory; use Magento\Framework\ObjectManagerInterface; diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php index 4e7707c1dc636..222b7a6812ab8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php @@ -33,7 +33,7 @@ class PackagesAuthTest extends \PHPUnit\Framework\TestCase public function setUp() { - $zendServiceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); + $zendServiceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); $zendServiceLocator ->expects($this->any()) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php index 63fad7d79a314..cbe84f81a0bfa 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php @@ -18,7 +18,7 @@ class ConnectionFactoryTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $serviceLocatorMock = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); + $serviceLocatorMock = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); $objectManagerProviderMock = $this->createMock(\Magento\Setup\Model\ObjectManagerProvider::class); $serviceLocatorMock->expects($this->once()) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php index 870c929f3648c..19fdc172b5306 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php @@ -19,7 +19,7 @@ class ResourceFactoryTest extends \PHPUnit\Framework\TestCase protected function setUp() { $serviceLocatorMock = $this->getMockForAbstractClass( - \Zend\ServiceManager\ServiceLocatorInterface::class, + \Laminas\ServiceManager\ServiceLocatorInterface::class, ['get'] ); $connectionFactory = new ConnectionFactory($serviceLocatorMock); diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index 79717ab555580..eb37e5452bac0 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -9,7 +9,7 @@ use Magento\Framework\App\Bootstrap as AppBootstrap; use Magento\Framework\App\Filesystem\DirectoryList; -use Zend\Mvc\MvcEvent; +use Laminas\Mvc\MvcEvent; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -46,11 +46,11 @@ public function testDetach() public function testOnBootstrap() { - /** @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + /** @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); + $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); $mvcEvent->expects($this->once())->method('getApplication')->willReturn($mvcApplication); - $serviceManager = $this->createMock(\Zend\ServiceManager\ServiceManager::class); + $serviceManager = $this->createMock(\Laminas\ServiceManager\ServiceManager::class); $initParams[AppBootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS][DirectoryList::ROOT] = ['path' => '/test']; $serviceManager->expects($this->once())->method('get') ->willReturn($initParams); @@ -67,7 +67,7 @@ public function testOnBootstrap() ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); - $eventManager = $this->getMockForAbstractClass(\Zend\EventManager\EventManagerInterface::class); + $eventManager = $this->getMockForAbstractClass(\Laminas\EventManager\EventManagerInterface::class); $mvcApplication->expects($this->any())->method('getEventManager')->willReturn($eventManager); $eventManager->expects($this->any())->method('attach'); @@ -95,11 +95,11 @@ public function testCreateDirectoryListException() public function testCreateServiceNotConsole() { /** - * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var \Laminas\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); - $request = $this->createMock(\Zend\Stdlib\RequestInterface::class); + $serviceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + $request = $this->createMock(\Laminas\Stdlib\RequestInterface::class); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); $serviceLocator->expects($this->once())->method('get')->with('Application') ->willReturn($mvcApplication); @@ -121,11 +121,11 @@ public function testCreateService($zfAppConfig, $env, $cliParam, $expectedArray) } $listener = new InitParamListener(); /** - * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var \Laminas\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); - $request = $this->getMockBuilder(\Zend\Console\Request::class)->disableOriginalConstructor()->getMock(); + $serviceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + $request = $this->getMockBuilder(\Laminas\Console\Request::class)->disableOriginalConstructor()->getMock(); $request->expects($this->any()) ->method('getContent') ->willReturn( @@ -230,12 +230,12 @@ private function prepareEventManager() { $this->callbacks[] = [$this->listener, 'onBootstrap']; - /** @var \Zend\EventManager\EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ - $eventManager = $this->createMock(\Zend\EventManager\EventManagerInterface::class); + /** @var \Laminas\EventManager\EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ + $eventManager = $this->createMock(\Laminas\EventManager\EventManagerInterface::class); - $sharedManager = $this->createMock(\Zend\EventManager\SharedEventManager::class); + $sharedManager = $this->createMock(\Laminas\EventManager\SharedEventManager::class); $sharedManager->expects($this->once())->method('attach')->with( - \Zend\Mvc\Application::class, + \Laminas\Mvc\Application::class, MvcEvent::EVENT_BOOTSTRAP, [$this->listener, 'onBootstrap'] ); @@ -252,16 +252,16 @@ private function prepareEventManager() public function testAuthPreDispatch() { $cookiePath = 'test'; - $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(\Laminas\Mvc\MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(\Laminas\Mvc\Router\Http\RouteMatch::class) ->disableOriginalConstructor() ->getMock(); - $applicationMock = $this->getMockBuilder(\Zend\Mvc\Application::class) + $applicationMock = $this->getMockBuilder(\Laminas\Mvc\Application::class) ->disableOriginalConstructor() ->getMock(); - $serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + $serviceManagerMock = $this->getMockBuilder(\Laminas\ServiceManager\ServiceManager::class) ->disableOriginalConstructor() ->getMock(); $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) @@ -295,10 +295,10 @@ public function testAuthPreDispatch() $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) ->disableOriginalConstructor() ->getMock(); - $responseMock = $this->getMockBuilder(\Zend\Http\Response::class) + $responseMock = $this->getMockBuilder(\Laminas\Http\Response::class) ->disableOriginalConstructor() ->getMock(); - $headersMock = $this->getMockBuilder(\Zend\Http\Headers::class) + $headersMock = $this->getMockBuilder(\Laminas\Http\Headers::class) ->disableOriginalConstructor() ->getMock(); @@ -433,10 +433,10 @@ public function testAuthPreDispatch() public function testAuthPreDispatchSkip() { - $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(\Laminas\Mvc\MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(\Laminas\Mvc\Router\Http\RouteMatch::class) ->disableOriginalConstructor() ->getMock(); $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) diff --git a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php index acd7e07fd13aa..b32f4970db1d8 100644 --- a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php +++ b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php @@ -1,32 +1,32 @@ Date: Fri, 17 Jan 2020 17:11:47 +0200 Subject: [PATCH 147/734] Re-Authorize Tokens Integration Entity --- ...wResourcesAccessIntegrationActionGroup.xml | 16 ++++ ...nClickReauthoriseLinkOnGridActionGroup.xml | 15 ++++ ...tegrationOpenExistingEntityActionGroup.xml | 15 ++++ ...nIntegrationSaveAndActivateActionGroup.xml | 16 ++++ ...ationTokensAfterReauthorizeActionGroup.xml | 24 ++++++ .../AdminIntegrationDetailsSection.xml | 17 ++++ .../Section/AdminIntegrationsGridSection.xml | 1 + .../AdminNewIntegrationFormSection.xml | 2 + .../Section/AdminNewIntegrationSection.xml | 6 ++ ...ReAuthorizeTokensIntegrationEntityTest.xml | 77 +++++++++++++++++++ ...ReAuthorizeTokensIntegrationEntityTest.xml | 1 + 11 files changed, 190 insertions(+) create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationClickReauthoriseLinkOnGridActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationSaveAndActivateActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationDetailsSection.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml new file mode 100644 index 0000000000000..272cf130b8aec --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationClickReauthoriseLinkOnGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationClickReauthoriseLinkOnGridActionGroup.xml new file mode 100644 index 0000000000000..053dfb11ce319 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationClickReauthoriseLinkOnGridActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml new file mode 100644 index 0000000000000..8d9af21c63ef6 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationSaveAndActivateActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationSaveAndActivateActionGroup.xml new file mode 100644 index 0000000000000..d5e0cad99181a --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationSaveAndActivateActionGroup.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml new file mode 100644 index 0000000000000..6976ebe9411c8 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationDetailsSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationDetailsSection.xml new file mode 100644 index 0000000000000..8b9776f0bd26c --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationDetailsSection.xml @@ -0,0 +1,17 @@ + + + + +
+ + + + +
+
diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml index ae601542f3b37..7326bef963da1 100644 --- a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml @@ -17,5 +17,6 @@ + diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationFormSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationFormSection.xml index edb9e5dd5eb51..8a83f95e7cbba 100644 --- a/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationFormSection.xml +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationFormSection.xml @@ -18,5 +18,7 @@ + + diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml index 3e7214784c2b5..5f2d1a6b8a8eb 100644 --- a/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml @@ -14,5 +14,11 @@ + + + + + + diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml new file mode 100644 index 0000000000000..2ba04757eb430 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml @@ -0,0 +1,77 @@ + + + + + + + + + + <description value="ReAuthorising Tokens For Created Integration"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login As Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <!-- Click the "Add New Integration" button --> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <!-- Create New Integration --> + <actionGroup ref="AdminFillIntegrationFormActionGroup" stepKey="createIntegration"> + <argument name="integration" value="defaultIntegrationData"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <!-- Click Save And Activate Button --> + <actionGroup ref="AdminIntegrationSaveAndActivateActionGroup" stepKey="clickSaveAndActivateButton"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="allowAccess"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickDoneButton"/> + </before> + <after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="returnToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="searchForReAuthorizedIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteCreatedIntegration"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminIntegrationClickReauthoriseLinkOnGridActionGroup" stepKey="clickReauthoriseLink"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickReauthoriseButton"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabConsumerKey"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabConsumerSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabAccessToken"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabAccessTokenSecret"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="finishTheProcess"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The integration '{{defaultIntegrationData.name}}' has been re-authorized."/> + </actionGroup> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findReAuthorizedIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminIntegrationOpenExistingEntityActionGroup" stepKey="openIntegrationEntity"/> + <actionGroup ref="AssertAdminIntegrationTokensAfterReauthoriseActionGroup" stepKey="assertTokens"> + <argument name="consumerKey" value="{$grabConsumerKey}"/> + <argument name="consumerSecret" value="{$grabConsumerSecret}"/> + <argument name="accessToken" value="{$grabAccessToken}"/> + <argument name="accessTokenSecret" value="{$grabAccessTokenSecret}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ReAuthorizeTokensIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ReAuthorizeTokensIntegrationEntityTest.xml index 739aaf3b1e1c3..bd090ad18a26f 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ReAuthorizeTokensIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ReAuthorizeTokensIntegrationEntityTest.xml @@ -9,6 +9,7 @@ <testCase name="Magento\Integration\Test\TestCase\ReAuthorizeTokensIntegrationEntityTest" summary="Re-Authorize Tokens for the Integration" ticketId="MAGETWO-29648"> <variation name="ReAuthorizeTokensIntegrationEntityTestVariation1"> <data name="description" xsi:type="string">Re-authorize integration</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationSuccessReauthorizeMessage" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationTokensAfterReauthorize" /> </variation> From 2c952d7d3ec24bbbf6ffecc21e053ce80a84e9b9 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Fri, 17 Jan 2020 17:20:58 +0200 Subject: [PATCH 148/734] Migrate ZF2 components to Laminas components --- app/code/Magento/Email/Model/Transport.php | 16 +++++++-------- .../Test/Unit/Model/Oauth/ConsumerTest.php | 4 ++-- .../Magento/Framework/Filesystem/Glob.php | 4 ++-- .../Magento/Framework/Mail/EmailMessage.php | 20 +++++++++---------- .../Magento/Framework/Mail/MimeMessage.php | 6 +++--- .../Magento/Framework/Mail/MimePart.php | 6 +++--- .../Magento/Framework/Mail/Transport.php | 10 +++++----- .../Code/Generator/RemoteServiceGenerator.php | 2 +- .../Magento/Framework/Stdlib/Parameters.php | 11 +++++----- .../Framework/Url/Test/Unit/ValidatorTest.php | 10 +++++----- setup/src/Magento/Setup/Application.php | 6 +++--- .../Magento/Setup/Model/InstallerFactory.php | 2 +- .../Setup/Model/ObjectManagerProvider.php | 2 +- .../Magento/Setup/Module/ResourceFactory.php | 2 +- .../Setup/Mvc/Bootstrap/InitParamListener.php | 4 ++-- .../Mvc/View/Http/InjectTemplateListener.php | 4 ++-- .../Test/Unit/Model/PackagesAuthTest.php | 6 +++--- .../Mvc/Bootstrap/InitParamListenerTest.php | 2 +- 18 files changed, 58 insertions(+), 59 deletions(-) diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index a1e25c41f3bb5..8811809207313 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -53,7 +53,7 @@ class Transport implements TransportInterface /** * @var Sendmail */ - private $zendTransport; + private $laminasTransport; /** * @var MessageInterface @@ -79,7 +79,7 @@ public function __construct( ScopeInterface::SCOPE_STORE ); - $this->zendTransport = new Sendmail($parameters); + $this->laminasTransport = new Sendmail($parameters); $this->message = $message; } @@ -89,16 +89,16 @@ public function __construct( public function sendMessage() { try { - $zendMessage = Message::fromString($this->message->getRawMessage())->setEncoding('utf-8'); + $laminasMessage = Message::fromString($this->message->getRawMessage())->setEncoding('utf-8'); if (2 === $this->isSetReturnPath && $this->returnPathValue) { - $zendMessage->setSender($this->returnPathValue); - } elseif (1 === $this->isSetReturnPath && $zendMessage->getFrom()->count()) { - $fromAddressList = $zendMessage->getFrom(); + $laminasMessage->setSender($this->returnPathValue); + } elseif (1 === $this->isSetReturnPath && $laminasMessage->getFrom()->count()) { + $fromAddressList = $laminasMessage->getFrom(); $fromAddressList->rewind(); - $zendMessage->setSender($fromAddressList->current()->getEmail()); + $laminasMessage->setSender($fromAddressList->current()->getEmail()); } - $this->zendTransport->send($zendMessage); + $this->laminasTransport->send($laminasMessage); } catch (\Exception $e) { throw new MailException(new Phrase($e->getMessage()), $e); } diff --git a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php index 55c494e1ed2dd..13df65f05fcca 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php @@ -6,7 +6,7 @@ namespace Magento\Integration\Test\Unit\Model\Oauth; use Magento\Framework\Url\Validator as UrlValidator; -use Laminas\Validator\Uri as ZendUriValidator; +use Laminas\Validator\Uri as LaminasUriValidator; use Magento\Integration\Model\Oauth\Consumer\Validator\KeyLength; /** @@ -85,7 +85,7 @@ protected function setUp() $this->keyLengthValidator = new KeyLength(); - $this->urlValidator = new UrlValidator(new ZendUriValidator()); + $this->urlValidator = new UrlValidator(new LaminasUriValidator()); $this->oauthDataMock = $this->createPartialMock( \Magento\Integration\Helper\Oauth\Data::class, diff --git a/lib/internal/Magento/Framework/Filesystem/Glob.php b/lib/internal/Magento/Framework/Filesystem/Glob.php index a73fa1a371da6..e7f049dfdf80d 100644 --- a/lib/internal/Magento/Framework/Filesystem/Glob.php +++ b/lib/internal/Magento/Framework/Filesystem/Glob.php @@ -6,7 +6,7 @@ namespace Magento\Framework\Filesystem; use Laminas\Stdlib\Glob as LaminasGlob; -use Laminas\Stdlib\Exception\RuntimeException as ZendRuntimeException; +use Laminas\Stdlib\Exception\RuntimeException as LaminasRuntimeException; /** * Wrapper for Laminas\Stdlib\Glob @@ -25,7 +25,7 @@ public static function glob($pattern, $flags = 0, $forceFallback = false) { try { $result = LaminasGlob::glob($pattern, $flags, $forceFallback); - } catch (ZendRuntimeException $e) { + } catch (LaminasRuntimeException $e) { $result = []; } return $result; diff --git a/lib/internal/Magento/Framework/Mail/EmailMessage.php b/lib/internal/Magento/Framework/Mail/EmailMessage.php index 0b8a1da8a4b85..ccb8a77227a27 100644 --- a/lib/internal/Magento/Framework/Mail/EmailMessage.php +++ b/lib/internal/Magento/Framework/Mail/EmailMessage.php @@ -8,9 +8,9 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Laminas\Mail\Address as ZendAddress; +use Laminas\Mail\Address as LaminasAddress; use Laminas\Mail\AddressList; -use Laminas\Mime\Message as ZendMimeMessage; +use Laminas\Mime\Message as LaminasMimeMessage; /** * Email message @@ -61,7 +61,7 @@ public function __construct( ?string $encoding = 'utf-8' ) { parent::__construct($encoding); - $mimeMessage = new ZendMimeMessage(); + $mimeMessage = new LaminasMimeMessage(); $mimeMessage->setParts($body->getParts()); $this->zendMessage->setBody($mimeMessage); if ($subject) { @@ -153,15 +153,15 @@ public function getReplyTo(): ?array */ public function getSender(): ?Address { - /** @var ZendAddress $zendSender */ - if (!$zendSender = $this->zendMessage->getSender()) { + /** @var LaminasAddress $laminasSender */ + if (!$laminasSender = $this->zendMessage->getSender()) { return null; } return $this->addressFactory->create( [ - 'email' => $zendSender->getEmail(), - 'name' => $zendSender->getName() + 'email' => $laminasSender->getEmail(), + 'name' => $laminasSender->getName() ] ); } @@ -222,11 +222,11 @@ private function convertAddressListToAddressArray(AddressList $addressList): arr */ private function convertAddressArrayToAddressList(array $arrayList): AddressList { - $zendAddressList = new AddressList(); + $laminasAddressList = new AddressList(); foreach ($arrayList as $address) { - $zendAddressList->add($address->getEmail(), $address->getName()); + $laminasAddressList->add($address->getEmail(), $address->getName()); } - return $zendAddressList; + return $laminasAddressList; } } diff --git a/lib/internal/Magento/Framework/Mail/MimeMessage.php b/lib/internal/Magento/Framework/Mail/MimeMessage.php index 6c293fd957e35..78d2a42637ff2 100644 --- a/lib/internal/Magento/Framework/Mail/MimeMessage.php +++ b/lib/internal/Magento/Framework/Mail/MimeMessage.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Mail; -use Laminas\Mime\Message as ZendMimeMessage; +use Laminas\Mime\Message as LaminasMimeMessage; /** * Class MimeMessage @@ -15,7 +15,7 @@ class MimeMessage implements MimeMessageInterface { /** - * @var ZendMimeMessage + * @var LaminasMimeMessage */ private $mimeMessage; @@ -26,7 +26,7 @@ class MimeMessage implements MimeMessageInterface */ public function __construct(array $parts) { - $this->mimeMessage = new ZendMimeMessage(); + $this->mimeMessage = new LaminasMimeMessage(); $this->mimeMessage->setParts($parts); } diff --git a/lib/internal/Magento/Framework/Mail/MimePart.php b/lib/internal/Magento/Framework/Mail/MimePart.php index 7cb0e9fbf0097..d02ebffd5dc7b 100644 --- a/lib/internal/Magento/Framework/Mail/MimePart.php +++ b/lib/internal/Magento/Framework/Mail/MimePart.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Laminas\Mime\Part as ZendMimePart; +use Laminas\Mime\Part as LaminasMimePart; /** * @inheritDoc @@ -21,7 +21,7 @@ class MimePart implements MimePartInterface public const CHARSET_UTF8 = 'utf-8'; /** - * @var ZendMimePart + * @var LaminasMimePart */ private $mimePart; @@ -61,7 +61,7 @@ public function __construct( ?bool $isStream = null ) { try { - $this->mimePart = new ZendMimePart($content); + $this->mimePart = new LaminasMimePart($content); } catch (\Exception $e) { throw new InvalidArgumentException($e->getMessage()); } diff --git a/lib/internal/Magento/Framework/Mail/Transport.php b/lib/internal/Magento/Framework/Mail/Transport.php index 44e8242c965a1..0be387f22ac08 100644 --- a/lib/internal/Magento/Framework/Mail/Transport.php +++ b/lib/internal/Magento/Framework/Mail/Transport.php @@ -7,7 +7,7 @@ use Magento\Framework\Exception\MailException; use Magento\Framework\Phrase; -use Laminas\Mail\Message as ZendMessage; +use Laminas\Mail\Message as LaminasMessage; use Laminas\Mail\Transport\Sendmail; class Transport implements \Magento\Framework\Mail\TransportInterface @@ -15,7 +15,7 @@ class Transport implements \Magento\Framework\Mail\TransportInterface /** * @var Sendmail */ - private $zendTransport; + private $laminasTransport; /** * @var MessageInterface @@ -28,7 +28,7 @@ class Transport implements \Magento\Framework\Mail\TransportInterface */ public function __construct(MessageInterface $message, $parameters = null) { - $this->zendTransport = new Sendmail($parameters); + $this->laminasTransport = new Sendmail($parameters); $this->message = $message; } @@ -38,8 +38,8 @@ public function __construct(MessageInterface $message, $parameters = null) public function sendMessage() { try { - $this->zendTransport->send( - ZendMessage::fromString($this->message->getRawMessage()) + $this->laminasTransport->send( + LaminasMessage::fromString($this->message->getRawMessage()) ); } catch (\Exception $e) { throw new MailException(new Phrase($e->getMessage()), $e); diff --git a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php index 74053d58f0503..22d93b2cc7dcf 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php +++ b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php @@ -126,7 +126,7 @@ protected function _getClassMethods() $methods = [$this->_getDefaultConstructorDefinition()]; $interfaceMethodsMap = $this->serviceMethodsMap->getMethodsMap($this->getSourceClassName()); foreach (array_keys($interfaceMethodsMap) as $methodName) { - // Uses Zend Reflection instead MethodsMap service, because second does not support features of PHP 7.x + // Uses Laminas Reflection instead MethodsMap service, because second does not support features of PHP 7.x $methodReflection = new MethodReflection($this->getSourceClassName(), $methodName); $sourceMethodParameters = $methodReflection->getParameters(); $methodParameters = []; diff --git a/lib/internal/Magento/Framework/Stdlib/Parameters.php b/lib/internal/Magento/Framework/Stdlib/Parameters.php index e5743355527c8..98bb7aa945226 100644 --- a/lib/internal/Magento/Framework/Stdlib/Parameters.php +++ b/lib/internal/Magento/Framework/Stdlib/Parameters.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Stdlib; -use Laminas\Stdlib\Parameters as ZendParameters; +use Laminas\Stdlib\Parameters as LaminasParameters; /** * Class Parameters @@ -15,16 +15,15 @@ class Parameters { /** - * @var ZendParameters + * @var LaminasParameters */ private $parameters; /** - * @param ZendParameters $parameters + * @param LaminasParameters $parameters */ - public function __construct( - ZendParameters $parameters - ) { + public function __construct(LaminasParameters $parameters) + { $this->parameters = $parameters; } diff --git a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php index 171430df906fe..fbdfec82a58dd 100644 --- a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php +++ b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php @@ -13,7 +13,7 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase protected $object; /** @var \Laminas\Validator\Uri */ - protected $zendValidator; + protected $laminasValidator; /** @var string[] */ protected $expectedValidationMessages = ['invalidUrl' => "Invalid URL '%value%'."]; @@ -22,10 +22,10 @@ protected function setUp() { $objectManager = new ObjectManager($this); - $this->zendValidator = $this->createMock(\Laminas\Validator\Uri::class); + $this->laminasValidator = $this->createMock(\Laminas\Validator\Uri::class); $this->object = $objectManager->getObject( \Magento\Framework\Url\Validator::class, - ['validator' => $this->zendValidator] + ['validator' => $this->laminasValidator] ); } @@ -36,7 +36,7 @@ public function testConstruct() public function testIsValidWhenValid() { - $this->zendValidator + $this->laminasValidator ->method('isValid') ->with('http://example.com') ->willReturn(true); @@ -47,7 +47,7 @@ public function testIsValidWhenValid() public function testIsValidWhenInvalid() { - $this->zendValidator + $this->laminasValidator ->method('isValid') ->with('%value%') ->willReturn(false); diff --git a/setup/src/Magento/Setup/Application.php b/setup/src/Magento/Setup/Application.php index 2c34cf00aa66c..5a729dc03e97e 100644 --- a/setup/src/Magento/Setup/Application.php +++ b/setup/src/Magento/Setup/Application.php @@ -5,7 +5,7 @@ */ namespace Magento\Setup; -use Laminas\Mvc\Application as ZendApplication; +use Laminas\Mvc\Application as LaminasApplication; use Laminas\Mvc\Service\ServiceManagerConfig; use Laminas\ServiceManager\ServiceManager; @@ -21,7 +21,7 @@ class Application * Magento specific services. * * @param array $configuration - * @return ZendApplication + * @return LaminasApplication */ public function bootstrap(array $configuration) { @@ -40,7 +40,7 @@ public function bootstrap(array $configuration) } $listeners = $this->getListeners($serviceManager, $configuration); - $application = new ZendApplication( + $application = new LaminasApplication( $configuration, $serviceManager, $serviceManager->get('EventManager'), diff --git a/setup/src/Magento/Setup/Model/InstallerFactory.php b/setup/src/Magento/Setup/Model/InstallerFactory.php index aaa33bd1a5ae4..24634be6beba8 100644 --- a/setup/src/Magento/Setup/Model/InstallerFactory.php +++ b/setup/src/Magento/Setup/Model/InstallerFactory.php @@ -17,7 +17,7 @@ class InstallerFactory { /** - * Zend Framework's service locator + * Laminas Framework's service locator * * @var ServiceLocatorInterface */ diff --git a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php index d89976552ab33..022f89f2eea78 100644 --- a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php +++ b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php @@ -15,7 +15,7 @@ /** * Object manager provider * - * Links Zend Framework's service locator and Magento object manager. + * Links Laminas Framework's service locator and Magento object manager. * Guaranties single object manager per application run. * Hides complexity of creating Magento object manager */ diff --git a/setup/src/Magento/Setup/Module/ResourceFactory.php b/setup/src/Magento/Setup/Module/ResourceFactory.php index e29e6a8726c4b..947afda59dc34 100644 --- a/setup/src/Magento/Setup/Module/ResourceFactory.php +++ b/setup/src/Magento/Setup/Module/ResourceFactory.php @@ -12,7 +12,7 @@ class ResourceFactory { /** - * Zend Framework's service locator + * Laminas Framework's service locator * * @var ServiceLocatorInterface */ diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index f57fdd4c66962..dc564c3a8f7c5 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -54,8 +54,8 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface /** * @inheritdoc * - * The $priority argument is added to support latest versions of Zend Event Manager. - * Starting from Zend Event Manager 3.0.0 release the ListenerAggregateInterface::attach() + * The $priority argument is added to support latest versions of Laminas Event Manager. + * Starting from Laminas Event Manager 3.0.0 release the ListenerAggregateInterface::attach() * supports the `priority` argument. * * @param EventManagerInterface $events diff --git a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php index 2b4b2af1bc84f..1f230ddbaefa5 100644 --- a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php +++ b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php @@ -7,9 +7,9 @@ namespace Magento\Setup\Mvc\View\Http; use Laminas\Mvc\MvcEvent; -use Laminas\Mvc\View\Http\InjectTemplateListener as ZendInjectTemplateListener; +use Laminas\Mvc\View\Http\InjectTemplateListener as LaminasInjectTemplateListener; -class InjectTemplateListener extends ZendInjectTemplateListener +class InjectTemplateListener extends LaminasInjectTemplateListener { /** * Determine the top-level namespace of the controller diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php index 222b7a6812ab8..8f0f9d830da43 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php @@ -33,8 +33,8 @@ class PackagesAuthTest extends \PHPUnit\Framework\TestCase public function setUp() { - $zendServiceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); - $zendServiceLocator + $laminasServiceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); + $laminasServiceLocator ->expects($this->any()) ->method('get') ->with('config') @@ -55,7 +55,7 @@ function ($serializedData) { } ); $this->packagesAuth = new PackagesAuth( - $zendServiceLocator, + $laminasServiceLocator, $this->curl, $this->filesystem, $this->serializerMock diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index eb37e5452bac0..b063186c26fca 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -107,7 +107,7 @@ public function testCreateServiceNotConsole() } /** - * @param array $zfAppConfig Data that comes from Zend Framework Application config + * @param array $zfAppConfig Data that comes from Laminas Framework Application config * @param array $env Config that comes from SetEnv * @param string $cliParam Parameter string * @param array $expectedArray Expected result array From c509a4e735ba67d27f48d57964f8c94f057721e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Daniel <krzysztof.daniel@creativestyle.pl> Date: Sat, 14 Dec 2019 00:01:16 +0100 Subject: [PATCH 149/734] Fixes phpcs errors and warnings for Magento\Framework\Image I was working with those classes and spotted a few warnings and errors that prevent from smooth pull requests. This PR fixes them. It introduces Localized Exceptions in place of \Exception too. Watermark method was to long so it was devided to separate parts --- .../Image/Adapter/AbstractAdapter.php | 8 +- .../Image/Adapter/AdapterInterface.php | 3 + .../Framework/Image/Adapter/Config.php | 4 +- .../Image/Adapter/ConfigInterface.php | 4 + .../Magento/Framework/Image/Adapter/Gd2.php | 2 +- .../Framework/Image/Adapter/ImageMagick.php | 165 ++++++++++++------ .../Image/Adapter/UploadConfigInterface.php | 1 + .../Framework/Image/AdapterFactory.php | 3 + .../Magento/Framework/Image/Factory.php | 3 + .../Framework/View/Element/BlockFactory.php | 2 +- .../Element/UiComponent/SubjectInterface.php | 3 + 11 files changed, 140 insertions(+), 58 deletions(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php index b06f2f9e62397..8b983809e643f 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php @@ -24,8 +24,9 @@ abstract class AbstractAdapter implements AdapterInterface */ public $imageBackgroundColor = 0; - /** - * Position constants + /**#@+ + * Position constants. + * Used mainly for watermarks */ const POSITION_TOP_LEFT = 'top-left'; @@ -40,9 +41,10 @@ abstract class AbstractAdapter implements AdapterInterface const POSITION_TILE = 'tile'; const POSITION_CENTER = 'center'; + /**#@-*/ /** - * Default font size + * The size of the font to use as default */ const DEFAULT_FONT_SIZE = 15; diff --git a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php index b31ed5c773495..a981df9d9fddc 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php @@ -28,6 +28,8 @@ interface AdapterInterface public function getColorAt($x, $y); /** + * Render image and return its binary contents + * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -99,6 +101,7 @@ public function crop($top = 0, $left = 0, $right = 0, $bottom = 0); /** * Save image to specific path. + * * If some folders of path does not exist they will be created * * @param null|string $destination diff --git a/lib/internal/Magento/Framework/Image/Adapter/Config.php b/lib/internal/Magento/Framework/Image/Adapter/Config.php index 636bbcdcbdb41..d4a28b4efc600 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Config.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Config.php @@ -18,13 +18,13 @@ class Config implements ConfigInterface, UploadConfigInterface /** * Config path for the maximal image width value - * @deprecated + * @deprecated Used in a method that is deprecated */ const XML_PATH_MAX_WIDTH_IMAGE = 'system/upload_configuration/max_width'; /** * Config path for the maximal image height value - * @deprecated + * @deprecated Used in a method that is deprecated */ const XML_PATH_MAX_HEIGHT_IMAGE = 'system/upload_configuration/max_height'; diff --git a/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php index dab642b14ac6c..aa014b61e867a 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php @@ -12,11 +12,15 @@ interface ConfigInterface { /** + * Get adapter alias + * * @return string */ public function getAdapterAlias(); /** + * Get adapters + * * @return array */ public function getAdapters(); diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 7e92b336cfdc0..d60cdcb6fdf86 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -11,7 +11,7 @@ * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ -class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter +class Gd2 extends AbstractAdapter { /** * Required extensions diff --git a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php index cd49f283d33a7..24e036c02e718 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php @@ -5,7 +5,12 @@ */ namespace Magento\Framework\Image\Adapter; -class ImageMagick extends \Magento\Framework\Image\Adapter\AbstractAdapter +use Magento\Framework\Exception\LocalizedException; + +/** + * Image adapter from ImageMagick + */ +class ImageMagick extends AbstractAdapter { /** * The blur factor where > 1 is blurry, < 1 is sharp @@ -66,7 +71,7 @@ public function backgroundColor($color = null) * * @param string $filename * @return void - * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException */ public function open($filename) { @@ -77,7 +82,11 @@ public function open($filename) try { $this->_imageHandler = new \Imagick($this->_fileName); } catch (\ImagickException $e) { - throw new \Exception(sprintf('Unsupported image format. File: %s', $this->_fileName), $e->getCode(), $e); + throw new LocalizedException( + __('Unsupported image format. File: %1', $this->_fileName), + $e, + $e->getCode() + ); } $this->backgroundColor(); @@ -86,12 +95,13 @@ public function open($filename) /** * Save image to specific path. + * * If some folders of path does not exist they will be created * * @param null|string $destination * @param null|string $newName * @return void - * @throws \Exception If destination path is not writable + * @throws \Magento\Framework\Exception\LocalizedException If destination path is not writable */ public function save($destination = null, $newName = null) { @@ -124,6 +134,8 @@ protected function _applyOptions() } /** + * Render image and return its binary contents + * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -241,7 +253,7 @@ public function crop($top = 0, $left = 0, $right = 0, $bottom = 0) * @param bool $tile * @return void * @throws \LogicException - * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -253,28 +265,12 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = $this->_checkCanProcess(); $opacity = $this->getWatermarkImageOpacity() ? $this->getWatermarkImageOpacity() : $opacity; - $opacity = (double)number_format($opacity / 100, 1); - $watermark = new \Imagick($imagePath); - if ($this->getWatermarkWidth() && - $this->getWatermarkHeight() && - $this->getWatermarkPosition() != self::POSITION_STRETCH - ) { - $watermark->resizeImage( - $this->getWatermarkWidth(), - $this->getWatermarkHeight(), - \Imagick::FILTER_CUBIC, - self::BLUR_FACTOR - ); - } + $watermark = new \Imagick($imagePath); - if (method_exists($watermark, 'getImageAlphaChannel')) { - // available from imagick 6.4.0 - if ($watermark->getImageAlphaChannel() == 0) { - $watermark->setImageAlphaChannel(\Imagick::ALPHACHANNEL_OPAQUE); - } - } + $this->resizeWatermark($watermark); + $this->handleWatermarkAlphaChannel($watermark); $compositeChannels = \Imagick::CHANNEL_ALL; $watermark->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $opacity, \Imagick::CHANNEL_OPACITY); @@ -307,33 +303,16 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = try { if ($tile) { - $offsetX = $positionX; - $offsetY = $positionY; - while ($offsetY <= $this->_imageSrcHeight + $watermark->getImageHeight()) { - while ($offsetX <= $this->_imageSrcWidth + $watermark->getImageWidth()) { - $this->_imageHandler->compositeImage( - $watermark, - \Imagick::COMPOSITE_OVER, - $offsetX, - $offsetY, - $compositeChannels - ); - $offsetX += $watermark->getImageWidth(); - } - $offsetX = $positionX; - $offsetY += $watermark->getImageHeight(); - } + $this->addTiledWatermark($positionX, $positionY, $watermark, $compositeChannels); } else { - $this->_imageHandler->compositeImage( - $watermark, - \Imagick::COMPOSITE_OVER, - $positionX, - $positionY, - $compositeChannels - ); + $this->addSingleWatermark($positionX, $positionY, $watermark, $compositeChannels); } } catch (\ImagickException $e) { - throw new \Exception('Unable to create watermark.', $e->getCode(), $e); + throw new LocalizedException( + __('Unable to create watermark.'), + $e, + $e->getCode() + ); } // merge layers @@ -346,12 +325,14 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = * Checks required dependencies * * @return void - * @throws \Exception If some of dependencies are missing + * @throws \Magento\Framework\Exception\LocalizedException If some of dependencies are missing */ public function checkDependencies() { - if (!class_exists('\Imagick', false)) { - throw new \Exception("Required PHP extension 'Imagick' was not loaded."); + if (!class_exists('Imagick', false)) { + throw new LocalizedException( + __('Required PHP extension \'Imagick\' was not loaded.') + ); } } @@ -499,4 +480,86 @@ protected function _getImagickPixelObject($color = null) { return new \ImagickPixel($color); } + + /** + * Resizes watermark to desired size, when it is not stretched + * + * @param \Imagick $watermark + */ + private function resizeWatermark(\Imagick $watermark): void + { + if ($this->getWatermarkWidth() && + $this->getWatermarkHeight() && + $this->getWatermarkPosition() != self::POSITION_STRETCH + ) { + $watermark->resizeImage( + $this->getWatermarkWidth(), + $this->getWatermarkHeight(), + \Imagick::FILTER_CUBIC, + self::BLUR_FACTOR + ); + } + } + + /** + * Keeps transparenty if watermark is transparent + * + * @param \Imagick $watermark + */ + private function handleWatermarkAlphaChannel(\Imagick $watermark): void + { + if (method_exists($watermark, 'getImageAlphaChannel')) { + // available from imagick 6.4.0 + if ($watermark->getImageAlphaChannel() == 0) { + $watermark->setImageAlphaChannel(\Imagick::ALPHACHANNEL_OPAQUE); + } + } + } + + /** + * Add tiled watermark at starting given X,Y position + * + * @param int $positionX + * @param int $positionY + * @param \Imagick $watermark + * @param bool $compositeChannels + */ + private function addTiledWatermark($positionX, $positionY, \Imagick $watermark, $compositeChannels): void + { + $offsetX = $positionX; + $offsetY = $positionY; + while ($offsetY <= $this->_imageSrcHeight + $watermark->getImageHeight()) { + while ($offsetX <= $this->_imageSrcWidth + $watermark->getImageWidth()) { + $this->_imageHandler->compositeImage( + $watermark, + \Imagick::COMPOSITE_OVER, + $offsetX, + $offsetY, + $compositeChannels + ); + $offsetX += $watermark->getImageWidth(); + } + $offsetX = $positionX; + $offsetY += $watermark->getImageHeight(); + } + } + + /** + * Add watermark at given X,Y position + * + * @param int $positionX + * @param int $positionY + * @param \Imagick $watermark + * @param bool $compositeChannels + */ + private function addSingleWatermark($positionX, int $positionY, \Imagick $watermark, bool $compositeChannels): void + { + $this->_imageHandler->compositeImage( + $watermark, + \Imagick::COMPOSITE_OVER, + $positionX, + $positionY, + $compositeChannels + ); + } } diff --git a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php index 0a2dbefff8ee0..54fa7a57c7c7e 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php @@ -9,6 +9,7 @@ /** * Interface UploadConfigInterface + * * @deprecated moved to proper namespace and extended * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface; */ diff --git a/lib/internal/Magento/Framework/Image/AdapterFactory.php b/lib/internal/Magento/Framework/Image/AdapterFactory.php index 8cb7b65ae73c9..4a380bab433db 100644 --- a/lib/internal/Magento/Framework/Image/AdapterFactory.php +++ b/lib/internal/Magento/Framework/Image/AdapterFactory.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Image; +/** + * Factory for Adapters that Image Library is using to process images + */ class AdapterFactory { /** diff --git a/lib/internal/Magento/Framework/Image/Factory.php b/lib/internal/Magento/Framework/Image/Factory.php index a0fb6bef4ae46..06cada6504058 100644 --- a/lib/internal/Magento/Framework/Image/Factory.php +++ b/lib/internal/Magento/Framework/Image/Factory.php @@ -8,6 +8,9 @@ use Magento\Framework\ObjectManagerInterface; +/** + * Factory for object to manipulate images + */ class Factory { /** diff --git a/lib/internal/Magento/Framework/View/Element/BlockFactory.php b/lib/internal/Magento/Framework/View/Element/BlockFactory.php index 068cb49a66319..d691af8d98f53 100644 --- a/lib/internal/Magento/Framework/View/Element/BlockFactory.php +++ b/lib/internal/Magento/Framework/View/Element/BlockFactory.php @@ -8,7 +8,7 @@ use Magento\Framework\ObjectManagerInterface; /** - * Class BlockFactory + * Creates Blocks * * @api */ diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php b/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php index dce42950e0ab6..821ebbb4d5050 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php @@ -12,6 +12,7 @@ interface SubjectInterface { /** * Attach an observer by type + * * @param string $type * @param ObserverInterface $observer * @return void @@ -20,6 +21,7 @@ public function attach($type, ObserverInterface $observer); /** * Detach an observer by type + * * @param string $type * @param ObserverInterface $observer * @return void @@ -28,6 +30,7 @@ public function detach($type, ObserverInterface $observer); /** * Notify an observer(s) by type + * * @param string $type * @return void */ From 95fce13800748fe5b4e7c556fbd0302f69b1adaa Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 18 Jan 2020 15:37:41 -0500 Subject: [PATCH 150/734] fix static tests (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/Plugin/AdminUserForm.php | 2 -- .../Magento/Security/Model/Plugin/UserValidationRules.php | 2 -- app/code/Magento/Security/Model/UserExpiration/Validator.php | 4 +--- .../Magento/Security/Observer/AdminUserAuthenticateBefore.php | 2 -- .../Test/Unit/Model/Plugin/UserValidationRulesTest.php | 4 +--- .../Security/Test/Unit/Model/UserExpiration/ValidatorTest.php | 4 +--- .../Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php | 2 -- .../Security/Test/Unit/Observer/AfterAdminUserSaveTest.php | 2 -- 8 files changed, 3 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index d6276519578f9..106295d5774ff 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -9,8 +9,6 @@ /** * Add the `expires_at` form field to the User main form. - * - * @package Magento\Security\Model\Plugin */ class AdminUserForm { diff --git a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php index f400d962644d6..00d5bc13b65e8 100644 --- a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php +++ b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php @@ -9,8 +9,6 @@ /** * \Magento\User\Model\UserValidationRules decorator - * - * @package Magento\Security\Model\Plugin */ class UserValidationRules { diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index c71b2ecb849a2..46633db260692 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -10,9 +10,7 @@ use Magento\Framework\Validator\AbstractValidator; /** - * Class Validator - * - * @package Magento\Security\Model\Validator + * Validates that the expires_at field is later than the current date/time. */ class Validator extends AbstractValidator { diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index 61ce889e9722a..3c07285afcb32 100644 --- a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -14,8 +14,6 @@ /** * Check for expired users. - * - * @package Magento\Security\Observer */ class AdminUserAuthenticateBefore implements ObserverInterface { diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php index 4498e514e45d3..00b3356c2e11d 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php @@ -8,9 +8,7 @@ namespace Magento\Security\Test\Unit\Model\Plugin; /** - * Class UserValidationRulesTest - * - * @package Magento\Security\Test\Unit\Model\Plugin + * Test class for expiration date user validation rule. */ class UserValidationRulesTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php index 1cc53e76ee969..28541231cb123 100644 --- a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -10,9 +10,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** - * Class ExpiresAtValidatorTest - * - * @package Magento\User\Test\Unit\Model + * Test class for \Magento\Security\Model\UserExpiration\Validator. */ class ValidatorTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php index ecfd3e3759dd3..403255cbb60f8 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -9,8 +9,6 @@ /** * Test for \Magento\Security\Observer\AdminUserAuthenticateBefore - * - * @package Magento\Security\Test\Unit\Observer */ class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php index 439ec3f88548b..85505632e1eb6 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php @@ -9,8 +9,6 @@ /** * Test class for \Magento\Security\Observer\AfterAdminUserSave - * - * @package Magento\Security\Test\Unit\Observer */ class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase { From 932559d17549ec30d8b27c86be0439a355ff33a7 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 18 Jan 2020 16:05:48 -0500 Subject: [PATCH 151/734] fix static tests (magento/magento2#22833: Short-term admin accounts) --- .../Model/ResourceModel/UserExpiration/CollectionTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php index b78ed9dc82377..33a2e339fa717 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php @@ -10,8 +10,6 @@ /** * Class CollectionTest - * - * @package Magento\Security\Model\ResourceModel\UserExpiration */ class CollectionTest extends \PHPUnit\Framework\TestCase { From dbc1fce4c26c8c741850b21835d7387ed50a67b5 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 22 Jan 2020 15:22:04 +0200 Subject: [PATCH 152/734] Adding Tokens Comparing After Rauthorizing --- ...ationTokensAfterReauthorizeActionGroup.xml | 24 ----------------- ...ReAuthorizeTokensIntegrationEntityTest.xml | 26 ++++++++++--------- 2 files changed, 14 insertions(+), 36 deletions(-) delete mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml deleted file mode 100644 index 6976ebe9411c8..0000000000000 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminIntegrationTokensAfterReauthorizeActionGroup.xml +++ /dev/null @@ -1,24 +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="AssertAdminIntegrationTokensAfterReauthoriseActionGroup"> - <arguments> - <argument name="consumerKey" type="string"/> - <argument name="consumerSecret" type="string"/> - <argument name="accessToken" type="string"/> - <argument name="accessTokenSecret" type="string"/> - </arguments> - <seeInField stepKey="seeConsumerKey" selector="{{AdminIntegrationDetailsSection.consumerKey}}" userInput="{{consumerKey}}"/> - <seeInField stepKey="seeConsumerSecret" selector="{{AdminIntegrationDetailsSection.consumerSecret}}" userInput="{{consumerSecret}}"/> - <seeInField stepKey="seeAccessToken" selector="{{AdminIntegrationDetailsSection.accessToken}}" userInput="{{accessToken}}"/> - <seeInField stepKey="seeAccessTokenSecret" selector="{{AdminIntegrationDetailsSection.accessTokenSecret}}" userInput="{{accessTokenSecret}}"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml index 2ba04757eb430..949c57e081866 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml @@ -35,7 +35,6 @@ <!-- Click Save And Activate Button --> <actionGroup ref="AdminIntegrationSaveAndActivateActionGroup" stepKey="clickSaveAndActivateButton"/> <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="allowAccess"/> - <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickDoneButton"/> </before> <after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="returnToIntegrationsPage"> @@ -48,17 +47,21 @@ <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteCreatedIntegration"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <!-- TEST BODY --> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabConsumerKey"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabConsumerSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabAccessToken"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabAccessTokenSecret"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickDoneButton"/> <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <actionGroup ref="AdminIntegrationClickReauthoriseLinkOnGridActionGroup" stepKey="clickReauthoriseLink"/> <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickReauthoriseButton"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabConsumerKey"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabConsumerSecret"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabAccessToken"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabAccessTokenSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabAfterReauthorizeConsumerKey"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabAfterReauthorizeConsumerSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabReauthorizedAccessToken"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabReauthorizedAccessTokenSecret"/> <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="finishTheProcess"/> <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> <argument name="message" value="The integration '{{defaultIntegrationData.name}}' has been re-authorized."/> @@ -67,11 +70,10 @@ <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <actionGroup ref="AdminIntegrationOpenExistingEntityActionGroup" stepKey="openIntegrationEntity"/> - <actionGroup ref="AssertAdminIntegrationTokensAfterReauthoriseActionGroup" stepKey="assertTokens"> - <argument name="consumerKey" value="{$grabConsumerKey}"/> - <argument name="consumerSecret" value="{$grabConsumerSecret}"/> - <argument name="accessToken" value="{$grabAccessToken}"/> - <argument name="accessTokenSecret" value="{$grabAccessTokenSecret}"/> - </actionGroup> + <!-- Compare Tokens After Reauthorizing --> + <assertEquals stepKey="assertConsumerKey" expected="($grabConsumerKey)" actual="$grabAfterReauthorizeConsumerKey" actualType="string"/> + <assertEquals stepKey="assertConsumerSecret" expected="($grabConsumerSecret)" actual="$grabAfterReauthorizeConsumerSecret" actualType="string"/> + <assertNotEquals stepKey="assertNotEqualsToken" expected="($grabAccessToken)" actual="$grabReauthorizedAccessToken" actualType="string"/> + <assertNotEquals stepKey="assertNotEqualsTokenSecret" expected="($grabAccessTokenSecret)" actual="$grabReauthorizedAccessTokenSecret" actualType="string"/> </test> </tests> From 13890db4775b4ee6b2eba44d9f3c4a8387e17ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tjitse=20Efd=C3=A9?= <tjitse@vendic.nl> Date: Sun, 26 Jan 2020 14:55:50 +0100 Subject: [PATCH 153/734] Refactor DateTime constructor --- .../Framework/Stdlib/DateTime/DateTime.php | 16 ++++------------ .../Test/Unit/DateTime/DateTimeTest.php | 15 +++++++++++++++ ...-55b4-4669-a60b-76127f9e1c48-testsuite.xml | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php index 7ac93c7b94305..fdb6dd5c816dc 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php @@ -13,13 +13,6 @@ */ class DateTime { - /** - * Current config offset in seconds - * - * @var int - */ - private $_offset = 0; - /** * @var TimezoneInterface */ @@ -31,7 +24,6 @@ class DateTime public function __construct(TimezoneInterface $localeDate) { $this->_localeDate = $localeDate; - $this->_offset = $this->calculateOffset($this->_localeDate->getConfigTimezone()); } /** @@ -157,18 +149,18 @@ public function timestamp($input = null) */ public function getGmtOffset($type = 'seconds') { - $result = $this->_offset; + $offset = $this->calculateOffset($this->_localeDate->getConfigTimezone()); switch ($type) { case 'seconds': default: break; case 'minutes': - $result = $result / 60; + $offset = $offset / 60; break; case 'hours': - $result = $result / 60 / 60; + $offset = $offset / 60 / 60; break; } - return $result; + return $offset; } } diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php index f86269b1647b2..3c7f49671d74a 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php @@ -46,6 +46,21 @@ public function testTimestamp($input) $this->assertEquals($expected, (new DateTime($timezone))->timestamp($input)); } + public function testGtmOffset() + { + /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); + $timezone->method('getConfigTimezone')->willReturn('Europe/Amsterdam'); + + /** @var DateTime|\PHPUnit_Framework_MockObject_MockObject $dateTime */ + $dateTime = $this->getMockBuilder(DateTime::class) + ->setConstructorArgs([$timezone]) + ->setMethods(null) + ->getMock(); + + $this->assertEquals(3600, $dateTime->getGmtOffset()); + } + /** * @return array */ diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml new file mode 100644 index 0000000000000..91077b15aeb69 --- /dev/null +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<alr:test-suite xmlns:alr="urn:model.allure.qatools.yandex.ru" start="1580046290235" stop="1580046290276" version="1.4.0"> + <name>Magento\Framework\Stdlib\Test\Unit\DateTime\DateTimeTest</name> + <test-cases> + <test-case start="1580046290237" stop="1580046290275" status="broken"> + <name>testGtmOffset</name> + <failure> + <message>Too few arguments to function PHPUnit\Framework\TestCase::getMockBuilder(), 0 passed in /Users/tjitse/sites/magentolive/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php on line 56 and exactly 1 expected</message> + <stack-trace>#0 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/Framework/TestCase.php(894): PHPUnit\Framework\TestResult->run(Object(Magento\Framework\Stdlib\Test\Unit\DateTime\DateTimeTest)) +#1 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/Framework/TestSuite.php(755): PHPUnit\Framework\TestCase->run(Object(PHPUnit\Framework\TestResult)) +#2 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(545): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult)) +#3 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/Command.php(195): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, true) +#4 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/Command.php(148): PHPUnit\TextUI\Command->run(Array, true) +#5 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/phpunit(53): PHPUnit\TextUI\Command::main() +#6 {main}</stack-trace> + </failure> + </test-case> + </test-cases> +</alr:test-suite> From 628991634eb5ebd6e3a49b798145795b071f9ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tjitse=20Efd=C3=A9?= <tjitse@vendic.nl> Date: Sun, 26 Jan 2020 14:57:20 +0100 Subject: [PATCH 154/734] Remove accidentally commited testsuite file --- ...-55b4-4669-a60b-76127f9e1c48-testsuite.xml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml deleted file mode 100644 index 91077b15aeb69..0000000000000 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/var/allure-results/618530c1-55b4-4669-a60b-76127f9e1c48-testsuite.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<alr:test-suite xmlns:alr="urn:model.allure.qatools.yandex.ru" start="1580046290235" stop="1580046290276" version="1.4.0"> - <name>Magento\Framework\Stdlib\Test\Unit\DateTime\DateTimeTest</name> - <test-cases> - <test-case start="1580046290237" stop="1580046290275" status="broken"> - <name>testGtmOffset</name> - <failure> - <message>Too few arguments to function PHPUnit\Framework\TestCase::getMockBuilder(), 0 passed in /Users/tjitse/sites/magentolive/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php on line 56 and exactly 1 expected</message> - <stack-trace>#0 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/Framework/TestCase.php(894): PHPUnit\Framework\TestResult->run(Object(Magento\Framework\Stdlib\Test\Unit\DateTime\DateTimeTest)) -#1 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/Framework/TestSuite.php(755): PHPUnit\Framework\TestCase->run(Object(PHPUnit\Framework\TestResult)) -#2 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(545): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult)) -#3 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/Command.php(195): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, true) -#4 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/src/TextUI/Command.php(148): PHPUnit\TextUI\Command->run(Array, true) -#5 /Users/tjitse/sites/magentolive/vendor/phpunit/phpunit/phpunit(53): PHPUnit\TextUI\Command::main() -#6 {main}</stack-trace> - </failure> - </test-case> - </test-cases> -</alr:test-suite> From 7fee060ce233387363eea80ca2a69083f8fc9d3d Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Mon, 27 Jan 2020 10:23:37 -0500 Subject: [PATCH 155/734] create DTO for user expiration (#19093) --- .../Api/Data/UserExpirationInterface.php | 50 +++++++++++++++++++ .../Magento/Security/Model/UserExpiration.php | 50 +++++++++++++++++-- app/code/Magento/Security/etc/di.xml | 1 + 3 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Security/Api/Data/UserExpirationInterface.php diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php new file mode 100644 index 0000000000000..80fc3f06d8338 --- /dev/null +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Security\Api\Data; + +/** + * Interface UserExpirationInterface to be used as a DTO for expires_at property on User model. + */ +interface UserExpirationInterface +{ + + const EXPIRES_AT = 'expires_at'; + + const USER_ID = 'user_id'; + + /** + * `expires_at` getter. + * + * @return string + */ + public function getExpiresAt(); + + /** + * `expires_at` setter. + * + * @param string $expiresAt + * @return $this + */ + public function setExpiresAt($expiresAt); + + /** + * `user_id` getter. + * + * @return string + */ + public function getUserId(); + + /** + * `user_id` setter. + * + * @param string $userId + * @return $this + */ + public function setUserId($userId); +} diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index 08f60ade672b6..c5c7aeaf68a51 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -7,14 +7,12 @@ namespace Magento\Security\Model; +use Magento\Security\Api\Data\UserExpirationInterface; + /** * Admin User Expiration model. - * @method string getUserId() - * @method \Magento\Security\Model\UserExpiration setUserId($userId) - * @method string getExpiresAt() - * @method \Magento\Security\Model\UserExpiration setExpiresAt($value) */ -class UserExpiration extends \Magento\Framework\Model\AbstractModel +class UserExpiration extends \Magento\Framework\Model\AbstractModel implements UserExpirationInterface { /** @@ -26,4 +24,46 @@ protected function _construct() { $this->_init(\Magento\Security\Model\ResourceModel\UserExpiration::class); } + + /** + * `expires_at` getter. + * + * @return string + */ + public function getExpiresAt() + { + return $this->getData(self::EXPIRES_AT); + } + + /** + * `expires_at` setter. + * + * @param string $expiresAt + * @return $this + */ + public function setExpiresAt($expiresAt) + { + return $this->setData(self::EXPIRES_AT, $expiresAt); + } + + /** + * `user_id` getter. + * + * @return string + */ + public function getUserId() + { + return $this->getData(self::USER_ID); + } + + /** + * `user_id` setter. + * + * @param string $userId + * @return $this + */ + public function setUserId($userId) + { + return $this->setData(self::USER_ID, $userId); + } } diff --git a/app/code/Magento/Security/etc/di.xml b/app/code/Magento/Security/etc/di.xml index 4fe88f219cf74..3b07bb84b1161 100644 --- a/app/code/Magento/Security/etc/di.xml +++ b/app/code/Magento/Security/etc/di.xml @@ -18,4 +18,5 @@ </argument> </arguments> </type> + <preference for="Magento\Security\Api\Data\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> </config> From 1818eaf28562c82de23a59b5b8ade0ec93dd538c Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 30 Jan 2020 13:08:41 +0200 Subject: [PATCH 156/734] Fixed codeStyle issues --- setup/src/Magento/Setup/Controller/Navigation.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 65bacebeed18a..08c92d3437238 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -5,12 +5,14 @@ */ namespace Magento\Setup\Controller; +use Magento\Backend\Model\UrlInterface; +use Magento\Setup\Exception; +use Magento\Setup\Model\Cron\Status; use Magento\Setup\Model\Navigation as NavModel; +use Magento\Setup\Model\ObjectManagerProvider; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\JsonModel; use Zend\View\Model\ViewModel; -use Magento\Setup\Model\Cron\Status; -use Magento\Setup\Model\ObjectManagerProvider; /** * Class Navigation @@ -47,7 +49,7 @@ public function __construct(NavModel $navigation, Status $status, ObjectManagerP $this->navigation = $navigation; $this->status = $status; $this->objectManagerProvider = $objectManagerProvider; - $this->view = new ViewModel; + $this->view = new ViewModel(); $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); } @@ -57,7 +59,7 @@ public function __construct(NavModel $navigation, Status $status, ObjectManagerP */ public function indexAction() { - $json = new JsonModel; + $json = new JsonModel(); $json->setVariable('nav', $this->navigation->getData()); $json->setVariable('menu', $this->navigation->getMenuItems()); $json->setVariable('main', $this->navigation->getMainItems()); @@ -79,11 +81,12 @@ public function menuAction() /** * @return array|ViewModel + * @throws Exception */ public function sideMenuAction() { - /** @var \Magento\Backend\Model\UrlInterface $backendUrl */ - $backendUrl = $this->objectManagerProvider->get()->get(\Magento\Backend\Model\UrlInterface::class); + /** @var UrlInterface $backendUrl */ + $backendUrl = $this->objectManagerProvider->get()->get(UrlInterface::class); $this->view->setTemplate('/magento/setup/navigation/side-menu.phtml'); $this->view->setVariable('isInstaller', $this->navigation->getType() == NavModel::NAV_INSTALLER); From 291058b336d9d31eb353669292b773f7f3f2e4af Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Fri, 31 Jan 2020 22:56:21 +0700 Subject: [PATCH 157/734] Correct docblock get method --- app/code/Magento/Quote/Model/Cart/CartTotalRepository.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php index 2c487cdea63fa..84ac7683c9cc6 100644 --- a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php +++ b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php @@ -78,10 +78,11 @@ public function __construct( } /** - * @inheritdoc + * Get cart total repository * - * @param int $cartId The cart ID. - * @return Totals Quote totals data. + * @param int $cartId + * @return Api\Data\TotalsInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function get($cartId) { From c98e03f46c6d6b1e00f738e22e0474113373500e Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 3 Feb 2020 18:41:15 +0200 Subject: [PATCH 158/734] fixed classes with new keyword --- .../Magento/Setup/Controller/Navigation.php | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 08c92d3437238..844e5e113c2ea 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -15,8 +15,7 @@ use Zend\View\Model\ViewModel; /** - * Class Navigation - * + * Controller Navigation class */ class Navigation extends AbstractActionController { @@ -35,6 +34,11 @@ class Navigation extends AbstractActionController */ protected $view; + /** + * @var JsonModel + */ + protected $json; + /** * @var ObjectManagerProvider $objectManagerProvider */ @@ -43,13 +47,22 @@ class Navigation extends AbstractActionController /** * @param NavModel $navigation * @param Status $status + * @param ViewModel $viewModel + * @param JsonModel $jsonModel + * @param ObjectManagerProvider $objectManagerProvider */ - public function __construct(NavModel $navigation, Status $status, ObjectManagerProvider $objectManagerProvider) - { + public function __construct( + NavModel $navigation, + Status $status, + ViewModel $viewModel, + JsonModel $jsonModel, + ObjectManagerProvider $objectManagerProvider + ) { $this->navigation = $navigation; $this->status = $status; - $this->objectManagerProvider = $objectManagerProvider; - $this->view = new ViewModel(); + $this->objectManagerProvider = $objectManagerProvider->get(); + $this->view = $viewModel; + $this->json = $jsonModel; $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); } @@ -59,12 +72,11 @@ public function __construct(NavModel $navigation, Status $status, ObjectManagerP */ public function indexAction() { - $json = new JsonModel(); - $json->setVariable('nav', $this->navigation->getData()); - $json->setVariable('menu', $this->navigation->getMenuItems()); - $json->setVariable('main', $this->navigation->getMainItems()); - $json->setVariable('titles', $this->navigation->getTitles()); - return $json; + $this->json->setVariable('nav', $this->navigation->getData()); + $this->json->setVariable('menu', $this->navigation->getMenuItems()); + $this->json->setVariable('main', $this->navigation->getMainItems()); + $this->json->setVariable('titles', $this->navigation->getTitles()); + return $this->json; } /** @@ -86,7 +98,7 @@ public function menuAction() public function sideMenuAction() { /** @var UrlInterface $backendUrl */ - $backendUrl = $this->objectManagerProvider->get()->get(UrlInterface::class); + $backendUrl = $this->objectManagerProvider->get(UrlInterface::class); $this->view->setTemplate('/magento/setup/navigation/side-menu.phtml'); $this->view->setVariable('isInstaller', $this->navigation->getType() == NavModel::NAV_INSTALLER); From 8c672ebfe92b13cdd8111777db227d6b05897def Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 4 Feb 2020 10:33:50 +0200 Subject: [PATCH 159/734] Fixed Code style issues --- setup/src/Magento/Setup/Controller/Navigation.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 844e5e113c2ea..46851510a1466 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -68,6 +68,8 @@ public function __construct( } /** + * Index Action + * * @return JsonModel */ public function indexAction() @@ -80,6 +82,8 @@ public function indexAction() } /** + * Menu Action + * * @return array|ViewModel */ public function menuAction() @@ -92,7 +96,10 @@ public function menuAction() } /** + * SideMenu Action + * * @return array|ViewModel + * * @throws Exception */ public function sideMenuAction() @@ -108,6 +115,8 @@ public function sideMenuAction() } /** + * HeaderBar Action + * * @return array|ViewModel */ public function headerBarAction() From b1b3e6ce6ddef57a8d6e59cc678160670f8b9e76 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Tue, 4 Feb 2020 14:08:07 +0200 Subject: [PATCH 160/734] Cover MFTF test --- .../Customer/Test/Mftf/Data/CustomerData.xml | 12 ++++ ...formationWithGeneratedEmailActionGroup.xml | 19 ++++++ .../Sales/Test/Mftf/Data/ConfigData.xml | 8 +++ ...reateOrderWithCustomerWithoutEmailTest.xml | 61 +++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index b9227505871cf..d75afe0388193 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -178,6 +178,18 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_CA</requiredEntity> </entity> + <entity name="Simple_US_Customer_CA_Without_Email" type="customer"> + <data key="group_id">0</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_CA</requiredEntity> + </entity> <entity name="Simple_US_Customer_For_Update" type="customer"> <var key="id" entityKey="id" entityType="customer"/> <data key="firstname">Jane</data> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml new file mode 100644 index 0000000000000..9b1666763b2bc --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCreatedOrderInformationWithGeneratedEmailActionGroup"> + <annotations> + <description>Validate customer email on order page. Starts on order page.</description> + </annotations> + <arguments> + <argument name="email" type="string" defaultValue="{{CustomerEntityOne.email}}"/> + </arguments> + <see selector="{{AdminOrderDetailsInformationSection.customerEmail}}" userInput="{{email}}" stepKey="seeGeneratedEmail"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml index 730bebc047f93..25e25006d040a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml @@ -20,4 +20,12 @@ <data key="label">No</data> <data key="value">0</data> </entity> + <entity name="DisableEmailRequiredForOrder"> + <data key="path">customer/create_account/email_required_create_order</data> + <data key="value">0</data> + </entity> + <entity name="EnableEmailRequiredForOrder"> + <data key="path">customer/create_account/email_required_create_order</data> + <data key="value">1</data> + </entity> </entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml new file mode 100644 index 0000000000000..bb1f5840ed6bf --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml @@ -0,0 +1,61 @@ +<?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="AdminCreateOrderWithCustomerWithoutEmailTest"> + <annotations> + <title value="Admin Create Order"/> + <stories value="Admin create order with customer without email."/> + <description value="Verify, admin able to create order with customer without email."/> + <severity value="MINOR"/> + <group value="Sales"/> + </annotations> + <before> + <!--Disable required 'email' field on create order page.--> + <magentoCLI command="config:set {{DisableEmailRequiredForOrder.path}} {{DisableEmailRequiredForOrder.value}}" stepKey="disableRequiredFieldEmailForAdminOrderCreation"/> + <!--Create test data.--> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!--Clean up created test data.--> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Enable required 'email' field on create order page.--> + <magentoCLI command="config:set {{EnableEmailRequiredForOrder.path}} {{EnableEmailRequiredForOrder.value}}" stepKey="enableRequiredFieldEmailForAdminOrderCreation"/> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + + <!--Create order.--> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPageNewCustomerActionGroup" /> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="{{SimpleProduct.quantity}}"/> + </actionGroup> + <!--Fill customer address without 'email'--> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInformation"> + <argument name="customer" value="Simple_US_Customer_CA_Without_Email"/> + <argument name="address" value="US_Address_CA"/> + </actionGroup> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <!--Verify, 'email' is generated.--> + <actionGroup ref="VerifyCreatedOrderInformationWithGeneratedEmailActionGroup" stepKey="verifyCustomerEmail"> + <argument name="email" value="@example.com"/> + </actionGroup> + <grabTextFrom selector="{{AdminOrderDetailsInformationSection.customerEmail}}" stepKey="generatedCustomerEmail"/> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="$generatedCustomerEmail"/> + </actionGroup> + </test> +</tests> From 0350be8f73f37e3a40a22319e300667caba31b6c Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 5 Feb 2020 17:27:32 +0200 Subject: [PATCH 161/734] Refactoring --- ...edNewsletterTemplateMessageActionGroup.xml | 19 ------------------- ...nMarketingNewsletterTemplateUpdateTest.xml | 3 ++- 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml deleted file mode 100644 index 3261f33894756..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AssertAdminCreatedNewsletterTemplateMessageActionGroup.xml +++ /dev/null @@ -1,19 +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="AssertAdminCreatedNewsletterTemplateMessageActionGroup"> - <arguments> - <argument name="message" type="string" defaultValue="The newsletter template has been saved."/> - <argument name="messageType" type="string" defaultValue="success"/> - </arguments> - <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="waitForMessage"/> - <see userInput="{{message}}" selector="{{AdminNewsletterGridMainActionsSection.messageByType(messageType)}}" stepKey="verifyMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml index b236dfee4ab6b..378dd71e13334 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml @@ -67,8 +67,9 @@ <argument name="templateContent" value="{{updatedNewsletter.textAreaContent}}"/> </actionGroup> <!-- Assert Success Message --> - <actionGroup ref="AssertAdminCreatedNewsletterTemplateMessageActionGroup" stepKey="assertSuccessMessage"> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="The newsletter template has been saved."/> + <argument name="messageType" value="success"/> </actionGroup> <!-- Find Created Newsletter on Grid --> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> From d913e155c19e5db594bd15042c6358018a5a9b49 Mon Sep 17 00:00:00 2001 From: konarshankar07 <konar.shankar2013@gmail.com> Date: Thu, 6 Feb 2020 23:44:18 +0530 Subject: [PATCH 162/734] SortBy component introduced --- .../Ui/view/base/web/js/grid/sortBy.js | 78 ++++++++++++ .../view/base/web/templates/grid/sortBy.html | 15 +++ .../Magento/Ui/base/js/grid/sortBy.test.js | 117 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 app/code/Magento/Ui/view/base/web/js/grid/sortBy.js create mode 100644 app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js new file mode 100644 index 0000000000000..cdb7ed784c150 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js @@ -0,0 +1,78 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'uiElement' +], function (Element) { + 'use strict'; + + return Element.extend({ + defaults: { + template: 'ui/grid/sortBy', + options: [], + applied: {}, + sorting: 'desc', + columnsProvider: 'ns = ${ $.ns }, componentType = columns', + selectedOption: '', + isVisible: true, + listens: { + 'selectedOption': 'applyChanges' + }, + statefull: { + selectedOption: true, + applied: true + }, + exports: { + applied: '${ $.provider }:params.sorting' + }, + imports: { + preparedOptions: '${ $.columnsProvider }:elems' + }, + modules: { + columns: '${ $.columnsProvider }' + } + }, + + /** + * @inheritdoc + */ + initObservable: function () { + return this._super() + .observe([ + 'applied', + 'selectedOption', + 'isVisible' + ]); + }, + + /** + * Prepared sort order options + */ + preparedOptions: function (columns) { + if (columns && columns.length > 0) { + columns.map(function (column) { + if (column.sortable === true) { + this.options.push({ + value: column.index, + label: column.label + }); + this.isVisible(true); + } else { + this.isVisible(false); + } + }.bind(this)); + } + }, + + /** + * Apply changes + */ + applyChanges: function () { + this.applied({ + field: this.selectedOption(), + direction: this.sorting + }); + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html new file mode 100644 index 0000000000000..e0bcf6cbe5514 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html @@ -0,0 +1,15 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div if="isVisible" class="masonry-sorting"> + <b><!-- ko i18n: 'Sort by' --><!-- /ko -->:</b> + <select class="admin__control-select" data-bind=" + options: options, + optionsValue: 'value', + optionsText: 'label', + value: selectedOption + "></select> +</div> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js new file mode 100644 index 0000000000000..ec6fb261c8d47 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -0,0 +1,117 @@ +define([ + 'Magento_Ui/js/grid/sortBy' +], function (sortBy) { + 'use strict'; + describe('Magento_Ui/js/grid/sortBy', function () { + + var sortByObj; + + beforeEach(function () { + sortByObj = new sortBy({ + options: [] + }); + }); + + describe('"initObservable" method', function () { + it('Check for defined ', function () { + expect(sortByObj.hasOwnProperty('initObservable')).toBeDefined(); + }); + it('Check method type', function () { + var type = typeof sortByObj.initObservable; + + expect(type).toEqual('function'); + }); + it('Check returned value if method called without arguments', function () { + expect(sortByObj.initObservable()).toBeDefined(); + }); + it('Check returned value type if method called without arguments', function () { + var type = typeof sortByObj.initObservable(); + expect(type).toEqual('object'); + }); + }); + + describe('"preparedOptions" method', function () { + it('Check for defined ', function () { + expect(sortByObj.hasOwnProperty('preparedOptions')).toBeDefined(); + }); + it('Check method type', function () { + var type = typeof sortByObj.preparedOptions; + expect(type).toEqual('function'); + }); + + it('Check "options" array is empty if sortable is set false', function () { + var columns = [{ + sortable: false, + label: 'magento', + index: 'test' + }], + expectedValue = []; + sortByObj.preparedOptions(columns); + expect(sortByObj.options).toEqual(expectedValue); + }); + + it('Check "options" array is set the correct value', function () { + var columns = [{ + sortable: true, + label: 'magento', + index: 'test' + }], + expectedValue = [{ + value: 'test', + label: 'magento' + }]; + sortByObj.preparedOptions(columns); + expect(sortByObj.options).toEqual(expectedValue); + }); + + it('Check "isVisible" set true if column is sortable', function () { + var columns = [{ + sortable: true, + label: 'magento', + index: 'test' + }]; + spyOn(sortByObj, "isVisible").and.callFake(function () { + return true; + }); + sortByObj.preparedOptions(columns); + expect(sortByObj.isVisible).toHaveBeenCalled(); + expect(sortByObj.isVisible()).toBeTruthy(); + }); + + it('Check "isVisible" set true if column is sortable', function () { + var columns = [{ + sortable: true, + label: 'magento', + index: 'test' + }]; + spyOn(sortByObj, "isVisible").and.callFake(function () { + return false; + }); + sortByObj.preparedOptions(columns); + expect(sortByObj.isVisible).toHaveBeenCalled(); + expect(sortByObj.isVisible()).toBeFalsy(); + }); + }); + describe('"applyChanges" method', function () { + it('Check for defined ', function () { + expect(sortByObj.hasOwnProperty('applyChanges')).toBeDefined(); + }); + it('Check method type', function () { + var type = typeof sortByObj.applyChanges; + expect(type).toEqual('function'); + }); + + it('Check "selectedOption" method has been called', function () { + spyOn(sortByObj, 'selectedOption'); + sortByObj.applyChanges(); + expect(sortByObj.selectedOption).toHaveBeenCalled(); + }); + + it('Check "applied" method has been called', function () { + spyOn(sortByObj, 'applied'); + sortByObj.applyChanges(); + expect(sortByObj.applied).toHaveBeenCalled(); + }); + }); + }); +}); From b1ecb96ac7639bf6c56de1dbfc49c210af9705be Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 6 Feb 2020 16:23:53 -0600 Subject: [PATCH 163/734] MC-31198: Group titles not found for downloadable products while importing products --- .../Model/Import/Product/Type/Downloadable.php | 5 ----- .../product_downloadable_with_link_url_and_sample_url.php | 1 - 2 files changed, 6 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index f148550dd96bb..30e08530d2536 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -406,11 +406,6 @@ protected function isRowValidLink(array $rowData) $linkData = $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]); - if ($this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == '') { - $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); - $result = true; - } - $result = $result ?? $this->isTitle($linkData); foreach ($linkData as $link) { diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php index 32fed4730adfc..d7de542be6e22 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php @@ -44,7 +44,6 @@ ->setVisibility(Visibility::VISIBILITY_BOTH) ->setStatus(Status::STATUS_ENABLED) ->setLinksPurchasedSeparately(true) - ->setLinksTitle('Links') ->setSamplesTitle('Samples') ->setStockData( [ From 5276ecb0b2675a84ec4dfc83cc25d28b9e5c749f Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 6 Feb 2020 16:42:17 -0600 Subject: [PATCH 164/734] MC-30636: TinyMCE4: some HTML code breaks the editor --- .../plugins/magentowidget/editor_plugin.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js index f163206a13656..a9259a9a2daf3 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js @@ -168,17 +168,22 @@ define([ /(<span class="[^"]*magento-widget[^"]*"[^>]*>)?<img([^>]+id="[^>]+)>(([^>]*)<\/span>)?/i, function (match) { var attributes = wysiwyg.parseAttributesString(match[2]), - widgetCode; + widgetCode, + result = match[0]; if (attributes.id) { - widgetCode = Base64.idDecode(attributes.id); + try { + widgetCode = Base64.idDecode(attributes.id); + } catch (e) { + // Ignore and continue. + } - if (widgetCode.indexOf('{{widget') !== -1) { - return widgetCode; + if (widgetCode && widgetCode.indexOf('{{widget') !== -1) { + result = widgetCode; } } - return match[0]; + return result; } ); }, From c5ca58cfcec9cff2e99610058757bd8174bc91df Mon Sep 17 00:00:00 2001 From: Antonino Bonumore <a.bonumore@emergento.com> Date: Fri, 7 Feb 2020 11:24:48 +0100 Subject: [PATCH 165/734] magento#26745 add method setAdditionalInformation to OrderPaymentInterface --- app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php b/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php index ea0cf5d7b32be..ac400206b8a2f 100644 --- a/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php @@ -1042,6 +1042,14 @@ public function setCcNumberEnc($ccNumberEnc); */ public function setCcTransId($id); + /** + * Set the additional information for the order payment. + * + * @param string[] $additionalInformation + * @return $this + */ + public function setAdditionalInformation($additionalInformation); + /** * Sets the address status for the order payment. * From ba120b94508c8ae1a0bf488ffb0c72794ec512e5 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 7 Feb 2020 09:26:07 -0600 Subject: [PATCH 166/734] MC-31198: Group titles not found for downloadable products while importing products --- .../Model/Import/Product/Type/Downloadable.php | 5 ----- .../product_downloadable_with_link_url_and_sample_url.php | 1 - 2 files changed, 6 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index 30e08530d2536..f0e50b2837153 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -365,11 +365,6 @@ protected function isRowValidSample(array $rowData) $sampleData = $this->prepareSampleData($rowData[static::COL_DOWNLOADABLE_SAMPLES]); - if ($this->sampleGroupTitle($rowData) == '') { - $result = true; - $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); - } - $result = $result ?? $this->isTitle($sampleData); foreach ($sampleData as $link) { diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php index d7de542be6e22..2686b7c095b2f 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php @@ -44,7 +44,6 @@ ->setVisibility(Visibility::VISIBILITY_BOTH) ->setStatus(Status::STATUS_ENABLED) ->setLinksPurchasedSeparately(true) - ->setSamplesTitle('Samples') ->setStockData( [ 'qty' => 100, From 191463693a45eed85f6b427ed8b82620d0b1047b Mon Sep 17 00:00:00 2001 From: Daniel Ruf <mac1@daniel-ruf.de> Date: Fri, 7 Feb 2020 22:34:41 +0100 Subject: [PATCH 167/734] fix: prevent undefined index error - closes #26762 --- .../src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php index 1d9fdd6098cbb..3dd2af0c4bdc8 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php @@ -46,6 +46,6 @@ public function getDriverOptions(array $options): array */ private function optionExists($options, $driverOptionKey): bool { - return $options[$driverOptionKey] === false || !empty($options[$driverOptionKey]); + return isset($options[$driverOptionKey]) && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); } } From 5e6fdebf20b4e9b084c2874fdee681297c3e8c79 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 8 Feb 2020 11:54:51 -0500 Subject: [PATCH 168/734] fix integration, unit tests (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/Plugin/AdminUserForm.php | 2 +- app/code/Magento/Security/Model/UserExpiration.php | 2 -- .../{Api/Data => Model}/UserExpirationInterface.php | 2 +- app/code/Magento/Security/Model/UserExpirationManager.php | 2 +- .../Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php | 6 +++--- app/code/Magento/Security/etc/di.xml | 2 +- .../Magento/Security/Model/Plugin/AuthSessionTest.php | 5 +++-- .../Model/ResourceModel/UserExpiration/CollectionTest.php | 2 +- .../Magento/Security/Model/UserExpirationManagerTest.php | 4 ++-- 9 files changed, 13 insertions(+), 14 deletions(-) rename app/code/Magento/Security/{Api/Data => Model}/UserExpirationInterface.php (95%) diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index 106295d5774ff..320809d288246 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -24,7 +24,7 @@ class AdminUserForm private $userExpirationResource; /** - * @var \Magento\Security\Model\UserExpirationFactory + * @var \Magento\Security\Model\UserExpirationInterfaceFactory */ private $userExpirationFactory; diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index c5c7aeaf68a51..b220a8092602f 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -7,8 +7,6 @@ namespace Magento\Security\Model; -use Magento\Security\Api\Data\UserExpirationInterface; - /** * Admin User Expiration model. */ diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Model/UserExpirationInterface.php similarity index 95% rename from app/code/Magento/Security/Api/Data/UserExpirationInterface.php rename to app/code/Magento/Security/Model/UserExpirationInterface.php index 80fc3f06d8338..06f8c258983ba 100644 --- a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php +++ b/app/code/Magento/Security/Model/UserExpirationInterface.php @@ -6,7 +6,7 @@ declare(strict_types=1); -namespace Magento\Security\Api\Data; +namespace Magento\Security\Model; /** * Interface UserExpirationInterface to be used as a DTO for expires_at property on User model. diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index 7043a559ca1d4..ffcaa9f8d9457 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -134,7 +134,7 @@ private function processExpiredUsers(ExpiredUsersCollection $expiredRecords): vo public function isUserExpired(string $userId): bool { $isExpired = false; - /** @var \Magento\Security\Model\UserExpiration $expiredRecord */ + /** @var \Magento\Security\Model\UserExpirationInterface $expiredRecord */ $expiredRecord = $this->userExpirationCollectionFactory->create() ->addExpiredRecordsForUserFilter($userId) ->getFirstItem(); diff --git a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php index 403255cbb60f8..8768a2a647f0e 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -48,7 +48,7 @@ class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase private $eventMock; /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationInterface */ private $userExpirationMock; @@ -77,8 +77,8 @@ protected function setUp() $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getUsername']); $this->userExpirationMock = $this->createPartialMock( - \Magento\Security\Model\UserExpiration::class, - ['getId', 'getExpiresAt', 'setId', 'setExpiresAt'] + \Magento\Security\Model\UserExpirationInterface::class, + ['getUserId', 'getExpiresAt', 'setUserId', 'setExpiresAt'] ); } diff --git a/app/code/Magento/Security/etc/di.xml b/app/code/Magento/Security/etc/di.xml index 3b07bb84b1161..0e415acb93ab3 100644 --- a/app/code/Magento/Security/etc/di.xml +++ b/app/code/Magento/Security/etc/di.xml @@ -18,5 +18,5 @@ </argument> </arguments> </type> - <preference for="Magento\Security\Api\Data\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> + <preference for="Magento\Security\Model\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> </config> diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index 598cb6daafae1..a7a18716874cf 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -159,8 +159,9 @@ public function testProcessProlongWithExpiredUser() /** @var \Magento\User\Model\User $user */ $user = $this->objectManager->create(\Magento\User\Model\User::class); $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); - $userExpirationFactory = $this->objectManager->create(\Magento\Security\Model\UserExpirationFactory::class); - /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpirationFactory = + $this->objectManager->create(\Magento\Security\Model\UserExpirationInterfaceFactory::class); + /** @var \Magento\Security\Model\UserExpirationInterface $userExpiration */ $userExpiration = $userExpirationFactory->create(); $userExpiration->setId($user->getId()) ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php index 33a2e339fa717..e52f84c68d851 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php @@ -9,7 +9,7 @@ namespace Magento\Security\Model\ResourceModel\UserExpiration; /** - * Class CollectionTest + * Test UserExpiration collection filters. */ class CollectionTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index 9ab7287c414a6..e8f99d6759302 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -156,8 +156,8 @@ private function expireUser(\Magento\User\Model\User $user) { $expireDate = new \DateTime(); $expireDate->modify('-10 days'); - /** @var \Magento\Security\Model\UserExpiration $userExpiration */ - $userExpiration = $this->objectManager->create(\Magento\Security\Model\UserExpiration::class); + /** @var \Magento\Security\Model\UserExpirationInterface $userExpiration */ + $userExpiration = $this->objectManager->create(\Magento\Security\Model\UserExpirationInterface::class); $userExpiration->setId($user->getId()) ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) ->save(); From 4e65dc9f9316c19baa238ffc79e85e41d863eace Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 10 Feb 2020 14:09:28 +0530 Subject: [PATCH 169/734] refactoring test --- .../Magento/Ui/base/js/grid/sortBy.test.js | 85 +++++-------------- 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js index ec6fb261c8d47..80546f8f18160 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -12,106 +12,63 @@ define([ }); }); - describe('"initObservable" method', function () { - it('Check for defined ', function () { - expect(sortByObj.hasOwnProperty('initObservable')).toBeDefined(); - }); - it('Check method type', function () { - var type = typeof sortByObj.initObservable; - - expect(type).toEqual('function'); - }); - it('Check returned value if method called without arguments', function () { - expect(sortByObj.initObservable()).toBeDefined(); - }); - it('Check returned value type if method called without arguments', function () { - var type = typeof sortByObj.initObservable(); - expect(type).toEqual('object'); - }); - }); - describe('"preparedOptions" method', function () { - it('Check for defined ', function () { - expect(sortByObj.hasOwnProperty('preparedOptions')).toBeDefined(); - }); - it('Check method type', function () { - var type = typeof sortByObj.preparedOptions; - expect(type).toEqual('function'); - }); - - it('Check "options" array is empty if sortable is set false', function () { + it('return empty array if sorting is disabled for the columns', function () { var columns = [{ sortable: false, label: 'magento', - index: 'test' + index: 'name' }], - expectedValue = []; + options = []; sortByObj.preparedOptions(columns); - expect(sortByObj.options).toEqual(expectedValue); + expect(sortByObj.options).toEqual(options); }); - it('Check "options" array is set the correct value', function () { + it('return array of options if sorting is enabled for the columns', function () { var columns = [{ sortable: true, label: 'magento', - index: 'test' + index: 'name' }], - expectedValue = [{ - value: 'test', + options = [{ + value: 'name', label: 'magento' }]; sortByObj.preparedOptions(columns); - expect(sortByObj.options).toEqual(expectedValue); + expect(sortByObj.options).toEqual(options); }); - it('Check "isVisible" set true if column is sortable', function () { + it('return "isVisible" method true if column is sortable', function () { var columns = [{ sortable: true, label: 'magento', - index: 'test' + index: 'name' }]; - spyOn(sortByObj, "isVisible").and.callFake(function () { - return true; - }); sortByObj.preparedOptions(columns); - expect(sortByObj.isVisible).toHaveBeenCalled(); expect(sortByObj.isVisible()).toBeTruthy(); }); - it('Check "isVisible" set true if column is sortable', function () { + it('return "isVisible" method false if column is sortable', function () { var columns = [{ - sortable: true, + sortable: false, label: 'magento', - index: 'test' + index: 'name' }]; - spyOn(sortByObj, "isVisible").and.callFake(function () { - return false; - }); sortByObj.preparedOptions(columns); - expect(sortByObj.isVisible).toHaveBeenCalled(); expect(sortByObj.isVisible()).toBeFalsy(); }); }); describe('"applyChanges" method', function () { - it('Check for defined ', function () { - expect(sortByObj.hasOwnProperty('applyChanges')).toBeDefined(); - }); - it('Check method type', function () { - var type = typeof sortByObj.applyChanges; - expect(type).toEqual('function'); - }); - - it('Check "selectedOption" method has been called', function () { - spyOn(sortByObj, 'selectedOption'); + it('return applied option', function () { + var applied = { + field: 'selectedOption', + direction: 'desc' + }; + spyOn(sortByObj, 'selectedOption').and.returnValue('selectedOption'); sortByObj.applyChanges(); + expect(sortByObj.applied()).toEqual(applied); expect(sortByObj.selectedOption).toHaveBeenCalled(); }); - - it('Check "applied" method has been called', function () { - spyOn(sortByObj, 'applied'); - sortByObj.applyChanges(); - expect(sortByObj.applied).toHaveBeenCalled(); - }); }); }); }); From a48327439b0ee65f38619c2f1ee0c12f6dce695b Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 10 Feb 2020 14:12:14 +0530 Subject: [PATCH 170/734] Fixed static test --- .../app/code/Magento/Ui/base/js/grid/sortBy.test.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js index 80546f8f18160..6dd0d68f6be46 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -1,13 +1,14 @@ define([ 'Magento_Ui/js/grid/sortBy' -], function (sortBy) { +], function (SortBy) { 'use strict'; + describe('Magento_Ui/js/grid/sortBy', function () { var sortByObj; beforeEach(function () { - sortByObj = new sortBy({ + sortByObj = new SortBy({ options: [] }); }); @@ -20,6 +21,7 @@ define([ index: 'name' }], options = []; + sortByObj.preparedOptions(columns); expect(sortByObj.options).toEqual(options); }); @@ -34,6 +36,7 @@ define([ value: 'name', label: 'magento' }]; + sortByObj.preparedOptions(columns); expect(sortByObj.options).toEqual(options); }); @@ -44,6 +47,7 @@ define([ label: 'magento', index: 'name' }]; + sortByObj.preparedOptions(columns); expect(sortByObj.isVisible()).toBeTruthy(); }); @@ -54,6 +58,7 @@ define([ label: 'magento', index: 'name' }]; + sortByObj.preparedOptions(columns); expect(sortByObj.isVisible()).toBeFalsy(); }); @@ -64,6 +69,7 @@ define([ field: 'selectedOption', direction: 'desc' }; + spyOn(sortByObj, 'selectedOption').and.returnValue('selectedOption'); sortByObj.applyChanges(); expect(sortByObj.applied()).toEqual(applied); From 7c1420dde318661dc7dc10442b229eb6fc64d33a Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 10 Feb 2020 14:45:13 -0600 Subject: [PATCH 171/734] MQE-1962: Remove support of deprecated locations for tests --- .../LICENSE.txt | 0 .../LICENSE_AFL.txt | 0 .../README.md | 0 .../Test/EndToEndB2CGuestUserTest.xml | 0 .../Test/EndToEndB2CLoggedInUserTest.xml | 0 .../composer.json | 19 +++++++++++++++++++ ...nfigurableProductInWishlistActionGroup.xml | 0 ...bleProductInWishlistSidebarActionGroup.xml | 0 .../ActionGroup/_Deprecated_ActionGroup.xml | 0 .../ConfigurableProductWishlist/LICENSE.txt | 0 .../LICENSE_AFL.txt | 0 .../ConfigurableProductWishlist/README.md | 0 .../Test/EndToEndB2CLoggedInUserTest.xml | 0 .../ConfigurableProductWishlist/composer.json | 19 +++++++++++++++++++ 14 files changed, 38 insertions(+) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductCatalogSearch/LICENSE.txt (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductCatalogSearch/LICENSE_AFL.txt (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductCatalogSearch/README.md (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml (100%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/LICENSE.txt (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/LICENSE_AFL.txt (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/README.md (100%) rename dev/tests/acceptance/tests/functional/Magento/{FunctionalTest => }/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml (100%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE.txt similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE_AFL.txt similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE_AFL.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE_AFL.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/README.md b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/README.md similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/README.md rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/README.md diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json new file mode 100644 index 0000000000000..e2e16acaa00d4 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json @@ -0,0 +1,19 @@ +{ + "name": "magento/module-configurable-product-catalog-search-functional-test", + "description": "MFTF test module for Magento_ConfigurableProduct and Magento_CatalogSearch", + "type": "magento2-functional-test-module", + "config": { + "sort-packages": true + }, + "require": { + "magento/magento2-functional-testing-framework": ">=2.5.2" + }, + "suggest": { + "magento/module-configurable-product": "type: magento2-module, name: Magento_ConfigurableProduct, version: *", + "magento/module-catalog-search": "type: magento2-module, name: Magento_CatalogSearch, version: *" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ] +} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE.txt similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE_AFL.txt similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE_AFL.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE_AFL.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/README.md b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/README.md similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/README.md rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/README.md diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json new file mode 100644 index 0000000000000..ad5922bccb338 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json @@ -0,0 +1,19 @@ +{ + "name": "magento/module-configurable-product-wishlist-functional-test", + "description": "MFTF test module for Magento_ConfigurableProduct and Magento_Wishlist", + "type": "magento2-functional-test-module", + "config": { + "sort-packages": true + }, + "require": { + "magento/magento2-functional-testing-framework": ">=2.5.2" + }, + "suggest": { + "magento/module-configurable-product": "type: magento2-module, name: Magento_ConfigurableProduct, version: *", + "magento/module-wishlist": "type: magento2-module, name: Magento_Wishlist, version: *" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ] +} From dce54cec4391c732f76830173da6b504cfa5c5e4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 17 Feb 2020 15:24:40 +0400 Subject: [PATCH 172/734] Removed unnecessary check from the plugin --- .../Plugin/Wysiwyg/Images/Storage.php | 4 ---- .../Test/Unit/Plugin/Images/StorageTest.php | 17 ----------------- 2 files changed, 21 deletions(-) diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index 7a4aa219cdf29..11331e4b9303f 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -111,7 +111,6 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, * * @return null * - * @throws CouldNotDeleteException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterDeleteDirectory(StorageSubject $subject, $result, $path) @@ -123,9 +122,6 @@ public function afterDeleteDirectory(StorageSubject $subject, $result, $path) try { $mediaDirectoryRead = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); $relativePath = $mediaDirectoryRead->getRelativePath($path); - if ($mediaDirectoryRead->isExist($relativePath) === false) { - throw new CouldNotDeleteException(__('Cannot remove assets, the provided path does not exist')); - } $this->deleteMediaAssetByDirectoryPath->execute($relativePath); } catch (ValidatorException $exception) { $this->logger->critical($exception); diff --git a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php index f57d6e9c7635b..4ac448733c47f 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php @@ -105,18 +105,6 @@ public function testAfterDeleteDirectory(string $path): void $result = $this->storagePlugin->afterDeleteDirectory($storageSubject, null, (int)$path); self::assertNull($result); break; - case self::NON_EXISTENT_PATH: - $directoryRead->expects($this->once()) - ->method('getRelativePath') - ->with($path) - ->willReturn($path); - $directoryRead->expects($this->once()) - ->method('isExist') - ->with($path) - ->willReturn(false); - self::expectException('Magento\Framework\Exception\CouldNotDeleteException'); - $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); - break; case self::INVALID_PATH: $exception = new ValidatorException(__('Path cannot be used with directory')); $directoryRead->expects($this->once()) @@ -133,10 +121,6 @@ public function testAfterDeleteDirectory(string $path): void ->method('getRelativePath') ->with($path) ->willReturn($path); - $directoryRead->expects($this->once()) - ->method('isExist') - ->with($path) - ->willReturn(true); $this->deleteMediaAssetByDirectoryPath->expects($this->once()) ->method('execute') ->with($path); @@ -154,7 +138,6 @@ public function pathPathDataProvider(): array { return [ 'Non string path' => [2020], - 'Non-existent path' => [self::NON_EXISTENT_PATH], 'Invalid path' => [self::INVALID_PATH], 'Existent path' => [self::VALID_PATH] ]; From 531e1b596c00eddd4af774c8d3a5ea998f409600 Mon Sep 17 00:00:00 2001 From: solwininfotech <info@solwininfotech.com> Date: Mon, 17 Feb 2020 17:58:01 +0530 Subject: [PATCH 173/734] resolved merge conflict --- app/code/Magento/Customer/etc/adminhtml/system.xml | 3 +++ app/code/Magento/Customer/etc/config.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 6234c2a84ac83..fca625d847a1d 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -86,6 +86,9 @@ <comment>To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> + <field id="email_domain" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Default Email Domain</label> + </field> <field id="email_template" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Default Welcome Email</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> diff --git a/app/code/Magento/Customer/etc/config.xml b/app/code/Magento/Customer/etc/config.xml index db04ad7c94963..ab2020580a2eb 100644 --- a/app/code/Magento/Customer/etc/config.xml +++ b/app/code/Magento/Customer/etc/config.xml @@ -23,6 +23,7 @@ <email_confirmed_template>customer_create_account_email_confirmed_template</email_confirmed_template> <viv_disable_auto_group_assign_default>0</viv_disable_auto_group_assign_default> <vat_frontend_visibility>0</vat_frontend_visibility> + <email_domain>example.com</email_domain> <generate_human_friendly_id>0</generate_human_friendly_id> </create_account> <default> From b90f20c157bed3580623caf479024cdb0ca18875 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 17 Feb 2020 19:46:06 +0200 Subject: [PATCH 174/734] Fix issue with UnitTests --- setup/src/Magento/Setup/Controller/Navigation.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 46851510a1466..f93c10b4411c9 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -47,22 +47,18 @@ class Navigation extends AbstractActionController /** * @param NavModel $navigation * @param Status $status - * @param ViewModel $viewModel - * @param JsonModel $jsonModel * @param ObjectManagerProvider $objectManagerProvider */ public function __construct( NavModel $navigation, Status $status, - ViewModel $viewModel, - JsonModel $jsonModel, ObjectManagerProvider $objectManagerProvider ) { $this->navigation = $navigation; $this->status = $status; $this->objectManagerProvider = $objectManagerProvider->get(); - $this->view = $viewModel; - $this->json = $jsonModel; + $this->json = new JsonModel(); + $this->view = new ViewModel(); $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); } From 80edb99fe41419547bcaf90dd41a9c9089540e96 Mon Sep 17 00:00:00 2001 From: Mark Shust <mark@shust.com> Date: Mon, 17 Feb 2020 15:30:28 -0500 Subject: [PATCH 175/734] Removed references to '%context%' (dead code) --- lib/internal/Magento/Framework/RequireJs/Config.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/internal/Magento/Framework/RequireJs/Config.php b/lib/internal/Magento/Framework/RequireJs/Config.php index f158aa8b1ebce..67fdd00b3d919 100644 --- a/lib/internal/Magento/Framework/RequireJs/Config.php +++ b/lib/internal/Magento/Framework/RequireJs/Config.php @@ -155,22 +155,15 @@ public function getConfig() { $distributedConfig = ''; $customConfigFiles = $this->fileSource->getFiles($this->design->getDesignTheme(), self::CONFIG_FILE_NAME); + foreach ($customConfigFiles as $file) { /** @var $fileReader \Magento\Framework\Filesystem\File\Read */ $fileReader = $this->readFactory->create($file->getFilename(), DriverPool::FILE); $config = $fileReader->readAll($file->getName()); - $distributedConfig .= str_replace( - ['%config%', '%context%'], - [$config, $file->getModule()], - self::PARTIAL_CONFIG_TEMPLATE - ); + $distributedConfig .= str_replace('%config%', $config, self::PARTIAL_CONFIG_TEMPLATE); } - $fullConfig = str_replace( - ['%function%', '%usages%'], - [$distributedConfig], - self::FULL_CONFIG_TEMPLATE - ); + $fullConfig = str_replace(['%function%', '%usages%'], [$distributedConfig], self::FULL_CONFIG_TEMPLATE); if ($this->minification->isEnabled('js')) { $fullConfig = $this->minifyAdapter->minify($fullConfig); From c47464d926a7ab4f1ca6411cee7a60fd6948fc59 Mon Sep 17 00:00:00 2001 From: Mark Shust <mark@shust.com> Date: Mon, 17 Feb 2020 18:16:35 -0500 Subject: [PATCH 176/734] Remove corresponding test call to getModule --- .../Magento/Framework/RequireJs/Test/Unit/ConfigTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php index 05f040e8f406d..b6b8203bef2bc 100644 --- a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php @@ -115,9 +115,6 @@ function ($file) { $fileOne->expects($this->once()) ->method('getName') ->will($this->returnValue('file_one.js')); - $fileOne->expects($this->once()) - ->method('getModule') - ->will($this->returnValue('Module_One')); $fileTwo = $this->createMock(\Magento\Framework\View\File::class); $fileTwo->expects($this->once()) ->method('getFilename') From e06f660f7d72019f125e6a8eaa8f1a59c9dba14a Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Wed, 19 Feb 2020 18:40:37 +0700 Subject: [PATCH 177/734] Replace hard-code scope store string --- .../Customer/Model/EmailNotification.php | 6 +- .../Test/Unit/Model/EmailNotificationTest.php | 300 +++++++++--------- 2 files changed, 156 insertions(+), 150 deletions(-) diff --git a/app/code/Magento/Customer/Model/EmailNotification.php b/app/code/Magento/Customer/Model/EmailNotification.php index 432317444f4b7..c778fd4004cdc 100644 --- a/app/code/Magento/Customer/Model/EmailNotification.php +++ b/app/code/Magento/Customer/Model/EmailNotification.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Model; @@ -15,6 +16,7 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Exception\LocalizedException; +use Magento\Store\Model\ScopeInterface; /** * Customer email notification @@ -253,14 +255,14 @@ private function sendEmailTemplate( $storeId = null, $email = null ) { - $templateId = $this->scopeConfig->getValue($template, 'store', $storeId); + $templateId = $this->scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId); if ($email === null) { $email = $customer->getEmail(); } /** @var array $from */ $from = $this->senderResolver->resolve( - $this->scopeConfig->getValue($sender, 'store', $storeId), + $this->scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId), $storeId ); diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index ff83ef62c6aa7..bf271e138ec58 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Test\Unit\Model; @@ -11,51 +12,70 @@ use Magento\Framework\App\Area; use Magento\Framework\Mail\Template\SenderResolverInterface; use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Store\Model\Store; +use Magento\Customer\Model\Data\CustomerSecure; +use Magento\Store\Model\Website; /** * Class EmailNotificationTest * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class EmailNotificationTest extends \PHPUnit\Framework\TestCase +class EmailNotificationTest extends TestCase { + private const STUB_CUSTOMER_ID = 1; + + private const STUB_CUSTOMER_STORE_ID = 2; + + private const STUB_CUSTOMER_WEBSITE_ID = 1; + + private const STUB_CUSTOMER_EMAIL = 'email@email.com'; + + private const STUB_CUSTOMER_NAME = 'Customer Name'; + + private const STUB_EMAIL_IDENTIFIER = 'Template Identifier'; + + private const STUB_Sender = 'Sender'; + /** - * @var \Magento\Customer\Model\CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\CustomerRegistry|MockObject */ private $customerRegistryMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Mail\Template\TransportBuilder|MockObject */ private $transportBuilderMock; /** - * @var \Magento\Customer\Helper\View|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Helper\View|MockObject */ private $customerViewHelperMock; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Reflection\DataObjectProcessor|MockObject */ private $dataProcessorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\Data\CustomerSecure + * @var CustomerSecure|MockObject */ private $customerSecureMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|MockObject */ private $scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store + * @var \Magento\Store\Model\Store|MockObject */ private $storeMock; @@ -65,11 +85,14 @@ class EmailNotificationTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var SenderResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SenderResolverInterface|MockObject */ private $senderResolverMock; - public function setUp() + /** + * @inheritdoc + */ + public function setUp(): void { $this->customerRegistryMock = $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); @@ -92,9 +115,9 @@ public function setUp() ->method('getScopeConfig') ->willReturn($this->scopeConfigMock); - $this->customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $this->customerSecureMock = $this->createMock(CustomerSecure::class); - $this->storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->storeMock = $this->createMock(Store::class); $this->senderResolverMock = $this->getMockBuilder(SenderResolverInterface::class) ->setMethods(['resolve']) @@ -118,6 +141,8 @@ public function setUp() } /** + * Test email notify when credentials changed + * * @param int $testNumber * @param string $oldEmail * @param string $newEmail @@ -126,16 +151,10 @@ public function setUp() * @dataProvider sendNotificationEmailsDataProvider * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPasswordChanged) + public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPasswordChanged): void { - $customerId = 1; - $customerStoreId = 2; - $customerWebsiteId = 1; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $expects = $this->once(); $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; @@ -157,44 +176,44 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->senderResolverMock ->expects($expects) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var \PHPUnit_Framework_MockObject_MockObject $origCustomer */ + /** @var MockObject $origCustomer */ $origCustomer = $this->createMock(CustomerInterface::class); $origCustomer->expects($this->any()) ->method('getStoreId') ->willReturn(0); $origCustomer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $origCustomer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); - $storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $storeMock = $this->createMock(Store::class); $storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects(clone $expects) ->method('getStore') ->willReturn($storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') - ->willReturn([$customerStoreId]); + ->willReturn([self::STUB_CUSTOMER_STORE_ID]); $this->storeManagerMock->expects(clone $expects) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); - $customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $customerSecureMock = $this->createMock(CustomerSecure::class); $this->customerRegistryMock->expects(clone $expects) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($customerSecureMock); $this->dataProcessorMock->expects(clone $expects) @@ -205,7 +224,7 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($origCustomer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $customerSecureMock->expects(clone $expects) ->method('addData') @@ -213,10 +232,10 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas ->willReturnSelf(); $customerSecureMock->expects(clone $expects) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $savedCustomer */ + /** @var CustomerInterface|MockObject $savedCustomer */ $savedCustomer = clone $origCustomer; $origCustomer->expects($this->any()) @@ -230,28 +249,28 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->scopeConfigMock->expects($this->any()) ->method('getValue') ->withConsecutive( - [$xmlPathTemplate, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $customerStoreId], + [$xmlPathTemplate, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID], [ \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $customerStoreId + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID ], - [$xmlPathTemplate, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $customerStoreId], + [$xmlPathTemplate, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID], [ \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $customerStoreId + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID ] ) - ->willReturnOnConsecutiveCalls($templateIdentifier, $sender, $templateIdentifier, $sender); + ->willReturnOnConsecutiveCalls(self::STUB_EMAIL_IDENTIFIER, self::STUB_SENDER, self::STUB_EMAIL_IDENTIFIER, self::STUB_SENDER); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateIdentifier') - ->with($templateIdentifier) + ->with(self::STUB_EMAIL_IDENTIFIER) ->willReturnSelf(); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateOptions') - ->with(['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $customerStoreId]) + ->with(['area' => Area::AREA_FRONTEND, 'store' => self::STUB_CUSTOMER_STORE_ID]) ->willReturnSelf(); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateVars') @@ -264,7 +283,7 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->transportBuilderMock->expects(clone $expects) ->method('addTo') - ->withConsecutive([$oldEmail, $customerName], [$newEmail, $customerName]) + ->withConsecutive([$oldEmail, self::STUB_CUSTOMER_NAME], [$newEmail, self::STUB_CUSTOMER_NAME]) ->willReturnSelf(); $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); @@ -280,9 +299,12 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas } /** + * Provides Emails Data Provider + * + * @param void * @return array */ - public function sendNotificationEmailsDataProvider() + public function sendNotificationEmailsDataProvider(): array { return [ [ @@ -309,43 +331,36 @@ public function sendNotificationEmailsDataProvider() /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordReminder() + public function testPasswordReminder(): void { - $customerId = 1; - $customerWebsiteId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $storeIds = [1, 2]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn($self::STUB_CUSTOMER_WEBSITE_ID); $customer->expects($this->any()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->at(0)) ->method('getStore') @@ -358,12 +373,12 @@ public function testPasswordReminder() $this->storeManagerMock->expects($this->any()) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -374,7 +389,7 @@ public function testPasswordReminder() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -382,24 +397,24 @@ public function testPasswordReminder() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); @@ -407,40 +422,36 @@ public function testPasswordReminder() } /** + * Test password reminder customer withouer store id info + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordReminderCustomerWithoutStoreId() + public function testPasswordReminderCustomerWithoutStoreId(): void { - $customerId = 1; - $customerWebsiteId = 1; $customerStoreId = null; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $storeIds = [1, 2]; $defaultStoreId = reset($storeIds); $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $defaultStoreId) + ->with(self::STUB_SENDER, $defaultStoreId) ->willReturn($senderValues); - /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $customer */ + /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); $customer->expects($this->any()) ->method('getStoreId') ->willReturn($customerStoreId); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') ->willReturn($defaultStoreId); @@ -457,12 +468,12 @@ public function testPasswordReminderCustomerWithoutStoreId() ->willReturn($storeIds); $this->storeManagerMock->expects($this->any()) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) ->method('buildOutputDataArray') @@ -471,69 +482,65 @@ public function testPasswordReminderCustomerWithoutStoreId() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') ->with($customerData) ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn($templateIdentifier); + ->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn($sender); + ->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, + self::STUB_EMAIL_IDENTIFIER, $defaultStoreId, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); $this->model->passwordReminder($customer); } /** + * Test email notify for password reset confirm + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordResetConfirmation() + public function testPasswordResetConfirmation(): void { - $customerId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->once()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->at(0)) ->method('getStore') @@ -541,7 +548,7 @@ public function testPasswordResetConfirmation() $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -552,7 +559,7 @@ public function testPasswordResetConfirmation() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -560,24 +567,24 @@ public function testPasswordResetConfirmation() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); @@ -585,49 +592,46 @@ public function testPasswordResetConfirmation() } /** + * Test email notify with new account + * + * @param void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testNewAccount() + public function testNewAccount(): void { - $customerId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->once()) ->method('getStore') - ->with($customerStoreId) + ->with(self::STUB_CUSTOMER_STORE_ID) ->willReturn($this->storeMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -638,7 +642,7 @@ public function testNewAccount() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -646,28 +650,28 @@ public function testNewAccount() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) + ->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'back_url' => '', 'store' => $this->storeMock] ); - $this->model->newAccount($customer, EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, '', $customerStoreId); + $this->model->newAccount($customer, EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, '', self::STUB_CUSTOMER_STORE_ID); } /** From 889e67668b8bb6fa9699a2195d31fa856c8c6046 Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Wed, 19 Feb 2020 18:49:18 +0700 Subject: [PATCH 178/734] Update fixes --- .../Customer/Model/EmailNotification.php | 27 ++++++++++--------- .../Test/Unit/Model/EmailNotificationTest.php | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Customer/Model/EmailNotification.php b/app/code/Magento/Customer/Model/EmailNotification.php index c778fd4004cdc..b72929a9de3a5 100644 --- a/app/code/Magento/Customer/Model/EmailNotification.php +++ b/app/code/Magento/Customer/Model/EmailNotification.php @@ -17,6 +17,7 @@ use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\ScopeInterface; +use Magento\Customer\Model\Data\CustomerSecure; /** * Customer email notification @@ -126,7 +127,7 @@ public function __construct( $this->customerViewHelper = $customerViewHelper; $this->dataProcessor = $dataProcessor; $this->scopeConfig = $scopeConfig; - $this->senderResolver = $senderResolver ?: ObjectManager::getInstance()->get(SenderResolverInterface::class); + $this->senderResolver = $senderResolver ?? ObjectManager::getInstance()->get(SenderResolverInterface::class); } /** @@ -141,7 +142,7 @@ public function credentialsChanged( CustomerInterface $savedCustomer, $origCustomerEmail, $isPasswordChanged = false - ) { + ): void { if ($origCustomerEmail != $savedCustomer->getEmail()) { if ($isPasswordChanged) { $this->emailAndPasswordChanged($savedCustomer, $origCustomerEmail); @@ -166,7 +167,7 @@ public function credentialsChanged( * @param string $email * @return void */ - private function emailAndPasswordChanged(CustomerInterface $customer, $email) + private function emailAndPasswordChanged(CustomerInterface $customer, $email): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -192,7 +193,7 @@ private function emailAndPasswordChanged(CustomerInterface $customer, $email) * @param string $email * @return void */ - private function emailChanged(CustomerInterface $customer, $email) + private function emailChanged(CustomerInterface $customer, $email): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -217,7 +218,7 @@ private function emailChanged(CustomerInterface $customer, $email) * @param CustomerInterface $customer * @return void */ - private function passwordReset(CustomerInterface $customer) + private function passwordReset(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -254,7 +255,7 @@ private function sendEmailTemplate( $templateParams = [], $storeId = null, $email = null - ) { + ): void { $templateId = $this->scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId); if ($email === null) { $email = $customer->getEmail(); @@ -280,15 +281,15 @@ private function sendEmailTemplate( * Create an object with data merged from Customer and CustomerSecure * * @param CustomerInterface $customer - * @return \Magento\Customer\Model\Data\CustomerSecure + * @return CustomerSecure */ - private function getFullCustomerObject($customer) + private function getFullCustomerObject($customer): CustomerSecure { // No need to flatten the custom attributes or nested objects since the only usage is for email templates and // object passed for events $mergedCustomerData = $this->customerRegistry->retrieveSecureData($customer->getId()); $customerData = $this->dataProcessor - ->buildOutputDataArray($customer, \Magento\Customer\Api\Data\CustomerInterface::class); + ->buildOutputDataArray($customer, CustomerInterface::class); $mergedCustomerData->addData($customerData); $mergedCustomerData->setData('name', $this->customerViewHelper->getCustomerName($customer)); return $mergedCustomerData; @@ -301,7 +302,7 @@ private function getFullCustomerObject($customer) * @param int|string|null $defaultStoreId * @return int */ - private function getWebsiteStoreId($customer, $defaultStoreId = null) + private function getWebsiteStoreId($customer, $defaultStoreId = null): int { if ($customer->getWebsiteId() != 0 && empty($defaultStoreId)) { $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); @@ -316,7 +317,7 @@ private function getWebsiteStoreId($customer, $defaultStoreId = null) * @param CustomerInterface $customer * @return void */ - public function passwordReminder(CustomerInterface $customer) + public function passwordReminder(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -340,7 +341,7 @@ public function passwordReminder(CustomerInterface $customer) * @param CustomerInterface $customer * @return void */ - public function passwordResetConfirmation(CustomerInterface $customer) + public function passwordResetConfirmation(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -375,7 +376,7 @@ public function newAccount( $backUrl = '', $storeId = 0, $sendemailStoreId = null - ) { + ): void { $types = self::TEMPLATE_TYPES; if (!isset($types[$type])) { diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index bf271e138ec58..f805ad79452aa 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -37,7 +37,7 @@ class EmailNotificationTest extends TestCase private const STUB_EMAIL_IDENTIFIER = 'Template Identifier'; - private const STUB_Sender = 'Sender'; + private const STUB_SENDER = 'Sender'; /** * @var \Magento\Customer\Model\CustomerRegistry|MockObject From 9404f53dcc4e5104f9ba92d02724e42ac05e2e00 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Wed, 19 Feb 2020 18:11:17 +0530 Subject: [PATCH 179/734] Refactoring jasmine test --- .../view/base/web/templates/grid/sortBy.html | 2 +- .../Magento/Ui/base/js/grid/sortBy.test.js | 63 +++++++++---------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html index e0bcf6cbe5514..14f1da81fbc8b 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<div if="isVisible" class="masonry-sorting"> +<div if="isVisible" class="masonry-image-sorting"> <b><!-- ko i18n: 'Sort by' --><!-- /ko -->:</b> <select class="admin__control-select" data-bind=" options: options, diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js index 6dd0d68f6be46..7797180ce65b5 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -14,57 +14,54 @@ define([ }); describe('"preparedOptions" method', function () { - it('return empty array if sorting is disabled for the columns', function () { - var columns = [{ - sortable: false, - label: 'magento', - index: 'name' - }], - options = []; + it('sort option will not available if sorting is disabled for the columns', function () { + var columns = { + sortable: false, + label: 'magento', + index: 'name' + }; - sortByObj.preparedOptions(columns); - expect(sortByObj.options).toEqual(options); + sortByObj.preparedOptions([columns]); + expect(sortByObj.options[0]).toBeUndefined(); + expect(sortByObj.options[0]).toBeUndefined(); }); - it('return array of options if sorting is enabled for the columns', function () { - var columns = [{ - sortable: true, - label: 'magento', - index: 'name' - }], - options = [{ - value: 'name', - label: 'magento' - }]; + it('sort option will available if sorting is enabled for the columns', function () { + var columns = { + sortable: true, + label: 'magento', + index: 'name' + }; - sortByObj.preparedOptions(columns); - expect(sortByObj.options).toEqual(options); + sortByObj.preparedOptions([columns]); + expect(sortByObj.options[0].value).toEqual('name'); + expect(sortByObj.options[0].label).toEqual('magento'); }); - it('return "isVisible" method true if column is sortable', function () { - var columns = [{ - sortable: true, - label: 'magento', - index: 'name' - }]; + it('return "isVisible" method true if sorting is enabled for column', function () { + var columns = { + sortable: true, + label: 'magento', + index: 'name' + }; - sortByObj.preparedOptions(columns); + sortByObj.preparedOptions([columns]); expect(sortByObj.isVisible()).toBeTruthy(); }); - it('return "isVisible" method false if column is sortable', function () { - var columns = [{ + it('return "isVisible" method false if sorting is disabled for column', function () { + var columns = { sortable: false, label: 'magento', index: 'name' - }]; + }; - sortByObj.preparedOptions(columns); + sortByObj.preparedOptions([columns]); expect(sortByObj.isVisible()).toBeFalsy(); }); }); describe('"applyChanges" method', function () { - it('return applied option', function () { + it('return applied options for sorting column', function () { var applied = { field: 'selectedOption', direction: 'desc' From 18c6f1501d1ecdda811750c8b76b465b98014414 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 19 Feb 2020 16:36:01 +0200 Subject: [PATCH 180/734] MC-23795: Error page after flush cache on Storefront product page (product created via API) --- .../Customer/Test/Mftf/Data/CustomerData.xml | 24 +++++++++---------- app/code/Magento/Customer/etc/di.xml | 5 ++++ app/code/Magento/Persistent/etc/di.xml | 3 --- .../Magento/Persistent/etc/frontend/di.xml | 3 +++ .../AdminInlineTranslationOnCheckoutTest.xml | 3 +++ 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index b9227505871cf..72d22a5105825 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -9,7 +9,7 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerEntityOne" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">defaultBillingValue</data> <data key="default_shipping">defaultShippingValue</data> <data key="confirmation">confirmationData</data> @@ -111,7 +111,7 @@ <data key="website_id">0</data> </entity> <entity name="Simple_US_Customer_Multiple_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -125,7 +125,7 @@ <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> </entity> <entity name="Simple_US_Customer_Multiple_Addresses_No_Default_Address" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -139,7 +139,7 @@ <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> </entity> <entity name="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -153,7 +153,7 @@ <requiredEntity type="address">US_Address_NY_Default_Shipping</requiredEntity> </entity> <entity name="Simple_US_Customer_NY" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -166,7 +166,7 @@ <requiredEntity type="address">US_Address_NY</requiredEntity> </entity> <entity name="Simple_US_Customer_CA" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -209,7 +209,7 @@ <requiredEntity type="address">US_Address_Utah</requiredEntity> </entity> <entity name="Simple_GB_Customer" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">Jane.Doe@example.com</data> @@ -245,7 +245,7 @@ <data key="website_id">0</data> </entity> <entity name="UKCustomer" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">david@email.com</data> @@ -259,7 +259,7 @@ <requiredEntity type="address">updateCustomerUKAddress</requiredEntity> </entity> <entity name="Customer_US_UK_DE" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -287,7 +287,7 @@ <requiredEntity type="address">US_Address_CA</requiredEntity> </entity> <entity name="Simple_US_Customer_Two_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -319,7 +319,7 @@ <data key="password">123123qa</data> </entity> <entity name="Simple_US_Customer_Incorrect_Email" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email">><script>alert(1);</script>@example.com</data> @@ -342,7 +342,7 @@ <data key="website_id">0</data> </entity> <entity name="Simple_US_Customer_ArmedForcesEurope" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index be219a81fd990..7882cc81d94b0 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -357,6 +357,11 @@ <type name="Magento\Framework\App\Action\AbstractAction"> <plugin name="customerNotification" type="Magento\Customer\Model\Plugin\CustomerNotification"/> </type> + <type name="Magento\Customer\Model\Authorization\CustomerSessionUserContext"> + <arguments> + <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument> + </arguments> + </type> <type name="Magento\PageCache\Observer\FlushFormKey"> <plugin name="customerFlushFormKey" type="Magento\Customer\Model\Plugin\CustomerFlushFormKey"/> </type> diff --git a/app/code/Magento/Persistent/etc/di.xml b/app/code/Magento/Persistent/etc/di.xml index c28426b4f25bf..f49d4361acb52 100644 --- a/app/code/Magento/Persistent/etc/di.xml +++ b/app/code/Magento/Persistent/etc/di.xml @@ -12,7 +12,4 @@ <type name="Magento\Customer\CustomerData\Customer"> <plugin name="section_data" type="Magento\Persistent\Model\Plugin\CustomerData" /> </type> - <type name="Magento\Framework\App\Http\Context"> - <plugin name="persistent_page_cache_variation" type="Magento\Persistent\Model\Plugin\PersistentCustomerContext" /> - </type> </config> diff --git a/app/code/Magento/Persistent/etc/frontend/di.xml b/app/code/Magento/Persistent/etc/frontend/di.xml index 3c33f8a51c418..fae706fcc5808 100644 --- a/app/code/Magento/Persistent/etc/frontend/di.xml +++ b/app/code/Magento/Persistent/etc/frontend/di.xml @@ -49,4 +49,7 @@ </argument> </arguments> </type> + <type name="Magento\Framework\App\Http\Context"> + <plugin name="persistent_page_cache_variation" type="Magento\Persistent\Model\Plugin\PersistentCustomerContext" /> + </type> </config> diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index 08448f7735f7c..b65a7caf31619 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-11505"/> <group value="translation"/> <group value="checkout"/> + <skip> + <issueId value="MC-31663"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From b4debc7d5ecccfaeda743af5fbf512b3d93713df Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Wed, 19 Feb 2020 23:18:04 +0700 Subject: [PATCH 181/734] fix unit test --- .../Magento/Customer/Test/Unit/Model/EmailNotificationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index f805ad79452aa..0225271e69667 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -347,7 +347,7 @@ public function testPasswordReminder(): void $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($self::STUB_CUSTOMER_WEBSITE_ID); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); $customer->expects($this->any()) ->method('getStoreId') ->willReturn(self::STUB_CUSTOMER_STORE_ID); From fe7611f2313d4ecb7007bde54bfda0278257f586 Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Thu, 20 Feb 2020 09:39:19 +0700 Subject: [PATCH 182/734] Fix static tests for b2b --- .../Test/Unit/Model/EmailNotificationTest.php | 113 ++++++++++++------ 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index 0225271e69667..de16cfb02c7b6 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + declare(strict_types=1); namespace Magento\Customer\Test\Unit\Model; @@ -17,9 +18,10 @@ use Magento\Store\Model\Store; use Magento\Customer\Model\Data\CustomerSecure; use Magento\Store\Model\Website; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class EmailNotificationTest + * Unit test for \Magento\Customer\Model\EmailNotification * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -80,7 +82,7 @@ class EmailNotificationTest extends TestCase private $storeMock; /** - * @var \Magento\Customer\Model\EmailNotification + * @var EmailNotification */ private $model; @@ -124,7 +126,7 @@ public function setUp(): void ->disableOriginalConstructor() ->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManagerHelper($this); $this->model = $objectManager->getObject( EmailNotification::class, @@ -142,7 +144,7 @@ public function setUp(): void /** * Test email notify when credentials changed - * + * * @param int $testNumber * @param string $oldEmail * @param string $newEmail @@ -164,7 +166,7 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n $expects = $this->once(); break; case 2: - $xmlPathTemplate = \Magento\Customer\Model\EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; $expects = $this->exactly(2); break; case 3: @@ -249,20 +251,32 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n $this->scopeConfigMock->expects($this->any()) ->method('getValue') ->withConsecutive( - [$xmlPathTemplate, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID], [ - \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + $xmlPathTemplate, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + ], + [ + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + ], + [ + $xmlPathTemplate, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID ], - [$xmlPathTemplate, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID], [ - \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID ] - ) - ->willReturnOnConsecutiveCalls(self::STUB_EMAIL_IDENTIFIER, self::STUB_SENDER, self::STUB_EMAIL_IDENTIFIER, self::STUB_SENDER); + )->willReturnOnConsecutiveCalls( + self::STUB_EMAIL_IDENTIFIER, + self::STUB_SENDER, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_SENDER + ); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateIdentifier') @@ -300,7 +314,7 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n /** * Provides Emails Data Provider - * + * * @param void * @return array */ @@ -329,6 +343,8 @@ public function sendNotificationEmailsDataProvider(): array } /** + * Test Password Reminder Email Notify + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testPasswordReminder(): void @@ -366,7 +382,7 @@ public function testPasswordReminder(): void ->method('getStore') ->willReturn($this->storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') ->willReturn($storeIds); @@ -402,12 +418,18 @@ public function testPasswordReminder(): void $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_EMAIL_IDENTIFIER); + ->with( + EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_SENDER); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( self::STUB_EMAIL_IDENTIFIER, @@ -423,7 +445,7 @@ public function testPasswordReminder(): void /** * Test password reminder customer withouer store id info - * + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testPasswordReminderCustomerWithoutStoreId(): void @@ -462,7 +484,7 @@ public function testPasswordReminderCustomerWithoutStoreId(): void ->method('getStore') ->with($defaultStoreId) ->willReturn($this->storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') ->willReturn($storeIds); @@ -493,12 +515,18 @@ public function testPasswordReminderCustomerWithoutStoreId(): void ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn(self::STUB_EMAIL_IDENTIFIER); + ->with( + EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + $defaultStoreId + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn(self::STUB_SENDER); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + $defaultStoreId + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( self::STUB_EMAIL_IDENTIFIER, $defaultStoreId, @@ -512,7 +540,7 @@ public function testPasswordReminderCustomerWithoutStoreId(): void /** * Test email notify for password reset confirm - * + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testPasswordResetConfirmation(): void @@ -572,12 +600,18 @@ public function testPasswordResetConfirmation(): void $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_EMAIL_IDENTIFIER); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_SENDER); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( self::STUB_EMAIL_IDENTIFIER, @@ -593,7 +627,7 @@ public function testPasswordResetConfirmation(): void /** * Test email notify with new account - * + * * @param void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -655,12 +689,18 @@ public function testNewAccount(): void $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_EMAIL_IDENTIFIER); + ->with( + EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, self::STUB_CUSTOMER_STORE_ID) - ->willReturn(self::STUB_SENDER); + ->with( + EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( self::STUB_EMAIL_IDENTIFIER, @@ -671,7 +711,12 @@ public function testNewAccount(): void ['customer' => $this->customerSecureMock, 'back_url' => '', 'store' => $this->storeMock] ); - $this->model->newAccount($customer, EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, '', self::STUB_CUSTOMER_STORE_ID); + $this->model->newAccount( + $customer, + EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, + '', + self::STUB_CUSTOMER_STORE_ID + ); } /** From 602100be66a3ec5072f9caf668ca980fee3080ec Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Thu, 20 Feb 2020 09:58:05 +0700 Subject: [PATCH 183/734] Fix for code standards --- .../Test/Unit/Model/EmailNotificationTest.php | 81 +++++++++++++------ 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index de16cfb02c7b6..7089dccc320b0 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -27,18 +27,39 @@ */ class EmailNotificationTest extends TestCase { + /** + * @var int + */ private const STUB_CUSTOMER_ID = 1; + /** + * @var int + */ private const STUB_CUSTOMER_STORE_ID = 2; + /** + * @var int + */ private const STUB_CUSTOMER_WEBSITE_ID = 1; + /** + * @var string + */ private const STUB_CUSTOMER_EMAIL = 'email@email.com'; + /** + * @var string + */ private const STUB_CUSTOMER_NAME = 'Customer Name'; + /** + * @var string + */ private const STUB_EMAIL_IDENTIFIER = 'Template Identifier'; + /** + * @var string + */ private const STUB_SENDER = 'Sender'; /** @@ -145,10 +166,10 @@ public function setUp(): void /** * Test email notify when credentials changed * - * @param int $testNumber + * @param int $testNumber * @param string $oldEmail * @param string $newEmail - * @param bool $isPasswordChanged + * @param bool $isPasswordChanged * * @dataProvider sendNotificationEmailsDataProvider * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -161,18 +182,18 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n $expects = $this->once(); $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; switch ($testNumber) { - case 1: - $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; - $expects = $this->once(); - break; - case 2: - $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; - $expects = $this->exactly(2); - break; - case 3: - $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE; - $expects = $this->exactly(2); - break; + case 1: + $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; + $expects = $this->once(); + break; + case 2: + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; + $expects = $this->exactly(2); + break; + case 3: + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE; + $expects = $this->exactly(2); + break; } $this->senderResolverMock @@ -181,7 +202,9 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var MockObject $origCustomer */ + /** + * @var MockObject $origCustomer + */ $origCustomer = $this->createMock(CustomerInterface::class); $origCustomer->expects($this->any()) ->method('getStoreId') @@ -237,7 +260,9 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); - /** @var CustomerInterface|MockObject $savedCustomer */ + /** + * @var CustomerInterface|MockObject $savedCustomer + */ $savedCustomer = clone $origCustomer; $origCustomer->expects($this->any()) @@ -315,7 +340,7 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n /** * Provides Emails Data Provider * - * @param void + * @param void * @return array */ public function sendNotificationEmailsDataProvider(): array @@ -359,7 +384,9 @@ public function testPasswordReminder(): void ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') @@ -460,7 +487,9 @@ public function testPasswordReminderCustomerWithoutStoreId(): void ->method('resolve') ->with(self::STUB_SENDER, $defaultStoreId) ->willReturn($senderValues); - /** @var CustomerInterface|MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') @@ -554,7 +583,9 @@ public function testPasswordResetConfirmation(): void ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->once()) ->method('getStoreId') @@ -642,7 +673,9 @@ public function testNewAccount(): void ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getStoreId') @@ -723,11 +756,11 @@ public function testNewAccount(): void * Create default mock for $this->transportBuilderMock. * * @param string $templateIdentifier - * @param int $customerStoreId - * @param array $senderValues + * @param int $customerStoreId + * @param array $senderValues * @param string $customerEmail * @param string $customerName - * @param array $templateVars + * @param array $templateVars * * @return void */ From 1d8253679701bcaa1ce730a5c0666f0094b31b28 Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Thu, 20 Feb 2020 11:59:05 +0700 Subject: [PATCH 184/734] Fix code styles --- .../Test/Unit/Model/EmailNotificationTest.php | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index 7089dccc320b0..063a12063e968 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -166,10 +166,10 @@ public function setUp(): void /** * Test email notify when credentials changed * - * @param int $testNumber + * @param int $testNumber * @param string $oldEmail * @param string $newEmail - * @param bool $isPasswordChanged + * @param bool $isPasswordChanged * * @dataProvider sendNotificationEmailsDataProvider * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -182,18 +182,18 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n $expects = $this->once(); $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; switch ($testNumber) { - case 1: - $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; - $expects = $this->once(); - break; - case 2: - $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; - $expects = $this->exactly(2); - break; - case 3: - $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE; - $expects = $this->exactly(2); - break; + case 1: + $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; + $expects = $this->once(); + break; + case 2: + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; + $expects = $this->exactly(2); + break; + case 3: + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE; + $expects = $this->exactly(2); + break; } $this->senderResolverMock @@ -340,7 +340,7 @@ public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $n /** * Provides Emails Data Provider * - * @param void + * @param void * @return array */ public function sendNotificationEmailsDataProvider(): array @@ -756,11 +756,11 @@ public function testNewAccount(): void * Create default mock for $this->transportBuilderMock. * * @param string $templateIdentifier - * @param int $customerStoreId - * @param array $senderValues + * @param int $customerStoreId + * @param array $senderValues * @param string $customerEmail * @param string $customerName - * @param array $templateVars + * @param array $templateVars * * @return void */ @@ -771,7 +771,8 @@ private function mockDefaultTransportBuilder( string $customerEmail, string $customerName, array $templateVars = [] - ): void { + ): void + { $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); $this->transportBuilderMock->expects($this->once()) From 4d4036dc1bee74592bd5951beabaff793babe573 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Thu, 20 Feb 2020 17:48:32 +0700 Subject: [PATCH 185/734] fix phpcs warning --- .../Magento/Customer/Test/Unit/Model/EmailNotificationTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index 063a12063e968..a8bf94247fd6d 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -771,8 +771,7 @@ private function mockDefaultTransportBuilder( string $customerEmail, string $customerName, array $templateVars = [] - ): void - { + ): void { $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); $this->transportBuilderMock->expects($this->once()) From fda138bb25192fd4c1b973f223a305449bd8eb77 Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Fri, 21 Feb 2020 10:58:44 +0700 Subject: [PATCH 186/734] Add unit test for changes --- .../Quote/Model/Cart/CartTotalRepository.php | 13 +- .../Model/Cart/CartTotalRepositoryTest.php | 175 +++++++++++++----- 2 files changed, 136 insertions(+), 52 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php index 84ac7683c9cc6..d686ade787ff5 100644 --- a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php +++ b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Quote\Model\Cart; use Magento\Quote\Api; @@ -12,9 +15,11 @@ use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Quote\Model\Cart\Totals\ItemConverter; use Magento\Quote\Api\CouponManagementInterface; +use Magento\Quote\Api\Data\TotalsInterface as QuoteTotalsInterface; /** * Cart totals data object. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartTotalRepository implements CartTotalRepositoryInterface @@ -81,10 +86,10 @@ public function __construct( * Get cart total repository * * @param int $cartId - * @return Api\Data\TotalsInterface + * @return QuoteTotalsInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function get($cartId) + public function get($cartId): QuoteTotalsInterface { /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); @@ -97,12 +102,12 @@ public function get($cartId) } unset($addressTotalsData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); - /** @var \Magento\Quote\Api\Data\TotalsInterface $quoteTotals */ + /** @var QuoteTotalsInterface $quoteTotals */ $quoteTotals = $this->totalsFactory->create(); $this->dataObjectHelper->populateWithArray( $quoteTotals, $addressTotalsData, - \Magento\Quote\Api\Data\TotalsInterface::class + QuoteTotalsInterface::class ); $items = []; foreach ($quote->getAllVisibleItems() as $index => $item) { diff --git a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php index 804f0863d2d2a..83e23dbf3e886 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php @@ -4,73 +4,110 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Quote\Test\Unit\Model\Cart; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\CouponManagementInterface; +use Magento\Quote\Api\Data\TotalSegmentInterface; +use Magento\Quote\Model\Cart\CartTotalRepository; +use Magento\Quote\Model\Cart\Totals\ItemConverter; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Quote\Model\Cart\TotalsConverter; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** + * Cart total Repository Test + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CartTotalRepositoryTest extends \PHPUnit\Framework\TestCase +class CartTotalRepositoryTest extends TestCase { /** - * @var ObjectManager + * @var int + */ + private const STUB_CART_ID = 12; + + /** + * @var int + */ + private const STUB_ITEMS_QTY = 100; + + /** + * @var string + */ + private const STUB_CURRENCY_CODE = 'en_US'; + + /** + * @var string + */ + private const STUB_COUPON = 'coupon'; + + /** + * @var ObjectManagerHelper */ protected $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ItemConverter|MockObject */ protected $converterMock; /** - * @var \Magento\Quote\Model\Cart\CartTotalRepository + * @var CartTotalRepository */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ private $quoteRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $quoteMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Api\Data\TotalsInterfaceFactory|MockObject */ private $totalsFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $addressMock; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ protected $dataObjectHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CouponManagementInterface|MockObject */ protected $couponServiceMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TotalsConverter|MockObject */ protected $totalsConverterMock; protected function setUp() { - $this->objectManager = new ObjectManager($this); + $this->objectManager = new ObjectManagerHelper($this); $this->totalsFactoryMock = $this->createPartialMock( \Magento\Quote\Api\Data\TotalsInterfaceFactory::class, ['create'] ); - $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ + $this->quoteMock = $this->createPartialMock(Quote::class, [ 'isVirtual', 'getShippingAddress', 'getBillingAddress', @@ -80,20 +117,20 @@ protected function setUp() 'getItemsQty', 'collectTotals' ]); - $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); + $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); $this->addressMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address::class, + Address::class, ['getData', 'getTotals'] ); - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + $this->dataObjectHelperMock = $this->getMockBuilder(DataObjectHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->converterMock = $this->createMock(\Magento\Quote\Model\Cart\Totals\ItemConverter::class); + $this->converterMock = $this->createMock(ItemConverter::class); - $this->couponServiceMock = $this->createMock(\Magento\Quote\Api\CouponManagementInterface::class); - $this->totalsConverterMock = $this->createMock(\Magento\Quote\Model\Cart\TotalsConverter::class); + $this->couponServiceMock = $this->createMock(CouponManagementInterface::class); + $this->totalsConverterMock = $this->createMock(TotalsConverter::class); - $this->model = new \Magento\Quote\Model\Cart\CartTotalRepository( + $this->model = new CartTotalRepository( $this->totalsFactoryMock, $this->quoteRepositoryMock, $this->dataObjectHelperMock, @@ -104,17 +141,18 @@ protected function setUp() } /** + * Test get cart total + * * @param bool $isVirtual * @param string $getAddressType * @dataProvider getDataProvider + * + * @return void */ - public function testGet($isVirtual, $getAddressType) + public function testGetCartTotal($isVirtual, $getAddressType): void { - $cartId = 12; - $itemsQty = 100; - $coupon = 'coupon'; $addressTotals = ['address' => 'totals']; - $itemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class); + $itemMock = $this->createMock(QuoteItem::class); $visibleItems = [ 11 => $itemMock, ]; @@ -122,20 +160,34 @@ public function testGet($isVirtual, $getAddressType) 'name' => 'item', 'options' => [ 4 => ['label' => 'justLabel']], ]; - $currencyCode = 'US'; - $this->quoteRepositoryMock->expects($this->once()) ->method('getActive') - ->with($cartId) + ->with(self::STUB_CART_ID) ->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('isVirtual')->willReturn($isVirtual); - $this->quoteMock->expects($this->exactly(2))->method($getAddressType)->willReturn($this->addressMock); - $this->quoteMock->expects($this->once())->method('getAllVisibleItems')->willReturn($visibleItems); - $this->quoteMock->expects($this->once())->method('getBaseCurrencyCode')->willReturn($currencyCode); - $this->quoteMock->expects($this->once())->method('getQuoteCurrencyCode')->willReturn($currencyCode); - $this->quoteMock->expects($this->once())->method('getItemsQty')->willReturn($itemsQty); - $this->addressMock->expects($this->any())->method('getData')->willReturn($addressTotals); - $this->addressMock->expects($this->once())->method('getTotals')->willReturn($addressTotals); + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtual); + $this->quoteMock->expects($this->exactly(2)) + ->method($getAddressType) + ->willReturn($this->addressMock); + $this->quoteMock->expects($this->once()) + ->method('getAllVisibleItems') + ->willReturn($visibleItems); + $this->quoteMock->expects($this->once()) + ->method('getBaseCurrencyCode') + ->willReturn(self::STUB_CURRENCY_CODE); + $this->quoteMock->expects($this->once()) + ->method('getQuoteCurrencyCode') + ->willReturn(self::STUB_CURRENCY_CODE); + $this->quoteMock->expects($this->once()) + ->method('getItemsQty') + ->willReturn(self::STUB_ITEMS_QTY); + $this->addressMock->expects($this->any()) + ->method('getData') + ->willReturn($addressTotals); + $this->addressMock->expects($this->once()) + ->method('getTotals') + ->willReturn($addressTotals); $totalsMock = $this->createMock(\Magento\Quote\Api\Data\TotalsInterface::class); $this->totalsFactoryMock->expects($this->once())->method('create')->willReturn($totalsMock); @@ -145,29 +197,56 @@ public function testGet($isVirtual, $getAddressType) ->with($itemMock) ->willReturn($itemArray); - $totalSegmentsMock = $this->createMock(\Magento\Quote\Api\Data\TotalSegmentInterface::class); + $totalSegmentsMock = $this->createMock(TotalSegmentInterface::class); $this->totalsConverterMock->expects($this->once()) ->method('process') ->with($addressTotals) ->willReturn($totalSegmentsMock); - $this->couponServiceMock->expects($this->once())->method('get')->with($cartId)->willReturn($coupon); + $this->couponServiceMock + ->expects($this->once()) + ->method('get') + ->with(self::STUB_CART_ID) + ->willReturn(self::STUB_COUPON); - $totalsMock->expects($this->once())->method('setItems')->with([11 => $itemArray])->willReturnSelf(); - $totalsMock->expects($this->once())->method('setTotalSegments')->with($totalSegmentsMock)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setCouponCode')->with($coupon)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setGrandTotal')->willReturnSelf(); - $totalsMock->expects($this->once())->method('setItemsQty')->with($itemsQty)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setBaseCurrencyCode')->with($currencyCode)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setQuoteCurrencyCode')->with($currencyCode)->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setItems') + ->with([11 => $itemArray]) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setTotalSegments') + ->with($totalSegmentsMock) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setCouponCode') + ->with(self::STUB_COUPON) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setGrandTotal') + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setItemsQty') + ->with(self::STUB_ITEMS_QTY) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setBaseCurrencyCode') + ->with(self::STUB_CURRENCY_CODE) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setQuoteCurrencyCode') + ->with(self::STUB_CURRENCY_CODE) + ->willReturnSelf(); - $this->assertEquals($totalsMock, $this->model->get($cartId)); + $this->assertEquals($totalsMock, $this->model->get(self::STUB_CART_ID)); } /** + * Provide data for test different cases + * + * @param void * @return array */ - public function getDataProvider() + public function getDataProvider(): array { return [ 'Virtual Quote' => [ From 120269cdd16309ae9bd252b9e988a933d306b3c6 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 21 Feb 2020 08:12:59 +0200 Subject: [PATCH 187/734] MC-23795: Error page after flush cache on Storefront product page (product created via API) --- .../Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index b65a7caf31619..08448f7735f7c 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -17,9 +17,6 @@ <testCaseId value="MC-11505"/> <group value="translation"/> <group value="checkout"/> - <skip> - <issueId value="MC-31663"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From 71e795699211f0e81b73d76aa0b9b13b652164d8 Mon Sep 17 00:00:00 2001 From: mrtu <ladiesman9x@gmail.com> Date: Fri, 21 Feb 2020 13:16:45 +0700 Subject: [PATCH 188/734] Fix code style class description --- .../Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php index 83e23dbf3e886..14c7f073b9b89 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php @@ -24,7 +24,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Cart total Repository Test + * Test Cart totals object for class \Magento\Quote\Model\Cart\CartTotalRepository * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ From d6b94f688f96d6b1bbedc76bd364d6b9405d6a79 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Fri, 21 Feb 2020 10:47:28 +0200 Subject: [PATCH 189/734] Split the test into two separate testes, update action groups names --- ...ssibleDeleteYourOwnAccountActionGroup.xml} | 9 ++- ...l => AssertAdminUserInGridActionGroup.xml} | 4 +- ...> AssertAdminUserNotInGridActionGroup.xml} | 4 +- .../Test/AdminDeleteAdminUserEntityTest.xml | 65 ++++--------------- .../AdminDeleteOwnAdminUserAccountTest.xml | 52 +++++++++++++++ 5 files changed, 76 insertions(+), 58 deletions(-) rename app/code/Magento/User/Test/Mftf/ActionGroup/{AssertImpossibleDeleteYourOwnAccountActionGroup.xml => AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml} (73%) rename app/code/Magento/User/Test/Mftf/ActionGroup/{AssertUserInGridActionGroup.xml => AssertAdminUserInGridActionGroup.xml} (93%) rename app/code/Magento/User/Test/Mftf/ActionGroup/{AssertUserNotInGridActionGroup.xml => AssertAdminUserNotInGridActionGroup.xml} (93%) create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml similarity index 73% rename from app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml index 6659773f69639..54fe82831ef02 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertImpossibleDeleteYourOwnAccountActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml @@ -7,12 +7,15 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertImpossibleDeleteYourOwnAccountActionGroup"> - <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> + <actionGroup name="AssertAdminImpossibleDeleteYourOwnAccountActionGroup"> + <arguments> + <argument name="currentAdminPassword" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </arguments> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{currentAdminPassword}}" stepKey="enterThePassword" /> <click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteUser"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> <waitForElementVisible selector="{{AdminMessagesSection.error}}" stepKey="waitErrorMessage"/> <see selector="{{AdminMessagesSection.error}}" userInput="You cannot delete your own account." stepKey="seeErrorMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml similarity index 93% rename from app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml index 3b60f26a56809..b0ade7b3a8ce8 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserInGridActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertUserInGridActionGroup"> + <actionGroup name="AssertAdminUserInGridActionGroup"> <arguments> <argument name="userName" type="string"/> </arguments> @@ -19,4 +19,4 @@ <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{userName}}" stepKey="seeUser"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml similarity index 93% rename from app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml index 464c278813238..ed5c82aba84db 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserNotInGridActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertUserNotInGridActionGroup"> + <actionGroup name="AssertAdminUserNotInGridActionGroup"> <arguments> <argument name="userName" type="string"/> </arguments> @@ -19,4 +19,4 @@ <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> <see selector="{{AdminUserGridSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml index e9f57b9f6c8ad..a9cb1f3bcf919 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml @@ -12,72 +12,35 @@ <annotations> <features value="User"/> <stories value="Delete Admin User"/> - <title value="Admin user is not able to delete the own account"/> - <description value="Admin user is not able to delete the own account"/> + <title value="Admin user is able to delete a user account"/> + <description value="Admin user is able to delete a user account"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> <before> + <!--Create New Admin User--> + <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> + <createData entity="NewAdminUser" stepKey="user"> + <field key="current_password">{$adminPassword}</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> </before> + <after> <actionGroup ref="logout" stepKey="logOut"/> </after> - <!--Create New User--> - <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> - <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - - <!--Create New Role--> - <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> - <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> - <argument name="role" value="roleSales"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> - - <!--Assign New Role--> - <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"> - <argument name="roleName" value="{{roleSales.name}}"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> - <actionGroup ref="logout" stepKey="logOutAsDefaultAdminUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> - <argument name="adminUser" value="NewAdminUser"/> - </actionGroup> - - <!--Assert Impossible Delete Your Own Account--> - <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPageForDeleting"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="AssertImpossibleDeleteYourOwnAccountActionGroup" stepKey="assertErrorMessage"/> - <actionGroup ref="AssertUserInGridActionGroup" stepKey="assertUserInGrid"> - <argument name="userName" value="{{NewAdminUser.username}}"/> - </actionGroup> - - <actionGroup ref="logout" stepKey="logOutAsNewUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> - - <!--Delete New Role--> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> - <argument name="roleName" value="{{roleSales.name}}"/> - </actionGroup> - - <!--Delete New User--> + <!--Delete New Admin User--> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> - <argument name="user" value="NewAdminUser"/> + <argument name="user" value="$$user$$"/> </actionGroup> <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="You deleted the user."/> </actionGroup> - <actionGroup ref="AssertUserNotInGridActionGroup" stepKey="assertUserNotInGrid"> - <argument name="userName" value="{{NewAdminUser.username}}"/> + <actionGroup ref="AssertAdminUserNotInGridActionGroup" stepKey="assertUserNotInGrid"> + <argument name="userName" value="$$user.username$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml new file mode 100644 index 0000000000000..5b928a0e6dc9a --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml @@ -0,0 +1,52 @@ +<?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="AdminDeleteOwnAdminUserAccountTest"> + <annotations> + <features value="User"/> + <stories value="Delete Admin User"/> + <title value="Admin user is not able to delete the own account"/> + <description value="Admin user is not able to delete the own account"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!--Create New Admin User--> + <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> + <createData entity="NewAdminUser" stepKey="user"> + <field key="current_password">{$adminPassword}</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <argument name="adminUser" value="$$user$$"/> + </actionGroup> + </before> + <after> + <!-- Delete New Admin User --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> + <actionGroup ref="AdminDeleteUserViaCurlActionGroup" stepKey="deleteUser"> + <argument name="user" value="$$user$$" /> + </actionGroup> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Assert Impossible Delete Your Own Account--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPageForDeleting"> + <argument name="user" value="$$user$$"/> + </actionGroup> + <actionGroup ref="AssertAdminImpossibleDeleteYourOwnAccountActionGroup" stepKey="assertErrorMessage"> + <argument name="currentAdminPassword" value="$$user.password$$"/> + </actionGroup> + <actionGroup ref="AssertAdminUserInGridActionGroup" stepKey="assertUserInGrid"> + <argument name="userName" value="$$user.username$$"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOutAsNewUser"/> + </test> +</tests> From 5c29e1998e399696046e8562f8ec1a43bfd2ab42 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Mon, 24 Feb 2020 01:11:07 +0700 Subject: [PATCH 190/734] Remove unused requirejs alias resources defined --- app/code/Magento/Theme/view/frontend/requirejs-config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Theme/view/frontend/requirejs-config.js b/app/code/Magento/Theme/view/frontend/requirejs-config.js index b5ffd358c893b..e14c93d329a07 100644 --- a/app/code/Magento/Theme/view/frontend/requirejs-config.js +++ b/app/code/Magento/Theme/view/frontend/requirejs-config.js @@ -27,9 +27,7 @@ var config = { 'menu': 'mage/menu', 'popupWindow': 'mage/popup-window', 'validation': 'mage/validation/validation', - 'welcome': 'Magento_Theme/js/view/welcome', 'breadcrumbs': 'Magento_Theme/js/view/breadcrumbs', - 'criticalCssLoader': 'Magento_Theme/js/view/critical-css-loader', 'jquery/ui': 'jquery/compat', 'cookieStatus': 'Magento_Theme/js/cookie-status' } From a6f76f346596a80243596079f90fe0b78f7dc744 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sun, 23 Feb 2020 19:43:40 +0100 Subject: [PATCH 191/734] #26986 API Functional Test to cover Pagination issue --- .../Api/ProductRepositoryInterfaceTest.php | 100 ++++++++++++++++-- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 76107ebc6a13a..9f7db871a4ae6 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -8,19 +8,13 @@ namespace Magento\Catalog\Api; use Magento\Authorization\Model\Role; -use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RulesFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Downloadable\Model\Link; -use Magento\Integration\Api\AdminTokenServiceInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\Website; -use Magento\Store\Model\WebsiteRepository; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\WebapiAbstract; use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -28,6 +22,12 @@ use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Webapi\Exception as HTTPExceptionCodes; +use Magento\Integration\Api\AdminTokenServiceInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\Website; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; /** * Test for \Magento\Catalog\Api\ProductRepositoryInterface @@ -280,7 +280,7 @@ public function testCreateWithMultipleWebsites() $websitesData = [ 'website_ids' => [ 1, - (int) $website->getId(), + (int)$website->getId(), ] ]; $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData; @@ -1096,6 +1096,86 @@ public function testGetListWithFilteringByStoreDataProvider() ]; } + /** + * Test getList() method with pagination + * + * @param int $pageSize + * @param int $currentPage + * @param int $expectedCount + * + * @magentoAppIsolation enabled + * @magentoApiDataFixture Magento/Catalog/_files/products_for_search.php + * @dataProvider productPaginationDataProvider + */ + public function testGetListPagination(int $pageSize, int $currentPage, int $expectedCount) + { + $fixtureProducts = 5; + + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = Bootstrap::getObjectManager()->create(FilterBuilder::class); + + $categoryFilter = $filterBuilder->setField('category_id') + ->setValue(333) + ->create(); + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); + + $searchCriteriaBuilder->addFilters([$categoryFilter]); + $searchCriteriaBuilder->setPageSize($pageSize); + $searchCriteriaBuilder->setCurrentPage($currentPage); + + $searchData = $searchCriteriaBuilder->create()->__toArray(); + $requestData = ['searchCriteria' => $searchData]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '?' . http_build_query($requestData), + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'GetList', + ], + ]; + + $searchResult = $this->_webApiCall($serviceInfo, $requestData); + + $this->assertEquals($fixtureProducts, $searchResult['total_count']); + $this->assertCount($expectedCount, $searchResult['items']); + } + + /** + * Keep in mind: Fixture contains 5 products + * + * @return array + */ + public function productPaginationDataProvider() + { + return [ + 'expect-all-items' => [ + 'pageSize' => 10, + 'currentPage' => 1, + 'expectedCount' => 5 + ], + 'expect-page=size-items' => [ + 'pageSize' => 2, + 'currentPage' => 1, + 'expectedCount' => 2 + ], + 'expect-less-than-pagesize-elements' => [ + 'pageSize' => 3, + 'currentPage' => 2, + 'expectedCount' => 2 + ], + 'expect-no-items' => [ + 'pageSize' => 100, + 'currentPage' => 99, + 'expectedCount' => 0 + ] + ]; + } + /** * Test getList() method with multiple filter groups and sorting and pagination * @@ -1133,7 +1213,7 @@ public function testGetListWithMultipleFilterGroupsAndSortingAndPagination() $sortOrder = $sortOrderBuilder->setField('meta_title')->setDirection(SortOrder::SORT_DESC)->create(); /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); + $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); $searchCriteriaBuilder->addFilters([$filter1, $filter2, $filter3, $filter4]); $searchCriteriaBuilder->addFilters([$filter5]); @@ -1728,8 +1808,8 @@ private function assertMultiselectValue($productSku, $multiselectAttributeCode, * Test design settings authorization * * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php - * @throws \Throwable * @return void + * @throws \Throwable */ public function testSaveDesign(): void { From b254764a3c654b8ed48c36086864478bfb6c953e Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sun, 23 Feb 2020 20:43:08 +0100 Subject: [PATCH 192/734] #26986 Remove "feature" that overrides the currentPage value --- .../Magento/Framework/Data/Collection.php | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index c497c4de5a5e4..fc7dde9019aa6 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -6,7 +6,10 @@ namespace Magento\Framework\Data; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Collection\EntityFactoryInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Option\ArrayInterface; /** @@ -25,7 +28,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll /** * Collection items * - * @var \Magento\Framework\DataObject[] + * @var DataObject[] */ protected $_items = []; @@ -34,7 +37,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll * * @var string */ - protected $_itemObjectClass = \Magento\Framework\DataObject::class; + protected $_itemObjectClass = DataObject::class; /** * Order configuration @@ -46,7 +49,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll /** * Filters configuration * - * @var \Magento\Framework\DataObject[] + * @var DataObject[] */ protected $_filters = []; @@ -117,7 +120,7 @@ public function __construct(EntityFactoryInterface $entityFactory) */ public function addFilter($field, $value, $type = 'and') { - $filter = new \Magento\Framework\DataObject(); + $filter = new DataObject(); // implements ArrayAccess $filter['field'] = $field; $filter['value'] = $value; @@ -163,9 +166,9 @@ public function addFilter($field, $value, $type = 'and') * * @param string|array $field * @param string|int|array $condition - * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected. * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected. */ public function addFieldToFilter($field, $condition) { @@ -182,7 +185,7 @@ public function addFieldToFilter($field, $condition) * - array() -- get all filters * * @param string|string[] $field - * @return \Magento\Framework\DataObject|\Magento\Framework\DataObject[]|void + * @return DataObject|DataObject[]|void */ public function getFilter($field) { @@ -234,18 +237,16 @@ protected function _setIsLoaded($flag = true) /** * Get current collection page * - * @param int $displacement + * @param int $displacement * @return int */ public function getCurPage($displacement = 0) { if ($this->_curPage + $displacement < 1) { return 1; - } elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) { - return $this->getLastPageNumber(); - } else { - return $this->_curPage + $displacement; } + + return $this->_curPage + $displacement; } /** @@ -260,9 +261,9 @@ public function getLastPageNumber() return 1; } elseif ($this->_pageSize) { return (int)ceil($collectionSize / $this->_pageSize); - } else { - return 1; } + + return 1; } /** @@ -292,7 +293,7 @@ public function getSize() /** * Retrieve collection first item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getFirstItem() { @@ -309,7 +310,7 @@ public function getFirstItem() /** * Retrieve collection last item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getLastItem() { @@ -325,7 +326,7 @@ public function getLastItem() /** * Retrieve collection items * - * @return \Magento\Framework\DataObject[] + * @return DataObject[] */ public function getItems() { @@ -336,7 +337,7 @@ public function getItems() /** * Retrieve field values from all items * - * @param string $colName + * @param string $colName * @return array */ public function getColumnValues($colName) @@ -353,8 +354,8 @@ public function getColumnValues($colName) /** * Search all items by field value * - * @param string $column - * @param mixed $value + * @param string $column + * @param array $value * @return array */ public function getItemsByColumnValue($column, $value) @@ -373,9 +374,9 @@ public function getItemsByColumnValue($column, $value) /** * Search first item by field value * - * @param string $column - * @param mixed $value - * @return \Magento\Framework\DataObject || null + * @param string $column + * @param string|int $value + * @return DataObject|null */ public function getItemByColumnValue($column, $value) { @@ -392,11 +393,11 @@ public function getItemByColumnValue($column, $value) /** * Adding item to item array * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this * @throws \Exception */ - public function addItem(\Magento\Framework\DataObject $item) + public function addItem(DataObject $item) { $itemId = $this->_getItemId($item); @@ -417,7 +418,7 @@ public function addItem(\Magento\Framework\DataObject $item) /** * Add item that has no id to collection * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this */ protected function _addItem($item) @@ -429,10 +430,10 @@ protected function _addItem($item) /** * Retrieve item id * - * @param \Magento\Framework\DataObject $item - * @return mixed + * @param DataObject $item + * @return string|int */ - protected function _getItemId(\Magento\Framework\DataObject $item) + protected function _getItemId(DataObject $item) { return $item->getId(); } @@ -454,7 +455,7 @@ public function getAllIds() /** * Remove item from collection by item key * - * @param mixed $key + * @param string $key * @return $this */ public function removeItemByKey($key) @@ -542,8 +543,8 @@ public function each($objMethod, $args = []) /** * Setting data for all collection items * - * @param mixed $key - * @param mixed $value + * @param string $key + * @param string|int|null $value * @return $this */ public function setDataToAll($key, $value = null) @@ -606,7 +607,7 @@ public function setOrder($field, $direction = self::SORT_ORDER_DESC) */ public function setItemObjectClass($className) { - if (!is_a($className, \Magento\Framework\DataObject::class, true)) { + if (!is_a($className, DataObject::class, true)) { throw new \InvalidArgumentException($className . ' does not extend \Magento\Framework\DataObject'); } $this->_itemObjectClass = $className; @@ -616,7 +617,7 @@ public function setItemObjectClass($className) /** * Retrieve collection empty item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getNewEmptyItem() { @@ -748,8 +749,8 @@ public function toArray($arrRequiredFields = []) * Return items array * array( * $index => array( - * 'value' => mixed - * 'label' => mixed + * 'value' => string + * 'label' => string * ) * ) * @@ -815,8 +816,8 @@ protected function _toOptionHash($valueField = 'id', $labelField = 'name') /** * Retrieve item by id * - * @param mixed $idValue - * @return \Magento\Framework\DataObject + * @param string|int $idValue + * @return DataObject */ public function getItemById($idValue) { @@ -910,7 +911,7 @@ public function __sleep() */ public function __wakeup() { - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $objectManager = ObjectManager::getInstance(); $this->_entityFactory = $objectManager->get(EntityFactoryInterface::class); } } From 11567f6004dbc6819c19bd42e95915dfbf3a046f Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sun, 23 Feb 2020 21:23:10 +0100 Subject: [PATCH 193/734] Fix static tests (`@return` alignment) --- lib/internal/Magento/Framework/Data/Collection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index fc7dde9019aa6..7c9cf02ac6a47 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -338,7 +338,7 @@ public function getItems() * Retrieve field values from all items * * @param string $colName - * @return array + * @return array */ public function getColumnValues($colName) { @@ -356,7 +356,7 @@ public function getColumnValues($colName) * * @param string $column * @param array $value - * @return array + * @return array */ public function getItemsByColumnValue($column, $value) { From 1cdedc70e3de0a4eebdd046b50314b7d8477e7ae Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 24 Feb 2020 08:03:41 +0200 Subject: [PATCH 194/734] MC-23795: Error page after flush cache on Storefront product page (product created via API) --- .../Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index 08448f7735f7c..b65a7caf31619 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-11505"/> <group value="translation"/> <group value="checkout"/> + <skip> + <issueId value="MC-31663"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From d00b206e048d04c72382dd592bfed550fa891e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Fri, 24 Jan 2020 10:23:22 +0100 Subject: [PATCH 195/734] Improve dashboard charts - migrate to chart.js --- app/code/Magento/Backend/Block/Dashboard.php | 45 +- .../Backend/Block/Dashboard/Diagrams.php | 6 + .../Magento/Backend/Block/Dashboard/Graph.php | 2 + .../Magento/Backend/Block/Dashboard/Grids.php | 12 +- .../Backend/Block/Dashboard/Orders/Grid.php | 26 +- .../Magento/Backend/Block/Dashboard/Sales.php | 23 +- .../Backend/Block/Dashboard/Tab/Amounts.php | 6 +- .../Backend/Block/Dashboard/Tab/Orders.php | 6 +- .../Backend/Block/Dashboard/Totals.php | 37 +- .../Adminhtml/Dashboard/AjaxBlock.php | 50 +- .../Adminhtml/Dashboard/Chart/Amounts.php | 64 + .../Adminhtml/Dashboard/Chart/Orders.php | 64 + .../Controller/Adminhtml/Dashboard/Tunnel.php | 10 +- .../Magento/Backend/Model/Dashboard/Chart.php | 97 + .../Backend/Model/Dashboard/Chart/Date.php | 94 + ...tOrderGraphImageOnDashboardActionGroup.xml | 2 +- .../Mftf/Section/AdminDashboardSection.xml | 2 +- .../Adminhtml/Dashboard/TunnelTest.php | 196 - .../Test/Unit/Model/Dashboard/ChartTest.php | 226 + .../Backend/ViewModel/ChartDisabled.php | 72 + .../Backend/ViewModel/ChartsPeriod.php | 40 + .../layout/adminhtml_dashboard_index.xml | 60 +- .../adminhtml/templates/dashboard/chart.phtml | 36 + .../templates/dashboard/chart/disabled.phtml | 22 + .../templates/dashboard/chart/period.phtml | 32 + .../adminhtml/templates/dashboard/graph.phtml | 5 + .../templates/dashboard/graph/disabled.phtml | 5 + .../adminhtml/templates/dashboard/index.phtml | 75 +- .../templates/dashboard/totalbar.phtml | 37 +- .../templates/dashboard/totalbar/script.phtml | 27 + .../view/adminhtml/web/js/dashboard/chart.js | 157 + .../view/adminhtml/web/js/dashboard/totals.js | 60 + .../Magento/Ui/view/base/requirejs-config.js | 2 + .../css/source/module/pages/_dashboard.less | 13 +- .../Backend/Block/Dashboard/GraphTest.php | 34 - .../Adminhtml/Dashboard/AjaxBlockTest.php | 15 +- .../Controller/Adminhtml/DashboardTest.php | 70 - lib/web/chartjs/Chart.bundle.js | 20755 ++++++++++++++++ lib/web/chartjs/Chart.bundle.min.js | 7 + lib/web/chartjs/Chart.css | 47 + lib/web/chartjs/Chart.js | 16151 ++++++++++++ lib/web/chartjs/Chart.min.css | 1 + lib/web/chartjs/Chart.min.js | 7 + 43 files changed, 38199 insertions(+), 499 deletions(-) create mode 100644 app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php create mode 100644 app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php create mode 100644 app/code/Magento/Backend/Model/Dashboard/Chart.php create mode 100644 app/code/Magento/Backend/Model/Dashboard/Chart/Date.php delete mode 100644 app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php create mode 100644 app/code/Magento/Backend/ViewModel/ChartDisabled.php create mode 100644 app/code/Magento/Backend/ViewModel/ChartsPeriod.php create mode 100644 app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml create mode 100644 app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml create mode 100644 app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml create mode 100644 app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml create mode 100644 app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js create mode 100644 app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js delete mode 100644 dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php create mode 100644 lib/web/chartjs/Chart.bundle.js create mode 100644 lib/web/chartjs/Chart.bundle.min.js create mode 100644 lib/web/chartjs/Chart.css create mode 100644 lib/web/chartjs/Chart.js create mode 100644 lib/web/chartjs/Chart.min.css create mode 100644 lib/web/chartjs/Chart.min.js diff --git a/app/code/Magento/Backend/Block/Dashboard.php b/app/code/Magento/Backend/Block/Dashboard.php index e1e87d8d4c5a3..28d3eeae9a1c6 100644 --- a/app/code/Magento/Backend/Block/Dashboard.php +++ b/app/code/Magento/Backend/Block/Dashboard.php @@ -3,14 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Backend\Block; /** + * Class used to initialize layout for MBO Dashboard + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard in adminhtml_dashboard_index.xml + * * @api * @since 100.0.2 */ -class Dashboard extends \Magento\Backend\Block\Template +class Dashboard extends Template { /** * Location of the "Enable Chart" config param @@ -23,42 +28,8 @@ class Dashboard extends \Magento\Backend\Block\Template protected $_template = 'Magento_Backend::dashboard/index.phtml'; /** - * @return void - */ - protected function _prepareLayout() - { - $this->addChild('lastOrders', \Magento\Backend\Block\Dashboard\Orders\Grid::class); - - $this->addChild('totals', \Magento\Backend\Block\Dashboard\Totals::class); - - $this->addChild('sales', \Magento\Backend\Block\Dashboard\Sales::class); - - $isChartEnabled = $this->_scopeConfig->getValue( - self::XML_PATH_ENABLE_CHARTS, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - if ($isChartEnabled) { - $block = $this->getLayout()->createBlock(\Magento\Backend\Block\Dashboard\Diagrams::class); - } else { - $block = $this->getLayout()->createBlock( - \Magento\Backend\Block\Template::class - )->setTemplate( - 'dashboard/graph/disabled.phtml' - )->setConfigUrl( - $this->getUrl( - 'adminhtml/system_config/edit', - ['section' => 'admin', '_fragment' => 'admin_dashboard-link'] - ) - ); - } - $this->setChild('diagrams', $block); - - $this->addChild('grids', \Magento\Backend\Block\Dashboard\Grids::class); - - parent::_prepareLayout(); - } - - /** + * Get url for switch action + * * @return string */ public function getSwitchUrl() diff --git a/app/code/Magento/Backend/Block/Dashboard/Diagrams.php b/app/code/Magento/Backend/Block/Dashboard/Diagrams.php index 12770bc12268d..7ebb81e3e2fbf 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Diagrams.php +++ b/app/code/Magento/Backend/Block/Dashboard/Diagrams.php @@ -7,6 +7,8 @@ /** * Adminhtml dashboard diagram tabs + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.diagrams in adminhtml_dashboard_index.xml * * @author Magento Core Team <core@magentocommerce.com> */ @@ -18,6 +20,8 @@ class Diagrams extends \Magento\Backend\Block\Widget\Tabs protected $_template = 'Magento_Backend::widget/tabshoriz.phtml'; /** + * Internal constructor, that is called from real constructor + * * @return void */ protected function _construct() @@ -28,6 +32,8 @@ protected function _construct() } /** + * Preparing global layout + * * @return $this */ protected function _prepareLayout() diff --git a/app/code/Magento/Backend/Block/Dashboard/Graph.php b/app/code/Magento/Backend/Block/Dashboard/Graph.php index 527bb2136b4c5..db95a64636c3a 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Graph.php +++ b/app/code/Magento/Backend/Block/Dashboard/Graph.php @@ -9,6 +9,8 @@ /** * Adminhtml dashboard google chart block + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.amounts and dashboard.chart.orders in adminhtml_dashboard_index.xml * * @author Magento Core Team <core@magentocommerce.com> */ diff --git a/app/code/Magento/Backend/Block/Dashboard/Grids.php b/app/code/Magento/Backend/Block/Dashboard/Grids.php index 986854da93cf8..f40aaaf33fed7 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Grids.php +++ b/app/code/Magento/Backend/Block/Dashboard/Grids.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Dashboard\Tab\Products\Ordered; +use Magento\Backend\Block\Widget\Tabs; + /** * Adminhtml dashboard bottom tabs * + * @api * @author Magento Core Team <core@magentocommerce.com> */ -class Grids extends \Magento\Backend\Block\Widget\Tabs +class Grids extends Tabs { /** * @var string @@ -18,6 +24,8 @@ class Grids extends \Magento\Backend\Block\Widget\Tabs protected $_template = 'Magento_Backend::widget/tabshoriz.phtml'; /** + * Internal constructor, that is called from real constructor + * * @return void */ protected function _construct() @@ -49,7 +57,7 @@ protected function _prepareLayout() [ 'label' => __('Bestsellers'), 'content' => $this->getLayout()->createBlock( - \Magento\Backend\Block\Dashboard\Tab\Products\Ordered::class + Ordered::class )->toHtml(), 'active' => true ] diff --git a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php index 0a73430aad0f3..8b3574e223236 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php +++ b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php @@ -5,36 +5,42 @@ */ namespace Magento\Backend\Block\Dashboard\Orders; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Helper\Data; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; + /** * Adminhtml dashboard recent orders grid * + * @api * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.DepthOfInheritance) */ class Grid extends \Magento\Backend\Block\Dashboard\Grid { /** - * @var \Magento\Reports\Model\ResourceModel\Order\CollectionFactory + * @var CollectionFactory */ protected $_collectionFactory; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\Manager $moduleManager - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory + * @param Context $context + * @param Data $backendHelper + * @param Manager $moduleManager + * @param CollectionFactory $collectionFactory * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\Manager $moduleManager, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, + Context $context, + Data $backendHelper, + Manager $moduleManager, + CollectionFactory $collectionFactory, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Block/Dashboard/Sales.php b/app/code/Magento/Backend/Block/Dashboard/Sales.php index b388339460102..ebe0932c3fa3b 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Sales.php +++ b/app/code/Magento/Backend/Block/Dashboard/Sales.php @@ -3,14 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Template\Context; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; + /** * Adminhtml dashboard sales statistics bar * + * @api * @author Magento Core Team <core@magentocommerce.com> */ -class Sales extends \Magento\Backend\Block\Dashboard\Bar +class Sales extends Bar { /** * @var string @@ -18,20 +25,20 @@ class Sales extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/salebar.phtml'; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CollectionFactory $collectionFactory + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CollectionFactory $collectionFactory, + Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php b/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php index 715d399fa1c7a..26243891fa007 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php +++ b/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php @@ -4,13 +4,15 @@ * See COPYING.txt for license details. */ +namespace Magento\Backend\Block\Dashboard\Tab; + /** * Adminhtml dashboard order amounts diagram + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.amounts in adminhtml_dashboard_index.xml * * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\Backend\Block\Dashboard\Tab; - class Amounts extends \Magento\Backend\Block\Dashboard\Graph { /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php b/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php index 72863f573c3bc..f88e6bb694671 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php +++ b/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php @@ -4,13 +4,15 @@ * See COPYING.txt for license details. */ +namespace Magento\Backend\Block\Dashboard\Tab; + /** * Adminhtml dashboard orders diagram + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.orders in adminhtml_dashboard_index.xml * * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\Backend\Block\Dashboard\Tab; - class Orders extends \Magento\Backend\Block\Dashboard\Graph { /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index 20bcfebe31a8d..3921148acd33a 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -3,18 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Adminhtml dashboard totals bar - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Template\Context; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\Collection; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; +use Magento\Store\Model\Store; + /** - * Totals block. + * Adminhtml dashboard totals bar + * @api */ -class Totals extends \Magento\Backend\Block\Dashboard\Bar +class Totals extends Bar { /** * @var string @@ -22,20 +25,20 @@ class Totals extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/totalbar.phtml'; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CollectionFactory $collectionFactory + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CollectionFactory $collectionFactory, + Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; @@ -60,7 +63,7 @@ protected function _prepareLayout() ); $period = $this->getRequest()->getParam('period', '24h'); - /* @var $collection \Magento\Reports\Model\ResourceModel\Order\Collection */ + /* @var $collection Collection */ $collection = $this->_collectionFactory->create()->addCreateAtPeriodFilter( $period )->calculateTotals( @@ -80,7 +83,7 @@ protected function _prepareLayout() } elseif (!$collection->isLive()) { $collection->addFieldToFilter( 'store_id', - ['eq' => $this->_storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] + ['eq' => $this->_storeManager->getStore(Store::ADMIN_CODE)->getId()] ); } } @@ -94,5 +97,7 @@ protected function _prepareLayout() $this->addTotal(__('Tax'), $totals->getTax()); $this->addTotal(__('Shipping'), $totals->getShipping()); $this->addTotal(__('Quantity'), $totals->getQuantity() * 1, true); + + return $this; } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php index 0b0b707557035..3ad0ab592f445 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php @@ -1,32 +1,45 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Controller\Adminhtml\Dashboard; -class AjaxBlock extends \Magento\Backend\Controller\Adminhtml\Dashboard +use Magento\Backend\App\Action\Context; +use Magento\Backend\Block\Dashboard\Totals; +use Magento\Backend\Controller\Adminhtml\Dashboard; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\View\LayoutFactory; + +/** + * Class used to retrieve content of dashboard totals block via ajax + */ +class AjaxBlock extends Dashboard implements HttpPostActionInterface { /** - * @var \Magento\Framework\Controller\Result\RawFactory + * @var RawFactory */ protected $resultRawFactory; /** - * @var \Magento\Framework\View\LayoutFactory + * @var LayoutFactory */ protected $layoutFactory; /** - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory - * @param \Magento\Framework\View\LayoutFactory $layoutFactory + * @param Context $context + * @param RawFactory $resultRawFactory + * @param LayoutFactory $layoutFactory */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Controller\Result\RawFactory $resultRawFactory, - \Magento\Framework\View\LayoutFactory $layoutFactory + Context $context, + RawFactory $resultRawFactory, + LayoutFactory $layoutFactory ) { parent::__construct($context); $this->resultRawFactory = $resultRawFactory; @@ -34,23 +47,22 @@ public function __construct( } /** - * @return \Magento\Framework\Controller\Result\Raw + * Retrieve block content via ajax + * + * @return Raw */ public function execute() { $output = ''; $blockTab = $this->getRequest()->getParam('block'); - $blockClassSuffix = str_replace( - ' ', - '\\', - ucwords(str_replace('_', ' ', $blockTab)) - ); - if (in_array($blockTab, ['tab_orders', 'tab_amounts', 'totals'])) { + + if ($blockTab === 'totals') { $output = $this->layoutFactory->create() - ->createBlock('Magento\\Backend\\Block\\Dashboard\\' . $blockClassSuffix) + ->createBlock(Totals::class) ->toHtml(); } - /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ + + /** @var Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); return $resultRaw->setContents($output); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php new file mode 100644 index 0000000000000..f46ab3a3a7d26 --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Controller\Adminhtml\Dashboard\Chart; + +use Magento\Backend\App\Action\Context; +use Magento\Backend\Controller\Adminhtml\Dashboard; +use Magento\Backend\Model\Dashboard\Chart; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; + +/** + * Get order amounts chart data controller + */ +class Amounts extends Dashboard implements HttpPostActionInterface +{ + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @var Chart + */ + private $chart; + + /** + * Amounts constructor. + * + * @param Context $context + * @param JsonFactory $resultJsonFactory + * @param Chart $chart + */ + public function __construct( + Context $context, + JsonFactory $resultJsonFactory, + Chart $chart + ) { + parent::__construct($context); + $this->resultJsonFactory = $resultJsonFactory; + $this->chart = $chart; + } + + /** + * Get chart data + * + * @return Json + */ + public function execute(): Json + { + $data = [ + 'data' => $this->chart->getByPeriod($this->_request->getParam('period'), 'revenue'), + 'label' => __('Revenue') + ]; + + return $this->resultJsonFactory->create() + ->setData($data); + } +} diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php new file mode 100644 index 0000000000000..11d3d875f1626 --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Controller\Adminhtml\Dashboard\Chart; + +use Magento\Backend\App\Action\Context; +use Magento\Backend\Controller\Adminhtml\Dashboard; +use Magento\Backend\Model\Dashboard\Chart; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; + +/** + * Get order quantities chart data controller + */ +class Orders extends Dashboard implements HttpPostActionInterface +{ + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @var Chart + */ + private $chart; + + /** + * Orders constructor. + * + * @param Context $context + * @param JsonFactory $resultJsonFactory + * @param Chart $chart + */ + public function __construct( + Context $context, + JsonFactory $resultJsonFactory, + Chart $chart + ) { + parent::__construct($context); + $this->resultJsonFactory = $resultJsonFactory; + $this->chart = $chart; + } + + /** + * Get chart data + * + * @return Json + */ + public function execute(): Json + { + $data = [ + 'data' => $this->chart->getByPeriod($this->_request->getParam('period'), 'quantity'), + 'label' => __('Quantity') + ]; + + return $this->resultJsonFactory->create() + ->setData($data); + } +} diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php index eb991baf13f41..2e0ed47a97bbf 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php @@ -1,16 +1,21 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backend\Controller\Adminhtml\Dashboard; use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\Result; use Magento\Framework\Encryption\Helper\Security; -class Tunnel extends \Magento\Backend\Controller\Adminhtml\Dashboard +/** + * Dashboard graph image tunnel + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.amounts and dashboard.chart.orders in adminhtml_dashboard_index.xml + */ +class Tunnel extends \Magento\Backend\Controller\Adminhtml\Dashboard implements HttpGetActionInterface { /** * @var \Magento\Framework\Controller\Result\RawFactory @@ -50,6 +55,7 @@ public function execute() $newHash = $helper->getChartDataHash($gaData); if (Security::compareStrings($newHash, $gaHash)) { $params = null; + // phpcs:ignore Magento2.Functions.DiscouragedFunction $paramsJson = base64_decode(urldecode($gaData)); if ($paramsJson) { $params = json_decode($paramsJson, true); diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart.php b/app/code/Magento/Backend/Model/Dashboard/Chart.php new file mode 100644 index 0000000000000..d986a7b8f7063 --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Chart.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Dashboard; + +use Magento\Backend\Helper\Dashboard\Data as DataHelper; +use Magento\Backend\Helper\Dashboard\Order as OrderHelper; +use Magento\Backend\Model\Dashboard\Chart\Date; +use Magento\Framework\App\RequestInterface; + +/** + * Dashboard chart data retriever + */ +class Chart +{ + /** + * @var RequestInterface + */ + private $request; + + /** + * @var Date + */ + private $dateRetriever; + + /** + * @var OrderHelper + */ + private $orderHelper; + + /** + * @var DataHelper + */ + private $dataHelper; + + /** + * Chart constructor. + * @param RequestInterface $request + * @param Date $dateRetriever + * @param OrderHelper $orderHelper + * @param DataHelper $dataHelper + */ + public function __construct( + RequestInterface $request, + Date $dateRetriever, + OrderHelper $orderHelper, + DataHelper $dataHelper + ) { + $this->request = $request; + $this->dateRetriever = $dateRetriever; + $this->orderHelper = $orderHelper; + $this->dataHelper = $dataHelper; + } + + /** + * Get chart data by period and chart type parameter + * + * @param string $period + * @param string $chartParam + * + * @return array + */ + public function getByPeriod($period, $chartParam): array + { + $this->orderHelper->setParam('store', $this->request->getParam('store')); + $this->orderHelper->setParam('website', $this->request->getParam('website')); + $this->orderHelper->setParam('group', $this->request->getParam('group')); + + $availablePeriods = array_keys($this->dataHelper->getDatePeriods()); + $this->orderHelper->setParam( + 'period', + $period && in_array($period, $availablePeriods, false) ? $period : '24h' + ); + + $dates = $this->dateRetriever->getByPeriod($period); + $collection = $this->orderHelper->getCollection(); + + $data = []; + + if ($collection->count() > 0) { + foreach ($dates as $date) { + $item = $collection->getItemByColumnValue('range', $date); + + $data[] = [ + 'x' => $date, + 'y' => $item ? (float)$item->getData($chartParam) : 0 + ]; + } + } + + return $data; + } +} diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php new file mode 100644 index 0000000000000..c91c42f940228 --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Dashboard\Chart; + +use DateTimeZone; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; + +/** + * Dashboard chart dates retriever + */ +class Date +{ + /** + * @var TimezoneInterface + */ + private $localeDate; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * Date constructor. + * @param CollectionFactory $collectionFactory + * @param TimezoneInterface $localeDate + */ + public function __construct( + CollectionFactory $collectionFactory, + TimezoneInterface $localeDate + ) { + $this->localeDate = $localeDate; + $this->collectionFactory = $collectionFactory; + } + + /** + * Get chart dates data by period + * + * @param string $period + * + * @return array + */ + public function getByPeriod($period): array + { + [$dateStart, $dateEnd] = $this->collectionFactory->create()->getDateRange( + $period, + '', + '', + true + ); + + $timezoneLocal = $this->localeDate->getConfigTimezone(); + + $dateStart->setTimezone(new DateTimeZone($timezoneLocal)); + $dateEnd->setTimezone(new DateTimeZone($timezoneLocal)); + + if ($period === '24h') { + $dateEnd->modify('-1 hour'); + } else { + $dateEnd->setTime(23, 59, 59); + $dateStart->setTime(0, 0, 0); + } + + $dates = []; + + while ($dateStart <= $dateEnd) { + switch ($period) { + case '7d': + case '1m': + $d = $dateStart->format('Y-m-d'); + $dateStart->modify('+1 day'); + break; + case '1y': + case '2y': + $d = $dateStart->format('Y-m'); + $dateStart->modify('first day of next month'); + break; + default: + $d = $dateStart->format('Y-m-d H:00'); + $dateStart->modify('+1 hour'); + } + + $dates[] = $d; + } + + return $dates; + } +} diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml index 3e3b0bc6a8a43..4be01fda862f8 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertOrderGraphImageOnDashboardActionGroup"> <click selector="{{AdminDashboardSection.ordersTab}}" stepKey="clickOrdersBtn"/> - <seeElement selector="{{AdminDashboardSection.ordersChart}}" stepKey="seeGraphImage"/> + <seeElement selector="{{AdminDashboardSection.ordersChart}}" stepKey="seeOrdersChart"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml index 61fe7ffa48e23..e67025cfa68d5 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDashboardSection"> <element name="ordersTab" type="button" selector="#diagram_tab_orders"/> - <element name="ordersChart" type="button" selector="#diagram_tab_orders_content .dashboard-diagram-image img"/> + <element name="ordersChart" type="button" selector="#diagram_tab_orders_content #chart_orders_period"/> <element name="dashboardDiagramContent" type="button" selector="#diagram_tab_content"/> <element name="dashboardDiagramOrderContentTab" type="block" selector="#diagram_tab_orders_content"/> <element name="dashboardDiagramAmounts" type="button" selector="#diagram_tab_amounts"/> diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php deleted file mode 100644 index b7713078863cb..0000000000000 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Backend\Test\Unit\Controller\Adminhtml\Dashboard; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class TunnelTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_request; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_response; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_objectManager; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRaw; - - protected function setUp() - { - $this->_request = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->_response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->_objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - } - - protected function tearDown() - { - $this->_request = null; - $this->_response = null; - $this->_objectManager = null; - } - - public function testTunnelAction() - { - $fixture = uniqid(); - $this->_request->expects($this->at(0)) - ->method('getParam') - ->with('ga') - ->will($this->returnValue(urlencode(base64_encode(json_encode([1]))))); - $this->_request->expects($this->at(1))->method('getParam')->with('h')->will($this->returnValue($fixture)); - $tunnelResponse = $this->createMock(\Magento\Framework\App\Response\Http::class); - $httpClient = $this->createPartialMock( - \Magento\Framework\HTTP\ZendClient::class, - ['setUri', 'setParameterGet', 'setConfig', 'request', 'getHeaders'] - ); - /** @var $helper \Magento\Backend\Helper\Dashboard\Data|\PHPUnit_Framework_MockObject_MockObject */ - $helper = $this->createPartialMock(\Magento\Backend\Helper\Dashboard\Data::class, ['getChartDataHash']); - $helper->expects($this->any())->method('getChartDataHash')->will($this->returnValue($fixture)); - - $this->_objectManager->expects($this->at(0)) - ->method('get') - ->with(\Magento\Backend\Helper\Dashboard\Data::class) - ->will($this->returnValue($helper)); - $this->_objectManager->expects($this->at(1)) - ->method('create') - ->with(\Magento\Framework\HTTP\ZendClient::class) - ->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setUri')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setParameterGet')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setConfig')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('request')->with('GET')->will($this->returnValue($tunnelResponse)); - $tunnelResponse->expects($this->any())->method('getHeaders') - ->will($this->returnValue(['Content-type' => 'test_header'])); - $tunnelResponse->expects($this->any())->method('getBody')->will($this->returnValue('success_msg')); - $this->_response->expects($this->any())->method('getBody')->will($this->returnValue('success_msg')); - - $controller = $this->_factory($this->_request, $this->_response); - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->with('Content-type', 'test_header') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('success_msg') - ->willReturnSelf(); - - $controller->execute(); - $this->assertEquals('success_msg', $controller->getResponse()->getBody()); - } - - public function testTunnelAction400() - { - $controller = $this->_factory($this->_request, $this->_response); - - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setHttpResponseCode') - ->with(400) - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('Service unavailable: invalid request') - ->willReturnSelf(); - - $controller->execute(); - } - - public function testTunnelAction503() - { - $fixture = uniqid(); - $this->_request->expects($this->at(0)) - ->method('getParam') - ->with('ga') - ->will($this->returnValue(urlencode(base64_encode(json_encode([1]))))); - $this->_request->expects($this->at(1))->method('getParam')->with('h')->will($this->returnValue($fixture)); - /** @var $helper \Magento\Backend\Helper\Dashboard\Data|\PHPUnit_Framework_MockObject_MockObject */ - $helper = $this->createPartialMock(\Magento\Backend\Helper\Dashboard\Data::class, ['getChartDataHash']); - $helper->expects($this->any())->method('getChartDataHash')->will($this->returnValue($fixture)); - - $this->_objectManager->expects($this->at(0)) - ->method('get') - ->with(\Magento\Backend\Helper\Dashboard\Data::class) - ->will($this->returnValue($helper)); - $exceptionMock = new \Exception(); - $this->_objectManager->expects($this->at(1)) - ->method('create') - ->with(\Magento\Framework\HTTP\ZendClient::class) - ->will($this->throwException($exceptionMock)); - $loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $loggerMock->expects($this->once())->method('critical')->with($exceptionMock); - $this->_objectManager->expects($this->at(2)) - ->method('get') - ->with(\Psr\Log\LoggerInterface::class) - ->will($this->returnValue($loggerMock)); - - $controller = $this->_factory($this->_request, $this->_response); - - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setHttpResponseCode') - ->with(503) - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('Service unavailable: see error log for details') - ->willReturnSelf(); - - $controller->execute(); - } - - /** - * Create the tested object - * - * @param \Magento\Framework\App\Request\Http $request - * @param \Magento\Framework\App\Response\Http|null $response - * @return \Magento\Backend\Controller\Adminhtml\Dashboard|\PHPUnit_Framework_MockObject_MockObject - */ - protected function _factory($request, $response = null) - { - if (!$response) { - /** @var $response \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ - $response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $response->headersSentThrowsException = false; - } - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $varienFront = $helper->getObject(\Magento\Framework\App\FrontController::class); - - $arguments = [ - 'request' => $request, - 'response' => $response, - 'objectManager' => $this->_objectManager, - 'frontController' => $varienFront, - ]; - $this->resultRaw = $this->getMockBuilder(\Magento\Framework\Controller\Result\Raw::class) - ->disableOriginalConstructor() - ->getMock(); - - $resultRawFactory = $this->getMockBuilder(\Magento\Framework\Controller\Result\RawFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $resultRawFactory->expects($this->atLeastOnce()) - ->method('create') - ->willReturn($this->resultRaw); - $context = $helper->getObject(\Magento\Backend\App\Action\Context::class, $arguments); - return new \Magento\Backend\Controller\Adminhtml\Dashboard\Tunnel($context, $resultRawFactory); - } -} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php new file mode 100644 index 0000000000000..5aedb953dab37 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php @@ -0,0 +1,226 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Test\Unit\Model\Dashboard; + +use Magento\Backend\Helper\Dashboard\Data as DataHelper; +use Magento\Backend\Helper\Dashboard\Order as OrderHelper; +use Magento\Backend\Model\Dashboard\Chart; +use Magento\Backend\Model\Dashboard\Chart\Date as DateRetriever; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\DataObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\ResourceModel\Order\Collection; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ChartTest extends TestCase +{ + /** + * @var Chart + */ + private $model; + + /** + * @var ObjectManager + */ + private $objectManagerHelper; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var DateRetriever|MockObject + */ + private $dateRetrieverMock; + + /** + * @var DataHelper|MockObject + */ + private $dataHelperMock; + + /** + * @var OrderHelper|MockObject + */ + private $orderHelperMock; + + /** + * @var Collection|MockObject + */ + private $collectionMock; + + protected function setUp() + { + $this->objectManagerHelper = new ObjectManager($this); + + $this->requestMock = $this->getMockForAbstractClass(RequestInterface::class); + $this->requestMock->method('getParam')->willReturn(null); + + $this->dateRetrieverMock = $this->getMockBuilder(DateRetriever::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->dataHelperMock = $this->getMockBuilder(DataHelper::class) + ->disableOriginalConstructor() + ->getMock(); + $this->dataHelperMock->method('getDatePeriods') + ->willReturn([ + '24h' => __('Last 24 Hours'), + '7d' => __('Last 7 Days'), + '1m' => __('Current Month'), + '1y' => __('YTD'), + '2y' => __('2YTD') + ]); + + $this->orderHelperMock = $this->getMockBuilder(OrderHelper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderHelperMock->method('getCollection') + ->willReturn($this->collectionMock); + + $this->model = $this->objectManagerHelper->getObject( + Chart::class, + [ + 'request' => $this->requestMock, + 'dateRetriever' => $this->dateRetrieverMock, + 'dataHelper' => $this->dataHelperMock, + 'orderHelper' => $this->orderHelperMock + ] + ); + } + + /** + * @param string $period + * @param string $chartParam + * @param array $result + * @dataProvider getByPeriodDataProvider + */ + public function testGetByPeriod($period, $chartParam, $result) + { + $this->orderHelperMock->expects($this->at(0)) + ->method('setParam') + ->with('store', null); + $this->orderHelperMock->expects($this->at(1)) + ->method('setParam') + ->with('website', null); + $this->orderHelperMock->expects($this->at(2)) + ->method('setParam') + ->with('group', null); + $this->orderHelperMock->expects($this->at(3)) + ->method('setParam') + ->with('period', $period); + + $this->dateRetrieverMock->expects($this->once()) + ->method('getByPeriod') + ->with($period) + ->willReturn(array_map(static function ($item) { + return $item['x']; + }, $result)); + + $this->collectionMock->method('count') + ->willReturn(2); + + $valueMap = []; + foreach ($result as $resultItem) { + $dataObjectMock = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->getMock(); + $dataObjectMock->method('getData') + ->with($chartParam) + ->willReturn($resultItem['y']); + + $valueMap[] = [ + 'range', + $resultItem['x'], + $dataObjectMock + ]; + } + $this->collectionMock->method('getItemByColumnValue') + ->willReturnMap($valueMap); + + $this->assertEquals( + $result, + $this->model->getByPeriod($period, $chartParam) + ); + } + + public function getByPeriodDataProvider(): array + { + return [ + [ + '7d', + 'revenue', + [ + [ + 'x' => '2020-01-21', + 'y' => 0 + ], + [ + 'x' => '2020-01-22', + 'y' => 2 + ], + [ + 'x' => '2020-01-23', + 'y' => 0 + ], + [ + 'x' => '2020-01-24', + 'y' => 7 + ] + ] + ], + [ + '1m', + 'quantity', + [ + [ + 'x' => '2020-01-21', + 'y' => 0 + ], + [ + 'x' => '2020-01-22', + 'y' => 2 + ], + [ + 'x' => '2020-01-23', + 'y' => 0 + ], + [ + 'x' => '2020-01-24', + 'y' => 7 + ] + ] + ], + [ + '1y', + 'quantity', + [ + [ + 'x' => '2020-01', + 'y' => 0 + ], + [ + 'x' => '2020-02', + 'y' => 2 + ], + [ + 'x' => '2020-03', + 'y' => 0 + ], + [ + 'x' => '2020-04', + 'y' => 7 + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Backend/ViewModel/ChartDisabled.php b/app/code/Magento/Backend/ViewModel/ChartDisabled.php new file mode 100644 index 0000000000000..f40e757ee7b64 --- /dev/null +++ b/app/code/Magento/Backend/ViewModel/ChartDisabled.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\ViewModel; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * View model for dashboard chart disabled notice + */ +class ChartDisabled implements ArgumentInterface +{ + /** + * Location of the "Enable Chart" config param + */ + private const XML_PATH_ENABLE_CHARTS = 'admin/dashboard/enable_charts'; + + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param UrlInterface $urlBuilder + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + UrlInterface $urlBuilder, + ScopeConfigInterface $scopeConfig + ) { + $this->urlBuilder = $urlBuilder; + $this->scopeConfig = $scopeConfig; + } + + /** + * Get url to dashboard chart configuration + * + * @return string + */ + public function getConfigUrl(): string + { + return $this->urlBuilder->getUrl( + 'adminhtml/system_config/edit', + ['section' => 'admin', '_fragment' => 'admin_dashboard-link'] + ); + } + + /** + * Check if dashboard chart is enabled + * + * @return bool + */ + public function isChartEnabled(): bool + { + return $this->scopeConfig->isSetFlag( + static::XML_PATH_ENABLE_CHARTS, + ScopeInterface::SCOPE_STORE + ); + } +} diff --git a/app/code/Magento/Backend/ViewModel/ChartsPeriod.php b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php new file mode 100644 index 0000000000000..c118cb7c02a5f --- /dev/null +++ b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\ViewModel; + +use Magento\Backend\Helper\Dashboard\Data; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * View model for dashboard charts period select + */ +class ChartsPeriod implements ArgumentInterface +{ + /** + * @var Data + */ + private $dataHelper; + + /** + * @param Data $dataHelper + */ + public function __construct(Data $dataHelper) + { + $this->dataHelper = $dataHelper; + } + + /** + * Get chart date periods + * + * @return array + */ + public function getDatePeriods(): array + { + return $this->dataHelper->getDatePeriods(); + } +} diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml index 803eb1dbe903b..86909162abb7c 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -17,7 +17,65 @@ <block class="Magento\Backend\Block\Template" name="refresh_statistics" after="store_switcher" template="Magento_Backend::dashboard/totalbar/refreshstatistics.phtml"/> </referenceContainer> <referenceContainer name="content"> - <block class="Magento\Backend\Block\Dashboard" name="dashboard"/> + <block name="dashboard" template="Magento_Backend::dashboard/index.phtml"> + <block class="Magento\Backend\Block\Dashboard\Orders\Grid" name="dashboard.lastOrders" as="lastOrders"/> + <block class="Magento\Backend\Block\Dashboard\Totals" name="dashboard.totals" as="totals"> + <block template="Magento_Backend::dashboard/totalbar/script.phtml" name="dashboard.totals.script"/> + </block> + <block class="Magento\Backend\Block\Dashboard\Sales" name="dashboard.sales" as="sales"/> + <block class="Magento\Backend\Block\Dashboard\Grids" name="dashboard.grids" as="grids"/> + <block name="dashboard.chart.disabled" + as="chartDisabled" + template="Magento_Backend::dashboard/chart/disabled.phtml"> + <arguments> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartDisabled</argument> + </arguments> + </block> + <block class="Magento\Backend\Block\Widget\Tabs" + name="dashboard.diagrams" + as="diagrams" + template="Magento_Backend::widget/tabshoriz.phtml" + ifconfig="admin/dashboard/enable_charts"> + <action method="setId"> + <argument name="id" xsi:type="string">diagram_tab</argument> + </action> + <action method="setDestElementId"> + <argument name="dest_element_id" xsi:type="string">diagram_tab_content</argument> + </action> + <action method="addTab"> + <argument name="name" xsi:type="string">orders</argument> + <argument name="block" xsi:type="string">orders</argument> + </action> + <action method="addTab"> + <argument name="name" xsi:type="string">amounts</argument> + <argument name="block" xsi:type="string">amounts</argument> + </action> + <block class="Magento\Backend\Block\Widget\Tab" name="dashboard.chart.orders" as="orders" template="Magento_Backend::dashboard/chart.phtml"> + <arguments> + <argument name="html_id" xsi:type="string">orders</argument> + <argument name="label" xsi:type="string" translate="true">Orders</argument> + <argument name="title" xsi:type="string" translate="true">Orders</argument> + <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/orders</argument> + </arguments> + </block> + <block class="Magento\Backend\Block\Widget\Tab" name="dashboard.chart.amounts" as="amounts" template="Magento_Backend::dashboard/chart.phtml"> + <arguments> + <argument name="html_id" xsi:type="string">amounts</argument> + <argument name="label" xsi:type="string" translate="true">Amounts</argument> + <argument name="title" xsi:type="string" translate="true">Amounts</argument> + <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/amounts</argument> + </arguments> + </block> + </block> + <block name="dashboard.diagrams.period" + as="diagramsPeriod" + template="Magento_Backend::dashboard/chart/period.phtml" + ifconfig="admin/dashboard/enable_charts"> + <arguments> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> + </arguments> + </block> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml new file mode 100644 index 0000000000000..f289246157c47 --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + */ +?> +<div class="dashboard-diagram"> + <div class="dashboard-diagram-graph"> + <canvas id="chart_<?= $escaper->escapeHtmlAttr($block->getData('html_id')) ?>_period" + style="display: none;"></canvas> + <div class="dashboard-diagram-nodata"> + <span><?= $escaper->escapeHtml(__('No Data Found')) ?></span> + </div> + </div> + <script type="text/x-magento-init"> + { + "#chart_<?= $escaper->escapeJs($block->getData('html_id')) ?>_period": { + "Magento_Backend/js/dashboard/chart": { + "updateUrl": "<?= $escaper->escapeUrl($block->getUrl($block->getData('update_url'), [ + '_current' => true + ])) ?>", + "periodSelect": "#dashboard_chart_period", + "type": "<?= $escaper->escapeJs($block->getData('html_id')) ?>" + } + } + } + </script> +</div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml new file mode 100644 index 0000000000000..ac600c67e662f --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Backend\ViewModel\ChartDisabled; +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + * @var ChartDisabled $viewModel + * @phpcs:ignoreFile + */ +$viewModel = $block->getViewModel(); +?> +<?php if (!$viewModel->isChartEnabled()): ?> + <div class="dashboard-diagram-disabled"> + <?= $escaper->escapeHtml(__('Chart is disabled. To enable the chart, click <a href="%1">here</a>.', $escaper->escapeUrl($viewModel->getConfigUrl())), ['a']) ?> + </div> +<?php endif; ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml new file mode 100644 index 0000000000000..f30bfc7a57fb9 --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Backend\ViewModel\ChartsPeriod; +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + * @var ChartsPeriod $viewModel + */ +$viewModel = $block->getViewModel(); +?> +<div class="dashboard-diagram-switcher"> + <label for="dashboard_chart_period" class="label"><?= $escaper->escapeHtml(__('Select Range:')) ?></label> + <select name="period" id="dashboard_chart_period" class="admin__control-select"> + <?php foreach ($viewModel->getDatePeriods() as $value => $label): ?> + <?php + if ($value === 'custom') { + continue; + } + ?> + <option value="<?= $escaper->escapeHtmlAttr($value) ?>" + <?php if ($block->getRequest()->getParam('period') === $value): ?> selected="selected"<?php endif; ?> + ><?= $escaper->escapeHtml($label) ?></option> + <?php endforeach; ?> + </select> +</div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml index 12b388c210774..21fd3f45a2965 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml @@ -3,6 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see Magento_Backend::dashboard/chart.phtml + * @phpcs:ignoreFile + */ ?> <div class="dashboard-diagram"> <div class="dashboard-diagram-switcher"> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml index f8e584ce5b9cd..513a76cde29c0 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml @@ -3,6 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see Magento_Backend::dashboard/chart/disabled.phtml + * @phpcs:ignoreFile + */ ?> <div class="dashboard-diagram-disabled"> <?= /* @noEscape */ __('Chart is disabled. To enable the chart, click <a href="%1">here</a>.', $block->escapeUrl($block->getConfigUrl())) ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml index 6152c8fe1cff1..40df857968070 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml @@ -5,78 +5,17 @@ */ ?> -<?php if (is_array($block->getChildBlock('diagrams')->getTabsIds())) : ?> -<script> -require([ - 'Magento_Ui/js/modal/alert', - 'prototype' -], function(alert){ - -window.changeDiagramsPeriod = function(periodObj) { - periodParam = periodObj.value ? 'period/' + periodObj.value + '/' : ''; - <?php foreach ($block->getChildBlock('diagrams')->getTabsIds() as $tabId) : ?> - ajaxBlockParam = 'block/tab_<?= $block->escapeJs($tabId) ?>/'; - ajaxBlockUrl = '<?= $block->escapeJs($block->getUrl('adminhtml/*/ajaxBlock', ['_current' => true, 'block' => '', 'period' => ''])) ?>' + ajaxBlockParam + periodParam; - new Ajax.Request(ajaxBlockUrl, { - parameters: {isAjax: 'true', form_key: FORM_KEY}, - onSuccess: function(transport) { - tabContentElementId = '<?= $block->escapeJs($block->getChildBlock('diagrams')->getId()) ?>_<?= $block->escapeJs($tabId) ?>_content'; - try { - if (transport.responseText.isJSON()) { - var response = transport.responseText.evalJSON() - if (response.error) { - alert({ - content: response.message - }); - } - if(response.ajaxExpired && response.ajaxRedirect) { - setLocation(response.ajaxRedirect); - } - } else { - $(tabContentElementId).update(transport.responseText); - } - } - catch (e) { - $(tabContentElementId).update(transport.responseText); - } - } - }); - <?php endforeach; ?> - ajaxBlockUrl = '<?= $block->escapeJs($block->getUrl('adminhtml/*/ajaxBlock', ['_current' => true, 'block' => 'totals', 'period' => ''])) ?>' + periodParam; - new Ajax.Request(ajaxBlockUrl, { - parameters: {isAjax: 'true', form_key: FORM_KEY}, - onSuccess: function(transport) { - tabContentElementId = 'dashboard_diagram_totals'; - try { - if (transport.responseText.isJSON()) { - var response = transport.responseText.evalJSON(); - if (response.error) { - alert({ - content: response.message - }); - } - if(response.ajaxExpired && response.ajaxRedirect) { - setLocation(response.ajaxRedirect); - } - } else { - $(tabContentElementId).replace(transport.responseText); - } - } - catch (e) { - $(tabContentElementId).replace(transport.responseText); - } - } - }); -} - -}); -</script> -<?php endif; ?> <div class="dashboard-container row"> <div class="dashboard-main col-m-8 col-m-push-4"> <div class="dashboard-diagram-container"> + <?= $block->getChildHtml('chartDisabled') ?> <?= $block->getChildHtml('diagrams') ?> - <?php if (is_array($block->getChildBlock('diagrams')->getTabsIds())) : ?> + <?php + $diagramsBlock = $block->getChildBlock('diagrams'); + $tabsIds = $diagramsBlock ? $diagramsBlock->getTabsIds() : []; + ?> + <?php if (is_array($tabsIds)): ?> + <?= $block->getChildHtml('diagramsPeriod') ?> <div id="diagram_tab_content" class="dashboard-diagram-tab-content"></div> <?php endif; ?> </div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml index 918eea75fab99..44320e247f9d3 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml @@ -3,19 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Backend\Block\Dashboard\Totals; +use Magento\Framework\Escaper; + +/** + * @var Totals $block + * @var Escaper $escaper + */ ?> -<?php if (count($block->getTotals()) > 0) : ?> -<div class="dashboard-totals" id="dashboard_diagram_totals"> - <ul class="dashboard-totals-list"> - <?php foreach ($block->getTotals() as $_total) : ?> - <li class="dashboard-totals-item"> - <span class="dashboard-totals-label"><?= $block->escapeHtml($_total['label']) ?></span> - <strong class="dashboard-totals-value"> - <?= /* @noEscape */ $_total['value'] ?> - <span class="dashboard-totals-decimals"><?= /* @noEscape */ $_total['decimals'] ?></span> - </strong> - </li> - <?php endforeach; ?> - </ul> -</div> +<?php if (count($block->getTotals()) > 0): ?> + <div class="dashboard-totals" id="dashboard_diagram_totals"> + <ul class="dashboard-totals-list"> + <?php foreach ($block->getTotals() as $total): ?> + <li class="dashboard-totals-item"> + <span class="dashboard-totals-label"><?= $escaper->escapeHtml($total['label']) ?></span> + <strong class="dashboard-totals-value"> + <?= /* @noEscape */ $total['value'] ?> + <span class="dashboard-totals-decimals"><?= /* @noEscape */ $total['decimals'] ?></span> + </strong> + </li> + <?php endforeach; ?> + </ul> + </div> + <?= $block->getChildHtml() ?> <?php endif; ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml new file mode 100644 index 0000000000000..7e8f12d19b7fd --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + */ +?> +<script type="text/x-magento-init"> +{ + "#dashboard_diagram_totals": { + "Magento_Backend/js/dashboard/totals": { + "updateUrl": "<?= $escaper->escapeUrl($block->getUrl('adminhtml/*/ajaxBlock', [ + '_current' => true, + 'block' => 'totals', + 'period' => '' + ])) ?>", + "periodSelect": "#dashboard_chart_period" + } + } +} +</script> diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js new file mode 100644 index 0000000000000..c618e5db5c84a --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js @@ -0,0 +1,157 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*global define*/ +/*global FORM_KEY*/ +define([ + 'jquery', + 'chartJs', + 'jquery-ui-modules/widget', + 'moment' +], function ($, Chart) { + 'use strict'; + + $.widget('mage.dashboardChart', { + options: { + updateUrl: '', + periodSelect: null, + type: '' + }, + chart: null, + + /** + * @private + */ + _create: function () { + this.createChart(); + + if (this.options.periodSelect) { + $(document).on('change', this.options.periodSelect, this.refreshChartData.bind(this)); + + this.period = $(this.options.periodSelect).val(); + } + }, + + /** + * @public + */ + createChart: function () { + this.chart = new Chart(this.element, this.getChartSettings()); + this.refreshChartData(); + }, + + /** + * @public + */ + refreshChartData: function () { + var data = { + 'form_key': FORM_KEY + }; + + if (this.options.periodSelect) { + this.period = data.period = $(this.options.periodSelect).val(); + } + + $.ajax({ + url: this.options.updateUrl, + showLoader: true, + data: data, + dataType: 'json', + type: 'POST', + success: this.updateChart.bind(this) + }); + }, + + /** + * @public + * @param {Object} response + */ + updateChart: function (response) { + $(this.element).toggle(response.data.length > 0); + $(this.element).next('.dashboard-diagram-nodata').toggle(response.data.length === 0); + + this.chart.options.scales.xAxes[0].time.unit = this.getPeriodUnit(); + this.chart.data.datasets[0].data = response.data; + this.chart.data.datasets[0].label = response.label; + this.chart.update(); + }, + + /** + * @returns {String} time unit per currently set period + */ + getPeriodUnit: function () { + switch (this.period) { + case '7d': + case '1m': + return 'day'; + + case '1y': + case '2y': + return 'month'; + } + + return 'hour'; + }, + + /** + * @returns {Number} precision of numeric chart data + */ + getPrecision: function () { + if (this.options.type === 'amounts') { + return 2; + } + + return 0; + }, + + /** + * @returns {Object} chart object configuration + */ + getChartSettings: function () { + return { + type: 'bar', + data: { + datasets: [{ + data: [], + backgroundColor: '#f1d4b3', + borderColor: '#eb5202', + borderWidth: 1 + }] + }, + options: { + legend: { + onClick: this.handleChartLegendClick, + position: 'bottom' + }, + scales: { + xAxes: [{ + offset: true, + type: 'time', + ticks: { + autoSkip: true, + source: 'data' + } + }], + yAxes: [{ + ticks: { + beginAtZero: true, + precision: this.getPrecision() + } + }] + } + } + }; + }, + + /** + * @public + */ + handleChartLegendClick: function () { + // don't hide dataset on clicking into legend item + } + }); + + return $.mage.dashboardChart; +}); diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js new file mode 100644 index 0000000000000..18953140e5b62 --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js @@ -0,0 +1,60 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*global define*/ +/*global FORM_KEY*/ +define([ + 'jquery', + 'jquery-ui-modules/widget' +], function ($) { + 'use strict'; + + $.widget('mage.graph', { + options: { + updateUrl: '', + periodSelect: null + }, + elementId: null, + + /** + * @private + */ + _create: function () { + this.elementId = $(this.element).attr('id'); + + if (this.options.periodSelect) { + $(document).on('change', this.options.periodSelect, $.proxy(function () { + this.refreshTotals(); + }, this)); + } + }, + + /** + * @public + */ + refreshTotals: function () { + var periodParam = ''; + + if (this.options.periodSelect && $(this.options.periodSelect).val()) { + periodParam = 'period/' + $(this.options.periodSelect).val() + '/'; + } + + $.ajax({ + url: this.options.updateUrl + periodParam, + showLoader: true, + data: { + 'form_key': FORM_KEY + }, + dataType: 'html', + type: 'POST', + success: $.proxy(function (response) { + $('#' + this.elementId).replaceWith(response); + }, this) + }); + } + }); + + return $.mage.graph; +}); diff --git a/app/code/Magento/Ui/view/base/requirejs-config.js b/app/code/Magento/Ui/view/base/requirejs-config.js index cdbbd03c93ae1..6685a51d6561a 100644 --- a/app/code/Magento/Ui/view/base/requirejs-config.js +++ b/app/code/Magento/Ui/view/base/requirejs-config.js @@ -5,6 +5,7 @@ var config = { shim: { + 'chartjs/Chart': ['moment'], 'tiny_mce_4/tinymce.min': { exports: 'tinyMCE' } @@ -23,6 +24,7 @@ var config = { consoleLogger: 'Magento_Ui/js/lib/logger/console-logger', uiLayout: 'Magento_Ui/js/core/renderer/layout', buttonAdapter: 'Magento_Ui/js/form/button-adapter', + chartJs: 'chartjs/Chart', tinymce4: 'tiny_mce_4/tinymce.min', wysiwygAdapter: 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter' } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less index 7ef304932a649..3c50fc02a05c5 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less @@ -126,16 +126,19 @@ // Range switcher .dashboard-diagram-switcher { - margin-bottom: 2rem; + border-top: 1px solid @color-gray68; + margin-top: -1px; + padding: 2rem 2rem 0; .label { &:extend(.abs-visually-hidden all); } -} -// Chart -.dashboard-diagram-image { - max-width: 100%; + + .dashboard-diagram-tab-content { + > .ui-tabs-panel { + border-top: 0 none; + } + } } // diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php deleted file mode 100644 index 497deb2c99110..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Backend\Block\Dashboard; - -/** - * @magentoAppArea adminhtml - */ -class GraphTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Backend\Block\Dashboard\Graph - */ - protected $_block; - - protected function setUp() - { - parent::setUp(); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Framework\View\LayoutInterface $layout */ - $layout = $objectManager->get(\Magento\Framework\View\LayoutInterface::class); - $this->_block = $layout->createBlock(\Magento\Backend\Block\Dashboard\Graph::class); - $this->_block->setDataHelper($objectManager->get(\Magento\Backend\Helper\Dashboard\Order::class)); - } - - public function testGetChartUrl() - { - $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php index 3deb225cead18..a21dff3dd854f 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php @@ -1,10 +1,8 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Backend\Controller\Adminhtml\Dashboard; @@ -20,6 +18,9 @@ class AjaxBlockTest extends AbstractBackendController /** * Test execute to check render block * + * @param string $block + * @param string $expectedResult + * * @dataProvider ajaxBlockDataProvider */ public function testExecute($block, $expectedResult) @@ -41,17 +42,9 @@ public function testExecute($block, $expectedResult) * * @return array */ - public function ajaxBlockDataProvider() + public function ajaxBlockDataProvider(): array { return [ - [ - 'tab_orders', - 'order_orders_period' - ], - [ - 'tab_amounts', - 'order_amounts_period' - ], [ 'totals', 'dashboard_diagram_totals' diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php deleted file mode 100644 index 0eb98379b4571..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Backend\Controller\Adminhtml; - -/** - * @magentoAppArea adminhtml - */ -class DashboardTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function testAjaxBlockAction() - { - $this->getRequest()->setParam('block', 'tab_orders'); - $this->dispatch('backend/admin/dashboard/ajaxBlock'); - - $actual = $this->getResponse()->getBody(); - $this->assertContains('dashboard-diagram', $actual); - } - - /** - * Tests tunnelAction - * - * @throws \Exception - * @return void - */ - public function testTunnelAction() - { - // phpcs:disable Magento2.Functions.DiscouragedFunction - $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; - $handle = curl_init(); - curl_setopt($handle, CURLOPT_URL, $testUrl); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - try { - if (false === curl_exec($handle)) { - $this->markTestSkipped('Third-party service is unavailable: ' . $testUrl); - } - curl_close($handle); - } catch (\Exception $e) { - curl_close($handle); - throw $e; - } - // phpcs:enable - - $gaData = [ - 'cht' => 'lc', - 'chf' => 'bg,s,f4f4f4|c,lg,90,ffffff,0.1,ededed,0', - 'chm' => 'B,f4d4b2,0,0,0', - 'chco' => 'db4814', - 'chd' => 'e:AAAAAAAAf.AAAA', - 'chxt' => 'x,y', - 'chxl' => '0:|10/13/12|10/14/12|10/15/12|10/16/12|10/17/12|10/18/12|10/19/12|1:|0|1|2', - 'chs' => '587x300', - 'chg' => '16.666666666667,50,1,0', - ]; - $gaFixture = urlencode(base64_encode(json_encode($gaData))); - - /** @var $helper \Magento\Backend\Helper\Dashboard\Data */ - $helper = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Backend\Helper\Dashboard\Data::class - ); - $hash = $helper->getChartDataHash($gaFixture); - $this->getRequest()->setParam('ga', $gaFixture)->setParam('h', $hash); - $this->dispatch('backend/admin/dashboard/tunnel'); - $this->assertStringStartsWith("\x89\x50\x4E\x47", $this->getResponse()->getBody()); // PNG header - } -} diff --git a/lib/web/chartjs/Chart.bundle.js b/lib/web/chartjs/Chart.bundle.js new file mode 100644 index 0000000000000..b6f4f388c7459 --- /dev/null +++ b/lib/web/chartjs/Chart.bundle.js @@ -0,0 +1,20755 @@ +/*! + * Chart.js v2.9.3 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : +typeof define === 'function' && define.amd ? define(factory) : +(global = global || self, global.Chart = factory()); +}(this, (function () { 'use strict'; + +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function commonjsRequire () { + throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); +} + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +function getCjsExportFromNamespace (n) { + return n && n['default'] || n; +} + +var colorName = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +var conversions = createCommonjsModule(function (module) { +/* MIT license */ + + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +var reverseKeywords = {}; +for (var key in colorName) { + if (colorName.hasOwnProperty(key)) { + reverseKeywords[colorName[key]] = key; + } +} + +var convert = module.exports = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; + +// hide .channels and .labels properties +for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } + + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } + + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); + } +} + +convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; + + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; + + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); + + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } + + return [ + h * 360, + s * 100, + v * 100 + ]; +}; + +convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +/** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ +function comparativeDistance(x, y) { + return ( + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) + ); +} + +convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } + + var currentClosestDistance = Infinity; + var currentClosestKeyword; + + for (var keyword in colorName) { + if (colorName.hasOwnProperty(keyword)) { + var value = colorName[keyword]; + + // Compute comparative distance + var distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } + + return currentClosestKeyword; +}; + +convert.keyword.rgb = function (keyword) { + return colorName[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; + + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + n = wh + f * (v - wh); // linear interpolation + + var r; + var g; + var b; + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; + + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; + + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b * 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + var color = args % 10; + + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + var colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } + + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + + return [r, g, b]; +}; + +convert.rgb.hcg = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma + 4; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; + + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } + + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; + + var c = s * v; + var f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; + + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; + +convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var v = c + g * (1.0 - c); + var f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; +}; + +convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; +}); +var conversions_1 = conversions.rgb; +var conversions_2 = conversions.hsl; +var conversions_3 = conversions.hsv; +var conversions_4 = conversions.hwb; +var conversions_5 = conversions.cmyk; +var conversions_6 = conversions.xyz; +var conversions_7 = conversions.lab; +var conversions_8 = conversions.lch; +var conversions_9 = conversions.hex; +var conversions_10 = conversions.keyword; +var conversions_11 = conversions.ansi16; +var conversions_12 = conversions.ansi256; +var conversions_13 = conversions.hcg; +var conversions_14 = conversions.apple; +var conversions_15 = conversions.gray; + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + var graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + var models = Object.keys(conversions); + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +var route = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + +var convert = {}; + +var models = Object.keys(conversions); + +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + return fn(args); + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + var result = fn(args); + + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(function (fromModel) { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + var routes = route(fromModel); + var routeModels = Object.keys(routes); + + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +var colorConvert = convert; + +var colorName$1 = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +/* MIT license */ + + +var colorString = { + getRgba: getRgba, + getHsla: getHsla, + getRgb: getRgb, + getHsl: getHsl, + getHwb: getHwb, + getAlpha: getAlpha, + + hexString: hexString, + rgbString: rgbString, + rgbaString: rgbaString, + percentString: percentString, + percentaString: percentaString, + hslString: hslString, + hslaString: hslaString, + hwbString: hwbString, + keyword: keyword +}; + +function getRgba(string) { + if (!string) { + return; + } + var abbr = /^#([a-fA-F0-9]{3,4})$/i, + hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, + rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + keyword = /(\w+)/; + + var rgb = [0, 0, 0], + a = 1, + match = string.match(abbr), + hexAlpha = ""; + if (match) { + match = match[1]; + hexAlpha = match[3]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i] + match[i], 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(hex)) { + hexAlpha = match[2]; + match = match[1]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(rgba)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i + 1]); + } + a = parseFloat(match[4]); + } + else if (match = string.match(per)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); + } + a = parseFloat(match[4]); + } + else if (match = string.match(keyword)) { + if (match[1] == "transparent") { + return [0, 0, 0, 0]; + } + rgb = colorName$1[match[1]]; + if (!rgb) { + return; + } + } + + for (var i = 0; i < rgb.length; i++) { + rgb[i] = scale(rgb[i], 0, 255); + } + if (!a && a != 0) { + a = 1; + } + else { + a = scale(a, 0, 1); + } + rgb[3] = a; + return rgb; +} + +function getHsla(string) { + if (!string) { + return; + } + var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hsl); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + s = scale(parseFloat(match[2]), 0, 100), + l = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, s, l, a]; + } +} + +function getHwb(string) { + if (!string) { + return; + } + var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hwb); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + w = scale(parseFloat(match[2]), 0, 100), + b = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, w, b, a]; + } +} + +function getRgb(string) { + var rgba = getRgba(string); + return rgba && rgba.slice(0, 3); +} + +function getHsl(string) { + var hsla = getHsla(string); + return hsla && hsla.slice(0, 3); +} + +function getAlpha(string) { + var vals = getRgba(string); + if (vals) { + return vals[3]; + } + else if (vals = getHsla(string)) { + return vals[3]; + } + else if (vals = getHwb(string)) { + return vals[3]; + } +} + +// generators +function hexString(rgba, a) { + var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; + return "#" + hexDouble(rgba[0]) + + hexDouble(rgba[1]) + + hexDouble(rgba[2]) + + ( + (a >= 0 && a < 1) + ? hexDouble(Math.round(a * 255)) + : "" + ); +} + +function rgbString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return rgbaString(rgba, alpha); + } + return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; +} + +function rgbaString(rgba, alpha) { + if (alpha === undefined) { + alpha = (rgba[3] !== undefined ? rgba[3] : 1); + } + return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + + ", " + alpha + ")"; +} + +function percentString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return percentaString(rgba, alpha); + } + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + + return "rgb(" + r + "%, " + g + "%, " + b + "%)"; +} + +function percentaString(rgba, alpha) { + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; +} + +function hslString(hsla, alpha) { + if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { + return hslaString(hsla, alpha); + } + return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; +} + +function hslaString(hsla, alpha) { + if (alpha === undefined) { + alpha = (hsla[3] !== undefined ? hsla[3] : 1); + } + return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + + alpha + ")"; +} + +// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax +// (hwb have alpha optional & 1 is default value) +function hwbString(hwb, alpha) { + if (alpha === undefined) { + alpha = (hwb[3] !== undefined ? hwb[3] : 1); + } + return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" + + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; +} + +function keyword(rgb) { + return reverseNames[rgb.slice(0, 3)]; +} + +// helpers +function scale(num, min, max) { + return Math.min(Math.max(min, num), max); +} + +function hexDouble(num) { + var str = num.toString(16).toUpperCase(); + return (str.length < 2) ? "0" + str : str; +} + + +//create a list of reverse color names +var reverseNames = {}; +for (var name in colorName$1) { + reverseNames[colorName$1[name]] = name; +} + +/* MIT license */ + + + +var Color = function (obj) { + if (obj instanceof Color) { + return obj; + } + if (!(this instanceof Color)) { + return new Color(obj); + } + + this.valid = false; + this.values = { + rgb: [0, 0, 0], + hsl: [0, 0, 0], + hsv: [0, 0, 0], + hwb: [0, 0, 0], + cmyk: [0, 0, 0, 0], + alpha: 1 + }; + + // parse Color() argument + var vals; + if (typeof obj === 'string') { + vals = colorString.getRgba(obj); + if (vals) { + this.setValues('rgb', vals); + } else if (vals = colorString.getHsla(obj)) { + this.setValues('hsl', vals); + } else if (vals = colorString.getHwb(obj)) { + this.setValues('hwb', vals); + } + } else if (typeof obj === 'object') { + vals = obj; + if (vals.r !== undefined || vals.red !== undefined) { + this.setValues('rgb', vals); + } else if (vals.l !== undefined || vals.lightness !== undefined) { + this.setValues('hsl', vals); + } else if (vals.v !== undefined || vals.value !== undefined) { + this.setValues('hsv', vals); + } else if (vals.w !== undefined || vals.whiteness !== undefined) { + this.setValues('hwb', vals); + } else if (vals.c !== undefined || vals.cyan !== undefined) { + this.setValues('cmyk', vals); + } + } +}; + +Color.prototype = { + isValid: function () { + return this.valid; + }, + rgb: function () { + return this.setSpace('rgb', arguments); + }, + hsl: function () { + return this.setSpace('hsl', arguments); + }, + hsv: function () { + return this.setSpace('hsv', arguments); + }, + hwb: function () { + return this.setSpace('hwb', arguments); + }, + cmyk: function () { + return this.setSpace('cmyk', arguments); + }, + + rgbArray: function () { + return this.values.rgb; + }, + hslArray: function () { + return this.values.hsl; + }, + hsvArray: function () { + return this.values.hsv; + }, + hwbArray: function () { + var values = this.values; + if (values.alpha !== 1) { + return values.hwb.concat([values.alpha]); + } + return values.hwb; + }, + cmykArray: function () { + return this.values.cmyk; + }, + rgbaArray: function () { + var values = this.values; + return values.rgb.concat([values.alpha]); + }, + hslaArray: function () { + var values = this.values; + return values.hsl.concat([values.alpha]); + }, + alpha: function (val) { + if (val === undefined) { + return this.values.alpha; + } + this.setValues('alpha', val); + return this; + }, + + red: function (val) { + return this.setChannel('rgb', 0, val); + }, + green: function (val) { + return this.setChannel('rgb', 1, val); + }, + blue: function (val) { + return this.setChannel('rgb', 2, val); + }, + hue: function (val) { + if (val) { + val %= 360; + val = val < 0 ? 360 + val : val; + } + return this.setChannel('hsl', 0, val); + }, + saturation: function (val) { + return this.setChannel('hsl', 1, val); + }, + lightness: function (val) { + return this.setChannel('hsl', 2, val); + }, + saturationv: function (val) { + return this.setChannel('hsv', 1, val); + }, + whiteness: function (val) { + return this.setChannel('hwb', 1, val); + }, + blackness: function (val) { + return this.setChannel('hwb', 2, val); + }, + value: function (val) { + return this.setChannel('hsv', 2, val); + }, + cyan: function (val) { + return this.setChannel('cmyk', 0, val); + }, + magenta: function (val) { + return this.setChannel('cmyk', 1, val); + }, + yellow: function (val) { + return this.setChannel('cmyk', 2, val); + }, + black: function (val) { + return this.setChannel('cmyk', 3, val); + }, + + hexString: function () { + return colorString.hexString(this.values.rgb); + }, + rgbString: function () { + return colorString.rgbString(this.values.rgb, this.values.alpha); + }, + rgbaString: function () { + return colorString.rgbaString(this.values.rgb, this.values.alpha); + }, + percentString: function () { + return colorString.percentString(this.values.rgb, this.values.alpha); + }, + hslString: function () { + return colorString.hslString(this.values.hsl, this.values.alpha); + }, + hslaString: function () { + return colorString.hslaString(this.values.hsl, this.values.alpha); + }, + hwbString: function () { + return colorString.hwbString(this.values.hwb, this.values.alpha); + }, + keyword: function () { + return colorString.keyword(this.values.rgb, this.values.alpha); + }, + + rgbNumber: function () { + var rgb = this.values.rgb; + return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; + }, + + luminosity: function () { + // http://www.w3.org/TR/WCAG20/#relativeluminancedef + var rgb = this.values.rgb; + var lum = []; + for (var i = 0; i < rgb.length; i++) { + var chan = rgb[i] / 255; + lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); + } + return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; + }, + + contrast: function (color2) { + // http://www.w3.org/TR/WCAG20/#contrast-ratiodef + var lum1 = this.luminosity(); + var lum2 = color2.luminosity(); + if (lum1 > lum2) { + return (lum1 + 0.05) / (lum2 + 0.05); + } + return (lum2 + 0.05) / (lum1 + 0.05); + }, + + level: function (color2) { + var contrastRatio = this.contrast(color2); + if (contrastRatio >= 7.1) { + return 'AAA'; + } + + return (contrastRatio >= 4.5) ? 'AA' : ''; + }, + + dark: function () { + // YIQ equation from http://24ways.org/2010/calculating-color-contrast + var rgb = this.values.rgb; + var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; + return yiq < 128; + }, + + light: function () { + return !this.dark(); + }, + + negate: function () { + var rgb = []; + for (var i = 0; i < 3; i++) { + rgb[i] = 255 - this.values.rgb[i]; + } + this.setValues('rgb', rgb); + return this; + }, + + lighten: function (ratio) { + var hsl = this.values.hsl; + hsl[2] += hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + darken: function (ratio) { + var hsl = this.values.hsl; + hsl[2] -= hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + saturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] += hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + desaturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] -= hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + whiten: function (ratio) { + var hwb = this.values.hwb; + hwb[1] += hwb[1] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + blacken: function (ratio) { + var hwb = this.values.hwb; + hwb[2] += hwb[2] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + greyscale: function () { + var rgb = this.values.rgb; + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; + this.setValues('rgb', [val, val, val]); + return this; + }, + + clearer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha - (alpha * ratio)); + return this; + }, + + opaquer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha + (alpha * ratio)); + return this; + }, + + rotate: function (degrees) { + var hsl = this.values.hsl; + var hue = (hsl[0] + degrees) % 360; + hsl[0] = hue < 0 ? 360 + hue : hue; + this.setValues('hsl', hsl); + return this; + }, + + /** + * Ported from sass implementation in C + * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 + */ + mix: function (mixinColor, weight) { + var color1 = this; + var color2 = mixinColor; + var p = weight === undefined ? 0.5 : weight; + + var w = 2 * p - 1; + var a = color1.alpha() - color2.alpha(); + + var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + return this + .rgb( + w1 * color1.red() + w2 * color2.red(), + w1 * color1.green() + w2 * color2.green(), + w1 * color1.blue() + w2 * color2.blue() + ) + .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); + }, + + toJSON: function () { + return this.rgb(); + }, + + clone: function () { + // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, + // making the final build way to big to embed in Chart.js. So let's do it manually, + // assuming that values to clone are 1 dimension arrays containing only numbers, + // except 'alpha' which is a number. + var result = new Color(); + var source = this.values; + var target = result.values; + var value, type; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + value = source[prop]; + type = ({}).toString.call(value); + if (type === '[object Array]') { + target[prop] = value.slice(0); + } else if (type === '[object Number]') { + target[prop] = value; + } else { + console.error('unexpected color value:', value); + } + } + } + + return result; + } +}; + +Color.prototype.spaces = { + rgb: ['red', 'green', 'blue'], + hsl: ['hue', 'saturation', 'lightness'], + hsv: ['hue', 'saturation', 'value'], + hwb: ['hue', 'whiteness', 'blackness'], + cmyk: ['cyan', 'magenta', 'yellow', 'black'] +}; + +Color.prototype.maxes = { + rgb: [255, 255, 255], + hsl: [360, 100, 100], + hsv: [360, 100, 100], + hwb: [360, 100, 100], + cmyk: [100, 100, 100, 100] +}; + +Color.prototype.getValues = function (space) { + var values = this.values; + var vals = {}; + + for (var i = 0; i < space.length; i++) { + vals[space.charAt(i)] = values[space][i]; + } + + if (values.alpha !== 1) { + vals.a = values.alpha; + } + + // {r: 255, g: 255, b: 255, a: 0.4} + return vals; +}; + +Color.prototype.setValues = function (space, vals) { + var values = this.values; + var spaces = this.spaces; + var maxes = this.maxes; + var alpha = 1; + var i; + + this.valid = true; + + if (space === 'alpha') { + alpha = vals; + } else if (vals.length) { + // [10, 10, 10] + values[space] = vals.slice(0, space.length); + alpha = vals[space.length]; + } else if (vals[space.charAt(0)] !== undefined) { + // {r: 10, g: 10, b: 10} + for (i = 0; i < space.length; i++) { + values[space][i] = vals[space.charAt(i)]; + } + + alpha = vals.a; + } else if (vals[spaces[space][0]] !== undefined) { + // {red: 10, green: 10, blue: 10} + var chans = spaces[space]; + + for (i = 0; i < space.length; i++) { + values[space][i] = vals[chans[i]]; + } + + alpha = vals.alpha; + } + + values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); + + if (space === 'alpha') { + return false; + } + + var capped; + + // cap values of the space prior converting all values + for (i = 0; i < space.length; i++) { + capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); + values[space][i] = Math.round(capped); + } + + // convert to all the other color spaces + for (var sname in spaces) { + if (sname !== space) { + values[sname] = colorConvert[space][sname](values[space]); + } + } + + return true; +}; + +Color.prototype.setSpace = function (space, args) { + var vals = args[0]; + + if (vals === undefined) { + // color.rgb() + return this.getValues(space); + } + + // color.rgb(10, 10, 10) + if (typeof vals === 'number') { + vals = Array.prototype.slice.call(args); + } + + this.setValues(space, vals); + return this; +}; + +Color.prototype.setChannel = function (space, index, val) { + var svalues = this.values[space]; + if (val === undefined) { + // color.red() + return svalues[index]; + } else if (val === svalues[index]) { + // color.red(color.red()) + return this; + } + + // color.red(100) + svalues[index] = val; + this.setValues(space, svalues); + + return this; +}; + +if (typeof window !== 'undefined') { + window.Color = Color; +} + +var chartjsColor = Color; + +/** + * @namespace Chart.helpers + */ +var helpers = { + /** + * An empty function that can be used, for example, for optional callback. + */ + noop: function() {}, + + /** + * Returns a unique id, sequentially generated from a global variable. + * @returns {number} + * @function + */ + uid: (function() { + var id = 0; + return function() { + return id++; + }; + }()), + + /** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isNullOrUndef: function(value) { + return value === null || typeof value === 'undefined'; + }, + + /** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @function + */ + isArray: function(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + var type = Object.prototype.toString.call(value); + if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { + return true; + } + return false; + }, + + /** + * Returns true if `value` is an object (excluding null), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isObject: function(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; + }, + + /** + * Returns true if `value` is a finite number, else returns false + * @param {*} value - The value to test. + * @returns {boolean} + */ + isFinite: function(value) { + return (typeof value === 'number' || value instanceof Number) && isFinite(value); + }, + + /** + * Returns `value` if defined, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is undefined. + * @returns {*} + */ + valueOrDefault: function(value, defaultValue) { + return typeof value === 'undefined' ? defaultValue : value; + }, + + /** + * Returns value at the given `index` in array if defined, else returns `defaultValue`. + * @param {Array} value - The array to lookup for value at `index`. + * @param {number} index - The index in `value` to lookup for value. + * @param {*} defaultValue - The value to return if `value[index]` is undefined. + * @returns {*} + */ + valueAtIndexOrDefault: function(value, index, defaultValue) { + return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); + }, + + /** + * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the + * value returned by `fn`. If `fn` is not a function, this method returns undefined. + * @param {function} fn - The function to call. + * @param {Array|undefined|null} args - The arguments with which `fn` should be called. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @returns {*} + */ + callback: function(fn, args, thisArg) { + if (fn && typeof fn.call === 'function') { + return fn.apply(thisArg, args); + } + }, + + /** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param {object|Array} loopable - The object or array to be iterated. + * @param {function} fn - The function to call for each item. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @param {boolean} [reverse] - If true, iterates backward on the loopable. + */ + each: function(loopable, fn, thisArg, reverse) { + var i, len, keys; + if (helpers.isArray(loopable)) { + len = loopable.length; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[i], i); + } + } + } else if (helpers.isObject(loopable)) { + keys = Object.keys(loopable); + len = keys.length; + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[keys[i]], keys[i]); + } + } + }, + + /** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @see https://stackoverflow.com/a/14853974 + * @param {Array} a0 - The array to compare + * @param {Array} a1 - The array to compare + * @returns {boolean} + */ + arrayEquals: function(a0, a1) { + var i, ilen, v0, v1; + + if (!a0 || !a1 || a0.length !== a1.length) { + return false; + } + + for (i = 0, ilen = a0.length; i < ilen; ++i) { + v0 = a0[i]; + v1 = a1[i]; + + if (v0 instanceof Array && v1 instanceof Array) { + if (!helpers.arrayEquals(v0, v1)) { + return false; + } + } else if (v0 !== v1) { + // NOTE: two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + + return true; + }, + + /** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param {*} source - The value to clone. + * @returns {*} + */ + clone: function(source) { + if (helpers.isArray(source)) { + return source.map(helpers.clone); + } + + if (helpers.isObject(source)) { + var target = {}; + var keys = Object.keys(source); + var klen = keys.length; + var k = 0; + + for (; k < klen; ++k) { + target[keys[k]] = helpers.clone(source[keys[k]]); + } + + return target; + } + + return source; + }, + + /** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. + * @private + */ + _merger: function(key, target, source, options) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.merge(tval, sval, options); + } else { + target[key] = helpers.clone(sval); + } + }, + + /** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ + _mergerIf: function(key, target, source) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.mergeIf(tval, sval); + } else if (!target.hasOwnProperty(key)) { + target[key] = helpers.clone(sval); + } + }, + + /** + * Recursively deep copies `source` properties into `target` with the given `options`. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @param {object} [options] - Merging options: + * @param {function} [options.merger] - The merge method (key, target, source, options) + * @returns {object} The `target` object. + */ + merge: function(target, source, options) { + var sources = helpers.isArray(source) ? source : [source]; + var ilen = sources.length; + var merge, i, keys, klen, k; + + if (!helpers.isObject(target)) { + return target; + } + + options = options || {}; + merge = options.merger || helpers._merger; + + for (i = 0; i < ilen; ++i) { + source = sources[i]; + if (!helpers.isObject(source)) { + continue; + } + + keys = Object.keys(source); + for (k = 0, klen = keys.length; k < klen; ++k) { + merge(keys[k], target, source, options); + } + } + + return target; + }, + + /** + * Recursively deep copies `source` properties into `target` *only* if not defined in target. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @returns {object} The `target` object. + */ + mergeIf: function(target, source) { + return helpers.merge(target, source, {merger: helpers._mergerIf}); + }, + + /** + * Applies the contents of two or more objects together into the first object. + * @param {object} target - The target object in which all objects are merged into. + * @param {object} arg1 - Object containing additional properties to merge in target. + * @param {object} argN - Additional objects containing properties to merge in target. + * @returns {object} The `target` object. + */ + extend: Object.assign || function(target) { + return helpers.merge(target, [].slice.call(arguments, 1), { + merger: function(key, dst, src) { + dst[key] = src[key]; + } + }); + }, + + /** + * Basic javascript inheritance based on the model created in Backbone.js + */ + inherits: function(extensions) { + var me = this; + var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { + return me.apply(this, arguments); + }; + + var Surrogate = function() { + this.constructor = ChartElement; + }; + + Surrogate.prototype = me.prototype; + ChartElement.prototype = new Surrogate(); + ChartElement.extend = helpers.inherits; + + if (extensions) { + helpers.extend(ChartElement.prototype, extensions); + } + + ChartElement.__super__ = me.prototype; + return ChartElement; + }, + + _deprecated: function(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + + '" is deprecated. Please use "' + current + '" instead'); + } + } +}; + +var helpers_core = helpers; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.callback instead. + * @function Chart.helpers.callCallback + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +helpers.callCallback = helpers.callback; + +/** + * Provided for backward compatibility, use Array.prototype.indexOf instead. + * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ + * @function Chart.helpers.indexOf + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.indexOf = function(array, item, fromIndex) { + return Array.prototype.indexOf.call(array, item, fromIndex); +}; + +/** + * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. + * @function Chart.helpers.getValueOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueOrDefault = helpers.valueOrDefault; + +/** + * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. + * @function Chart.helpers.getValueAtIndexOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easingEffects + * @see http://www.robertpenner.com/easing/ + */ +var effects = { + linear: function(t) { + return t; + }, + + easeInQuad: function(t) { + return t * t; + }, + + easeOutQuad: function(t) { + return -t * (t - 2); + }, + + easeInOutQuad: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t; + } + return -0.5 * ((--t) * (t - 2) - 1); + }, + + easeInCubic: function(t) { + return t * t * t; + }, + + easeOutCubic: function(t) { + return (t = t - 1) * t * t + 1; + }, + + easeInOutCubic: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t; + } + return 0.5 * ((t -= 2) * t * t + 2); + }, + + easeInQuart: function(t) { + return t * t * t * t; + }, + + easeOutQuart: function(t) { + return -((t = t - 1) * t * t * t - 1); + }, + + easeInOutQuart: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t; + } + return -0.5 * ((t -= 2) * t * t * t - 2); + }, + + easeInQuint: function(t) { + return t * t * t * t * t; + }, + + easeOutQuint: function(t) { + return (t = t - 1) * t * t * t * t + 1; + }, + + easeInOutQuint: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t * t; + } + return 0.5 * ((t -= 2) * t * t * t * t + 2); + }, + + easeInSine: function(t) { + return -Math.cos(t * (Math.PI / 2)) + 1; + }, + + easeOutSine: function(t) { + return Math.sin(t * (Math.PI / 2)); + }, + + easeInOutSine: function(t) { + return -0.5 * (Math.cos(Math.PI * t) - 1); + }, + + easeInExpo: function(t) { + return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); + }, + + easeOutExpo: function(t) { + return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; + }, + + easeInOutExpo: function(t) { + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if ((t /= 0.5) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + }, + + easeInCirc: function(t) { + if (t >= 1) { + return t; + } + return -(Math.sqrt(1 - t * t) - 1); + }, + + easeOutCirc: function(t) { + return Math.sqrt(1 - (t = t - 1) * t); + }, + + easeInOutCirc: function(t) { + if ((t /= 0.5) < 1) { + return -0.5 * (Math.sqrt(1 - t * t) - 1); + } + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }, + + easeInElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + }, + + easeOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; + }, + + easeInOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if ((t /= 0.5) === 2) { + return 1; + } + if (!p) { + p = 0.45; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + if (t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + easeInBack: function(t) { + var s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + + easeOutBack: function(t) { + var s = 1.70158; + return (t = t - 1) * t * ((s + 1) * t + s) + 1; + }, + + easeInOutBack: function(t) { + var s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }, + + easeInBounce: function(t) { + return 1 - effects.easeOutBounce(1 - t); + }, + + easeOutBounce: function(t) { + if (t < (1 / 2.75)) { + return 7.5625 * t * t; + } + if (t < (2 / 2.75)) { + return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; + } + if (t < (2.5 / 2.75)) { + return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; + } + return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; + }, + + easeInOutBounce: function(t) { + if (t < 0.5) { + return effects.easeInBounce(t * 2) * 0.5; + } + return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; + } +}; + +var helpers_easing = { + effects: effects +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.easing.effects instead. + * @function Chart.helpers.easingEffects + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.easingEffects = effects; + +var PI = Math.PI; +var RAD_PER_DEG = PI / 180; +var DOUBLE_PI = PI * 2; +var HALF_PI = PI / 2; +var QUARTER_PI = PI / 4; +var TWO_THIRDS_PI = PI * 2 / 3; + +/** + * @namespace Chart.helpers.canvas + */ +var exports$1 = { + /** + * Clears the entire canvas associated to the given `chart`. + * @param {Chart} chart - The chart for which to clear the canvas. + */ + clear: function(chart) { + chart.ctx.clearRect(0, 0, chart.width, chart.height); + }, + + /** + * Creates a "path" for a rectangle with rounded corners at position (x, y) with a + * given size (width, height) and the same `radius` for all corners. + * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. + * @param {number} x - The x axis of the coordinate for the rectangle starting point. + * @param {number} y - The y axis of the coordinate for the rectangle starting point. + * @param {number} width - The rectangle's width. + * @param {number} height - The rectangle's height. + * @param {number} radius - The rounded amount (in pixels) for the four corners. + * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? + */ + roundedRect: function(ctx, x, y, width, height, radius) { + if (radius) { + var r = Math.min(radius, height / 2, width / 2); + var left = x + r; + var top = y + r; + var right = x + width - r; + var bottom = y + height - r; + + ctx.moveTo(x, top); + if (left < right && top < bottom) { + ctx.arc(left, top, r, -PI, -HALF_PI); + ctx.arc(right, top, r, -HALF_PI, 0); + ctx.arc(right, bottom, r, 0, HALF_PI); + ctx.arc(left, bottom, r, HALF_PI, PI); + } else if (left < right) { + ctx.moveTo(left, y); + ctx.arc(right, top, r, -HALF_PI, HALF_PI); + ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); + } else if (top < bottom) { + ctx.arc(left, top, r, -PI, 0); + ctx.arc(left, bottom, r, 0, PI); + } else { + ctx.arc(left, top, r, -PI, PI); + } + ctx.closePath(); + ctx.moveTo(x, y); + } else { + ctx.rect(x, y, width, height); + } + }, + + drawPoint: function(ctx, style, radius, x, y, rotation) { + var type, xOffset, yOffset, size, cornerRadius; + var rad = (rotation || 0) * RAD_PER_DEG; + + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + ctx.beginPath(); + + switch (style) { + // Default includes circle + default: + ctx.arc(x, y, radius, 0, DOUBLE_PI); + ctx.closePath(); + break; + case 'triangle': + ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + // NOTE: the rounded rect implementation changed to use `arc` instead of + // `quadraticCurveTo` since it generates better results when rect is + // almost a circle. 0.516 (instead of 0.5) produces results with visually + // closer proportion to the previous impl and it is inscribed in the + // circle with `radius`. For more details, see the following PRs: + // https://github.com/chartjs/Chart.js/issues/5597 + // https://github.com/chartjs/Chart.js/issues/5858 + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + yOffset = Math.sin(rad + QUARTER_PI) * size; + ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + ctx.rect(x - size, y - size, 2 * size, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ + case 'rectRot': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + yOffset, y - xOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.lineTo(x - yOffset, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + /* falls through */ + case 'cross': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'star': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + rad += QUARTER_PI; + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'line': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case 'dash': + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); + break; + } + + ctx.fill(); + ctx.stroke(); + }, + + /** + * Returns true if the point is inside the rectangle + * @param {object} point - The point to test + * @param {object} area - The rectangle + * @returns {boolean} + * @private + */ + _isPointInArea: function(point, area) { + var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. + + return point.x > area.left - epsilon && point.x < area.right + epsilon && + point.y > area.top - epsilon && point.y < area.bottom + epsilon; + }, + + clipArea: function(ctx, area) { + ctx.save(); + ctx.beginPath(); + ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); + ctx.clip(); + }, + + unclipArea: function(ctx) { + ctx.restore(); + }, + + lineTo: function(ctx, previous, target, flip) { + var stepped = target.steppedLine; + if (stepped) { + if (stepped === 'middle') { + var midpoint = (previous.x + target.x) / 2.0; + ctx.lineTo(midpoint, flip ? target.y : previous.y); + ctx.lineTo(midpoint, flip ? previous.y : target.y); + } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { + ctx.lineTo(previous.x, target.y); + } else { + ctx.lineTo(target.x, previous.y); + } + ctx.lineTo(target.x, target.y); + return; + } + + if (!target.tension) { + ctx.lineTo(target.x, target.y); + return; + } + + ctx.bezierCurveTo( + flip ? previous.controlPointPreviousX : previous.controlPointNextX, + flip ? previous.controlPointPreviousY : previous.controlPointNextY, + flip ? target.controlPointNextX : target.controlPointPreviousX, + flip ? target.controlPointNextY : target.controlPointPreviousY, + target.x, + target.y); + } +}; + +var helpers_canvas = exports$1; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. + * @namespace Chart.helpers.clear + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.clear = exports$1.clear; + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. + * @namespace Chart.helpers.drawRoundedRectangle + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.drawRoundedRectangle = function(ctx) { + ctx.beginPath(); + exports$1.roundedRect.apply(exports$1, arguments); +}; + +var defaults = { + /** + * @private + */ + _set: function(scope, values) { + return helpers_core.merge(this[scope] || (this[scope] = {}), values); + } +}; + +// TODO(v3): remove 'global' from namespace. all default are global and +// there's inconsistency around which options are under 'global' +defaults._set('global', { + defaultColor: 'rgba(0,0,0,0.1)', + defaultFontColor: '#666', + defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + defaultFontSize: 12, + defaultFontStyle: 'normal', + defaultLineHeight: 1.2, + showLines: true +}); + +var core_defaults = defaults; + +var valueOrDefault = helpers_core.valueOrDefault; + +/** + * Converts the given font object into a CSS font string. + * @param {object} font - A font object. + * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font + * @private + */ +function toFontString(font) { + if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { + return null; + } + + return (font.style ? font.style + ' ' : '') + + (font.weight ? font.weight + ' ' : '') + + font.size + 'px ' + + font.family; +} + +/** + * @alias Chart.helpers.options + * @namespace + */ +var helpers_options = { + /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param {number} size - The font size (in pixels) used to resolve relative `value`. + * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height + * @since 2.7.0 + */ + toLineHeight: function(value, size) { + var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); + if (!matches || matches[1] === 'normal') { + return size * 1.2; + } + + value = +matches[2]; + + switch (matches[3]) { + case 'px': + return value; + case '%': + value /= 100; + break; + } + + return size * value; + }, + + /** + * Converts the given value into a padding object with pre-computed width/height. + * @param {number|object} value - If a number, set the value to all TRBL component, + * else, if and object, use defined properties and sets undefined ones to 0. + * @returns {object} The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ + toPadding: function(value) { + var t, r, b, l; + + if (helpers_core.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + top: t, + right: r, + bottom: b, + left: l, + height: t + b, + width: l + r + }; + }, + + /** + * Parses font options and returns the font object. + * @param {object} options - A object that contains font options to be parsed. + * @return {object} The font object. + * @todo Support font.* options and renamed to toFont(). + * @private + */ + _parseFont: function(options) { + var globalDefaults = core_defaults.global; + var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); + var font = { + family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), + lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), + size: size, + style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), + weight: null, + string: '' + }; + + font.string = toFontString(font); + return font; + }, + + /** + * Evaluates the given `inputs` sequentially and returns the first defined value. + * @param {Array} inputs - An array of values, falling back to the last value. + * @param {object} [context] - If defined and the current value is a function, the value + * is called with `context` as first argument and the result becomes the new input. + * @param {number} [index] - If defined and the current value is an array, the value + * at `index` become the new input. + * @param {object} [info] - object to return information about resolution in + * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. + * @since 2.7.0 + */ + resolve: function(inputs, context, index, info) { + var cacheable = true; + var i, ilen, value; + + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + cacheable = false; + } + if (index !== undefined && helpers_core.isArray(value)) { + value = value[index]; + cacheable = false; + } + if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } + return value; + } + } + } +}; + +/** + * @alias Chart.helpers.math + * @namespace + */ +var exports$2 = { + /** + * Returns an array of factors sorted from 1 to sqrt(value) + * @private + */ + _factorize: function(value) { + var result = []; + var sqrt = Math.sqrt(value); + var i; + + for (i = 1; i < sqrt; i++) { + if (value % i === 0) { + result.push(i); + result.push(value / i); + } + } + if (sqrt === (sqrt | 0)) { // if value is a square number + result.push(sqrt); + } + + result.sort(function(a, b) { + return a - b; + }).pop(); + return result; + }, + + log10: Math.log10 || function(x) { + var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. + // Check for whole powers of 10, + // which due to floating point rounding error should be corrected. + var powerOf10 = Math.round(exponent); + var isPowerOf10 = x === Math.pow(10, powerOf10); + + return isPowerOf10 ? powerOf10 : exponent; + } +}; + +var helpers_math = exports$2; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.math.log10 instead. + * @namespace Chart.helpers.log10 + * @deprecated since version 2.9.0 + * @todo remove at version 3 + * @private + */ +helpers_core.log10 = exports$2.log10; + +var getRtlAdapter = function(rectX, width) { + return { + x: function(x) { + return rectX + rectX + width - x; + }, + setWidth: function(w) { + width = w; + }, + textAlign: function(align) { + if (align === 'center') { + return align; + } + return align === 'right' ? 'left' : 'right'; + }, + xPlus: function(x, value) { + return x - value; + }, + leftForLtr: function(x, itemWidth) { + return x - itemWidth; + }, + }; +}; + +var getLtrAdapter = function() { + return { + x: function(x) { + return x; + }, + setWidth: function(w) { // eslint-disable-line no-unused-vars + }, + textAlign: function(align) { + return align; + }, + xPlus: function(x, value) { + return x + value; + }, + leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars + return x; + }, + }; +}; + +var getAdapter = function(rtl, rectX, width) { + return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter(); +}; + +var overrideTextDirection = function(ctx, direction) { + var style, original; + if (direction === 'ltr' || direction === 'rtl') { + style = ctx.canvas.style; + original = [ + style.getPropertyValue('direction'), + style.getPropertyPriority('direction'), + ]; + + style.setProperty('direction', direction, 'important'); + ctx.prevTextDirection = original; + } +}; + +var restoreTextDirection = function(ctx) { + var original = ctx.prevTextDirection; + if (original !== undefined) { + delete ctx.prevTextDirection; + ctx.canvas.style.setProperty('direction', original[0], original[1]); + } +}; + +var helpers_rtl = { + getRtlAdapter: getAdapter, + overrideTextDirection: overrideTextDirection, + restoreTextDirection: restoreTextDirection, +}; + +var helpers$1 = helpers_core; +var easing = helpers_easing; +var canvas = helpers_canvas; +var options = helpers_options; +var math = helpers_math; +var rtl = helpers_rtl; +helpers$1.easing = easing; +helpers$1.canvas = canvas; +helpers$1.options = options; +helpers$1.math = math; +helpers$1.rtl = rtl; + +function interpolate(start, view, model, ease) { + var keys = Object.keys(model); + var i, ilen, key, actual, origin, target, type, c0, c1; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + + target = model[key]; + + // if a value is added to the model after pivot() has been called, the view + // doesn't contain it, so let's initialize the view to the target value. + if (!view.hasOwnProperty(key)) { + view[key] = target; + } + + actual = view[key]; + + if (actual === target || key[0] === '_') { + continue; + } + + if (!start.hasOwnProperty(key)) { + start[key] = actual; + } + + origin = start[key]; + + type = typeof target; + + if (type === typeof origin) { + if (type === 'string') { + c0 = chartjsColor(origin); + if (c0.valid) { + c1 = chartjsColor(target); + if (c1.valid) { + view[key] = c1.mix(c0, ease).rgbString(); + continue; + } + } + } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { + view[key] = origin + (target - origin) * ease; + continue; + } + } + + view[key] = target; + } +} + +var Element = function(configuration) { + helpers$1.extend(this, configuration); + this.initialize.apply(this, arguments); +}; + +helpers$1.extend(Element.prototype, { + _type: undefined, + + initialize: function() { + this.hidden = false; + }, + + pivot: function() { + var me = this; + if (!me._view) { + me._view = helpers$1.extend({}, me._model); + } + me._start = {}; + return me; + }, + + transition: function(ease) { + var me = this; + var model = me._model; + var start = me._start; + var view = me._view; + + // No animation -> No Transition + if (!model || ease === 1) { + me._view = helpers$1.extend({}, model); + me._start = null; + return me; + } + + if (!view) { + view = me._view = {}; + } + + if (!start) { + start = me._start = {}; + } + + interpolate(start, view, model, ease); + + return me; + }, + + tooltipPosition: function() { + return { + x: this._model.x, + y: this._model.y + }; + }, + + hasValue: function() { + return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); + } +}); + +Element.extend = helpers$1.inherits; + +var core_element = Element; + +var exports$3 = core_element.extend({ + chart: null, // the animation associated chart instance + currentStep: 0, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null, // user specified callback to fire when the animation finishes +}); + +var core_animation = exports$3; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.Animation instead + * @prop Chart.Animation#animationObject + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$3.prototype, 'animationObject', { + get: function() { + return this; + } +}); + +/** + * Provided for backward compatibility, use Chart.Animation#chart instead + * @prop Chart.Animation#chartInstance + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$3.prototype, 'chartInstance', { + get: function() { + return this.chart; + }, + set: function(value) { + this.chart = value; + } +}); + +core_defaults._set('global', { + animation: { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers$1.noop, + onComplete: helpers$1.noop + } +}); + +var core_animations = { + animations: [], + request: null, + + /** + * @param {Chart} chart - The chart to animate. + * @param {Chart.Animation} animation - The animation that we will animate. + * @param {number} duration - The animation duration in ms. + * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions + */ + addAnimation: function(chart, animation, duration, lazy) { + var animations = this.animations; + var i, ilen; + + animation.chart = chart; + animation.startTime = Date.now(); + animation.duration = duration; + + if (!lazy) { + chart.animating = true; + } + + for (i = 0, ilen = animations.length; i < ilen; ++i) { + if (animations[i].chart === chart) { + animations[i] = animation; + return; + } + } + + animations.push(animation); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (animations.length === 1) { + this.requestAnimationFrame(); + } + }, + + cancelAnimation: function(chart) { + var index = helpers$1.findIndex(this.animations, function(animation) { + return animation.chart === chart; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chart.animating = false; + } + }, + + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers$1.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + + /** + * @private + */ + startDigest: function() { + var me = this; + + me.advance(); + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + }, + + /** + * @private + */ + advance: function() { + var animations = this.animations; + var animation, chart, numSteps, nextStep; + var i = 0; + + // 1 animation per chart, so we are looping charts here + while (i < animations.length) { + animation = animations[i]; + chart = animation.chart; + numSteps = animation.numSteps; + + // Make sure that currentStep starts at 1 + // https://github.com/chartjs/Chart.js/issues/6104 + nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; + animation.currentStep = Math.min(nextStep, numSteps); + + helpers$1.callback(animation.render, [chart, animation], chart); + helpers$1.callback(animation.onAnimationProgress, [animation], chart); + + if (animation.currentStep >= numSteps) { + helpers$1.callback(animation.onAnimationComplete, [animation], chart); + chart.animating = false; + animations.splice(i, 1); + } else { + ++i; + } + } + } +}; + +var resolve = helpers$1.options.resolve; + +var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; + +/** + * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', + * 'unshift') and notify the listener AFTER the array has been altered. Listeners are + * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. + */ +function listenArrayEvents(array, listener) { + if (array._chartjs) { + array._chartjs.listeners.push(listener); + return; + } + + Object.defineProperty(array, '_chartjs', { + configurable: true, + enumerable: false, + value: { + listeners: [listener] + } + }); + + arrayEvents.forEach(function(key) { + var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); + var base = array[key]; + + Object.defineProperty(array, key, { + configurable: true, + enumerable: false, + value: function() { + var args = Array.prototype.slice.call(arguments); + var res = base.apply(this, args); + + helpers$1.each(array._chartjs.listeners, function(object) { + if (typeof object[method] === 'function') { + object[method].apply(object, args); + } + }); + + return res; + } + }); + }); +} + +/** + * Removes the given array event listener and cleanup extra attached properties (such as + * the _chartjs stub and overridden methods) if array doesn't have any more listeners. + */ +function unlistenArrayEvents(array, listener) { + var stub = array._chartjs; + if (!stub) { + return; + } + + var listeners = stub.listeners; + var index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + + if (listeners.length > 0) { + return; + } + + arrayEvents.forEach(function(key) { + delete array[key]; + }); + + delete array._chartjs; +} + +// Base class for all dataset controllers (line, bar, etc) +var DatasetController = function(chart, datasetIndex) { + this.initialize(chart, datasetIndex); +}; + +helpers$1.extend(DatasetController.prototype, { + + /** + * Element type used to generate a meta dataset (e.g. Chart.element.Line). + * @type {Chart.core.element} + */ + datasetElementType: null, + + /** + * Element type used to generate a meta data (e.g. Chart.element.Point). + * @type {Chart.core.element} + */ + dataElementType: null, + + /** + * Dataset element option keys to be resolved in _resolveDatasetElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth' + ], + + /** + * Data element option keys to be resolved in _resolveDataElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'pointStyle' + ], + + initialize: function(chart, datasetIndex) { + var me = this; + me.chart = chart; + me.index = datasetIndex; + me.linkScales(); + me.addElements(); + me._type = me.getMeta().type; + }, + + updateIndex: function(datasetIndex) { + this.index = datasetIndex; + }, + + linkScales: function() { + var me = this; + var meta = me.getMeta(); + var chart = me.chart; + var scales = chart.scales; + var dataset = me.getDataset(); + var scalesOpts = chart.options.scales; + + if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) { + meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id; + } + if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) { + meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id; + } + }, + + getDataset: function() { + return this.chart.data.datasets[this.index]; + }, + + getMeta: function() { + return this.chart.getDatasetMeta(this.index); + }, + + getScaleForId: function(scaleID) { + return this.chart.scales[scaleID]; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().yAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getValueScale: function() { + return this.getScaleForId(this._getValueScaleId()); + }, + + /** + * @private + */ + _getIndexScale: function() { + return this.getScaleForId(this._getIndexScaleId()); + }, + + reset: function() { + this._update(true); + }, + + /** + * @private + */ + destroy: function() { + if (this._data) { + unlistenArrayEvents(this._data, this); + } + }, + + createMetaDataset: function() { + var me = this; + var type = me.datasetElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index + }); + }, + + createMetaData: function(index) { + var me = this; + var type = me.dataElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index, + _index: index + }); + }, + + addElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data || []; + var metaData = meta.data; + var i, ilen; + + for (i = 0, ilen = data.length; i < ilen; ++i) { + metaData[i] = metaData[i] || me.createMetaData(i); + } + + meta.dataset = meta.dataset || me.createMetaDataset(); + }, + + addElementAndReset: function(index) { + var element = this.createMetaData(index); + this.getMeta().data.splice(index, 0, element); + this.updateElement(element, index, true); + }, + + buildOrUpdateElements: function() { + var me = this; + var dataset = me.getDataset(); + var data = dataset.data || (dataset.data = []); + + // In order to correctly handle data addition/deletion animation (an thus simulate + // real-time charts), we need to monitor these data modifications and synchronize + // the internal meta data accordingly. + if (me._data !== data) { + if (me._data) { + // This case happens when the user replaced the data array instance. + unlistenArrayEvents(me._data, me); + } + + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, me); + } + me._data = data; + } + + // Re-sync meta data in case the user replaced the data array or if we missed + // any updates and so make sure that we handle number of datapoints changing. + me.resyncElements(); + }, + + /** + * Returns the merged user-supplied and default dataset-level options + * @private + */ + _configure: function() { + var me = this; + me._config = helpers$1.merge({}, [ + me.chart.options.datasets[me._type], + me.getDataset(), + ], { + merger: function(key, target, source) { + if (key !== '_meta' && key !== 'data') { + helpers$1._merger(key, target, source); + } + } + }); + }, + + _update: function(reset) { + var me = this; + me._configure(); + me._cachedDataOpts = null; + me.update(reset); + }, + + update: helpers$1.noop, + + transition: function(easingValue) { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + for (; i < ilen; ++i) { + elements[i].transition(easingValue); + } + + if (meta.dataset) { + meta.dataset.transition(easingValue); + } + }, + + draw: function() { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + if (meta.dataset) { + meta.dataset.draw(); + } + + for (; i < ilen; ++i) { + elements[i].draw(); + } + }, + + /** + * Returns a set of predefined style properties that should be used to represent the dataset + * or the data if the index is specified + * @param {number} index - data index + * @return {IStyleInterface} style object + */ + getStyle: function(index) { + var me = this; + var meta = me.getMeta(); + var dataset = meta.dataset; + var style; + + me._configure(); + if (dataset && index === undefined) { + style = me._resolveDatasetElementOptions(dataset || {}); + } else { + index = index || 0; + style = me._resolveDataElementOptions(meta.data[index] || {}, index); + } + + if (style.fill === false || style.fill === null) { + style.backgroundColor = style.borderColor; + } + + return style; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function(element, hover) { + var me = this; + var chart = me.chart; + var datasetOpts = me._config; + var custom = element.custom || {}; + var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; + var elementOptions = me._datasetElementOptions; + var values = {}; + var i, ilen, key, readKey; + + // Scriptable options + var context = { + chart: chart, + dataset: me.getDataset(), + datasetIndex: me.index, + hover: hover + }; + + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key; + values[key] = resolve([ + custom[readKey], + datasetOpts[readKey], + options[readKey] + ], context); + } + + return values; + }, + + /** + * @private + */ + _resolveDataElementOptions: function(element, index) { + var me = this; + var custom = element && element.custom; + var cached = me._cachedDataOpts; + if (cached && !custom) { + return cached; + } + var chart = me.chart; + var datasetOpts = me._config; + var options = chart.options.elements[me.dataElementType.prototype._type] || {}; + var elementOptions = me._dataElementOptions; + var values = {}; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: me.getDataset(), + datasetIndex: me.index + }; + + // `resolve` sets cacheable to `false` if any option is indexed or scripted + var info = {cacheable: !custom}; + + var keys, i, ilen, key; + + custom = custom || {}; + + if (helpers$1.isArray(elementOptions)) { + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + values[key] = resolve([ + custom[key], + datasetOpts[key], + options[key] + ], context, index, info); + } + } else { + keys = Object.keys(elementOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + datasetOpts[elementOptions[key]], + datasetOpts[key], + options[key] + ], context, index, info); + } + } + + if (info.cacheable) { + me._cachedDataOpts = Object.freeze(values); + } + + return values; + }, + + removeHoverStyle: function(element) { + helpers$1.merge(element._model, element.$previousStyle || {}); + delete element.$previousStyle; + }, + + setHoverStyle: function(element) { + var dataset = this.chart.data.datasets[element._datasetIndex]; + var index = element._index; + var custom = element.custom || {}; + var model = element._model; + var getHoverColor = helpers$1.getHoverColor; + + element.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth + }; + + model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); + model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); + model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); + }, + + /** + * @private + */ + _removeDatasetHoverStyle: function() { + var element = this.getMeta().dataset; + + if (element) { + this.removeHoverStyle(element); + } + }, + + /** + * @private + */ + _setDatasetHoverStyle: function() { + var element = this.getMeta().dataset; + var prev = {}; + var i, ilen, key, keys, hoverOptions, model; + + if (!element) { + return; + } + + model = element._model; + hoverOptions = this._resolveDatasetElementOptions(element, true); + + keys = Object.keys(hoverOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + prev[key] = model[key]; + model[key] = hoverOptions[key]; + } + + element.$previousStyle = prev; + }, + + /** + * @private + */ + resyncElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var numMeta = meta.data.length; + var numData = data.length; + + if (numData < numMeta) { + meta.data.splice(numData, numMeta - numData); + } else if (numData > numMeta) { + me.insertElements(numMeta, numData - numMeta); + } + }, + + /** + * @private + */ + insertElements: function(start, count) { + for (var i = 0; i < count; ++i) { + this.addElementAndReset(start + i); + } + }, + + /** + * @private + */ + onDataPush: function() { + var count = arguments.length; + this.insertElements(this.getDataset().data.length - count, count); + }, + + /** + * @private + */ + onDataPop: function() { + this.getMeta().data.pop(); + }, + + /** + * @private + */ + onDataShift: function() { + this.getMeta().data.shift(); + }, + + /** + * @private + */ + onDataSplice: function(start, count) { + this.getMeta().data.splice(start, count); + this.insertElements(start, arguments.length - 2); + }, + + /** + * @private + */ + onDataUnshift: function() { + this.insertElements(0, arguments.length); + } +}); + +DatasetController.extend = helpers$1.inherits; + +var core_datasetController = DatasetController; + +var TAU = Math.PI * 2; + +core_defaults._set('global', { + elements: { + arc: { + backgroundColor: core_defaults.global.defaultColor, + borderColor: '#fff', + borderWidth: 2, + borderAlign: 'center' + } + } +}); + +function clipArc(ctx, arc) { + var startAngle = arc.startAngle; + var endAngle = arc.endAngle; + var pixelMargin = arc.pixelMargin; + var angleMargin = pixelMargin / arc.outerRadius; + var x = arc.x; + var y = arc.y; + + // Draw an inner border by cliping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + ctx.beginPath(); + ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin); + if (arc.innerRadius > pixelMargin) { + angleMargin = pixelMargin / arc.innerRadius; + ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true); + } else { + ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2); + } + ctx.closePath(); + ctx.clip(); +} + +function drawFullCircleBorders(ctx, vm, arc, inner) { + var endAngle = arc.endAngle; + var i; + + if (inner) { + arc.endAngle = arc.startAngle + TAU; + clipArc(ctx, arc); + arc.endAngle = endAngle; + if (arc.endAngle === arc.startAngle && arc.fullCircles) { + arc.endAngle += TAU; + arc.fullCircles--; + } + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } +} + +function drawBorder(ctx, vm, arc) { + var inner = vm.borderAlign === 'inner'; + + if (inner) { + ctx.lineWidth = vm.borderWidth * 2; + ctx.lineJoin = 'round'; + } else { + ctx.lineWidth = vm.borderWidth; + ctx.lineJoin = 'bevel'; + } + + if (arc.fullCircles) { + drawFullCircleBorders(ctx, vm, arc, inner); + } + + if (inner) { + clipArc(ctx, arc); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + ctx.stroke(); +} + +var element_arc = core_element.extend({ + _type: 'arc', + + inLabelRange: function(mouseX) { + var vm = this._view; + + if (vm) { + return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); + } + return false; + }, + + inRange: function(chartX, chartY) { + var vm = this._view; + + if (vm) { + var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); + var angle = pointRelativePosition.angle; + var distance = pointRelativePosition.distance; + + // Sanitise angle range + var startAngle = vm.startAngle; + var endAngle = vm.endAngle; + while (endAngle < startAngle) { + endAngle += TAU; + } + while (angle > endAngle) { + angle -= TAU; + } + while (angle < startAngle) { + angle += TAU; + } + + // Check if within the range of the open/close angle + var betweenAngles = (angle >= startAngle && angle <= endAngle); + var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); + + return (betweenAngles && withinRadius); + } + return false; + }, + + getCenterPoint: function() { + var vm = this._view; + var halfAngle = (vm.startAngle + vm.endAngle) / 2; + var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; + return { + x: vm.x + Math.cos(halfAngle) * halfRadius, + y: vm.y + Math.sin(halfAngle) * halfRadius + }; + }, + + getArea: function() { + var vm = this._view; + return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); + }, + + tooltipPosition: function() { + var vm = this._view; + var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); + var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; + + return { + x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), + y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) + }; + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; + var arc = { + x: vm.x, + y: vm.y, + innerRadius: vm.innerRadius, + outerRadius: Math.max(vm.outerRadius - pixelMargin, 0), + pixelMargin: pixelMargin, + startAngle: vm.startAngle, + endAngle: vm.endAngle, + fullCircles: Math.floor(vm.circumference / TAU) + }; + var i; + + ctx.save(); + + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + + if (arc.fullCircles) { + arc.endAngle = arc.startAngle + TAU; + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.fill(); + } + arc.endAngle = arc.startAngle + vm.circumference % TAU; + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + ctx.fill(); + + if (vm.borderWidth) { + drawBorder(ctx, vm, arc); + } + + ctx.restore(); + } +}); + +var valueOrDefault$1 = helpers$1.valueOrDefault; + +var defaultColor = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + line: { + tension: 0.4, + backgroundColor: defaultColor, + borderWidth: 3, + borderColor: defaultColor, + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + capBezierPoints: true, + fill: true, // do we fill in the area between the line and its base axis + } + } +}); + +var element_line = core_element.extend({ + _type: 'line', + + draw: function() { + var me = this; + var vm = me._view; + var ctx = me._chart.ctx; + var spanGaps = vm.spanGaps; + var points = me._children.slice(); // clone array + var globalDefaults = core_defaults.global; + var globalOptionLineElements = globalDefaults.elements.line; + var lastDrawnIndex = -1; + var closePath = me._loop; + var index, previous, currentVM; + + if (!points.length) { + return; + } + + if (me._loop) { + for (index = 0; index < points.length; ++index) { + previous = helpers$1.previousItem(points, index); + // If the line has an open path, shift the point array + if (!points[index]._view.skip && previous._view.skip) { + points = points.slice(index).concat(points.slice(0, index)); + closePath = spanGaps; + break; + } + } + // If the line has a close path, add the first point again + if (closePath) { + points.push(points[0]); + } + } + + ctx.save(); + + // Stroke Line Options + ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; + + // IE 9 and 10 do not support line dash + if (ctx.setLineDash) { + ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); + } + + ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); + ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; + ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); + ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; + + // Stroke Line + ctx.beginPath(); + + // First point moves to it's starting position no matter what + currentVM = points[0]._view; + if (!currentVM.skip) { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = 0; + } + + for (index = 1; index < points.length; ++index) { + currentVM = points[index]._view; + previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex]; + + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + helpers$1.canvas.lineTo(ctx, previous._view, currentVM); + } + lastDrawnIndex = index; + } + } + + if (closePath) { + ctx.closePath(); + } + + ctx.stroke(); + ctx.restore(); + } +}); + +var valueOrDefault$2 = helpers$1.valueOrDefault; + +var defaultColor$1 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + point: { + radius: 3, + pointStyle: 'circle', + backgroundColor: defaultColor$1, + borderColor: defaultColor$1, + borderWidth: 1, + // Hover + hitRadius: 1, + hoverRadius: 4, + hoverBorderWidth: 1 + } + } +}); + +function xRange(mouseX) { + var vm = this._view; + return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; +} + +function yRange(mouseY) { + var vm = this._view; + return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; +} + +var element_point = core_element.extend({ + _type: 'point', + + inRange: function(mouseX, mouseY) { + var vm = this._view; + return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; + }, + + inLabelRange: xRange, + inXRange: xRange, + inYRange: yRange, + + getCenterPoint: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + }, + + getArea: function() { + return Math.PI * Math.pow(this._view.radius, 2); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y, + padding: vm.radius + vm.borderWidth + }; + }, + + draw: function(chartArea) { + var vm = this._view; + var ctx = this._chart.ctx; + var pointStyle = vm.pointStyle; + var rotation = vm.rotation; + var radius = vm.radius; + var x = vm.x; + var y = vm.y; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow + + if (vm.skip) { + return; + } + + // Clipping for Points. + if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { + ctx.strokeStyle = vm.borderColor || defaultColor; + ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); + ctx.fillStyle = vm.backgroundColor || defaultColor; + helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); + } + } +}); + +var defaultColor$2 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + rectangle: { + backgroundColor: defaultColor$2, + borderColor: defaultColor$2, + borderSkipped: 'bottom', + borderWidth: 0 + } + } +}); + +function isVertical(vm) { + return vm && vm.width !== undefined; +} + +/** + * Helper function to get the bounds of the bar regardless of the orientation + * @param bar {Chart.Element.Rectangle} the bar + * @return {Bounds} bounds of the bar + * @private + */ +function getBarBounds(vm) { + var x1, x2, y1, y2, half; + + if (isVertical(vm)) { + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; + y1 = Math.min(vm.y, vm.base); + y2 = Math.max(vm.y, vm.base); + } else { + half = vm.height / 2; + x1 = Math.min(vm.x, vm.base); + x2 = Math.max(vm.x, vm.base); + y1 = vm.y - half; + y2 = vm.y + half; + } + + return { + left: x1, + top: y1, + right: x2, + bottom: y2 + }; +} + +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} + +function parseBorderSkipped(vm) { + var edge = vm.borderSkipped; + var res = {}; + + if (!edge) { + return res; + } + + if (vm.horizontal) { + if (vm.base > vm.x) { + edge = swap(edge, 'left', 'right'); + } + } else if (vm.base < vm.y) { + edge = swap(edge, 'bottom', 'top'); + } + + res[edge] = true; + return res; +} + +function parseBorderWidth(vm, maxW, maxH) { + var value = vm.borderWidth; + var skip = parseBorderSkipped(vm); + var t, r, b, l; + + if (helpers$1.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, + r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, + b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, + l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l + }; +} + +function boundingRects(vm) { + var bounds = getBarBounds(vm); + var width = bounds.right - bounds.left; + var height = bounds.bottom - bounds.top; + var border = parseBorderWidth(vm, width / 2, height / 2); + + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b + } + }; +} + +function inRange(vm, x, y) { + var skipX = x === null; + var skipY = y === null; + var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); + + return bounds + && (skipX || x >= bounds.left && x <= bounds.right) + && (skipY || y >= bounds.top && y <= bounds.bottom); +} + +var element_rectangle = core_element.extend({ + _type: 'rectangle', + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var rects = boundingRects(vm); + var outer = rects.outer; + var inner = rects.inner; + + ctx.fillStyle = vm.backgroundColor; + ctx.fillRect(outer.x, outer.y, outer.w, outer.h); + + if (outer.w === inner.w && outer.h === inner.h) { + return; + } + + ctx.save(); + ctx.beginPath(); + ctx.rect(outer.x, outer.y, outer.w, outer.h); + ctx.clip(); + ctx.fillStyle = vm.borderColor; + ctx.rect(inner.x, inner.y, inner.w, inner.h); + ctx.fill('evenodd'); + ctx.restore(); + }, + + height: function() { + var vm = this._view; + return vm.base - vm.y; + }, + + inRange: function(mouseX, mouseY) { + return inRange(this._view, mouseX, mouseY); + }, + + inLabelRange: function(mouseX, mouseY) { + var vm = this._view; + return isVertical(vm) + ? inRange(vm, mouseX, null) + : inRange(vm, null, mouseY); + }, + + inXRange: function(mouseX) { + return inRange(this._view, mouseX, null); + }, + + inYRange: function(mouseY) { + return inRange(this._view, null, mouseY); + }, + + getCenterPoint: function() { + var vm = this._view; + var x, y; + if (isVertical(vm)) { + x = vm.x; + y = (vm.y + vm.base) / 2; + } else { + x = (vm.x + vm.base) / 2; + y = vm.y; + } + + return {x: x, y: y}; + }, + + getArea: function() { + var vm = this._view; + + return isVertical(vm) + ? vm.width * Math.abs(vm.y - vm.base) + : vm.height * Math.abs(vm.x - vm.base); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + } +}); + +var elements = {}; +var Arc = element_arc; +var Line = element_line; +var Point = element_point; +var Rectangle = element_rectangle; +elements.Arc = Arc; +elements.Line = Line; +elements.Point = Point; +elements.Rectangle = Rectangle; + +var deprecated = helpers$1._deprecated; +var valueOrDefault$3 = helpers$1.valueOrDefault; + +core_defaults._set('bar', { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + offset: true, + gridLines: { + offsetGridLines: true + } + }], + + yAxes: [{ + type: 'linear' + }] + } +}); + +core_defaults._set('global', { + datasets: { + bar: { + categoryPercentage: 0.8, + barPercentage: 0.9 + } + } +}); + +/** + * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. + * @private + */ +function computeMinSampleSize(scale, pixels) { + var min = scale._length; + var prev, curr, i, ilen; + + for (i = 1, ilen = pixels.length; i < ilen; ++i) { + min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); + } + + for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) { + curr = scale.getPixelForTick(i); + min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min; + prev = curr; + } + + return min; +} + +/** + * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, + * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This + * mode currently always generates bars equally sized (until we introduce scriptable options?). + * @private + */ +function computeFitCategoryTraits(index, ruler, options) { + var thickness = options.barThickness; + var count = ruler.stackCount; + var curr = ruler.pixels[index]; + var min = helpers$1.isNullOrUndef(thickness) + ? computeMinSampleSize(ruler.scale, ruler.pixels) + : -1; + var size, ratio; + + if (helpers$1.isNullOrUndef(thickness)) { + size = min * options.categoryPercentage; + ratio = options.barPercentage; + } else { + // When bar thickness is enforced, category and bar percentages are ignored. + // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') + // and deprecate barPercentage since this value is ignored when thickness is absolute. + size = thickness * count; + ratio = 1; + } + + return { + chunk: size / count, + ratio: ratio, + start: curr - (size / 2) + }; +} + +/** + * Computes an "optimal" category that globally arranges bars side by side (no gap when + * percentage options are 1), based on the previous and following categories. This mode + * generates bars with different widths when data are not evenly spaced. + * @private + */ +function computeFlexCategoryTraits(index, ruler, options) { + var pixels = ruler.pixels; + var curr = pixels[index]; + var prev = index > 0 ? pixels[index - 1] : null; + var next = index < pixels.length - 1 ? pixels[index + 1] : null; + var percent = options.categoryPercentage; + var start, size; + + if (prev === null) { + // first data: its size is double based on the next point or, + // if it's also the last data, we use the scale size. + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); + } + + if (next === null) { + // last data: its size is also double based on the previous point. + next = curr + curr - prev; + } + + start = curr - (curr - Math.min(prev, next)) / 2 * percent; + size = Math.abs(next - prev) / 2 * percent; + + return { + chunk: size / ruler.stackCount, + ratio: options.barPercentage, + start: start + }; +} + +var controller_bar = core_datasetController.extend({ + + dataElementType: elements.Rectangle, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderSkipped', + 'borderWidth', + 'barPercentage', + 'barThickness', + 'categoryPercentage', + 'maxBarThickness', + 'minBarLength' + ], + + initialize: function() { + var me = this; + var meta, scaleOpts; + + core_datasetController.prototype.initialize.apply(me, arguments); + + meta = me.getMeta(); + meta.stack = me.getDataset().stack; + meta.bar = true; + + scaleOpts = me._getIndexScale().options; + deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage'); + deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness'); + deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage'); + deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength'); + deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness'); + }, + + update: function(reset) { + var me = this; + var rects = me.getMeta().data; + var i, ilen; + + me._ruler = me.getRuler(); + + for (i = 0, ilen = rects.length; i < ilen; ++i) { + me.updateElement(rects[i], i, reset); + } + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var dataset = me.getDataset(); + var options = me._resolveDataElementOptions(rectangle, index); + + rectangle._xScale = me.getScaleForId(meta.xAxisID); + rectangle._yScale = me.getScaleForId(meta.yAxisID); + rectangle._datasetIndex = me.index; + rectangle._index = index; + rectangle._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderSkipped: options.borderSkipped, + borderWidth: options.borderWidth, + datasetLabel: dataset.label, + label: me.chart.data.labels[index] + }; + + if (helpers$1.isArray(dataset.data[index])) { + rectangle._model.borderSkipped = null; + } + + me._updateElementGeometry(rectangle, index, reset, options); + + rectangle.pivot(); + }, + + /** + * @private + */ + _updateElementGeometry: function(rectangle, index, reset, options) { + var me = this; + var model = rectangle._model; + var vscale = me._getValueScale(); + var base = vscale.getBasePixel(); + var horizontal = vscale.isHorizontal(); + var ruler = me._ruler || me.getRuler(); + var vpixels = me.calculateBarValuePixels(me.index, index, options); + var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options); + + model.horizontal = horizontal; + model.base = reset ? base : vpixels.base; + model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; + model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; + model.height = horizontal ? ipixels.size : undefined; + model.width = horizontal ? undefined : ipixels.size; + }, + + /** + * Returns the stacks based on groups and bar visibility. + * @param {number} [last] - The dataset index + * @returns {string[]} The list of stack IDs + * @private + */ + _getStacks: function(last) { + var me = this; + var scale = me._getIndexScale(); + var metasets = scale._getMatchingVisibleMetas(me._type); + var stacked = scale.options.stacked; + var ilen = metasets.length; + var stacks = []; + var i, meta; + + for (i = 0; i < ilen; ++i) { + meta = metasets[i]; + // stacked | meta.stack + // | found | not found | undefined + // false | x | x | x + // true | | x | + // undefined | | x | x + if (stacked === false || stacks.indexOf(meta.stack) === -1 || + (stacked === undefined && meta.stack === undefined)) { + stacks.push(meta.stack); + } + if (meta.index === last) { + break; + } + } + + return stacks; + }, + + /** + * Returns the effective number of stacks based on groups and bar visibility. + * @private + */ + getStackCount: function() { + return this._getStacks().length; + }, + + /** + * Returns the stack index for the given dataset based on groups and bar visibility. + * @param {number} [datasetIndex] - The dataset index + * @param {string} [name] - The stack name to find + * @returns {number} The stack index + * @private + */ + getStackIndex: function(datasetIndex, name) { + var stacks = this._getStacks(datasetIndex); + var index = (name !== undefined) + ? stacks.indexOf(name) + : -1; // indexOf returns -1 if element is not present + + return (index === -1) + ? stacks.length - 1 + : index; + }, + + /** + * @private + */ + getRuler: function() { + var me = this; + var scale = me._getIndexScale(); + var pixels = []; + var i, ilen; + + for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { + pixels.push(scale.getPixelForValue(null, i, me.index)); + } + + return { + pixels: pixels, + start: scale._startPixel, + end: scale._endPixel, + stackCount: me.getStackCount(), + scale: scale + }; + }, + + /** + * Note: pixel values are not clamped to the scale area. + * @private + */ + calculateBarValuePixels: function(datasetIndex, index, options) { + var me = this; + var chart = me.chart; + var scale = me._getValueScale(); + var isHorizontal = scale.isHorizontal(); + var datasets = chart.data.datasets; + var metasets = scale._getMatchingVisibleMetas(me._type); + var value = scale._parseValue(datasets[datasetIndex].data[index]); + var minBarLength = options.minBarLength; + var stacked = scale.options.stacked; + var stack = me.getMeta().stack; + var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; + var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; + var ilen = metasets.length; + var i, imeta, ivalue, base, head, size, stackLength; + + if (stacked || (stacked === undefined && stack !== undefined)) { + for (i = 0; i < ilen; ++i) { + imeta = metasets[i]; + + if (imeta.index === datasetIndex) { + break; + } + + if (imeta.stack === stack) { + stackLength = scale._parseValue(datasets[imeta.index].data[index]); + ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; + + if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { + start += ivalue; + } + } + } + } + + base = scale.getPixelForValue(start); + head = scale.getPixelForValue(start + length); + size = head - base; + + if (minBarLength !== undefined && Math.abs(size) < minBarLength) { + size = minBarLength; + if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { + head = base - minBarLength; + } else { + head = base + minBarLength; + } + } + + return { + size: size, + base: base, + head: head, + center: head + size / 2 + }; + }, + + /** + * @private + */ + calculateBarIndexPixels: function(datasetIndex, index, ruler, options) { + var me = this; + var range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options) + : computeFitCategoryTraits(index, ruler, options); + + var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); + var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + var size = Math.min( + valueOrDefault$3(options.maxBarThickness, Infinity), + range.chunk * range.ratio); + + return { + base: center - size / 2, + head: center + size / 2, + center: center, + size: size + }; + }, + + draw: function() { + var me = this; + var chart = me.chart; + var scale = me._getValueScale(); + var rects = me.getMeta().data; + var dataset = me.getDataset(); + var ilen = rects.length; + var i = 0; + + helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); + + for (; i < ilen; ++i) { + var val = scale._parseValue(dataset.data[i]); + if (!isNaN(val.min) && !isNaN(val.max)) { + rects[i].draw(); + } + } + + helpers$1.canvas.unclipArea(chart.ctx); + }, + + /** + * @private + */ + _resolveDataElementOptions: function() { + var me = this; + var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments)); + var indexOpts = me._getIndexScale().options; + var valueOpts = me._getValueScale().options; + + values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage); + values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness); + values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage); + values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness); + values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength); + + return values; + } + +}); + +var valueOrDefault$4 = helpers$1.valueOrDefault; +var resolve$1 = helpers$1.options.resolve; + +core_defaults._set('bubble', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + var dataPoint = data.datasets[item.datasetIndex].data[item.index]; + return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; + } + } + } +}); + +var controller_bubble = core_datasetController.extend({ + /** + * @protected + */ + dataElementType: elements.Point, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle', + 'rotation' + ], + + /** + * @protected + */ + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var points = meta.data; + + // Update Points + helpers$1.each(points, function(point, index) { + me.updateElement(point, index, reset); + }); + }, + + /** + * @protected + */ + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var options = me._resolveDataElementOptions(point, index); + var data = me.getDataset().data[index]; + var dsIndex = me.index; + + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); + + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = dsIndex; + point._index = index; + point._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + hitRadius: options.hitRadius, + pointStyle: options.pointStyle, + rotation: options.rotation, + radius: reset ? 0 : options.radius, + skip: custom.skip || isNaN(x) || isNaN(y), + x: x, + y: y, + }; + + point.pivot(); + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); + model.radius = options.radius + options.hoverRadius; + }, + + /** + * @private + */ + _resolveDataElementOptions: function(point, index) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var custom = point.custom || {}; + var data = dataset.data[index] || {}; + var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments); + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + // In case values were cached (and thus frozen), we need to clone the values + if (me._cachedDataOpts === values) { + values = helpers$1.extend({}, values); + } + + // Custom radius resolution + values.radius = resolve$1([ + custom.radius, + data.r, + me._config.radius, + chart.options.elements.point.radius + ], context, index); + + return values; + } +}); + +var valueOrDefault$5 = helpers$1.valueOrDefault; + +var PI$1 = Math.PI; +var DOUBLE_PI$1 = PI$1 * 2; +var HALF_PI$1 = PI$1 / 2; + +core_defaults._set('doughnut', { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var list = document.createElement('ul'); + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + if (datasets.length) { + for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; + if (labels[i]) { + listItem.appendChild(document.createTextNode(labels[i])); + } + } + } + + return list.outerHTML; + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var style = meta.controller.getStyle(i); + + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: -HALF_PI$1, + + // The total circumference of the chart. + circumference: DOUBLE_PI$1, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + var dataLabel = data.labels[tooltipItem.index]; + var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + + if (helpers$1.isArray(dataLabel)) { + // show value on first line of multiline label + // need to clone because we are changing the value + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + + return dataLabel; + } + } + } +}); + +var controller_doughnut = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly + getRingIndex: function(datasetIndex) { + var ringIndex = 0; + + for (var j = 0; j < datasetIndex; ++j) { + if (this.chart.isDatasetVisible(j)) { + ++ringIndex; + } + } + + return ringIndex; + }, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var ratioX = 1; + var ratioY = 1; + var offsetX = 0; + var offsetY = 0; + var meta = me.getMeta(); + var arcs = meta.data; + var cutout = opts.cutoutPercentage / 100 || 0; + var circumference = opts.circumference; + var chartWeight = me._getRingWeight(me.index); + var maxWidth, maxHeight, i, ilen; + + // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc + if (circumference < DOUBLE_PI$1) { + var startAngle = opts.rotation % DOUBLE_PI$1; + startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0; + var endAngle = startAngle + circumference; + var startX = Math.cos(startAngle); + var startY = Math.sin(startAngle); + var endX = Math.cos(endAngle); + var endY = Math.sin(endAngle); + var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1; + var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1; + var contains180 = startAngle === -PI$1 || endAngle >= PI$1; + var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1; + var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout); + var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout); + var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout); + var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout); + ratioX = (maxX - minX) / 2; + ratioY = (maxY - minY) / 2; + offsetX = -(maxX + minX) / 2; + offsetY = -(maxY + minY) / 2; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); + } + + chart.borderWidth = me.getMaxBorderWidth(); + maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX; + maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY; + chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); + chart.innerRadius = Math.max(chart.outerRadius * cutout, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); + chart.offsetX = offsetX * chart.outerRadius; + chart.offsetY = offsetY * chart.outerRadius; + + meta.total = me.calculateTotal(); + + me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); + me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + me.updateElement(arcs[i], i, reset); + } + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var animationOpts = opts.animation; + var centerX = (chartArea.left + chartArea.right) / 2; + var centerY = (chartArea.top + chartArea.bottom) / 2; + var startAngle = opts.rotation; // non reset case handled later + var endAngle = opts.rotation; // non reset case handled later + var dataset = me.getDataset(); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1); + var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; + var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX + chart.offsetX, + y: centerY + chart.offsetY, + startAngle: startAngle, + endAngle: endAngle, + circumference: circumference, + outerRadius: outerRadius, + innerRadius: innerRadius, + label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) + } + }); + + var model = arc._model; + + // Set correct angles if not resetting + if (!reset || !animationOpts.animateRotate) { + if (index === 0) { + model.startAngle = opts.rotation; + } else { + model.startAngle = me.getMeta().data[index - 1]._model.endAngle; + } + + model.endAngle = model.startAngle + model.circumference; + } + + arc.pivot(); + }, + + calculateTotal: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var total = 0; + var value; + + helpers$1.each(meta.data, function(element, index) { + value = dataset.data[index]; + if (!isNaN(value) && !element.hidden) { + total += Math.abs(value); + } + }); + + /* if (total === 0) { + total = NaN; + }*/ + + return total; + }, + + calculateCircumference: function(value) { + var total = this.getMeta().total; + if (total > 0 && !isNaN(value)) { + return DOUBLE_PI$1 * (Math.abs(value) / total); + } + return 0; + }, + + // gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function(arcs) { + var me = this; + var max = 0; + var chart = me.chart; + var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; + + if (!arcs) { + // Find the outmost visible dataset + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + arcs = meta.data; + if (i !== me.index) { + controller = meta.controller; + } + break; + } + } + } + + if (!arcs) { + return 0; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arc = arcs[i]; + if (controller) { + controller._configure(); + options = controller._resolveDataElementOptions(arc, i); + } else { + options = arc._options; + } + if (options.borderAlign !== 'inner') { + borderWidth = options.borderWidth; + hoverWidth = options.hoverBorderWidth; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + } + return max; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly + * @private + */ + _getRingWeightOffset: function(datasetIndex) { + var ringWeightOffset = 0; + + for (var i = 0; i < datasetIndex; ++i) { + if (this.chart.isDatasetVisible(i)) { + ringWeightOffset += this._getRingWeight(i); + } + } + + return ringWeightOffset; + }, + + /** + * @private + */ + _getRingWeight: function(dataSetIndex) { + return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0); + }, + + /** + * Returns the sum of all visibile data set weights. This value can be 0. + * @private + */ + _getVisibleDatasetWeightTotal: function() { + return this._getRingWeightOffset(this.chart.data.datasets.length); + } +}); + +core_defaults._set('horizontalBar', { + hover: { + mode: 'index', + axis: 'y' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + + yAxes: [{ + type: 'category', + position: 'left', + offset: true, + gridLines: { + offsetGridLines: true + } + }] + }, + + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + + tooltips: { + mode: 'index', + axis: 'y' + } +}); + +core_defaults._set('global', { + datasets: { + horizontalBar: { + categoryPercentage: 0.8, + barPercentage: 0.9 + } + } +}); + +var controller_horizontalBar = controller_bar.extend({ + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().yAxisID; + } +}); + +var valueOrDefault$6 = helpers$1.valueOrDefault; +var resolve$2 = helpers$1.options.resolve; +var isPointInArea = helpers$1.canvas._isPointInArea; + +core_defaults._set('line', { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } +}); + +function scaleClip(scale, halfBorderWidth) { + var tickOpts = scale && scale.options.ticks || {}; + var reverse = tickOpts.reverse; + var min = tickOpts.min === undefined ? halfBorderWidth : 0; + var max = tickOpts.max === undefined ? halfBorderWidth : 0; + return { + start: reverse ? max : min, + end: reverse ? min : max + }; +} + +function defaultClip(xScale, yScale, borderWidth) { + var halfBorderWidth = borderWidth / 2; + var x = scaleClip(xScale, halfBorderWidth); + var y = scaleClip(yScale, halfBorderWidth); + + return { + top: y.end, + right: x.end, + bottom: y.start, + left: x.start + }; +} + +function toClip(value) { + var t, r, b, l; + + if (helpers$1.isObject(value)) { + t = value.top; + r = value.right; + b = value.bottom; + l = value.left; + } else { + t = r = b = l = value; + } + + return { + top: t, + right: r, + bottom: b, + left: l + }; +} + + +var controller_line = core_datasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth', + 'cubicInterpolationMode', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var options = me.chart.options; + var config = me._config; + var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines); + var i, ilen; + + me._xScale = me.getScaleForId(meta.xAxisID); + me._yScale = me.getScaleForId(meta.yAxisID); + + // Update Line + if (showLine) { + // Compatibility: If the properties are defined with only the old name, use those values + if (config.tension !== undefined && config.lineTension === undefined) { + config.lineTension = config.tension; + } + + // Utility + line._scale = me._yScale; + line._datasetIndex = me.index; + // Data + line._children = points; + // Model + line._model = me._resolveDatasetElementOptions(line); + + line.pivot(); + } + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + if (showLine && line._model.tension !== 0) { + me.updateBezierControlPoints(); + } + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var dataset = me.getDataset(); + var datasetIndex = me.index; + var value = dataset.data[index]; + var xScale = me._xScale; + var yScale = me._yScale; + var lineModel = meta.dataset._model; + var x, y; + + var options = me._resolveDataElementOptions(point, index); + + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); + y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); + + // Utility + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = datasetIndex; + point._index = index; + + // Desired view properties + point._model = { + x: x, + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), + steppedLine: lineModel ? lineModel.steppedLine : false, + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function(element) { + var me = this; + var config = me._config; + var custom = element.custom || {}; + var options = me.chart.options; + var lineOptions = options.elements.line; + var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); + + // The default behavior of lines is to break at null values, according + // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 + // This option gives lines the ability to span gaps + values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps); + values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension); + values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]); + values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth))); + + return values; + }, + + calculatePointY: function(value, index, datasetIndex) { + var me = this; + var chart = me.chart; + var yScale = me._yScale; + var sumPos = 0; + var sumNeg = 0; + var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen; + + if (yScale.options.stacked) { + rightValue = +yScale.getRightValue(value); + metasets = chart._getSortedVisibleDatasetMetas(); + ilen = metasets.length; + + for (i = 0; i < ilen; ++i) { + dsMeta = metasets[i]; + if (dsMeta.index === datasetIndex) { + break; + } + + ds = chart.data.datasets[dsMeta.index]; + if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) { + stackedRightValue = +yScale.getRightValue(ds.data[index]); + if (stackedRightValue < 0) { + sumNeg += stackedRightValue || 0; + } else { + sumPos += stackedRightValue || 0; + } + } + } + + if (rightValue < 0) { + return yScale.getPixelForValue(sumNeg + rightValue); + } + return yScale.getPixelForValue(sumPos + rightValue); + } + return yScale.getPixelForValue(value); + }, + + updateBezierControlPoints: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var lineModel = meta.dataset._model; + var area = chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (lineModel.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + if (lineModel.cubicInterpolationMode === 'monotone') { + helpers$1.splineCurveMonotone(points); + } else { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i)._model, + model, + helpers$1.nextItem(points, i)._model, + lineModel.tension + ); + model.controlPointPreviousX = controlPoints.previous.x; + model.controlPointPreviousY = controlPoints.previous.y; + model.controlPointNextX = controlPoints.next.x; + model.controlPointNextY = controlPoints.next.y; + } + } + + if (chart.options.elements.line.capBezierPoints) { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + if (isPointInArea(model, area)) { + if (i > 0 && isPointInArea(points[i - 1]._model, area)) { + model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); + model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); + } + if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { + model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); + model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); + } + } + } + } + }, + + draw: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var points = meta.data || []; + var area = chart.chartArea; + var canvas = chart.canvas; + var i = 0; + var ilen = points.length; + var clip; + + if (me._showLine) { + clip = meta.dataset._model.clip; + + helpers$1.canvas.clipArea(chart.ctx, { + left: clip.left === false ? 0 : area.left - clip.left, + right: clip.right === false ? canvas.width : area.right + clip.right, + top: clip.top === false ? 0 : area.top - clip.top, + bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom + }); + + meta.dataset.draw(); + + helpers$1.canvas.unclipArea(chart.ctx); + } + + // Draw the points + for (; i < ilen; ++i) { + points[i].draw(area); + } + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$6(options.hoverRadius, options.radius); + }, +}); + +var resolve$3 = helpers$1.options.resolve; + +core_defaults._set('polarArea', { + scale: { + type: 'radialLinear', + angleLines: { + display: false + }, + gridLines: { + circular: true + }, + pointLabels: { + display: false + }, + ticks: { + beginAtZero: true + } + }, + + // Boolean - Whether to animate the rotation of the chart + animation: { + animateRotate: true, + animateScale: true + }, + + startAngle: -0.5 * Math.PI, + legendCallback: function(chart) { + var list = document.createElement('ul'); + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + if (datasets.length) { + for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; + if (labels[i]) { + listItem.appendChild(document.createTextNode(labels[i])); + } + } + } + + return list.outerHTML; + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var style = meta.controller.getStyle(i); + + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(item, data) { + return data.labels[item.index] + ': ' + item.yLabel; + } + } + } +}); + +var controller_polarArea = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + + /** + * @private + */ + _getIndexScaleId: function() { + return this.chart.scale.id; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.chart.scale.id; + }, + + update: function(reset) { + var me = this; + var dataset = me.getDataset(); + var meta = me.getMeta(); + var start = me.chart.options.startAngle || 0; + var starts = me._starts = []; + var angles = me._angles = []; + var arcs = meta.data; + var i, ilen, angle; + + me._updateRadius(); + + meta.count = me.countVisibleElements(); + + for (i = 0, ilen = dataset.data.length; i < ilen; i++) { + starts[i] = start; + angle = me._computeAngle(i); + angles[i] = angle; + start += angle; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); + me.updateElement(arcs[i], i, reset); + } + }, + + /** + * @private + */ + _updateRadius: function() { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + + chart.outerRadius = Math.max(minSize / 2, 0); + chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); + me.innerRadius = me.outerRadius - chart.radiusLength; + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var opts = chart.options; + var animationOpts = opts.animation; + var scale = chart.scale; + var labels = chart.data.labels; + + var centerX = scale.xCenter; + var centerY = scale.yCenter; + + // var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; + var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var startAngle = me._starts[index]; + var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); + + var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius: reset ? resetRadius : distance, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, + label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) + } + }); + + arc.pivot(); + }, + + countVisibleElements: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var count = 0; + + helpers$1.each(meta.data, function(element, index) { + if (!isNaN(dataset.data[index]) && !element.hidden) { + count++; + } + }); + + return count; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + var valueOrDefault = helpers$1.valueOrDefault; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * @private + */ + _computeAngle: function(index) { + var me = this; + var count = this.getMeta().count; + var dataset = me.getDataset(); + var meta = me.getMeta(); + + if (isNaN(dataset.data[index]) || meta.data[index].hidden) { + return 0; + } + + // Scriptable options + var context = { + chart: me.chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + return resolve$3([ + me.chart.options.elements.arc.angle, + (2 * Math.PI) / count + ], context, index); + } +}); + +core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); +core_defaults._set('pie', { + cutoutPercentage: 0 +}); + +// Pie charts are Doughnut chart with different defaults +var controller_pie = controller_doughnut; + +var valueOrDefault$7 = helpers$1.valueOrDefault; + +core_defaults._set('radar', { + spanGaps: false, + scale: { + type: 'radialLinear' + }, + elements: { + line: { + fill: 'start', + tension: 0 // no bezier in radar + } + } +}); + +var controller_radar = core_datasetController.extend({ + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderCapStyle', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.chart.scale.id; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.chart.scale.id; + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var scale = me.chart.scale; + var config = me._config; + var i, ilen; + + // Compatibility: If the properties are defined with only the old name, use those values + if (config.tension !== undefined && config.lineTension === undefined) { + config.lineTension = config.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + line._loop = true; + // Model + line._model = me._resolveDatasetElementOptions(line); + + line.pivot(); + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + // Update bezier control points + me.updateBezierControlPoints(); + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var custom = point.custom || {}; + var dataset = me.getDataset(); + var scale = me.chart.scale; + var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); + var options = me._resolveDataElementOptions(point, index); + var lineModel = me.getMeta().dataset._model; + var x = reset ? scale.xCenter : pointPosition.x; + var y = reset ? scale.yCenter : pointPosition.y; + + // Utility + point._scale = scale; + point._options = options; + point._datasetIndex = me.index; + point._index = index; + + // Desired view properties + point._model = { + x: x, // value not used in dataset scale, but we want a consistent API between scales + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0), + + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function() { + var me = this; + var config = me._config; + var options = me.chart.options; + var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); + + values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps); + values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension); + + return values; + }, + + updateBezierControlPoints: function() { + var me = this; + var meta = me.getMeta(); + var area = me.chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (meta.dataset._model.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i, true)._model, + model, + helpers$1.nextItem(points, i, true)._model, + model.tension + ); + + // Prevent the bezier going outside of the bounds of the graph + model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); + model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); + model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); + model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); + } + }, + + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$7(options.hoverRadius, options.radius); + } +}); + +core_defaults._set('scatter', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + id: 'x-axis-1', // need an ID so datasets can reference the scale + type: 'linear', // scatter should not use a category axis + position: 'bottom' + }], + yAxes: [{ + id: 'y-axis-1', + type: 'linear', + position: 'left' + }] + }, + + tooltips: { + callbacks: { + title: function() { + return ''; // doesn't make sense for scatter since data are formatted as a point + }, + label: function(item) { + return '(' + item.xLabel + ', ' + item.yLabel + ')'; + } + } + } +}); + +core_defaults._set('global', { + datasets: { + scatter: { + showLine: false + } + } +}); + +// Scatter charts use line controllers +var controller_scatter = controller_line; + +// NOTE export a map in which the key represents the controller type, not +// the class, and so must be CamelCase in order to be correctly retrieved +// by the controller in core.controller.js (`controllers[meta.type]`). + +var controllers = { + bar: controller_bar, + bubble: controller_bubble, + doughnut: controller_doughnut, + horizontalBar: controller_horizontalBar, + line: controller_line, + polarArea: controller_polarArea, + pie: controller_pie, + radar: controller_radar, + scatter: controller_scatter +}; + +/** + * Helper function to get relative position for an event + * @param {Event|IEvent} event - The event to get the position for + * @param {Chart} chart - The chart + * @returns {object} the event position + */ +function getRelativePosition(e, chart) { + if (e.native) { + return { + x: e.x, + y: e.y + }; + } + + return helpers$1.getRelativePosition(e, chart); +} + +/** + * Helper function to traverse all of the visible elements in the chart + * @param {Chart} chart - the chart + * @param {function} handler - the callback to execute for each visible item + */ +function parseVisibleItems(chart, handler) { + var metasets = chart._getSortedVisibleDatasetMetas(); + var metadata, i, j, ilen, jlen, element; + + for (i = 0, ilen = metasets.length; i < ilen; ++i) { + metadata = metasets[i].data; + for (j = 0, jlen = metadata.length; j < jlen; ++j) { + element = metadata[j]; + if (!element._view.skip) { + handler(element); + } + } + } +} + +/** + * Helper function to get the items that intersect the event position + * @param {ChartElement[]} items - elements to filter + * @param {object} position - the point to be nearest to + * @return {ChartElement[]} the nearest items + */ +function getIntersectItems(chart, position) { + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + } + }); + + return elements; +} + +/** + * Helper function to get the items nearest to the event position considering all visible items in teh chart + * @param {Chart} chart - the chart to look at elements from + * @param {object} position - the point to be nearest to + * @param {boolean} intersect - if true, only consider items that intersect the position + * @param {function} distanceMetric - function to provide the distance between points + * @return {ChartElement[]} the nearest items + */ +function getNearestItems(chart, position, intersect, distanceMetric) { + var minDistance = Number.POSITIVE_INFINITY; + var nearestItems = []; + + parseVisibleItems(chart, function(element) { + if (intersect && !element.inRange(position.x, position.y)) { + return; + } + + var center = element.getCenterPoint(); + var distance = distanceMetric(position, center); + if (distance < minDistance) { + nearestItems = [element]; + minDistance = distance; + } else if (distance === minDistance) { + // Can have multiple items at the same distance in which case we sort by size + nearestItems.push(element); + } + }); + + return nearestItems; +} + +/** + * Get a distance metric function for two points based on the + * axis mode setting + * @param {string} axis - the axis mode. x|y|xy + */ +function getDistanceMetricForAxis(axis) { + var useX = axis.indexOf('x') !== -1; + var useY = axis.indexOf('y') !== -1; + + return function(pt1, pt2) { + var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; +} + +function indexMode(chart, e, options) { + var position = getRelativePosition(e, chart); + // Default axis for index mode is 'x' to match old behaviour + options.axis = options.axis || 'x'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + var elements = []; + + if (!items.length) { + return []; + } + + chart._getSortedVisibleDatasetMetas().forEach(function(meta) { + var element = meta.data[items[0]._index]; + + // don't count items that are skipped (null data) + if (element && !element._view.skip) { + elements.push(element); + } + }); + + return elements; +} + +/** + * @interface IInteractionOptions + */ +/** + * If true, only consider items that intersect the point + * @name IInterfaceOptions#boolean + * @type Boolean + */ + +/** + * Contains interaction related functions + * @namespace Chart.Interaction + */ +var core_interaction = { + // Helper function for different modes + modes: { + single: function(chart, e) { + var position = getRelativePosition(e, chart); + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + return elements; + } + }); + + return elements.slice(0, 1); + }, + + /** + * @function Chart.Interaction.modes.label + * @deprecated since version 2.4.0 + * @todo remove at version 3 + * @private + */ + label: indexMode, + + /** + * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item + * @function Chart.Interaction.modes.index + * @since v2.4.0 + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + index: indexMode, + + /** + * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect is false, we find the nearest item and return the items in that dataset + * @function Chart.Interaction.modes.dataset + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + dataset: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + + if (items.length > 0) { + items = chart.getDatasetMeta(items[0]._datasetIndex).data; + } + + return items; + }, + + /** + * @function Chart.Interaction.modes.x-axis + * @deprecated since version 2.4.0. Use index mode and intersect == true + * @todo remove at version 3 + * @private + */ + 'x-axis': function(chart, e) { + return indexMode(chart, e, {intersect: false}); + }, + + /** + * Point mode returns all elements that hit test based on the event position + * of the event + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + point: function(chart, e) { + var position = getRelativePosition(e, chart); + return getIntersectItems(chart, position); + }, + + /** + * nearest mode returns the element closest to the point + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + nearest: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + return getNearestItems(chart, position, options.intersect, distanceMetric); + }, + + /** + * x mode returns the elements that hit-test at the current x coordinate + * @function Chart.Interaction.modes.x + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + x: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inXRange(position.x)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + }, + + /** + * y mode returns the elements that hit-test at the current y coordinate + * @function Chart.Interaction.modes.y + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + y: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inYRange(position.y)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + } + } +}; + +var extend = helpers$1.extend; + +function filterByPosition(array, position) { + return helpers$1.where(array, function(v) { + return v.pos === position; + }); +} + +function sortByWeight(array, reverse) { + return array.sort(function(a, b) { + var v0 = reverse ? b : a; + var v1 = reverse ? a : b; + return v0.weight === v1.weight ? + v0.index - v1.index : + v0.weight - v1.weight; + }); +} + +function wrapBoxes(boxes) { + var layoutBoxes = []; + var i, ilen, box; + + for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) { + box = boxes[i]; + layoutBoxes.push({ + index: i, + box: box, + pos: box.position, + horizontal: box.isHorizontal(), + weight: box.weight + }); + } + return layoutBoxes; +} + +function setLayoutDims(layouts, params) { + var i, ilen, layout; + for (i = 0, ilen = layouts.length; i < ilen; ++i) { + layout = layouts[i]; + // store width used instead of chartArea.w in fitBoxes + layout.width = layout.horizontal + ? layout.box.fullWidth && params.availableWidth + : params.vBoxMaxWidth; + // store height used instead of chartArea.h in fitBoxes + layout.height = layout.horizontal && params.hBoxMaxHeight; + } +} + +function buildLayoutBoxes(boxes) { + var layoutBoxes = wrapBoxes(boxes); + var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true); + var right = sortByWeight(filterByPosition(layoutBoxes, 'right')); + var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true); + var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom')); + + return { + leftAndTop: left.concat(top), + rightAndBottom: right.concat(bottom), + chartArea: filterByPosition(layoutBoxes, 'chartArea'), + vertical: left.concat(right), + horizontal: top.concat(bottom) + }; +} + +function getCombinedMax(maxPadding, chartArea, a, b) { + return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]); +} + +function updateDims(chartArea, params, layout) { + var box = layout.box; + var maxPadding = chartArea.maxPadding; + var newWidth, newHeight; + + if (layout.size) { + // this layout was already counted for, lets first reduce old size + chartArea[layout.pos] -= layout.size; + } + layout.size = layout.horizontal ? box.height : box.width; + chartArea[layout.pos] += layout.size; + + if (box.getPadding) { + var boxPadding = box.getPadding(); + maxPadding.top = Math.max(maxPadding.top, boxPadding.top); + maxPadding.left = Math.max(maxPadding.left, boxPadding.left); + maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom); + maxPadding.right = Math.max(maxPadding.right, boxPadding.right); + } + + newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'); + newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'); + + if (newWidth !== chartArea.w || newHeight !== chartArea.h) { + chartArea.w = newWidth; + chartArea.h = newHeight; + + // return true if chart area changed in layout's direction + return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h; + } +} + +function handleMaxPadding(chartArea) { + var maxPadding = chartArea.maxPadding; + + function updatePos(pos) { + var change = Math.max(maxPadding[pos] - chartArea[pos], 0); + chartArea[pos] += change; + return change; + } + chartArea.y += updatePos('top'); + chartArea.x += updatePos('left'); + updatePos('right'); + updatePos('bottom'); +} + +function getMargins(horizontal, chartArea) { + var maxPadding = chartArea.maxPadding; + + function marginForPositions(positions) { + var margin = {left: 0, top: 0, right: 0, bottom: 0}; + positions.forEach(function(pos) { + margin[pos] = Math.max(chartArea[pos], maxPadding[pos]); + }); + return margin; + } + + return horizontal + ? marginForPositions(['left', 'right']) + : marginForPositions(['top', 'bottom']); +} + +function fitBoxes(boxes, chartArea, params) { + var refitBoxes = []; + var i, ilen, layout, box, refit, changed; + + for (i = 0, ilen = boxes.length; i < ilen; ++i) { + layout = boxes[i]; + box = layout.box; + + box.update( + layout.width || chartArea.w, + layout.height || chartArea.h, + getMargins(layout.horizontal, chartArea) + ); + if (updateDims(chartArea, params, layout)) { + changed = true; + if (refitBoxes.length) { + // Dimensions changed and there were non full width boxes before this + // -> we have to refit those + refit = true; + } + } + if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case + refitBoxes.push(layout); + } + } + + return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed; +} + +function placeBoxes(boxes, chartArea, params) { + var userPadding = params.padding; + var x = chartArea.x; + var y = chartArea.y; + var i, ilen, layout, box; + + for (i = 0, ilen = boxes.length; i < ilen; ++i) { + layout = boxes[i]; + box = layout.box; + if (layout.horizontal) { + box.left = box.fullWidth ? userPadding.left : chartArea.left; + box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w; + box.top = y; + box.bottom = y + box.height; + box.width = box.right - box.left; + y = box.bottom; + } else { + box.left = x; + box.right = x + box.width; + box.top = chartArea.top; + box.bottom = chartArea.top + chartArea.h; + box.height = box.bottom - box.top; + x = box.right; + } + } + + chartArea.x = x; + chartArea.y = y; +} + +core_defaults._set('global', { + layout: { + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } +}); + +/** + * @interface ILayoutItem + * @prop {string} position - The position of the item in the chart layout. Possible values are + * 'left', 'top', 'right', 'bottom', and 'chartArea' + * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area + * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down + * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) + * @prop {function} update - Takes two parameters: width and height. Returns size of item + * @prop {function} getPadding - Returns an object with padding on the edges + * @prop {number} width - Width of item. Must be valid after update() + * @prop {number} height - Height of item. Must be valid after update() + * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update + * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update + * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update + * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update + */ + +// The layout service is very self explanatory. It's responsible for the layout within a chart. +// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need +// It is this service's responsibility of carrying out that layout. +var core_layouts = { + defaults: {}, + + /** + * Register a box to a chart. + * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. + * @param {Chart} chart - the chart to use + * @param {ILayoutItem} item - the item to add to be layed out + */ + addBox: function(chart, item) { + if (!chart.boxes) { + chart.boxes = []; + } + + // initialize item with default values + item.fullWidth = item.fullWidth || false; + item.position = item.position || 'top'; + item.weight = item.weight || 0; + item._layers = item._layers || function() { + return [{ + z: 0, + draw: function() { + item.draw.apply(item, arguments); + } + }]; + }; + + chart.boxes.push(item); + }, + + /** + * Remove a layoutItem from a chart + * @param {Chart} chart - the chart to remove the box from + * @param {ILayoutItem} layoutItem - the item to remove from the layout + */ + removeBox: function(chart, layoutItem) { + var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 1); + } + }, + + /** + * Sets (or updates) options on the given `item`. + * @param {Chart} chart - the chart in which the item lives (or will be added to) + * @param {ILayoutItem} item - the item to configure with the given options + * @param {object} options - the new item options. + */ + configure: function(chart, item, options) { + var props = ['fullWidth', 'position', 'weight']; + var ilen = props.length; + var i = 0; + var prop; + + for (; i < ilen; ++i) { + prop = props[i]; + if (options.hasOwnProperty(prop)) { + item[prop] = options[prop]; + } + } + }, + + /** + * Fits boxes of the given chart into the given size by having each box measure itself + * then running a fitting algorithm + * @param {Chart} chart - the chart + * @param {number} width - the width to fit into + * @param {number} height - the height to fit into + */ + update: function(chart, width, height) { + if (!chart) { + return; + } + + var layoutOptions = chart.options.layout || {}; + var padding = helpers$1.options.toPadding(layoutOptions.padding); + + var availableWidth = width - padding.width; + var availableHeight = height - padding.height; + var boxes = buildLayoutBoxes(chart.boxes); + var verticalBoxes = boxes.vertical; + var horizontalBoxes = boxes.horizontal; + + // Essentially we now have any number of boxes on each of the 4 sides. + // Our canvas looks like the following. + // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and + // B1 is the bottom axis + // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays + // These locations are single-box locations only, when trying to register a chartArea location that is already taken, + // an error will be thrown. + // + // |----------------------------------------------------| + // | T1 (Full Width) | + // |----------------------------------------------------| + // | | | T2 | | + // | |----|-------------------------------------|----| + // | | | C1 | | C2 | | + // | | |----| |----| | + // | | | | | + // | L1 | L2 | ChartArea (C0) | R1 | + // | | | | | + // | | |----| |----| | + // | | | C3 | | C4 | | + // | |----|-------------------------------------|----| + // | | | B1 | | + // |----------------------------------------------------| + // | B2 (Full Width) | + // |----------------------------------------------------| + // + + var params = Object.freeze({ + outerWidth: width, + outerHeight: height, + padding: padding, + availableWidth: availableWidth, + vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length, + hBoxMaxHeight: availableHeight / 2 + }); + var chartArea = extend({ + maxPadding: extend({}, padding), + w: availableWidth, + h: availableHeight, + x: padding.left, + y: padding.top + }, padding); + + setLayoutDims(verticalBoxes.concat(horizontalBoxes), params); + + // First fit vertical boxes + fitBoxes(verticalBoxes, chartArea, params); + + // Then fit horizontal boxes + if (fitBoxes(horizontalBoxes, chartArea, params)) { + // if the area changed, re-fit vertical boxes + fitBoxes(verticalBoxes, chartArea, params); + } + + handleMaxPadding(chartArea); + + // Finally place the boxes to correct coordinates + placeBoxes(boxes.leftAndTop, chartArea, params); + + // Move to opposite side of chart + chartArea.x += chartArea.w; + chartArea.y += chartArea.h; + + placeBoxes(boxes.rightAndBottom, chartArea, params); + + chart.chartArea = { + left: chartArea.left, + top: chartArea.top, + right: chartArea.left + chartArea.w, + bottom: chartArea.top + chartArea.h + }; + + // Finally update boxes in chartArea (radial scale for example) + helpers$1.each(boxes.chartArea, function(layout) { + var box = layout.box; + extend(box, chart.chartArea); + box.update(chartArea.w, chartArea.h); + }); + } +}; + +/** + * Platform fallback implementation (minimal). + * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 + */ + +var platform_basic = { + acquireContext: function(item) { + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + return item && item.getContext('2d') || null; + } +}; + +var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; + +var platform_dom$1 = /*#__PURE__*/Object.freeze({ +__proto__: null, +'default': platform_dom +}); + +var stylesheet = getCjsExportFromNamespace(platform_dom$1); + +var EXPANDO_KEY = '$chartjs'; +var CSS_PREFIX = 'chartjs-'; +var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; +var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; +var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; +var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; + +/** + * DOM event types -> Chart.js event types. + * Note: only events with different types are mapped. + * @see https://developer.mozilla.org/en-US/docs/Web/Events + */ +var EVENT_TYPES = { + touchstart: 'mousedown', + touchmove: 'mousemove', + touchend: 'mouseup', + pointerenter: 'mouseenter', + pointerdown: 'mousedown', + pointermove: 'mousemove', + pointerup: 'mouseup', + pointerleave: 'mouseout', + pointerout: 'mouseout' +}; + +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns {number} Size in pixels or undefined if unknown. + */ +function readUsedSize(element, property) { + var value = helpers$1.getStyle(element, property); + var matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? Number(matches[1]) : undefined; +} + +/** + * Initializes the canvas style and render size without modifying the canvas display size, + * since responsiveness is handled by the controller.resize() method. The config is used + * to determine the aspect ratio to apply in case no explicit height has been specified. + */ +function initCanvas(canvas, config) { + var style = canvas.style; + + // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it + // returns null or '' if no explicit value has been set to the canvas attribute. + var renderHeight = canvas.getAttribute('height'); + var renderWidth = canvas.getAttribute('width'); + + // Chart.js modifies some canvas values that we want to restore on destroy + canvas[EXPANDO_KEY] = { + initial: { + height: renderHeight, + width: renderWidth, + style: { + display: style.display, + height: style.height, + width: style.width + } + } + }; + + // Force canvas to display as block to avoid extra space caused by inline + // elements, which would interfere with the responsive resize process. + // https://github.com/chartjs/Chart.js/issues/2538 + style.display = style.display || 'block'; + + if (renderWidth === null || renderWidth === '') { + var displayWidth = readUsedSize(canvas, 'width'); + if (displayWidth !== undefined) { + canvas.width = displayWidth; + } + } + + if (renderHeight === null || renderHeight === '') { + if (canvas.style.height === '') { + // If no explicit render height and style height, let's apply the aspect ratio, + // which one can be specified by the user but also by charts as default option + // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. + canvas.height = canvas.width / (config.options.aspectRatio || 2); + } else { + var displayHeight = readUsedSize(canvas, 'height'); + if (displayWidth !== undefined) { + canvas.height = displayHeight; + } + } + } + + return canvas; +} + +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ +var supportsEventListenerOptions = (function() { + var supports = false; + try { + var options = Object.defineProperty({}, 'passive', { + // eslint-disable-next-line getter-return + get: function() { + supports = true; + } + }); + window.addEventListener('e', null, options); + } catch (e) { + // continue regardless of error + } + return supports; +}()); + +// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. +// https://github.com/chartjs/Chart.js/issues/4287 +var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; + +function addListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} + +function removeListener(node, type, listener) { + node.removeEventListener(type, listener, eventListenerOptions); +} + +function createEvent(type, chart, x, y, nativeEvent) { + return { + type: type, + chart: chart, + native: nativeEvent || null, + x: x !== undefined ? x : null, + y: y !== undefined ? y : null, + }; +} + +function fromNativeEvent(event, chart) { + var type = EVENT_TYPES[event.type] || event.type; + var pos = helpers$1.getRelativePosition(event, chart); + return createEvent(type, chart, pos.x, pos.y, event); +} + +function throttled(fn, thisArg) { + var ticking = false; + var args = []; + + return function() { + args = Array.prototype.slice.call(arguments); + thisArg = thisArg || this; + + if (!ticking) { + ticking = true; + helpers$1.requestAnimFrame.call(window, function() { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} + +function createDiv(cls) { + var el = document.createElement('div'); + el.className = cls || ''; + return el; +} + +// Implementation based on https://github.com/marcj/css-element-queries +function createResizer(handler) { + var maxSize = 1000000; + + // NOTE(SB) Don't use innerHTML because it could be considered unsafe. + // https://github.com/chartjs/Chart.js/issues/5902 + var resizer = createDiv(CSS_SIZE_MONITOR); + var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); + var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); + + expand.appendChild(createDiv()); + shrink.appendChild(createDiv()); + + resizer.appendChild(expand); + resizer.appendChild(shrink); + resizer._reset = function() { + expand.scrollLeft = maxSize; + expand.scrollTop = maxSize; + shrink.scrollLeft = maxSize; + shrink.scrollTop = maxSize; + }; + + var onScroll = function() { + resizer._reset(); + handler(); + }; + + addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); + addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); + + return resizer; +} + +// https://davidwalsh.name/detect-node-insertion +function watchForRender(node, handler) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + var proxy = expando.renderProxy = function(e) { + if (e.animationName === CSS_RENDER_ANIMATION) { + handler(); + } + }; + + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + addListener(node, type, proxy); + }); + + // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class + // is removed then added back immediately (same animation frame?). Accessing the + // `offsetParent` property will force a reflow and re-evaluate the CSS animation. + // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics + // https://github.com/chartjs/Chart.js/issues/4737 + expando.reflow = !!node.offsetParent; + + node.classList.add(CSS_RENDER_MONITOR); +} + +function unwatchForRender(node) { + var expando = node[EXPANDO_KEY] || {}; + var proxy = expando.renderProxy; + + if (proxy) { + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + removeListener(node, type, proxy); + }); + + delete expando.renderProxy; + } + + node.classList.remove(CSS_RENDER_MONITOR); +} + +function addResizeListener(node, listener, chart) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + + // Let's keep track of this added resizer and thus avoid DOM query when removing it. + var resizer = expando.resizer = createResizer(throttled(function() { + if (expando.resizer) { + var container = chart.options.maintainAspectRatio && node.parentNode; + var w = container ? container.clientWidth : 0; + listener(createEvent('resize', chart)); + if (container && container.clientWidth < w && chart.canvas) { + // If the container size shrank during chart resize, let's assume + // scrollbar appeared. So we resize again with the scrollbar visible - + // effectively making chart smaller and the scrollbar hidden again. + // Because we are inside `throttled`, and currently `ticking`, scroll + // events are ignored during this whole 2 resize process. + // If we assumed wrong and something else happened, we are resizing + // twice in a frame (potential performance issue) + listener(createEvent('resize', chart)); + } + } + })); + + // The resizer needs to be attached to the node parent, so we first need to be + // sure that `node` is attached to the DOM before injecting the resizer element. + watchForRender(node, function() { + if (expando.resizer) { + var container = node.parentNode; + if (container && container !== resizer.parentNode) { + container.insertBefore(resizer, container.firstChild); + } + + // The container size might have changed, let's reset the resizer state. + resizer._reset(); + } + }); +} + +function removeResizeListener(node) { + var expando = node[EXPANDO_KEY] || {}; + var resizer = expando.resizer; + + delete expando.resizer; + unwatchForRender(node); + + if (resizer && resizer.parentNode) { + resizer.parentNode.removeChild(resizer); + } +} + +/** + * Injects CSS styles inline if the styles are not already present. + * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>. + * @param {string} css - the CSS to be injected. + */ +function injectCSS(rootNode, css) { + // https://stackoverflow.com/q/3922139 + var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {}); + if (!expando.containsStyles) { + expando.containsStyles = true; + css = '/* Chart.js */\n' + css; + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.appendChild(document.createTextNode(css)); + rootNode.appendChild(style); + } +} + +var platform_dom$2 = { + /** + * When `true`, prevents the automatic injection of the stylesheet required to + * correctly detect when the chart is added to the DOM and then resized. This + * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) + * to be manually imported to make this library compatible with any CSP. + * See https://github.com/chartjs/Chart.js/issues/5208 + */ + disableCSSInjection: false, + + /** + * This property holds whether this platform is enabled for the current environment. + * Currently used by platform.js to select the proper implementation. + * @private + */ + _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', + + /** + * Initializes resources that depend on platform options. + * @param {HTMLCanvasElement} canvas - The Canvas element. + * @private + */ + _ensureLoaded: function(canvas) { + if (!this.disableCSSInjection) { + // If the canvas is in a shadow DOM, then the styles must also be inserted + // into the same shadow DOM. + // https://github.com/chartjs/Chart.js/issues/5763 + var root = canvas.getRootNode ? canvas.getRootNode() : document; + var targetNode = root.host ? root : document.head; + injectCSS(targetNode, stylesheet); + } + }, + + acquireContext: function(item, config) { + if (typeof item === 'string') { + item = document.getElementById(item); + } else if (item.length) { + // Support for array based queries (such as jQuery) + item = item[0]; + } + + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item && item.getContext && item.getContext('2d'); + + // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is + // inside an iframe or when running in a protected environment. We could guess the + // types from their toString() value but let's keep things flexible and assume it's + // a sufficient condition if the item has a context2D which has item as `canvas`. + // https://github.com/chartjs/Chart.js/issues/3887 + // https://github.com/chartjs/Chart.js/issues/4102 + // https://github.com/chartjs/Chart.js/issues/4152 + if (context && context.canvas === item) { + // Load platform resources on first chart creation, to make it possible to + // import the library before setting platform options. + this._ensureLoaded(item); + initCanvas(item, config); + return context; + } + + return null; + }, + + releaseContext: function(context) { + var canvas = context.canvas; + if (!canvas[EXPANDO_KEY]) { + return; + } + + var initial = canvas[EXPANDO_KEY].initial; + ['height', 'width'].forEach(function(prop) { + var value = initial[prop]; + if (helpers$1.isNullOrUndef(value)) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + + helpers$1.each(initial.style || {}, function(value, key) { + canvas.style[key] = value; + }); + + // The canvas render size might have been changed (and thus the state stack discarded), + // we can't use save() and restore() to restore the initial state. So make sure that at + // least the canvas context is reset to the default state by setting the canvas width. + // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html + // eslint-disable-next-line no-self-assign + canvas.width = canvas.width; + + delete canvas[EXPANDO_KEY]; + }, + + addEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + addResizeListener(canvas, listener, chart); + return; + } + + var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); + var proxies = expando.proxies || (expando.proxies = {}); + var proxy = proxies[chart.id + '_' + type] = function(event) { + listener(fromNativeEvent(event, chart)); + }; + + addListener(canvas, type, proxy); + }, + + removeEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + removeResizeListener(canvas); + return; + } + + var expando = listener[EXPANDO_KEY] || {}; + var proxies = expando.proxies || {}; + var proxy = proxies[chart.id + '_' + type]; + if (!proxy) { + return; + } + + removeListener(canvas, type, proxy); + } +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use EventTarget.addEventListener instead. + * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + * @function Chart.helpers.addEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.addEvent = addListener; + +/** + * Provided for backward compatibility, use EventTarget.removeEventListener instead. + * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener + * @function Chart.helpers.removeEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.removeEvent = removeListener; + +// @TODO Make possible to select another platform at build time. +var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; + +/** + * @namespace Chart.platform + * @see https://chartjs.gitbooks.io/proposals/content/Platform.html + * @since 2.4.0 + */ +var platform = helpers$1.extend({ + /** + * @since 2.7.0 + */ + initialize: function() {}, + + /** + * Called at chart construction time, returns a context2d instance implementing + * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. + * @param {*} item - The native item from which to acquire context (platform specific) + * @param {object} options - The chart options + * @returns {CanvasRenderingContext2D} context2d instance + */ + acquireContext: function() {}, + + /** + * Called at chart destruction time, releases any resources associated to the context + * previously returned by the acquireContext() method. + * @param {CanvasRenderingContext2D} context - The context2d instance + * @returns {boolean} true if the method succeeded, else false + */ + releaseContext: function() {}, + + /** + * Registers the specified listener on the given chart. + * @param {Chart} chart - Chart from which to listen for event + * @param {string} type - The ({@link IEvent}) type to listen for + * @param {function} listener - Receives a notification (an object that implements + * the {@link IEvent} interface) when an event of the specified type occurs. + */ + addEventListener: function() {}, + + /** + * Removes the specified listener previously registered with addEventListener. + * @param {Chart} chart - Chart from which to remove the listener + * @param {string} type - The ({@link IEvent}) type to remove + * @param {function} listener - The listener function to remove from the event target. + */ + removeEventListener: function() {} + +}, implementation); + +core_defaults._set('global', { + plugins: {} +}); + +/** + * The plugin service singleton + * @namespace Chart.plugins + * @since 2.1.0 + */ +var core_plugins = { + /** + * Globally registered plugins. + * @private + */ + _plugins: [], + + /** + * This identifier is used to invalidate the descriptors cache attached to each chart + * when a global plugin is registered or unregistered. In this case, the cache ID is + * incremented and descriptors are regenerated during following API calls. + * @private + */ + _cacheId: 0, + + /** + * Registers the given plugin(s) if not already registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + register: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + if (p.indexOf(plugin) === -1) { + p.push(plugin); + } + }); + + this._cacheId++; + }, + + /** + * Unregisters the given plugin(s) only if registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + unregister: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + var idx = p.indexOf(plugin); + if (idx !== -1) { + p.splice(idx, 1); + } + }); + + this._cacheId++; + }, + + /** + * Remove all registered plugins. + * @since 2.1.5 + */ + clear: function() { + this._plugins = []; + this._cacheId++; + }, + + /** + * Returns the number of registered plugins? + * @returns {number} + * @since 2.1.5 + */ + count: function() { + return this._plugins.length; + }, + + /** + * Returns all registered plugin instances. + * @returns {IPlugin[]} array of plugin objects. + * @since 2.1.5 + */ + getAll: function() { + return this._plugins; + }, + + /** + * Calls enabled plugins for `chart` on the specified hook and with the given args. + * This method immediately returns as soon as a plugin explicitly returns false. The + * returned value can be used, for instance, to interrupt the current action. + * @param {Chart} chart - The chart instance for which plugins should be called. + * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). + * @param {Array} [args] - Extra arguments to apply to the hook call. + * @returns {boolean} false if any of the plugins return false, else returns true. + */ + notify: function(chart, hook, args) { + var descriptors = this.descriptors(chart); + var ilen = descriptors.length; + var i, descriptor, plugin, params, method; + + for (i = 0; i < ilen; ++i) { + descriptor = descriptors[i]; + plugin = descriptor.plugin; + method = plugin[hook]; + if (typeof method === 'function') { + params = [chart].concat(args || []); + params.push(descriptor.options); + if (method.apply(plugin, params) === false) { + return false; + } + } + } + + return true; + }, + + /** + * Returns descriptors of enabled plugins for the given chart. + * @returns {object[]} [{ plugin, options }] + * @private + */ + descriptors: function(chart) { + var cache = chart.$plugins || (chart.$plugins = {}); + if (cache.id === this._cacheId) { + return cache.descriptors; + } + + var plugins = []; + var descriptors = []; + var config = (chart && chart.config) || {}; + var options = (config.options && config.options.plugins) || {}; + + this._plugins.concat(config.plugins || []).forEach(function(plugin) { + var idx = plugins.indexOf(plugin); + if (idx !== -1) { + return; + } + + var id = plugin.id; + var opts = options[id]; + if (opts === false) { + return; + } + + if (opts === true) { + opts = helpers$1.clone(core_defaults.global.plugins[id]); + } + + plugins.push(plugin); + descriptors.push({ + plugin: plugin, + options: opts || {} + }); + }); + + cache.descriptors = descriptors; + cache.id = this._cacheId; + return descriptors; + }, + + /** + * Invalidates cache for the given chart: descriptors hold a reference on plugin option, + * but in some cases, this reference can be changed by the user when updating options. + * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + * @private + */ + _invalidate: function(chart) { + delete chart.$plugins; + } +}; + +var core_scaleService = { + // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then + // use the new chart options to grab the correct scale + constructors: {}, + // Use a registration function so that we can move to an ES6 map when we no longer need to support + // old browsers + + // Scale config defaults + defaults: {}, + registerScaleType: function(type, scaleConstructor, scaleDefaults) { + this.constructors[type] = scaleConstructor; + this.defaults[type] = helpers$1.clone(scaleDefaults); + }, + getScaleConstructor: function(type) { + return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; + }, + getScaleDefaults: function(type) { + // Return the scale defaults merged with the global settings so that we always use the latest ones + return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; + }, + updateScaleDefaults: function(type, additions) { + var me = this; + if (me.defaults.hasOwnProperty(type)) { + me.defaults[type] = helpers$1.extend(me.defaults[type], additions); + } + }, + addScalesToLayout: function(chart) { + // Adds each scale to the chart.boxes array to be sized accordingly + helpers$1.each(chart.scales, function(scale) { + // Set ILayoutItem parameters for backwards compatibility + scale.fullWidth = scale.options.fullWidth; + scale.position = scale.options.position; + scale.weight = scale.options.weight; + core_layouts.addBox(chart, scale); + }); + } +}; + +var valueOrDefault$8 = helpers$1.valueOrDefault; +var getRtlHelper = helpers$1.rtl.getRtlAdapter; + +core_defaults._set('global', { + tooltips: { + enabled: true, + custom: null, + mode: 'nearest', + position: 'average', + intersect: true, + backgroundColor: 'rgba(0,0,0,0.8)', + titleFontStyle: 'bold', + titleSpacing: 2, + titleMarginBottom: 6, + titleFontColor: '#fff', + titleAlign: 'left', + bodySpacing: 2, + bodyFontColor: '#fff', + bodyAlign: 'left', + footerFontStyle: 'bold', + footerSpacing: 2, + footerMarginTop: 6, + footerFontColor: '#fff', + footerAlign: 'left', + yPadding: 6, + xPadding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + multiKeyBackground: '#fff', + displayColors: true, + borderColor: 'rgba(0,0,0,0)', + borderWidth: 0, + callbacks: { + // Args are: (tooltipItems, data) + beforeTitle: helpers$1.noop, + title: function(tooltipItems, data) { + var title = ''; + var labels = data.labels; + var labelCount = labels ? labels.length : 0; + + if (tooltipItems.length > 0) { + var item = tooltipItems[0]; + if (item.label) { + title = item.label; + } else if (item.xLabel) { + title = item.xLabel; + } else if (labelCount > 0 && item.index < labelCount) { + title = labels[item.index]; + } + } + + return title; + }, + afterTitle: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeBody: helpers$1.noop, + + // Args are: (tooltipItem, data) + beforeLabel: helpers$1.noop, + label: function(tooltipItem, data) { + var label = data.datasets[tooltipItem.datasetIndex].label || ''; + + if (label) { + label += ': '; + } + if (!helpers$1.isNullOrUndef(tooltipItem.value)) { + label += tooltipItem.value; + } else { + label += tooltipItem.yLabel; + } + return label; + }, + labelColor: function(tooltipItem, chart) { + var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); + var activeElement = meta.data[tooltipItem.index]; + var view = activeElement._view; + return { + borderColor: view.borderColor, + backgroundColor: view.backgroundColor + }; + }, + labelTextColor: function() { + return this._options.bodyFontColor; + }, + afterLabel: helpers$1.noop, + + // Args are: (tooltipItems, data) + afterBody: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeFooter: helpers$1.noop, + footer: helpers$1.noop, + afterFooter: helpers$1.noop + } + } +}); + +var positioners = { + /** + * Average mode places the tooltip at the average position of the elements shown + * @function Chart.Tooltip.positioners.average + * @param elements {ChartElement[]} the elements being displayed in the tooltip + * @returns {object} tooltip position + */ + average: function(elements) { + if (!elements.length) { + return false; + } + + var i, len; + var x = 0; + var y = 0; + var count = 0; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var pos = el.tooltipPosition(); + x += pos.x; + y += pos.y; + ++count; + } + } + + return { + x: x / count, + y: y / count + }; + }, + + /** + * Gets the tooltip position nearest of the item nearest to the event position + * @function Chart.Tooltip.positioners.nearest + * @param elements {Chart.Element[]} the tooltip elements + * @param eventPosition {object} the position of the event in canvas coordinates + * @returns {object} the tooltip position + */ + nearest: function(elements, eventPosition) { + var x = eventPosition.x; + var y = eventPosition.y; + var minDistance = Number.POSITIVE_INFINITY; + var i, len, nearestElement; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var center = el.getCenterPoint(); + var d = helpers$1.distanceBetweenPoints(eventPosition, center); + + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + + if (nearestElement) { + var tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + + return { + x: x, + y: y + }; + } +}; + +// Helper to push or concat based on if the 2nd parameter is an array or not +function pushOrConcat(base, toPush) { + if (toPush) { + if (helpers$1.isArray(toPush)) { + // base = base.concat(toPush); + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + + return base; +} + +/** + * Returns array of strings split by newline + * @param {string} value - The value to split by newline. + * @returns {string[]} value if newline present - Returned from String split() method + * @function + */ +function splitNewlines(str) { + if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { + return str.split('\n'); + } + return str; +} + + +/** + * Private helper to create a tooltip item model + * @param element - the chart element (point, arc, bar) to create the tooltip item for + * @return new tooltip item + */ +function createTooltipItem(element) { + var xScale = element._xScale; + var yScale = element._yScale || element._scale; // handle radar || polarArea charts + var index = element._index; + var datasetIndex = element._datasetIndex; + var controller = element._chart.getDatasetMeta(datasetIndex).controller; + var indexScale = controller._getIndexScale(); + var valueScale = controller._getValueScale(); + + return { + xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', + yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', + label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', + value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', + index: index, + datasetIndex: datasetIndex, + x: element._model.x, + y: element._model.y + }; +} + +/** + * Helper to get the reset model for the tooltip + * @param tooltipOpts {object} the tooltip options + */ +function getBaseModel(tooltipOpts) { + var globalDefaults = core_defaults.global; + + return { + // Positioning + xPadding: tooltipOpts.xPadding, + yPadding: tooltipOpts.yPadding, + xAlign: tooltipOpts.xAlign, + yAlign: tooltipOpts.yAlign, + + // Drawing direction and text direction + rtl: tooltipOpts.rtl, + textDirection: tooltipOpts.textDirection, + + // Body + bodyFontColor: tooltipOpts.bodyFontColor, + _bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), + _bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), + _bodyAlign: tooltipOpts.bodyAlign, + bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), + bodySpacing: tooltipOpts.bodySpacing, + + // Title + titleFontColor: tooltipOpts.titleFontColor, + _titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), + _titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), + titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), + _titleAlign: tooltipOpts.titleAlign, + titleSpacing: tooltipOpts.titleSpacing, + titleMarginBottom: tooltipOpts.titleMarginBottom, + + // Footer + footerFontColor: tooltipOpts.footerFontColor, + _footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), + _footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), + footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), + _footerAlign: tooltipOpts.footerAlign, + footerSpacing: tooltipOpts.footerSpacing, + footerMarginTop: tooltipOpts.footerMarginTop, + + // Appearance + caretSize: tooltipOpts.caretSize, + cornerRadius: tooltipOpts.cornerRadius, + backgroundColor: tooltipOpts.backgroundColor, + opacity: 0, + legendColorBackground: tooltipOpts.multiKeyBackground, + displayColors: tooltipOpts.displayColors, + borderColor: tooltipOpts.borderColor, + borderWidth: tooltipOpts.borderWidth + }; +} + +/** + * Get the size of the tooltip + */ +function getTooltipSize(tooltip, model) { + var ctx = tooltip._chart.ctx; + + var height = model.yPadding * 2; // Tooltip Padding + var width = 0; + + // Count of all lines in the body + var body = model.body; + var combinedBodyLength = body.reduce(function(count, bodyItem) { + return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; + }, 0); + combinedBodyLength += model.beforeBody.length + model.afterBody.length; + + var titleLineCount = model.title.length; + var footerLineCount = model.footer.length; + var titleFontSize = model.titleFontSize; + var bodyFontSize = model.bodyFontSize; + var footerFontSize = model.footerFontSize; + + height += titleLineCount * titleFontSize; // Title Lines + height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing + height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin + height += combinedBodyLength * bodyFontSize; // Body Lines + height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing + height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin + height += footerLineCount * (footerFontSize); // Footer Lines + height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing + + // Title width + var widthPadding = 0; + var maxLineWidth = function(line) { + width = Math.max(width, ctx.measureText(line).width + widthPadding); + }; + + ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); + helpers$1.each(model.title, maxLineWidth); + + // Body width + ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); + helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); + + // Body lines may include some extra width due to the color box + widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; + helpers$1.each(body, function(bodyItem) { + helpers$1.each(bodyItem.before, maxLineWidth); + helpers$1.each(bodyItem.lines, maxLineWidth); + helpers$1.each(bodyItem.after, maxLineWidth); + }); + + // Reset back to 0 + widthPadding = 0; + + // Footer width + ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); + helpers$1.each(model.footer, maxLineWidth); + + // Add padding + width += 2 * model.xPadding; + + return { + width: width, + height: height + }; +} + +/** + * Helper to get the alignment of a tooltip given the size + */ +function determineAlignment(tooltip, size) { + var model = tooltip._model; + var chart = tooltip._chart; + var chartArea = tooltip._chart.chartArea; + var xAlign = 'center'; + var yAlign = 'center'; + + if (model.y < size.height) { + yAlign = 'top'; + } else if (model.y > (chart.height - size.height)) { + yAlign = 'bottom'; + } + + var lf, rf; // functions to determine left, right alignment + var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart + var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges + var midX = (chartArea.left + chartArea.right) / 2; + var midY = (chartArea.top + chartArea.bottom) / 2; + + if (yAlign === 'center') { + lf = function(x) { + return x <= midX; + }; + rf = function(x) { + return x > midX; + }; + } else { + lf = function(x) { + return x <= (size.width / 2); + }; + rf = function(x) { + return x >= (chart.width - (size.width / 2)); + }; + } + + olf = function(x) { + return x + size.width + model.caretSize + model.caretPadding > chart.width; + }; + orf = function(x) { + return x - size.width - model.caretSize - model.caretPadding < 0; + }; + yf = function(y) { + return y <= midY ? 'top' : 'bottom'; + }; + + if (lf(model.x)) { + xAlign = 'left'; + + // Is tooltip too wide and goes over the right side of the chart.? + if (olf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } else if (rf(model.x)) { + xAlign = 'right'; + + // Is tooltip too wide and goes outside left edge of canvas? + if (orf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } + + var opts = tooltip._options; + return { + xAlign: opts.xAlign ? opts.xAlign : xAlign, + yAlign: opts.yAlign ? opts.yAlign : yAlign + }; +} + +/** + * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment + */ +function getBackgroundPoint(vm, size, alignment, chart) { + // Background Position + var x = vm.x; + var y = vm.y; + + var caretSize = vm.caretSize; + var caretPadding = vm.caretPadding; + var cornerRadius = vm.cornerRadius; + var xAlign = alignment.xAlign; + var yAlign = alignment.yAlign; + var paddingAndSize = caretSize + caretPadding; + var radiusAndPadding = cornerRadius + caretPadding; + + if (xAlign === 'right') { + x -= size.width; + } else if (xAlign === 'center') { + x -= (size.width / 2); + if (x + size.width > chart.width) { + x = chart.width - size.width; + } + if (x < 0) { + x = 0; + } + } + + if (yAlign === 'top') { + y += paddingAndSize; + } else if (yAlign === 'bottom') { + y -= size.height + paddingAndSize; + } else { + y -= (size.height / 2); + } + + if (yAlign === 'center') { + if (xAlign === 'left') { + x += paddingAndSize; + } else if (xAlign === 'right') { + x -= paddingAndSize; + } + } else if (xAlign === 'left') { + x -= radiusAndPadding; + } else if (xAlign === 'right') { + x += radiusAndPadding; + } + + return { + x: x, + y: y + }; +} + +function getAlignedX(vm, align) { + return align === 'center' + ? vm.x + vm.width / 2 + : align === 'right' + ? vm.x + vm.width - vm.xPadding + : vm.x + vm.xPadding; +} + +/** + * Helper to build before and after body lines + */ +function getBeforeAfterBodyLines(callback) { + return pushOrConcat([], splitNewlines(callback)); +} + +var exports$4 = core_element.extend({ + initialize: function() { + this._model = getBaseModel(this._options); + this._lastActive = []; + }, + + // Get the title + // Args are: (tooltipItem, data) + getTitle: function() { + var me = this; + var opts = me._options; + var callbacks = opts.callbacks; + + var beforeTitle = callbacks.beforeTitle.apply(me, arguments); + var title = callbacks.title.apply(me, arguments); + var afterTitle = callbacks.afterTitle.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeTitle)); + lines = pushOrConcat(lines, splitNewlines(title)); + lines = pushOrConcat(lines, splitNewlines(afterTitle)); + + return lines; + }, + + // Args are: (tooltipItem, data) + getBeforeBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); + }, + + // Args are: (tooltipItem, data) + getBody: function(tooltipItems, data) { + var me = this; + var callbacks = me._options.callbacks; + var bodyItems = []; + + helpers$1.each(tooltipItems, function(tooltipItem) { + var bodyItem = { + before: [], + lines: [], + after: [] + }; + pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); + pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); + pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); + + bodyItems.push(bodyItem); + }); + + return bodyItems; + }, + + // Args are: (tooltipItem, data) + getAfterBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); + }, + + // Get the footer and beforeFooter and afterFooter lines + // Args are: (tooltipItem, data) + getFooter: function() { + var me = this; + var callbacks = me._options.callbacks; + + var beforeFooter = callbacks.beforeFooter.apply(me, arguments); + var footer = callbacks.footer.apply(me, arguments); + var afterFooter = callbacks.afterFooter.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeFooter)); + lines = pushOrConcat(lines, splitNewlines(footer)); + lines = pushOrConcat(lines, splitNewlines(afterFooter)); + + return lines; + }, + + update: function(changed) { + var me = this; + var opts = me._options; + + // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition + // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time + // which breaks any animations. + var existingModel = me._model; + var model = me._model = getBaseModel(opts); + var active = me._active; + + var data = me._data; + + // In the case where active.length === 0 we need to keep these at existing values for good animations + var alignment = { + xAlign: existingModel.xAlign, + yAlign: existingModel.yAlign + }; + var backgroundPoint = { + x: existingModel.x, + y: existingModel.y + }; + var tooltipSize = { + width: existingModel.width, + height: existingModel.height + }; + var tooltipPosition = { + x: existingModel.caretX, + y: existingModel.caretY + }; + + var i, len; + + if (active.length) { + model.opacity = 1; + + var labelColors = []; + var labelTextColors = []; + tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); + + var tooltipItems = []; + for (i = 0, len = active.length; i < len; ++i) { + tooltipItems.push(createTooltipItem(active[i])); + } + + // If the user provided a filter function, use it to modify the tooltip items + if (opts.filter) { + tooltipItems = tooltipItems.filter(function(a) { + return opts.filter(a, data); + }); + } + + // If the user provided a sorting function, use it to modify the tooltip items + if (opts.itemSort) { + tooltipItems = tooltipItems.sort(function(a, b) { + return opts.itemSort(a, b, data); + }); + } + + // Determine colors for boxes + helpers$1.each(tooltipItems, function(tooltipItem) { + labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); + labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); + }); + + + // Build the Text Lines + model.title = me.getTitle(tooltipItems, data); + model.beforeBody = me.getBeforeBody(tooltipItems, data); + model.body = me.getBody(tooltipItems, data); + model.afterBody = me.getAfterBody(tooltipItems, data); + model.footer = me.getFooter(tooltipItems, data); + + // Initial positioning and colors + model.x = tooltipPosition.x; + model.y = tooltipPosition.y; + model.caretPadding = opts.caretPadding; + model.labelColors = labelColors; + model.labelTextColors = labelTextColors; + + // data points + model.dataPoints = tooltipItems; + + // We need to determine alignment of the tooltip + tooltipSize = getTooltipSize(this, model); + alignment = determineAlignment(this, tooltipSize); + // Final Size and Position + backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); + } else { + model.opacity = 0; + } + + model.xAlign = alignment.xAlign; + model.yAlign = alignment.yAlign; + model.x = backgroundPoint.x; + model.y = backgroundPoint.y; + model.width = tooltipSize.width; + model.height = tooltipSize.height; + + // Point where the caret on the tooltip points to + model.caretX = tooltipPosition.x; + model.caretY = tooltipPosition.y; + + me._model = model; + + if (changed && opts.custom) { + opts.custom.call(me, model); + } + + return me; + }, + + drawCaret: function(tooltipPoint, size) { + var ctx = this._chart.ctx; + var vm = this._view; + var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); + + ctx.lineTo(caretPosition.x1, caretPosition.y1); + ctx.lineTo(caretPosition.x2, caretPosition.y2); + ctx.lineTo(caretPosition.x3, caretPosition.y3); + }, + getCaretPosition: function(tooltipPoint, size, vm) { + var x1, x2, x3, y1, y2, y3; + var caretSize = vm.caretSize; + var cornerRadius = vm.cornerRadius; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var ptX = tooltipPoint.x; + var ptY = tooltipPoint.y; + var width = size.width; + var height = size.height; + + if (yAlign === 'center') { + y2 = ptY + (height / 2); + + if (xAlign === 'left') { + x1 = ptX; + x2 = x1 - caretSize; + x3 = x1; + + y1 = y2 + caretSize; + y3 = y2 - caretSize; + } else { + x1 = ptX + width; + x2 = x1 + caretSize; + x3 = x1; + + y1 = y2 - caretSize; + y3 = y2 + caretSize; + } + } else { + if (xAlign === 'left') { + x2 = ptX + cornerRadius + (caretSize); + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else if (xAlign === 'right') { + x2 = ptX + width - cornerRadius - caretSize; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else { + x2 = vm.caretX; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } + if (yAlign === 'top') { + y1 = ptY; + y2 = y1 - caretSize; + y3 = y1; + } else { + y1 = ptY + height; + y2 = y1 + caretSize; + y3 = y1; + // invert drawing order + var tmp = x3; + x3 = x1; + x1 = tmp; + } + } + return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; + }, + + drawTitle: function(pt, vm, ctx) { + var title = vm.title; + var length = title.length; + var titleFontSize, titleSpacing, i; + + if (length) { + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + pt.x = getAlignedX(vm, vm._titleAlign); + + ctx.textAlign = rtlHelper.textAlign(vm._titleAlign); + ctx.textBaseline = 'middle'; + + titleFontSize = vm.titleFontSize; + titleSpacing = vm.titleSpacing; + + ctx.fillStyle = vm.titleFontColor; + ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); + + for (i = 0; i < length; ++i) { + ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2); + pt.y += titleFontSize + titleSpacing; // Line Height and spacing + + if (i + 1 === length) { + pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing + } + } + } + }, + + drawBody: function(pt, vm, ctx) { + var bodyFontSize = vm.bodyFontSize; + var bodySpacing = vm.bodySpacing; + var bodyAlign = vm._bodyAlign; + var body = vm.body; + var drawColorBoxes = vm.displayColors; + var xLinePadding = 0; + var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; + + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + var fillLineOfText = function(line) { + ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2); + pt.y += bodyFontSize + bodySpacing; + }; + + var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen; + var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); + + ctx.textAlign = bodyAlign; + ctx.textBaseline = 'middle'; + ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); + + pt.x = getAlignedX(vm, bodyAlignForCalculation); + + // Before body lines + ctx.fillStyle = vm.bodyFontColor; + helpers$1.each(vm.beforeBody, fillLineOfText); + + xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right' + ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) + : 0; + + // Draw body lines now + for (i = 0, ilen = body.length; i < ilen; ++i) { + bodyItem = body[i]; + textColor = vm.labelTextColors[i]; + labelColors = vm.labelColors[i]; + + ctx.fillStyle = textColor; + helpers$1.each(bodyItem.before, fillLineOfText); + + lines = bodyItem.lines; + for (j = 0, jlen = lines.length; j < jlen; ++j) { + // Draw Legend-like boxes if needed + if (drawColorBoxes) { + var rtlColorX = rtlHelper.x(colorX); + + // Fill a white rect so that colours merge nicely if the opacity is < 1 + ctx.fillStyle = vm.legendColorBackground; + ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); + + // Border + ctx.lineWidth = 1; + ctx.strokeStyle = labelColors.borderColor; + ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); + + // Inner square + ctx.fillStyle = labelColors.backgroundColor; + ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); + ctx.fillStyle = textColor; + } + + fillLineOfText(lines[j]); + } + + helpers$1.each(bodyItem.after, fillLineOfText); + } + + // Reset back to 0 for after body + xLinePadding = 0; + + // After body lines + helpers$1.each(vm.afterBody, fillLineOfText); + pt.y -= bodySpacing; // Remove last body spacing + }, + + drawFooter: function(pt, vm, ctx) { + var footer = vm.footer; + var length = footer.length; + var footerFontSize, i; + + if (length) { + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + pt.x = getAlignedX(vm, vm._footerAlign); + pt.y += vm.footerMarginTop; + + ctx.textAlign = rtlHelper.textAlign(vm._footerAlign); + ctx.textBaseline = 'middle'; + + footerFontSize = vm.footerFontSize; + + ctx.fillStyle = vm.footerFontColor; + ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); + + for (i = 0; i < length; ++i) { + ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2); + pt.y += footerFontSize + vm.footerSpacing; + } + } + }, + + drawBackground: function(pt, vm, ctx, tooltipSize) { + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var x = pt.x; + var y = pt.y; + var width = tooltipSize.width; + var height = tooltipSize.height; + var radius = vm.cornerRadius; + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + if (yAlign === 'top') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + if (yAlign === 'center' && xAlign === 'right') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + if (yAlign === 'bottom') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + if (yAlign === 'center' && xAlign === 'left') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + + ctx.fill(); + + if (vm.borderWidth > 0) { + ctx.stroke(); + } + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + + if (vm.opacity === 0) { + return; + } + + var tooltipSize = { + width: vm.width, + height: vm.height + }; + var pt = { + x: vm.x, + y: vm.y + }; + + // IE11/Edge does not like very small opacities, so snap to 0 + var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; + + // Truthy/falsey value for empty tooltip + var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; + + if (this._options.enabled && hasTooltipContent) { + ctx.save(); + ctx.globalAlpha = opacity; + + // Draw Background + this.drawBackground(pt, vm, ctx, tooltipSize); + + // Draw Title, Body, and Footer + pt.y += vm.yPadding; + + helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection); + + // Titles + this.drawTitle(pt, vm, ctx); + + // Body + this.drawBody(pt, vm, ctx); + + // Footer + this.drawFooter(pt, vm, ctx); + + helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection); + + ctx.restore(); + } + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + * @returns {boolean} true if the tooltip changed + */ + handleEvent: function(e) { + var me = this; + var options = me._options; + var changed = false; + + me._lastActive = me._lastActive || []; + + // Find Active Elements for tooltips + if (e.type === 'mouseout') { + me._active = []; + } else { + me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); + if (options.reverse) { + me._active.reverse(); + } + } + + // Remember Last Actives + changed = !helpers$1.arrayEquals(me._active, me._lastActive); + + // Only handle target event on tooltip change + if (changed) { + me._lastActive = me._active; + + if (options.enabled || options.custom) { + me._eventPosition = { + x: e.x, + y: e.y + }; + + me.update(true); + me.pivot(); + } + } + + return changed; + } +}); + +/** + * @namespace Chart.Tooltip.positioners + */ +var positioners_1 = positioners; + +var core_tooltip = exports$4; +core_tooltip.positioners = positioners_1; + +var valueOrDefault$9 = helpers$1.valueOrDefault; + +core_defaults._set('global', { + elements: {}, + events: [ + 'mousemove', + 'mouseout', + 'click', + 'touchstart', + 'touchmove' + ], + hover: { + onHover: null, + mode: 'nearest', + intersect: true, + animationDuration: 400 + }, + onClick: null, + maintainAspectRatio: true, + responsive: true, + responsiveAnimationDuration: 0 +}); + +/** + * Recursively merge the given config objects representing the `scales` option + * by incorporating scale defaults in `xAxes` and `yAxes` array items, then + * returns a deep copy of the result, thus doesn't alter inputs. + */ +function mergeScaleConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + if (key === 'xAxes' || key === 'yAxes') { + var slen = source[key].length; + var i, type, scale; + + if (!target[key]) { + target[key] = []; + } + + for (i = 0; i < slen; ++i) { + scale = source[key][i]; + type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear'); + + if (i >= target[key].length) { + target[key].push({}); + } + + if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { + // new/untyped scale or type changed: let's apply the new defaults + // then merge source scale to correctly overwrite the defaults. + helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); + } else { + // scales type are the same + helpers$1.merge(target[key][i], scale); + } + } + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +/** + * Recursively merge the given config objects as the root options by handling + * default scale options for the `scales` and `scale` properties, then returns + * a deep copy of the result, thus doesn't alter inputs. + */ +function mergeConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + var tval = target[key] || {}; + var sval = source[key]; + + if (key === 'scales') { + // scale config merging is complex. Add our own function here for that + target[key] = mergeScaleConfig(tval, sval); + } else if (key === 'scale') { + // used in polar area & radar charts since there is only one scale + target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +function initConfig(config) { + config = config || {}; + + // Do NOT use mergeConfig for the data object because this method merges arrays + // and so would change references to labels and datasets, preventing data updates. + var data = config.data = config.data || {}; + data.datasets = data.datasets || []; + data.labels = data.labels || []; + + config.options = mergeConfig( + core_defaults.global, + core_defaults[config.type], + config.options || {}); + + return config; +} + +function updateConfig(chart) { + var newOptions = chart.options; + + helpers$1.each(chart.scales, function(scale) { + core_layouts.removeBox(chart, scale); + }); + + newOptions = mergeConfig( + core_defaults.global, + core_defaults[chart.config.type], + newOptions); + + chart.options = chart.config.options = newOptions; + chart.ensureScalesHaveIDs(); + chart.buildOrUpdateScales(); + + // Tooltip + chart.tooltip._options = newOptions.tooltips; + chart.tooltip.initialize(); +} + +function nextAvailableScaleId(axesOpts, prefix, index) { + var id; + var hasId = function(obj) { + return obj.id === id; + }; + + do { + id = prefix + index++; + } while (helpers$1.findIndex(axesOpts, hasId) >= 0); + + return id; +} + +function positionIsHorizontal(position) { + return position === 'top' || position === 'bottom'; +} + +function compare2Level(l1, l2) { + return function(a, b) { + return a[l1] === b[l1] + ? a[l2] - b[l2] + : a[l1] - b[l1]; + }; +} + +var Chart = function(item, config) { + this.construct(item, config); + return this; +}; + +helpers$1.extend(Chart.prototype, /** @lends Chart */ { + /** + * @private + */ + construct: function(item, config) { + var me = this; + + config = initConfig(config); + + var context = platform.acquireContext(item, config); + var canvas = context && context.canvas; + var height = canvas && canvas.height; + var width = canvas && canvas.width; + + me.id = helpers$1.uid(); + me.ctx = context; + me.canvas = canvas; + me.config = config; + me.width = width; + me.height = height; + me.aspectRatio = height ? width / height : null; + me.options = config.options; + me._bufferedRender = false; + me._layers = []; + + /** + * Provided for backward compatibility, Chart and Chart.Controller have been merged, + * the "instance" still need to be defined since it might be called from plugins. + * @prop Chart#chart + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ + me.chart = me; + me.controller = me; // chart.chart.controller #inception + + // Add the chart instance to the global namespace + Chart.instances[me.id] = me; + + // Define alias to the config data: `chart.data === chart.config.data` + Object.defineProperty(me, 'data', { + get: function() { + return me.config.data; + }, + set: function(value) { + me.config.data = value; + } + }); + + if (!context || !canvas) { + // The given item is not a compatible context2d element, let's return before finalizing + // the chart initialization but after setting basic chart / controller properties that + // can help to figure out that the chart is not valid (e.g chart.canvas !== null); + // https://github.com/chartjs/Chart.js/issues/2807 + console.error("Failed to create chart: can't acquire context from the given item"); + return; + } + + me.initialize(); + me.update(); + }, + + /** + * @private + */ + initialize: function() { + var me = this; + + // Before init plugin notification + core_plugins.notify(me, 'beforeInit'); + + helpers$1.retinaScale(me, me.options.devicePixelRatio); + + me.bindEvents(); + + if (me.options.responsive) { + // Initial resize before chart draws (must be silent to preserve initial animations). + me.resize(true); + } + + me.initToolTip(); + + // After init plugin notification + core_plugins.notify(me, 'afterInit'); + + return me; + }, + + clear: function() { + helpers$1.canvas.clear(this); + return this; + }, + + stop: function() { + // Stops any current animation loop occurring + core_animations.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var options = me.options; + var canvas = me.canvas; + var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; + + // the canvas render width and height will be casted to integers so make sure that + // the canvas display style uses the same integer values to avoid blurring effect. + + // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed + var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); + var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); + + if (me.width === newWidth && me.height === newHeight) { + return; + } + + canvas.width = me.width = newWidth; + canvas.height = me.height = newHeight; + canvas.style.width = newWidth + 'px'; + canvas.style.height = newHeight + 'px'; + + helpers$1.retinaScale(me, options.devicePixelRatio); + + if (!silent) { + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + core_plugins.notify(me, 'resize', [newSize]); + + // Notify of resize + if (options.onResize) { + options.onResize(me, newSize); + } + + me.stop(); + me.update({ + duration: options.responsiveAnimationDuration + }); + } + }, + + ensureScalesHaveIDs: function() { + var options = this.options; + var scalesOptions = options.scales || {}; + var scaleOptions = options.scale; + + helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { + if (!xAxisOptions.id) { + xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index); + } + }); + + helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { + if (!yAxisOptions.id) { + yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index); + } + }); + + if (scaleOptions) { + scaleOptions.id = scaleOptions.id || 'scale'; + } + }, + + /** + * Builds a map of scale ID to scale object for future lookup. + */ + buildOrUpdateScales: function() { + var me = this; + var options = me.options; + var scales = me.scales || {}; + var items = []; + var updated = Object.keys(scales).reduce(function(obj, id) { + obj[id] = false; + return obj; + }, {}); + + if (options.scales) { + items = items.concat( + (options.scales.xAxes || []).map(function(xAxisOptions) { + return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; + }) + ); + } + + if (options.scale) { + items.push({ + options: options.scale, + dtype: 'radialLinear', + isDefault: true, + dposition: 'chartArea' + }); + } + + helpers$1.each(items, function(item) { + var scaleOptions = item.options; + var id = scaleOptions.id; + var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype); + + if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { + scaleOptions.position = item.dposition; + } + + updated[id] = true; + var scale = null; + if (id in scales && scales[id].type === scaleType) { + scale = scales[id]; + scale.options = scaleOptions; + scale.ctx = me.ctx; + scale.chart = me; + } else { + var scaleClass = core_scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + scale = new scaleClass({ + id: id, + type: scaleType, + options: scaleOptions, + ctx: me.ctx, + chart: me + }); + scales[scale.id] = scale; + } + + scale.mergeTicksOptions(); + + // TODO(SB): I think we should be able to remove this custom case (options.scale) + // and consider it as a regular scale part of the "scales"" map only! This would + // make the logic easier and remove some useless? custom code. + if (item.isDefault) { + me.scale = scale; + } + }); + // clear up discarded scales + helpers$1.each(updated, function(hasUpdated, id) { + if (!hasUpdated) { + delete scales[id]; + } + }); + + me.scales = scales; + + core_scaleService.addScalesToLayout(this); + }, + + buildOrUpdateControllers: function() { + var me = this; + var newControllers = []; + var datasets = me.data.datasets; + var i, ilen; + + for (i = 0, ilen = datasets.length; i < ilen; i++) { + var dataset = datasets[i]; + var meta = me.getDatasetMeta(i); + var type = dataset.type || me.config.type; + + if (meta.type && meta.type !== type) { + me.destroyDatasetMeta(i); + meta = me.getDatasetMeta(i); + } + meta.type = type; + meta.order = dataset.order || 0; + meta.index = i; + + if (meta.controller) { + meta.controller.updateIndex(i); + meta.controller.linkScales(); + } else { + var ControllerClass = controllers[meta.type]; + if (ControllerClass === undefined) { + throw new Error('"' + meta.type + '" is not a chart type.'); + } + + meta.controller = new ControllerClass(me, i); + newControllers.push(meta.controller); + } + } + + return newControllers; + }, + + /** + * Reset the elements of all datasets + * @private + */ + resetElements: function() { + var me = this; + helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.reset(); + }, me); + }, + + /** + * Resets the chart back to it's state before the initial animation + */ + reset: function() { + this.resetElements(); + this.tooltip.initialize(); + }, + + update: function(config) { + var me = this; + var i, ilen; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + updateConfig(me); + + // plugins options references might have change, let's invalidate the cache + // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + core_plugins._invalidate(me); + + if (core_plugins.notify(me, 'beforeUpdate') === false) { + return; + } + + // In case the entire data object changed + me.tooltip._data = me.data; + + // Make sure dataset controllers are updated and new controllers are reset + var newControllers = me.buildOrUpdateControllers(); + + // Make sure all dataset controllers have correct meta data counts + for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) { + me.getDatasetMeta(i).controller.buildOrUpdateElements(); + } + + me.updateLayout(); + + // Can only reset the new controllers after the scales have been updated + if (me.options.animation && me.options.animation.duration) { + helpers$1.each(newControllers, function(controller) { + controller.reset(); + }); + } + + me.updateDatasets(); + + // Need to reset tooltip in case it is displayed with elements that are removed + // after update. + me.tooltip.initialize(); + + // Last active contains items that were previously in the tooltip. + // When we reset the tooltip, we need to clear it + me.lastActive = []; + + // Do this before render so that any plugins that need final scale updates can use it + core_plugins.notify(me, 'afterUpdate'); + + me._layers.sort(compare2Level('z', '_idx')); + + if (me._bufferedRender) { + me._bufferedRequest = { + duration: config.duration, + easing: config.easing, + lazy: config.lazy + }; + } else { + me.render(config); + } + }, + + /** + * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` + * hook, in which case, plugins will not be called on `afterLayout`. + * @private + */ + updateLayout: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeLayout') === false) { + return; + } + + core_layouts.update(this, this.width, this.height); + + me._layers = []; + helpers$1.each(me.boxes, function(box) { + // _configure is called twice, once in core.scale.update and once here. + // Here the boxes are fully updated and at their final positions. + if (box._configure) { + box._configure(); + } + me._layers.push.apply(me._layers, box._layers()); + }, me); + + me._layers.forEach(function(item, index) { + item._idx = index; + }); + + /** + * Provided for backward compatibility, use `afterLayout` instead. + * @method IPlugin#afterScaleUpdate + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ + core_plugins.notify(me, 'afterScaleUpdate'); + core_plugins.notify(me, 'afterLayout'); + }, + + /** + * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` + * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. + * @private + */ + updateDatasets: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { + return; + } + + for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.updateDataset(i); + } + + core_plugins.notify(me, 'afterDatasetsUpdate'); + }, + + /** + * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` + * hook, in which case, plugins will not be called on `afterDatasetUpdate`. + * @private + */ + updateDataset: function(index) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index + }; + + if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { + return; + } + + meta.controller._update(); + + core_plugins.notify(me, 'afterDatasetUpdate', [args]); + }, + + render: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + var animationOptions = me.options.animation; + var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration); + var lazy = config.lazy; + + if (core_plugins.notify(me, 'beforeRender') === false) { + return; + } + + var onComplete = function(animation) { + core_plugins.notify(me, 'afterRender'); + helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); + }; + + if (animationOptions && duration) { + var animation = new core_animation({ + numSteps: duration / 16.66, // 60 fps + easing: config.easing || animationOptions.easing, + + render: function(chart, animationObject) { + var easingFunction = helpers$1.easing.effects[animationObject.easing]; + var currentStep = animationObject.currentStep; + var stepDecimal = currentStep / animationObject.numSteps; + + chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); + }, + + onAnimationProgress: animationOptions.onProgress, + onAnimationComplete: onComplete + }); + + core_animations.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + + // See https://github.com/chartjs/Chart.js/issues/3781 + onComplete(new core_animation({numSteps: 0, chart: me})); + } + + return me; + }, + + draw: function(easingValue) { + var me = this; + var i, layers; + + me.clear(); + + if (helpers$1.isNullOrUndef(easingValue)) { + easingValue = 1; + } + + me.transition(easingValue); + + if (me.width <= 0 || me.height <= 0) { + return; + } + + if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { + return; + } + + // Because of plugin hooks (before/afterDatasetsDraw), datasets can't + // currently be part of layers. Instead, we draw + // layers <= 0 before(default, backward compat), and the rest after + layers = me._layers; + for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { + layers[i].draw(me.chartArea); + } + + me.drawDatasets(easingValue); + + // Rest of layers + for (; i < layers.length; ++i) { + layers[i].draw(me.chartArea); + } + + me._drawTooltip(easingValue); + + core_plugins.notify(me, 'afterDraw', [easingValue]); + }, + + /** + * @private + */ + transition: function(easingValue) { + var me = this; + + for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { + if (me.isDatasetVisible(i)) { + me.getDatasetMeta(i).controller.transition(easingValue); + } + } + + me.tooltip.transition(easingValue); + }, + + /** + * @private + */ + _getSortedDatasetMetas: function(filterVisible) { + var me = this; + var datasets = me.data.datasets || []; + var result = []; + var i, ilen; + + for (i = 0, ilen = datasets.length; i < ilen; ++i) { + if (!filterVisible || me.isDatasetVisible(i)) { + result.push(me.getDatasetMeta(i)); + } + } + + result.sort(compare2Level('order', 'index')); + + return result; + }, + + /** + * @private + */ + _getSortedVisibleDatasetMetas: function() { + return this._getSortedDatasetMetas(true); + }, + + /** + * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` + * hook, in which case, plugins will not be called on `afterDatasetsDraw`. + * @private + */ + drawDatasets: function(easingValue) { + var me = this; + var metasets, i; + + if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { + return; + } + + metasets = me._getSortedVisibleDatasetMetas(); + for (i = metasets.length - 1; i >= 0; --i) { + me.drawDataset(metasets[i], easingValue); + } + + core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); + }, + + /** + * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` + * hook, in which case, plugins will not be called on `afterDatasetDraw`. + * @private + */ + drawDataset: function(meta, easingValue) { + var me = this; + var args = { + meta: meta, + index: meta.index, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { + return; + } + + meta.controller.draw(easingValue); + + core_plugins.notify(me, 'afterDatasetDraw', [args]); + }, + + /** + * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` + * hook, in which case, plugins will not be called on `afterTooltipDraw`. + * @private + */ + _drawTooltip: function(easingValue) { + var me = this; + var tooltip = me.tooltip; + var args = { + tooltip: tooltip, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { + return; + } + + tooltip.draw(); + + core_plugins.notify(me, 'afterTooltipDraw', [args]); + }, + + /** + * Get the single element that was clicked on + * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw + */ + getElementAtEvent: function(e) { + return core_interaction.modes.single(this, e); + }, + + getElementsAtEvent: function(e) { + return core_interaction.modes.label(this, e, {intersect: true}); + }, + + getElementsAtXAxis: function(e) { + return core_interaction.modes['x-axis'](this, e, {intersect: true}); + }, + + getElementsAtEventForMode: function(e, mode, options) { + var method = core_interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options); + } + + return []; + }, + + getDatasetAtEvent: function(e) { + return core_interaction.modes.dataset(this, e, {intersect: true}); + }, + + getDatasetMeta: function(datasetIndex) { + var me = this; + var dataset = me.data.datasets[datasetIndex]; + if (!dataset._meta) { + dataset._meta = {}; + } + + var meta = dataset._meta[me.id]; + if (!meta) { + meta = dataset._meta[me.id] = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, // See isDatasetVisible() comment + xAxisID: null, + yAxisID: null, + order: dataset.order || 0, + index: datasetIndex + }; + } + + return meta; + }, + + getVisibleDatasetCount: function() { + var count = 0; + for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + if (this.isDatasetVisible(i)) { + count++; + } + } + return count; + }, + + isDatasetVisible: function(datasetIndex) { + var meta = this.getDatasetMeta(datasetIndex); + + // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, + // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. + return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; + }, + + generateLegend: function() { + return this.options.legendCallback(this); + }, + + /** + * @private + */ + destroyDatasetMeta: function(datasetIndex) { + var id = this.id; + var dataset = this.data.datasets[datasetIndex]; + var meta = dataset._meta && dataset._meta[id]; + + if (meta) { + meta.controller.destroy(); + delete dataset._meta[id]; + } + }, + + destroy: function() { + var me = this; + var canvas = me.canvas; + var i, ilen; + + me.stop(); + + // dataset controllers need to cleanup associated data + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.destroyDatasetMeta(i); + } + + if (canvas) { + me.unbindEvents(); + helpers$1.canvas.clear(me); + platform.releaseContext(me.ctx); + me.canvas = null; + me.ctx = null; + } + + core_plugins.notify(me, 'destroy'); + + delete Chart.instances[me.id]; + }, + + toBase64Image: function() { + return this.canvas.toDataURL.apply(this.canvas, arguments); + }, + + initToolTip: function() { + var me = this; + me.tooltip = new core_tooltip({ + _chart: me, + _chartInstance: me, // deprecated, backward compatibility + _data: me.data, + _options: me.options.tooltips + }, me); + }, + + /** + * @private + */ + bindEvents: function() { + var me = this; + var listeners = me._listeners = {}; + var listener = function() { + me.eventHandler.apply(me, arguments); + }; + + helpers$1.each(me.options.events, function(type) { + platform.addEventListener(me, type, listener); + listeners[type] = listener; + }); + + // Elements used to detect size change should not be injected for non responsive charts. + // See https://github.com/chartjs/Chart.js/issues/2210 + if (me.options.responsive) { + listener = function() { + me.resize(); + }; + + platform.addEventListener(me, 'resize', listener); + listeners.resize = listener; + } + }, + + /** + * @private + */ + unbindEvents: function() { + var me = this; + var listeners = me._listeners; + if (!listeners) { + return; + } + + delete me._listeners; + helpers$1.each(listeners, function(listener, type) { + platform.removeEventListener(me, type, listener); + }); + }, + + updateHoverStyle: function(elements, mode, enabled) { + var prefix = enabled ? 'set' : 'remove'; + var element, i, ilen; + + for (i = 0, ilen = elements.length; i < ilen; ++i) { + element = elements[i]; + if (element) { + this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element); + } + } + + if (mode === 'dataset') { + this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle'](); + } + }, + + /** + * @private + */ + eventHandler: function(e) { + var me = this; + var tooltip = me.tooltip; + + if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { + return; + } + + // Buffer any update calls so that renders do not occur + me._bufferedRender = true; + me._bufferedRequest = null; + + var changed = me.handleEvent(e); + // for smooth tooltip animations issue #4989 + // the tooltip should be the source of change + // Animation check workaround: + // tooltip._start will be null when tooltip isn't animating + if (tooltip) { + changed = tooltip._start + ? tooltip.handleEvent(e) + : changed | tooltip.handleEvent(e); + } + + core_plugins.notify(me, 'afterEvent', [e]); + + var bufferedRequest = me._bufferedRequest; + if (bufferedRequest) { + // If we have an update that was triggered, we need to do a normal render + me.render(bufferedRequest); + } else if (changed && !me.animating) { + // If entering, leaving, or changing elements, animate the change via pivot + me.stop(); + + // We only need to render at this point. Updating will cause scales to be + // recomputed generating flicker & using more memory than necessary. + me.render({ + duration: me.options.hover.animationDuration, + lazy: true + }); + } + + me._bufferedRender = false; + me._bufferedRequest = null; + + return me; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event the event to handle + * @return {boolean} true if the chart needs to re-render + */ + handleEvent: function(e) { + var me = this; + var options = me.options || {}; + var hoverOptions = options.hover; + var changed = false; + + me.lastActive = me.lastActive || []; + + // Find Active Elements for hover and tooltips + if (e.type === 'mouseout') { + me.active = []; + } else { + me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); + } + + // Invoke onHover hook + // Need to call with native event here to not break backwards compatibility + helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); + + if (e.type === 'mouseup' || e.type === 'click') { + if (options.onClick) { + // Use e.native here for backwards compatibility + options.onClick.call(me, e.native, me.active); + } + } + + // Remove styling for last active (even if it may still be active) + if (me.lastActive.length) { + me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); + } + + // Built in hover styling + if (me.active.length && hoverOptions.mode) { + me.updateHoverStyle(me.active, hoverOptions.mode, true); + } + + changed = !helpers$1.arrayEquals(me.active, me.lastActive); + + // Remember Last Actives + me.lastActive = me.active; + + return changed; + } +}); + +/** + * NOTE(SB) We actually don't use this container anymore but we need to keep it + * for backward compatibility. Though, it can still be useful for plugins that + * would need to work on multiple charts?! + */ +Chart.instances = {}; + +var core_controller = Chart; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart instead. + * @class Chart.Controller + * @deprecated since version 2.6 + * @todo remove at version 3 + * @private + */ +Chart.Controller = Chart; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +Chart.types = {}; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.configMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.configMerge = mergeConfig; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.scaleMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.scaleMerge = mergeScaleConfig; + +var core_helpers = function() { + + // -- Basic js utility methods + + helpers$1.where = function(collection, filterCallback) { + if (helpers$1.isArray(collection) && Array.prototype.filter) { + return collection.filter(filterCallback); + } + var filtered = []; + + helpers$1.each(collection, function(item) { + if (filterCallback(item)) { + filtered.push(item); + } + }); + + return filtered; + }; + helpers$1.findIndex = Array.prototype.findIndex ? + function(array, callback, scope) { + return array.findIndex(callback, scope); + } : + function(array, callback, scope) { + scope = scope === undefined ? array : scope; + for (var i = 0, ilen = array.length; i < ilen; ++i) { + if (callback.call(scope, array[i], i, array)) { + return i; + } + } + return -1; + }; + helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to start of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = -1; + } + for (var i = startIndex + 1; i < arrayToSearch.length; i++) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to end of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = arrayToSearch.length; + } + for (var i = startIndex - 1; i >= 0; i--) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + + // -- Math methods + helpers$1.isNumber = function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + helpers$1.almostEquals = function(x, y, epsilon) { + return Math.abs(x - y) < epsilon; + }; + helpers$1.almostWhole = function(x, epsilon) { + var rounded = Math.round(x); + return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x); + }; + helpers$1.max = function(array) { + return array.reduce(function(max, value) { + if (!isNaN(value)) { + return Math.max(max, value); + } + return max; + }, Number.NEGATIVE_INFINITY); + }; + helpers$1.min = function(array) { + return array.reduce(function(min, value) { + if (!isNaN(value)) { + return Math.min(min, value); + } + return min; + }, Number.POSITIVE_INFINITY); + }; + helpers$1.sign = Math.sign ? + function(x) { + return Math.sign(x); + } : + function(x) { + x = +x; // convert to a number + if (x === 0 || isNaN(x)) { + return x; + } + return x > 0 ? 1 : -1; + }; + helpers$1.toRadians = function(degrees) { + return degrees * (Math.PI / 180); + }; + helpers$1.toDegrees = function(radians) { + return radians * (180 / Math.PI); + }; + + /** + * Returns the number of decimal places + * i.e. the number of digits after the decimal point, of the value of this Number. + * @param {number} x - A number. + * @returns {number} The number of decimal places. + * @private + */ + helpers$1._decimalPlaces = function(x) { + if (!helpers$1.isFinite(x)) { + return; + } + var e = 1; + var p = 0; + while (Math.round(x * e) / e !== x) { + e *= 10; + p++; + } + return p; + }; + + // Gets the angle from vertical upright to the point about a centre. + helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { + var distanceFromXCenter = anglePoint.x - centrePoint.x; + var distanceFromYCenter = anglePoint.y - centrePoint.y; + var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + + var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); + + if (angle < (-0.5 * Math.PI)) { + angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + + return { + angle: angle, + distance: radialDistanceFromCenter + }; + }; + helpers$1.distanceBetweenPoints = function(pt1, pt2) { + return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + }; + + /** + * Provided for backward compatibility, not available anymore + * @function Chart.helpers.aliasPixel + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ + helpers$1.aliasPixel = function(pixelWidth) { + return (pixelWidth % 2 === 0) ? 0 : 0.5; + }; + + /** + * Returns the aligned pixel value to avoid anti-aliasing blur + * @param {Chart} chart - The chart instance. + * @param {number} pixel - A pixel value. + * @param {number} width - The width of the element. + * @returns {number} The aligned pixel value. + * @private + */ + helpers$1._alignPixel = function(chart, pixel, width) { + var devicePixelRatio = chart.currentDevicePixelRatio; + var halfWidth = width / 2; + return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; + }; + + helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + + // This function must also respect "skipped" points + + var previous = firstPoint.skip ? middlePoint : firstPoint; + var current = middlePoint; + var next = afterPoint.skip ? middlePoint : afterPoint; + + var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); + var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); + + var s01 = d01 / (d01 + d12); + var s12 = d12 / (d01 + d12); + + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + + var fa = t * s01; // scaling factor for triangle Ta + var fb = t * s12; + + return { + previous: { + x: current.x - fa * (next.x - previous.x), + y: current.y - fa * (next.y - previous.y) + }, + next: { + x: current.x + fb * (next.x - previous.x), + y: current.y + fb * (next.y - previous.y) + } + }; + }; + helpers$1.EPSILON = Number.EPSILON || 1e-14; + helpers$1.splineCurveMonotone = function(points) { + // This function calculates Bézier control points in a similar way than |splineCurve|, + // but preserves monotonicity of the provided data and ensures no local extremums are added + // between the dataset discrete points due to the interpolation. + // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + var pointsWithTangents = (points || []).map(function(point) { + return { + model: point._model, + deltaK: 0, + mK: 0 + }; + }); + + // Calculate slopes (deltaK) and initialize tangents (mK) + var pointsLen = pointsWithTangents.length; + var i, pointBefore, pointCurrent, pointAfter; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointAfter && !pointAfter.model.skip) { + var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); + + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; + } + + if (!pointBefore || pointBefore.model.skip) { + pointCurrent.mK = pointCurrent.deltaK; + } else if (!pointAfter || pointAfter.model.skip) { + pointCurrent.mK = pointBefore.deltaK; + } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { + pointCurrent.mK = 0; + } else { + pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; + } + } + + // Adjust tangents to ensure monotonic properties + var alphaK, betaK, tauK, squaredMagnitude; + for (i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointsWithTangents[i]; + pointAfter = pointsWithTangents[i + 1]; + if (pointCurrent.model.skip || pointAfter.model.skip) { + continue; + } + + if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { + pointCurrent.mK = pointAfter.mK = 0; + continue; + } + + alphaK = pointCurrent.mK / pointCurrent.deltaK; + betaK = pointAfter.mK / pointCurrent.deltaK; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) { + continue; + } + + tauK = 3 / Math.sqrt(squaredMagnitude); + pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; + pointAfter.mK = betaK * tauK * pointCurrent.deltaK; + } + + // Compute control points + var deltaX; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointBefore && !pointBefore.model.skip) { + deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; + pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; + pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; + } + if (pointAfter && !pointAfter.model.skip) { + deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; + pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; + pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; + } + } + }; + helpers$1.nextItem = function(collection, index, loop) { + if (loop) { + return index >= collection.length - 1 ? collection[0] : collection[index + 1]; + } + return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; + }; + helpers$1.previousItem = function(collection, index, loop) { + if (loop) { + return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; + } + return index <= 0 ? collection[0] : collection[index - 1]; + }; + // Implementation of the nice number algorithm used in determining where axis labels will go + helpers$1.niceNum = function(range, round) { + var exponent = Math.floor(helpers$1.log10(range)); + var fraction = range / Math.pow(10, exponent); + var niceFraction; + + if (round) { + if (fraction < 1.5) { + niceFraction = 1; + } else if (fraction < 3) { + niceFraction = 2; + } else if (fraction < 7) { + niceFraction = 5; + } else { + niceFraction = 10; + } + } else if (fraction <= 1.0) { + niceFraction = 1; + } else if (fraction <= 2) { + niceFraction = 2; + } else if (fraction <= 5) { + niceFraction = 5; + } else { + niceFraction = 10; + } + + return niceFraction * Math.pow(10, exponent); + }; + // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + helpers$1.requestAnimFrame = (function() { + if (typeof window === 'undefined') { + return function(callback) { + callback(); + }; + } + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + }()); + // -- DOM methods + helpers$1.getRelativePosition = function(evt, chart) { + var mouseX, mouseY; + var e = evt.originalEvent || evt; + var canvas = evt.target || evt.srcElement; + var boundingRect = canvas.getBoundingClientRect(); + + var touches = e.touches; + if (touches && touches.length > 0) { + mouseX = touches[0].clientX; + mouseY = touches[0].clientY; + + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + + // Scale mouse coordinates into canvas coordinates + // by following the pattern laid out by 'jerryj' in the comments of + // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ + var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); + var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); + var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); + var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); + var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; + var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; + + // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However + // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here + mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); + mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); + + return { + x: mouseX, + y: mouseY + }; + + }; + + // Private helper function to convert max-width/max-height values that may be percentages into a number + function parseMaxStyle(styleValue, node, parentProperty) { + var valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + + return valueInPixels; + } + + /** + * Returns if the given value contains an effective constraint. + * @private + */ + function isConstrainedValue(value) { + return value !== undefined && value !== null && value !== 'none'; + } + + /** + * Returns the max width or height of the given DOM node in a cross-browser compatible fashion + * @param {HTMLElement} domNode - the node to check the constraint on + * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') + * @param {string} percentageProperty - property of parent to use when calculating width as a percentage + * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} + */ + function getConstraintDimension(domNode, maxStyle, percentageProperty) { + var view = document.defaultView; + var parentNode = helpers$1._getParentNode(domNode); + var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; + var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; + var hasCNode = isConstrainedValue(constrainedNode); + var hasCContainer = isConstrainedValue(constrainedContainer); + var infinity = Number.POSITIVE_INFINITY; + + if (hasCNode || hasCContainer) { + return Math.min( + hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, + hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); + } + + return 'none'; + } + // returns Number or undefined if no constraint + helpers$1.getConstraintWidth = function(domNode) { + return getConstraintDimension(domNode, 'max-width', 'clientWidth'); + }; + // returns Number or undefined if no constraint + helpers$1.getConstraintHeight = function(domNode) { + return getConstraintDimension(domNode, 'max-height', 'clientHeight'); + }; + /** + * @private + */ + helpers$1._calculatePadding = function(container, padding, parentDimension) { + padding = helpers$1.getStyle(container, padding); + + return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); + }; + /** + * @private + */ + helpers$1._getParentNode = function(domNode) { + var parent = domNode.parentNode; + if (parent && parent.toString() === '[object ShadowRoot]') { + parent = parent.host; + } + return parent; + }; + helpers$1.getMaximumWidth = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientWidth; + } + + var clientWidth = container.clientWidth; + var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); + var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); + + var w = clientWidth - paddingLeft - paddingRight; + var cw = helpers$1.getConstraintWidth(domNode); + return isNaN(cw) ? w : Math.min(w, cw); + }; + helpers$1.getMaximumHeight = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientHeight; + } + + var clientHeight = container.clientHeight; + var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); + var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); + + var h = clientHeight - paddingTop - paddingBottom; + var ch = helpers$1.getConstraintHeight(domNode); + return isNaN(ch) ? h : Math.min(h, ch); + }; + helpers$1.getStyle = function(el, property) { + return el.currentStyle ? + el.currentStyle[property] : + document.defaultView.getComputedStyle(el, null).getPropertyValue(property); + }; + helpers$1.retinaScale = function(chart, forceRatio) { + var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; + if (pixelRatio === 1) { + return; + } + + var canvas = chart.canvas; + var height = chart.height; + var width = chart.width; + + canvas.height = height * pixelRatio; + canvas.width = width * pixelRatio; + chart.ctx.scale(pixelRatio, pixelRatio); + + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + if (!canvas.style.height && !canvas.style.width) { + canvas.style.height = height + 'px'; + canvas.style.width = width + 'px'; + } + }; + // -- Canvas methods + helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; + }; + helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + var data = cache.data = cache.data || {}; + var gc = cache.garbageCollect = cache.garbageCollect || []; + + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + + ctx.font = font; + var longest = 0; + var ilen = arrayOfThings.length; + var i, j, jlen, thing, nestedThing; + for (i = 0; i < ilen; i++) { + thing = arrayOfThings[i]; + + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { + longest = helpers$1.measureText(ctx, data, gc, longest, thing); + } else if (helpers$1.isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + for (j = 0, jlen = thing.length; j < jlen; j++) { + nestedThing = thing[j]; + // Undefined strings and arrays should not be measured + if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { + longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); + } + } + } + } + + var gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + return longest; + }; + helpers$1.measureText = function(ctx, data, gc, longest, string) { + var textWidth = data[string]; + if (!textWidth) { + textWidth = data[string] = ctx.measureText(string).width; + gc.push(string); + } + if (textWidth > longest) { + longest = textWidth; + } + return longest; + }; + + /** + * @deprecated + */ + helpers$1.numberOfLabelLines = function(arrayOfThings) { + var numberOfLines = 1; + helpers$1.each(arrayOfThings, function(thing) { + if (helpers$1.isArray(thing)) { + if (thing.length > numberOfLines) { + numberOfLines = thing.length; + } + } + }); + return numberOfLines; + }; + + helpers$1.color = !chartjsColor ? + function(value) { + console.error('Color.js not found!'); + return value; + } : + function(value) { + /* global CanvasGradient */ + if (value instanceof CanvasGradient) { + value = core_defaults.global.defaultColor; + } + + return chartjsColor(value); + }; + + helpers$1.getHoverColor = function(colorValue) { + /* global CanvasPattern */ + return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? + colorValue : + helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); + }; +}; + +function abstract() { + throw new Error( + 'This method is not implemented: either no adapter can ' + + 'be found or an incomplete integration was provided.' + ); +} + +/** + * Date adapter (current used by the time scale) + * @namespace Chart._adapters._date + * @memberof Chart._adapters + * @private + */ + +/** + * Currently supported unit string values. + * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} + * @memberof Chart._adapters._date + * @name Unit + */ + +/** + * @class + */ +function DateAdapter(options) { + this.options = options || {}; +} + +helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { + /** + * Returns a map of time formats for the supported formatting units defined + * in Unit as well as 'datetime' representing a detailed date/time string. + * @returns {{string: string}} + */ + formats: abstract, + + /** + * Parses the given `value` and return the associated timestamp. + * @param {any} value - the value to parse (usually comes from the data) + * @param {string} [format] - the expected data format + * @returns {(number|null)} + * @function + */ + parse: abstract, + + /** + * Returns the formatted date in the specified `format` for a given `timestamp`. + * @param {number} timestamp - the timestamp to format + * @param {string} format - the date/time token + * @return {string} + * @function + */ + format: abstract, + + /** + * Adds the specified `amount` of `unit` to the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {number} amount - the amount to add + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + add: abstract, + + /** + * Returns the number of `unit` between the given timestamps. + * @param {number} max - the input timestamp (reference) + * @param {number} min - the timestamp to substract + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + diff: abstract, + + /** + * Returns start of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @param {number} [weekday] - the ISO day of the week with 1 being Monday + * and 7 being Sunday (only needed if param *unit* is `isoWeek`). + * @function + */ + startOf: abstract, + + /** + * Returns end of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @function + */ + endOf: abstract, + + // DEPRECATIONS + + /** + * Provided for backward compatibility for scale.getValueForPixel(), + * this method should be overridden only by the moment adapter. + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(value) { + return value; + } +}); + +DateAdapter.override = function(members) { + helpers$1.extend(DateAdapter.prototype, members); +}; + +var _date = DateAdapter; + +var core_adapters = { + _date: _date +}; + +/** + * Namespace to hold static tick generation functions + * @namespace Chart.Ticks + */ +var core_ticks = { + /** + * Namespace to hold formatters for different types of ticks + * @namespace Chart.Ticks.formatters + */ + formatters: { + /** + * Formatter for value labels + * @method Chart.Ticks.formatters.values + * @param value the value to display + * @return {string|string[]} the label to display + */ + values: function(value) { + return helpers$1.isArray(value) ? value : '' + value; + }, + + /** + * Formatter for linear numeric ticks + * @method Chart.Ticks.formatters.linear + * @param tickValue {number} the value to be formatted + * @param index {number} the position of the tickValue parameter in the ticks array + * @param ticks {number[]} the list of ticks being converted + * @return {string} string representation of the tickValue parameter + */ + linear: function(tickValue, index, ticks) { + // If we have lots of ticks, don't use the ones + var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; + + // If we have a number like 2.5 as the delta, figure out how many decimal places we need + if (Math.abs(delta) > 1) { + if (tickValue !== Math.floor(tickValue)) { + // not an integer + delta = tickValue - Math.floor(tickValue); + } + } + + var logDelta = helpers$1.log10(Math.abs(delta)); + var tickString = ''; + + if (tickValue !== 0) { + var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); + if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation + var logTick = helpers$1.log10(Math.abs(tickValue)); + var numExponential = Math.floor(logTick) - Math.floor(logDelta); + numExponential = Math.max(Math.min(numExponential, 20), 0); + tickString = tickValue.toExponential(numExponential); + } else { + var numDecimal = -1 * Math.floor(logDelta); + numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } + } else { + tickString = '0'; // never show decimal places for 0 + } + + return tickString; + }, + + logarithmic: function(tickValue, index, ticks) { + var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); + + if (tickValue === 0) { + return '0'; + } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { + return tickValue.toExponential(); + } + return ''; + } + } +}; + +var isArray = helpers$1.isArray; +var isNullOrUndef = helpers$1.isNullOrUndef; +var valueOrDefault$a = helpers$1.valueOrDefault; +var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; + +core_defaults._set('scale', { + display: true, + position: 'left', + offset: false, + + // grid line settings + gridLines: { + display: true, + color: 'rgba(0,0,0,0.1)', + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickMarkLength: 10, + zeroLineWidth: 1, + zeroLineColor: 'rgba(0,0,0,0.25)', + zeroLineBorderDash: [], + zeroLineBorderDashOffset: 0.0, + offsetGridLines: false, + borderDash: [], + borderDashOffset: 0.0 + }, + + // scale label + scaleLabel: { + // display property + display: false, + + // actual label + labelString: '', + + // top/bottom padding + padding: { + top: 4, + bottom: 4 + } + }, + + // label settings + ticks: { + beginAtZero: false, + minRotation: 0, + maxRotation: 50, + mirror: false, + padding: 0, + reverse: false, + display: true, + autoSkip: true, + autoSkipPadding: 0, + labelOffset: 0, + // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. + callback: core_ticks.formatters.values, + minor: {}, + major: {} + } +}); + +/** Returns a new array containing numItems from arr */ +function sample(arr, numItems) { + var result = []; + var increment = arr.length / numItems; + var i = 0; + var len = arr.length; + + for (; i < len; i += increment) { + result.push(arr[Math.floor(i)]); + } + return result; +} + +function getPixelForGridLine(scale, index, offsetGridLines) { + var length = scale.getTicks().length; + var validIndex = Math.min(index, length - 1); + var lineValue = scale.getPixelForTick(validIndex); + var start = scale._startPixel; + var end = scale._endPixel; + var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. + var offset; + + if (offsetGridLines) { + if (length === 1) { + offset = Math.max(lineValue - start, end - lineValue); + } else if (index === 0) { + offset = (scale.getPixelForTick(1) - lineValue) / 2; + } else { + offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; + } + lineValue += validIndex < index ? offset : -offset; + + // Return undefined if the pixel is out of the range + if (lineValue < start - epsilon || lineValue > end + epsilon) { + return; + } + } + return lineValue; +} + +function garbageCollect(caches, length) { + helpers$1.each(caches, function(cache) { + var gc = cache.gc; + var gcLen = gc.length / 2; + var i; + if (gcLen > length) { + for (i = 0; i < gcLen; ++i) { + delete cache.data[gc[i]]; + } + gc.splice(0, gcLen); + } + }); +} + +/** + * Returns {width, height, offset} objects for the first, last, widest, highest tick + * labels where offset indicates the anchor point offset from the top in pixels. + */ +function computeLabelSizes(ctx, tickFonts, ticks, caches) { + var length = ticks.length; + var widths = []; + var heights = []; + var offsets = []; + var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest; + + for (i = 0; i < length; ++i) { + label = ticks[i].label; + tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor; + ctx.font = fontString = tickFont.string; + cache = caches[fontString] = caches[fontString] || {data: {}, gc: []}; + lineHeight = tickFont.lineHeight; + width = height = 0; + // Undefined labels and arrays should not be measured + if (!isNullOrUndef(label) && !isArray(label)) { + width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label); + height = lineHeight; + } else if (isArray(label)) { + // if it is an array let's measure each element + for (j = 0, jlen = label.length; j < jlen; ++j) { + nestedLabel = label[j]; + // Undefined labels and arrays should not be measured + if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { + width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel); + height += lineHeight; + } + } + } + widths.push(width); + heights.push(height); + offsets.push(lineHeight / 2); + } + garbageCollect(caches, length); + + widest = widths.indexOf(Math.max.apply(null, widths)); + highest = heights.indexOf(Math.max.apply(null, heights)); + + function valueAt(idx) { + return { + width: widths[idx] || 0, + height: heights[idx] || 0, + offset: offsets[idx] || 0 + }; + } + + return { + first: valueAt(0), + last: valueAt(length - 1), + widest: valueAt(widest), + highest: valueAt(highest) + }; +} + +function getTickMarkLength(options) { + return options.drawTicks ? options.tickMarkLength : 0; +} + +function getScaleLabelHeight(options) { + var font, padding; + + if (!options.display) { + return 0; + } + + font = helpers$1.options._parseFont(options); + padding = helpers$1.options.toPadding(options.padding); + + return font.lineHeight + padding.height; +} + +function parseFontOptions(options, nestedOpts) { + return helpers$1.extend(helpers$1.options._parseFont({ + fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily), + fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize), + fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle), + lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight) + }), { + color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor]) + }); +} + +function parseTickFontOptions(options) { + var minor = parseFontOptions(options, options.minor); + var major = options.major.enabled ? parseFontOptions(options, options.major) : minor; + + return {minor: minor, major: major}; +} + +function nonSkipped(ticksToFilter) { + var filtered = []; + var item, index, len; + for (index = 0, len = ticksToFilter.length; index < len; ++index) { + item = ticksToFilter[index]; + if (typeof item._index !== 'undefined') { + filtered.push(item); + } + } + return filtered; +} + +function getEvenSpacing(arr) { + var len = arr.length; + var i, diff; + + if (len < 2) { + return false; + } + + for (diff = arr[0], i = 1; i < len; ++i) { + if (arr[i] - arr[i - 1] !== diff) { + return false; + } + } + return diff; +} + +function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) { + var evenMajorSpacing = getEvenSpacing(majorIndices); + var spacing = (ticks.length - 1) / ticksLimit; + var factors, factor, i, ilen; + + // If the major ticks are evenly spaced apart, place the minor ticks + // so that they divide the major ticks into even chunks + if (!evenMajorSpacing) { + return Math.max(spacing, 1); + } + + factors = helpers$1.math._factorize(evenMajorSpacing); + for (i = 0, ilen = factors.length - 1; i < ilen; i++) { + factor = factors[i]; + if (factor > spacing) { + return factor; + } + } + return Math.max(spacing, 1); +} + +function getMajorIndices(ticks) { + var result = []; + var i, ilen; + for (i = 0, ilen = ticks.length; i < ilen; i++) { + if (ticks[i].major) { + result.push(i); + } + } + return result; +} + +function skipMajors(ticks, majorIndices, spacing) { + var count = 0; + var next = majorIndices[0]; + var i, tick; + + spacing = Math.ceil(spacing); + for (i = 0; i < ticks.length; i++) { + tick = ticks[i]; + if (i === next) { + tick._index = i; + count++; + next = majorIndices[count * spacing]; + } else { + delete tick.label; + } + } +} + +function skip(ticks, spacing, majorStart, majorEnd) { + var start = valueOrDefault$a(majorStart, 0); + var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length); + var count = 0; + var length, i, tick, next; + + spacing = Math.ceil(spacing); + if (majorEnd) { + length = majorEnd - majorStart; + spacing = length / Math.floor(length / spacing); + } + + next = start; + + while (next < 0) { + count++; + next = Math.round(start + count * spacing); + } + + for (i = Math.max(start, 0); i < end; i++) { + tick = ticks[i]; + if (i === next) { + tick._index = i; + count++; + next = Math.round(start + count * spacing); + } else { + delete tick.label; + } + } +} + +var Scale = core_element.extend({ + + zeroLineIndex: 0, + + /** + * Get the padding needed for the scale + * @method getPadding + * @private + * @returns {Padding} the necessary padding + */ + getPadding: function() { + var me = this; + return { + left: me.paddingLeft || 0, + top: me.paddingTop || 0, + right: me.paddingRight || 0, + bottom: me.paddingBottom || 0 + }; + }, + + /** + * Returns the scale tick objects ({label, major}) + * @since 2.7 + */ + getTicks: function() { + return this._ticks; + }, + + /** + * @private + */ + _getLabels: function() { + var data = this.chart.data; + return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || []; + }, + + // These methods are ordered by lifecyle. Utilities then follow. + // Any function defined here is inherited by all scale types. + // Any function can be extended by the scale type + + /** + * Provided for backward compatibility, not available anymore + * @function Chart.Scale.mergeTicksOptions + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ + mergeTicksOptions: function() { + // noop + }, + + beforeUpdate: function() { + helpers$1.callback(this.options.beforeUpdate, [this]); + }, + + /** + * @param {number} maxWidth - the max width in pixels + * @param {number} maxHeight - the max height in pixels + * @param {object} margins - the space between the edge of the other scales and edge of the chart + * This space comes from two sources: + * - padding - space that's required to show the labels at the edges of the scale + * - thickness of scales or legends in another orientation + */ + update: function(maxWidth, maxHeight, margins) { + var me = this; + var tickOpts = me.options.ticks; + var sampleSize = tickOpts.sampleSize; + var i, ilen, labels, ticks, samplingEnabled; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = helpers$1.extend({ + left: 0, + right: 0, + top: 0, + bottom: 0 + }, margins); + + me._ticks = null; + me.ticks = null; + me._labelSizes = null; + me._maxLabelLines = 0; + me.longestLabelWidth = 0; + me.longestTextCache = me.longestTextCache || {}; + me._gridLineItems = null; + me._labelItems = null; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + + // Data min/max + me.beforeDataLimits(); + me.determineDataLimits(); + me.afterDataLimits(); + + // Ticks - `this.ticks` is now DEPRECATED! + // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member + // and must not be accessed directly from outside this class. `this.ticks` being + // around for long time and not marked as private, we can't change its structure + // without unexpected breaking changes. If you need to access the scale ticks, + // use scale.getTicks() instead. + + me.beforeBuildTicks(); + + // New implementations should return an array of objects but for BACKWARD COMPAT, + // we still support no return (`this.ticks` internally set by calling this method). + ticks = me.buildTicks() || []; + + // Allow modification of ticks in callback. + ticks = me.afterBuildTicks(ticks) || ticks; + + // Ensure ticks contains ticks in new tick format + if ((!ticks || !ticks.length) && me.ticks) { + ticks = []; + for (i = 0, ilen = me.ticks.length; i < ilen; ++i) { + ticks.push({ + value: me.ticks[i], + major: false + }); + } + } + + me._ticks = ticks; + + // Compute tick rotation and fit using a sampled subset of labels + // We generally don't need to compute the size of every single label for determining scale size + samplingEnabled = sampleSize < ticks.length; + labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks); + + // _configure is called twice, once here, once from core.controller.updateLayout. + // Here we haven't been positioned yet, but dimensions are correct. + // Variables set in _configure are needed for calculateTickRotation, and + // it's ok that coordinates are not correct there, only dimensions matter. + me._configure(); + + // Tick Rotation + me.beforeCalculateTickRotation(); + me.calculateTickRotation(); + me.afterCalculateTickRotation(); + + me.beforeFit(); + me.fit(); + me.afterFit(); + + // Auto-skip + me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks; + + if (samplingEnabled) { + // Generate labels using all non-skipped ticks + labels = me._convertTicksToLabels(me._ticksToDraw); + } + + me.ticks = labels; // BACKWARD COMPATIBILITY + + // IMPORTANT: after this point, we consider that `this.ticks` will NEVER change! + + me.afterUpdate(); + + // TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused + // make maxWidth and maxHeight private + return me.minSize; + }, + + /** + * @private + */ + _configure: function() { + var me = this; + var reversePixels = me.options.ticks.reverse; + var startPixel, endPixel; + + if (me.isHorizontal()) { + startPixel = me.left; + endPixel = me.right; + } else { + startPixel = me.top; + endPixel = me.bottom; + // by default vertical scales are from bottom to top, so pixels are reversed + reversePixels = !reversePixels; + } + me._startPixel = startPixel; + me._endPixel = endPixel; + me._reversePixels = reversePixels; + me._length = endPixel - startPixel; + }, + + afterUpdate: function() { + helpers$1.callback(this.options.afterUpdate, [this]); + }, + + // + + beforeSetDimensions: function() { + helpers$1.callback(this.options.beforeSetDimensions, [this]); + }, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + }, + afterSetDimensions: function() { + helpers$1.callback(this.options.afterSetDimensions, [this]); + }, + + // Data limits + beforeDataLimits: function() { + helpers$1.callback(this.options.beforeDataLimits, [this]); + }, + determineDataLimits: helpers$1.noop, + afterDataLimits: function() { + helpers$1.callback(this.options.afterDataLimits, [this]); + }, + + // + beforeBuildTicks: function() { + helpers$1.callback(this.options.beforeBuildTicks, [this]); + }, + buildTicks: helpers$1.noop, + afterBuildTicks: function(ticks) { + var me = this; + // ticks is empty for old axis implementations here + if (isArray(ticks) && ticks.length) { + return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); + } + // Support old implementations (that modified `this.ticks` directly in buildTicks) + me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; + return ticks; + }, + + beforeTickToLabelConversion: function() { + helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); + }, + convertTicksToLabels: function() { + var me = this; + // Convert ticks to strings + var tickOpts = me.options.ticks; + me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); + }, + afterTickToLabelConversion: function() { + helpers$1.callback(this.options.afterTickToLabelConversion, [this]); + }, + + // + + beforeCalculateTickRotation: function() { + helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); + }, + calculateTickRotation: function() { + var me = this; + var options = me.options; + var tickOpts = options.ticks; + var numTicks = me.getTicks().length; + var minRotation = tickOpts.minRotation || 0; + var maxRotation = tickOpts.maxRotation; + var labelRotation = minRotation; + var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal; + + if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) { + me.labelRotation = minRotation; + return; + } + + labelSizes = me._getLabelSizes(); + maxLabelWidth = labelSizes.widest.width; + maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset; + + // Estimate the width of each grid based on the canvas width, the maximum + // label width and the number of tick intervals + maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth); + tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1); + + // Allow 3 pixels x2 padding either side for label readability + if (maxLabelWidth + 6 > tickWidth) { + tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1)); + maxHeight = me.maxHeight - getTickMarkLength(options.gridLines) + - tickOpts.padding - getScaleLabelHeight(options.scaleLabel); + maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight); + labelRotation = helpers$1.toDegrees(Math.min( + Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)), + Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal) + )); + labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation)); + } + + me.labelRotation = labelRotation; + }, + afterCalculateTickRotation: function() { + helpers$1.callback(this.options.afterCalculateTickRotation, [this]); + }, + + // + + beforeFit: function() { + helpers$1.callback(this.options.beforeFit, [this]); + }, + fit: function() { + var me = this; + // Reset + var minSize = me.minSize = { + width: 0, + height: 0 + }; + + var chart = me.chart; + var opts = me.options; + var tickOpts = opts.ticks; + var scaleLabelOpts = opts.scaleLabel; + var gridLineOpts = opts.gridLines; + var display = me._isVisible(); + var isBottom = opts.position === 'bottom'; + var isHorizontal = me.isHorizontal(); + + // Width + if (isHorizontal) { + minSize.width = me.maxWidth; + } else if (display) { + minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); + } + + // height + if (!isHorizontal) { + minSize.height = me.maxHeight; // fill all the height + } else if (display) { + minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); + } + + // Don't bother fitting the ticks if we are not showing the labels + if (tickOpts.display && display) { + var tickFonts = parseTickFontOptions(tickOpts); + var labelSizes = me._getLabelSizes(); + var firstLabelSize = labelSizes.first; + var lastLabelSize = labelSizes.last; + var widestLabelSize = labelSizes.widest; + var highestLabelSize = labelSizes.highest; + var lineSpace = tickFonts.minor.lineHeight * 0.4; + var tickPadding = tickOpts.padding; + + if (isHorizontal) { + // A horizontal axis is more constrained by the height. + var isRotated = me.labelRotation !== 0; + var angleRadians = helpers$1.toRadians(me.labelRotation); + var cosRotation = Math.cos(angleRadians); + var sinRotation = Math.sin(angleRadians); + + var labelHeight = sinRotation * widestLabelSize.width + + cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0)) + + (isRotated ? 0 : lineSpace); // padding + + minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); + + var offsetLeft = me.getPixelForTick(0) - me.left; + var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1); + var paddingLeft, paddingRight; + + // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned + // which means that the right padding is dominated by the font height + if (isRotated) { + paddingLeft = isBottom ? + cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset : + sinRotation * (firstLabelSize.height - firstLabelSize.offset); + paddingRight = isBottom ? + sinRotation * (lastLabelSize.height - lastLabelSize.offset) : + cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset; + } else { + paddingLeft = firstLabelSize.width / 2; + paddingRight = lastLabelSize.width / 2; + } + + // Adjust padding taking into account changes in offsets + // and add 3 px to move away from canvas edges + me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3; + me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3; + } else { + // A vertical axis is more constrained by the width. Labels are the + // dominant factor here, so get that length first and account for padding + var labelWidth = tickOpts.mirror ? 0 : + // use lineSpace for consistency with horizontal axis + // tickPadding is not implemented for horizontal + widestLabelSize.width + tickPadding + lineSpace; + + minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth); + + me.paddingTop = firstLabelSize.height / 2; + me.paddingBottom = lastLabelSize.height / 2; + } + } + + me.handleMargins(); + + if (isHorizontal) { + me.width = me._length = chart.width - me.margins.left - me.margins.right; + me.height = minSize.height; + } else { + me.width = minSize.width; + me.height = me._length = chart.height - me.margins.top - me.margins.bottom; + } + }, + + /** + * Handle margins and padding interactions + * @private + */ + handleMargins: function() { + var me = this; + if (me.margins) { + me.margins.left = Math.max(me.paddingLeft, me.margins.left); + me.margins.top = Math.max(me.paddingTop, me.margins.top); + me.margins.right = Math.max(me.paddingRight, me.margins.right); + me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom); + } + }, + + afterFit: function() { + helpers$1.callback(this.options.afterFit, [this]); + }, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + isFullWidth: function() { + return this.options.fullWidth; + }, + + // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not + getRightValue: function(rawValue) { + // Null and undefined values first + if (isNullOrUndef(rawValue)) { + return NaN; + } + // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values + if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { + return NaN; + } + + // If it is in fact an object, dive in one more level + if (rawValue) { + if (this.isHorizontal()) { + if (rawValue.x !== undefined) { + return this.getRightValue(rawValue.x); + } + } else if (rawValue.y !== undefined) { + return this.getRightValue(rawValue.y); + } + } + + // Value is good, return it + return rawValue; + }, + + _convertTicksToLabels: function(ticks) { + var me = this; + var labels, i, ilen; + + me.ticks = ticks.map(function(tick) { + return tick.value; + }); + + me.beforeTickToLabelConversion(); + + // New implementations should return the formatted tick labels but for BACKWARD + // COMPAT, we still support no return (`this.ticks` internally changed by calling + // this method and supposed to contain only string values). + labels = me.convertTicksToLabels(ticks) || me.ticks; + + me.afterTickToLabelConversion(); + + // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + ticks[i].label = labels[i]; + } + + return labels; + }, + + /** + * @private + */ + _getLabelSizes: function() { + var me = this; + var labelSizes = me._labelSizes; + + if (!labelSizes) { + me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache); + me.longestLabelWidth = labelSizes.widest.width; + } + + return labelSizes; + }, + + /** + * @private + */ + _parseValue: function(value) { + var start, end, min, max; + + if (isArray(value)) { + start = +this.getRightValue(value[0]); + end = +this.getRightValue(value[1]); + min = Math.min(start, end); + max = Math.max(start, end); + } else { + value = +this.getRightValue(value); + start = undefined; + end = value; + min = value; + max = value; + } + + return { + min: min, + max: max, + start: start, + end: end + }; + }, + + /** + * @private + */ + _getScaleLabel: function(rawValue) { + var v = this._parseValue(rawValue); + if (v.start !== undefined) { + return '[' + v.start + ', ' + v.end + ']'; + } + + return +this.getRightValue(rawValue); + }, + + /** + * Used to get the value to display in the tooltip for the data at the given index + * @param index + * @param datasetIndex + */ + getLabelForIndex: helpers$1.noop, + + /** + * Returns the location of the given data point. Value can either be an index or a numerical value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param value + * @param index + * @param datasetIndex + */ + getPixelForValue: helpers$1.noop, + + /** + * Used to get the data value from a given pixel. This is the inverse of getPixelForValue + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param pixel + */ + getValueForPixel: helpers$1.noop, + + /** + * Returns the location of the tick at the given index + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForTick: function(index) { + var me = this; + var offset = me.options.offset; + var numTicks = me._ticks.length; + var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1); + + return index < 0 || index > numTicks - 1 + ? null + : me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0)); + }, + + /** + * Utility for getting the pixel location of a percentage of scale + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForDecimal: function(decimal) { + var me = this; + + if (me._reversePixels) { + decimal = 1 - decimal; + } + + return me._startPixel + decimal * me._length; + }, + + getDecimalForPixel: function(pixel) { + var decimal = (pixel - this._startPixel) / this._length; + return this._reversePixels ? 1 - decimal : decimal; + }, + + /** + * Returns the pixel for the minimum chart value + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getBasePixel: function() { + return this.getPixelForValue(this.getBaseValue()); + }, + + getBaseValue: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0; + }, + + /** + * Returns a subset of ticks to be plotted to avoid overlapping labels. + * @private + */ + _autoSkip: function(ticks) { + var me = this; + var tickOpts = me.options.ticks; + var axisLength = me._length; + var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1; + var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; + var numMajorIndices = majorIndices.length; + var first = majorIndices[0]; + var last = majorIndices[numMajorIndices - 1]; + var i, ilen, spacing, avgMajorSpacing; + + // If there are too many major ticks to display them all + if (numMajorIndices > ticksLimit) { + skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit); + return nonSkipped(ticks); + } + + spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit); + + if (numMajorIndices > 0) { + for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) { + skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]); + } + avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null; + skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); + skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); + return nonSkipped(ticks); + } + skip(ticks, spacing); + return nonSkipped(ticks); + }, + + /** + * @private + */ + _tickSize: function() { + var me = this; + var optionTicks = me.options.ticks; + + // Calculate space needed by label in axis direction. + var rot = helpers$1.toRadians(me.labelRotation); + var cos = Math.abs(Math.cos(rot)); + var sin = Math.abs(Math.sin(rot)); + + var labelSizes = me._getLabelSizes(); + var padding = optionTicks.autoSkipPadding || 0; + var w = labelSizes ? labelSizes.widest.width + padding : 0; + var h = labelSizes ? labelSizes.highest.height + padding : 0; + + // Calculate space needed for 1 tick in axis direction. + return me.isHorizontal() + ? h * cos > w * sin ? w / cos : h / sin + : h * sin < w * cos ? h / cos : w / sin; + }, + + /** + * @private + */ + _isVisible: function() { + var me = this; + var chart = me.chart; + var display = me.options.display; + var i, ilen, meta; + + if (display !== 'auto') { + return !!display; + } + + // When 'auto', the scale is visible if at least one associated dataset is visible. + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + if (meta.xAxisID === me.id || meta.yAxisID === me.id) { + return true; + } + } + } + + return false; + }, + + /** + * @private + */ + _computeGridLineItems: function(chartArea) { + var me = this; + var chart = me.chart; + var options = me.options; + var gridLines = options.gridLines; + var position = options.position; + var offsetGridLines = gridLines.offsetGridLines; + var isHorizontal = me.isHorizontal(); + var ticks = me._ticksToDraw; + var ticksLength = ticks.length + (offsetGridLines ? 1 : 0); + + var tl = getTickMarkLength(gridLines); + var items = []; + var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; + var axisHalfWidth = axisWidth / 2; + var alignPixel = helpers$1._alignPixel; + var alignBorderValue = function(pixel) { + return alignPixel(chart, pixel, axisWidth); + }; + var borderValue, i, tick, lineValue, alignedLineValue; + var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset; + + if (position === 'top') { + borderValue = alignBorderValue(me.bottom); + ty1 = me.bottom - tl; + ty2 = borderValue - axisHalfWidth; + y1 = alignBorderValue(chartArea.top) + axisHalfWidth; + y2 = chartArea.bottom; + } else if (position === 'bottom') { + borderValue = alignBorderValue(me.top); + y1 = chartArea.top; + y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; + ty1 = borderValue + axisHalfWidth; + ty2 = me.top + tl; + } else if (position === 'left') { + borderValue = alignBorderValue(me.right); + tx1 = me.right - tl; + tx2 = borderValue - axisHalfWidth; + x1 = alignBorderValue(chartArea.left) + axisHalfWidth; + x2 = chartArea.right; + } else { + borderValue = alignBorderValue(me.left); + x1 = chartArea.left; + x2 = alignBorderValue(chartArea.right) - axisHalfWidth; + tx1 = borderValue + axisHalfWidth; + tx2 = me.left + tl; + } + + for (i = 0; i < ticksLength; ++i) { + tick = ticks[i] || {}; + + // autoskipper skipped this tick (#4635) + if (isNullOrUndef(tick.label) && i < ticks.length) { + continue; + } + + if (i === me.zeroLineIndex && options.offset === offsetGridLines) { + // Draw the first index specially + lineWidth = gridLines.zeroLineWidth; + lineColor = gridLines.zeroLineColor; + borderDash = gridLines.zeroLineBorderDash || []; + borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; + } else { + lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1); + lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)'); + borderDash = gridLines.borderDash || []; + borderDashOffset = gridLines.borderDashOffset || 0.0; + } + + lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines); + + // Skip if the pixel is out of the range + if (lineValue === undefined) { + continue; + } + + alignedLineValue = alignPixel(chart, lineValue, lineWidth); + + if (isHorizontal) { + tx1 = tx2 = x1 = x2 = alignedLineValue; + } else { + ty1 = ty2 = y1 = y2 = alignedLineValue; + } + + items.push({ + tx1: tx1, + ty1: ty1, + tx2: tx2, + ty2: ty2, + x1: x1, + y1: y1, + x2: x2, + y2: y2, + width: lineWidth, + color: lineColor, + borderDash: borderDash, + borderDashOffset: borderDashOffset, + }); + } + + items.ticksLength = ticksLength; + items.borderValue = borderValue; + + return items; + }, + + /** + * @private + */ + _computeLabelItems: function() { + var me = this; + var options = me.options; + var optionTicks = options.ticks; + var position = options.position; + var isMirrored = optionTicks.mirror; + var isHorizontal = me.isHorizontal(); + var ticks = me._ticksToDraw; + var fonts = parseTickFontOptions(optionTicks); + var tickPadding = optionTicks.padding; + var tl = getTickMarkLength(options.gridLines); + var rotation = -helpers$1.toRadians(me.labelRotation); + var items = []; + var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; + + if (position === 'top') { + y = me.bottom - tl - tickPadding; + textAlign = !rotation ? 'center' : 'left'; + } else if (position === 'bottom') { + y = me.top + tl + tickPadding; + textAlign = !rotation ? 'center' : 'right'; + } else if (position === 'left') { + x = me.right - (isMirrored ? 0 : tl) - tickPadding; + textAlign = isMirrored ? 'left' : 'right'; + } else { + x = me.left + (isMirrored ? 0 : tl) + tickPadding; + textAlign = isMirrored ? 'right' : 'left'; + } + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + tick = ticks[i]; + label = tick.label; + + // autoskipper skipped this tick (#4635) + if (isNullOrUndef(label)) { + continue; + } + + pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset; + font = tick.major ? fonts.major : fonts.minor; + lineHeight = font.lineHeight; + lineCount = isArray(label) ? label.length : 1; + + if (isHorizontal) { + x = pixel; + textOffset = position === 'top' + ? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight + : (!rotation ? 0.5 : 0) * lineHeight; + } else { + y = pixel; + textOffset = (1 - lineCount) * lineHeight / 2; + } + + items.push({ + x: x, + y: y, + rotation: rotation, + label: label, + font: font, + textOffset: textOffset, + textAlign: textAlign + }); + } + + return items; + }, + + /** + * @private + */ + _drawGrid: function(chartArea) { + var me = this; + var gridLines = me.options.gridLines; + + if (!gridLines.display) { + return; + } + + var ctx = me.ctx; + var chart = me.chart; + var alignPixel = helpers$1._alignPixel; + var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; + var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea)); + var width, color, i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + width = item.width; + color = item.color; + + if (width && color) { + ctx.save(); + ctx.lineWidth = width; + ctx.strokeStyle = color; + if (ctx.setLineDash) { + ctx.setLineDash(item.borderDash); + ctx.lineDashOffset = item.borderDashOffset; + } + + ctx.beginPath(); + + if (gridLines.drawTicks) { + ctx.moveTo(item.tx1, item.ty1); + ctx.lineTo(item.tx2, item.ty2); + } + + if (gridLines.drawOnChartArea) { + ctx.moveTo(item.x1, item.y1); + ctx.lineTo(item.x2, item.y2); + } + + ctx.stroke(); + ctx.restore(); + } + } + + if (axisWidth) { + // Draw the line at the edge of the axis + var firstLineWidth = axisWidth; + var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1); + var borderValue = items.borderValue; + var x1, x2, y1, y2; + + if (me.isHorizontal()) { + x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; + x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; + y1 = y2 = borderValue; + } else { + y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; + y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; + x1 = x2 = borderValue; + } + + ctx.lineWidth = axisWidth; + ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } + }, + + /** + * @private + */ + _drawLabels: function() { + var me = this; + var optionTicks = me.options.ticks; + + if (!optionTicks.display) { + return; + } + + var ctx = me.ctx; + var items = me._labelItems || (me._labelItems = me._computeLabelItems()); + var i, j, ilen, jlen, item, tickFont, label, y; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + tickFont = item.font; + + // Make sure we draw text in the correct color and font + ctx.save(); + ctx.translate(item.x, item.y); + ctx.rotate(item.rotation); + ctx.font = tickFont.string; + ctx.fillStyle = tickFont.color; + ctx.textBaseline = 'middle'; + ctx.textAlign = item.textAlign; + + label = item.label; + y = item.textOffset; + if (isArray(label)) { + for (j = 0, jlen = label.length; j < jlen; ++j) { + // We just make sure the multiline element is a string here.. + ctx.fillText('' + label[j], 0, y); + y += tickFont.lineHeight; + } + } else { + ctx.fillText(label, 0, y); + } + ctx.restore(); + } + }, + + /** + * @private + */ + _drawTitle: function() { + var me = this; + var ctx = me.ctx; + var options = me.options; + var scaleLabel = options.scaleLabel; + + if (!scaleLabel.display) { + return; + } + + var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor); + var scaleLabelFont = helpers$1.options._parseFont(scaleLabel); + var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); + var halfLineHeight = scaleLabelFont.lineHeight / 2; + var position = options.position; + var rotation = 0; + var scaleLabelX, scaleLabelY; + + if (me.isHorizontal()) { + scaleLabelX = me.left + me.width / 2; // midpoint of the width + scaleLabelY = position === 'bottom' + ? me.bottom - halfLineHeight - scaleLabelPadding.bottom + : me.top + halfLineHeight + scaleLabelPadding.top; + } else { + var isLeft = position === 'left'; + scaleLabelX = isLeft + ? me.left + halfLineHeight + scaleLabelPadding.top + : me.right - halfLineHeight - scaleLabelPadding.top; + scaleLabelY = me.top + me.height / 2; + rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; + } + + ctx.save(); + ctx.translate(scaleLabelX, scaleLabelY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = scaleLabelFontColor; // render in correct colour + ctx.font = scaleLabelFont.string; + ctx.fillText(scaleLabel.labelString, 0, 0); + ctx.restore(); + }, + + draw: function(chartArea) { + var me = this; + + if (!me._isVisible()) { + return; + } + + me._drawGrid(chartArea); + me._drawTitle(); + me._drawLabels(); + }, + + /** + * @private + */ + _layers: function() { + var me = this; + var opts = me.options; + var tz = opts.ticks && opts.ticks.z || 0; + var gz = opts.gridLines && opts.gridLines.z || 0; + + if (!me._isVisible() || tz === gz || me.draw !== me._draw) { + // backward compatibility: draw has been overridden by custom scale + return [{ + z: tz, + draw: function() { + me.draw.apply(me, arguments); + } + }]; + } + + return [{ + z: gz, + draw: function() { + me._drawGrid.apply(me, arguments); + me._drawTitle.apply(me, arguments); + } + }, { + z: tz, + draw: function() { + me._drawLabels.apply(me, arguments); + } + }]; + }, + + /** + * @private + */ + _getMatchingVisibleMetas: function(type) { + var me = this; + var isHorizontal = me.isHorizontal(); + return me.chart._getSortedVisibleDatasetMetas() + .filter(function(meta) { + return (!type || meta.type === type) + && (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id); + }); + } +}); + +Scale.prototype._draw = Scale.prototype.draw; + +var core_scale = Scale; + +var isNullOrUndef$1 = helpers$1.isNullOrUndef; + +var defaultConfig = { + position: 'bottom' +}; + +var scale_category = core_scale.extend({ + determineDataLimits: function() { + var me = this; + var labels = me._getLabels(); + var ticksOpts = me.options.ticks; + var min = ticksOpts.min; + var max = ticksOpts.max; + var minIndex = 0; + var maxIndex = labels.length - 1; + var findIndex; + + if (min !== undefined) { + // user specified min value + findIndex = labels.indexOf(min); + if (findIndex >= 0) { + minIndex = findIndex; + } + } + + if (max !== undefined) { + // user specified max value + findIndex = labels.indexOf(max); + if (findIndex >= 0) { + maxIndex = findIndex; + } + } + + me.minIndex = minIndex; + me.maxIndex = maxIndex; + me.min = labels[minIndex]; + me.max = labels[maxIndex]; + }, + + buildTicks: function() { + var me = this; + var labels = me._getLabels(); + var minIndex = me.minIndex; + var maxIndex = me.maxIndex; + + // If we are viewing some subset of labels, slice the original array + me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var chart = me.chart; + + if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { + return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); + } + + return me._getLabels()[index]; + }, + + _configure: function() { + var me = this; + var offset = me.options.offset; + var ticks = me.ticks; + + core_scale.prototype._configure.call(me); + + if (!me.isHorizontal()) { + // For backward compatibility, vertical category scale reverse is inverted. + me._reversePixels = !me._reversePixels; + } + + if (!ticks) { + return; + } + + me._startValue = me.minIndex - (offset ? 0.5 : 0); + me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1); + }, + + // Used to get data value locations. Value can either be an index or a numerical value + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var valueCategory, labels, idx; + + if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) { + value = me.chart.data.datasets[datasetIndex].data[index]; + } + + // If value is a data object, then index is the index in the data array, + // not the index of the scale. We need to change that. + if (!isNullOrUndef$1(value)) { + valueCategory = me.isHorizontal() ? value.x : value.y; + } + if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { + labels = me._getLabels(); + value = helpers$1.valueOrDefault(valueCategory, value); + idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + if (isNaN(index)) { + index = value; + } + } + return me.getPixelForDecimal((index - me._startValue) / me._valueRange); + }, + + getPixelForTick: function(index) { + var ticks = this.ticks; + return index < 0 || index > ticks.length - 1 + ? null + : this.getPixelForValue(ticks[index], index + this.minIndex); + }, + + getValueForPixel: function(pixel) { + var me = this; + var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange); + return Math.min(Math.max(value, 0), me.ticks.length - 1); + }, + + getBasePixel: function() { + return this.bottom; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults = defaultConfig; +scale_category._defaults = _defaults; + +var noop = helpers$1.noop; +var isNullOrUndef$2 = helpers$1.isNullOrUndef; + +/** + * Generate a set of linear ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks(generationOptions, dataRange) { + var ticks = []; + // To get a "nice" value for the tick spacing, we will use the appropriately named + // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks + // for details. + + var MIN_SPACING = 1e-14; + var stepSize = generationOptions.stepSize; + var unit = stepSize || 1; + var maxNumSpaces = generationOptions.maxTicks - 1; + var min = generationOptions.min; + var max = generationOptions.max; + var precision = generationOptions.precision; + var rmin = dataRange.min; + var rmax = dataRange.max; + var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; + var factor, niceMin, niceMax, numSpaces; + + // Beyond MIN_SPACING floating point numbers being to lose precision + // such that we can't do the math necessary to generate ticks + if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) { + return [rmin, rmax]; + } + + numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); + if (numSpaces > maxNumSpaces) { + // If the calculated num of spaces exceeds maxNumSpaces, recalculate it + spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; + } + + if (stepSize || isNullOrUndef$2(precision)) { + // If a precision is not specified, calculate factor based on spacing + factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); + } else { + // If the user specified a precision, round to that number of decimal places + factor = Math.pow(10, precision); + spacing = Math.ceil(spacing * factor) / factor; + } + + niceMin = Math.floor(rmin / spacing) * spacing; + niceMax = Math.ceil(rmax / spacing) * spacing; + + // If min, max and stepSize is set and they make an evenly spaced scale use it. + if (stepSize) { + // If very close to our whole number, use it. + if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { + niceMin = min; + } + if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { + niceMax = max; + } + } + + numSpaces = (niceMax - niceMin) / spacing; + // If very close to our rounded value, use it. + if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { + numSpaces = Math.round(numSpaces); + } else { + numSpaces = Math.ceil(numSpaces); + } + + niceMin = Math.round(niceMin * factor) / factor; + niceMax = Math.round(niceMax * factor) / factor; + ticks.push(isNullOrUndef$2(min) ? niceMin : min); + for (var j = 1; j < numSpaces; ++j) { + ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); + } + ticks.push(isNullOrUndef$2(max) ? niceMax : max); + + return ticks; +} + +var scale_linearbase = core_scale.extend({ + getRightValue: function(value) { + if (typeof value === 'string') { + return +value; + } + return core_scale.prototype.getRightValue.call(this, value); + }, + + handleTickRangeOptions: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, + // do nothing since that would make the chart weird. If the user really wants a weird chart + // axis, they can manually override it + if (tickOpts.beginAtZero) { + var minSign = helpers$1.sign(me.min); + var maxSign = helpers$1.sign(me.max); + + if (minSign < 0 && maxSign < 0) { + // move the top up to 0 + me.max = 0; + } else if (minSign > 0 && maxSign > 0) { + // move the bottom down to 0 + me.min = 0; + } + } + + var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; + var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; + + if (tickOpts.min !== undefined) { + me.min = tickOpts.min; + } else if (tickOpts.suggestedMin !== undefined) { + if (me.min === null) { + me.min = tickOpts.suggestedMin; + } else { + me.min = Math.min(me.min, tickOpts.suggestedMin); + } + } + + if (tickOpts.max !== undefined) { + me.max = tickOpts.max; + } else if (tickOpts.suggestedMax !== undefined) { + if (me.max === null) { + me.max = tickOpts.suggestedMax; + } else { + me.max = Math.max(me.max, tickOpts.suggestedMax); + } + } + + if (setMin !== setMax) { + // We set the min or the max but not both. + // So ensure that our range is good + // Inverted or 0 length range can happen when + // ticks.min is set, and no datasets are visible + if (me.min >= me.max) { + if (setMin) { + me.max = me.min + 1; + } else { + me.min = me.max - 1; + } + } + } + + if (me.min === me.max) { + me.max++; + + if (!tickOpts.beginAtZero) { + me.min--; + } + } + }, + + getTickLimit: function() { + var me = this; + var tickOpts = me.options.ticks; + var stepSize = tickOpts.stepSize; + var maxTicksLimit = tickOpts.maxTicksLimit; + var maxTicks; + + if (stepSize) { + maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; + } else { + maxTicks = me._computeTickLimit(); + maxTicksLimit = maxTicksLimit || 11; + } + + if (maxTicksLimit) { + maxTicks = Math.min(maxTicksLimit, maxTicks); + } + + return maxTicks; + }, + + _computeTickLimit: function() { + return Number.POSITIVE_INFINITY; + }, + + handleDirectionalChanges: noop, + + buildTicks: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph. Make sure we always have at least 2 ticks + var maxTicks = me.getTickLimit(); + maxTicks = Math.max(2, maxTicks); + + var numericGeneratorOptions = { + maxTicks: maxTicks, + min: tickOpts.min, + max: tickOpts.max, + precision: tickOpts.precision, + stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) + }; + var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); + + me.handleDirectionalChanges(); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + ticks.reverse(); + + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + }, + + convertTicksToLabels: function() { + var me = this; + me.ticksAsNumbers = me.ticks.slice(); + me.zeroLineIndex = me.ticks.indexOf(0); + + core_scale.prototype.convertTicksToLabels.call(me); + }, + + _configure: function() { + var me = this; + var ticks = me.getTicks(); + var start = me.min; + var end = me.max; + var offset; + + core_scale.prototype._configure.call(me); + + if (me.options.offset && ticks.length) { + offset = (end - start) / Math.max(ticks.length - 1, 1) / 2; + start -= offset; + end += offset; + } + me._startValue = start; + me._endValue = end; + me._valueRange = end - start; + } +}); + +var defaultConfig$1 = { + position: 'left', + ticks: { + callback: core_ticks.formatters.linear + } +}; + +var DEFAULT_MIN = 0; +var DEFAULT_MAX = 1; + +function getOrCreateStack(stacks, stacked, meta) { + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + stacked === undefined && meta.stack === undefined ? meta.index : '', + meta.stack + ].join('.'); + + if (stacks[key] === undefined) { + stacks[key] = { + pos: [], + neg: [] + }; + } + + return stacks[key]; +} + +function stackData(scale, stacks, meta, data) { + var opts = scale.options; + var stacked = opts.stacked; + var stack = getOrCreateStack(stacks, stacked, meta); + var pos = stack.pos; + var neg = stack.neg; + var ilen = data.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = scale._parseValue(data[i]); + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { + continue; + } + + pos[i] = pos[i] || 0; + neg[i] = neg[i] || 0; + + if (opts.relativePoints) { + pos[i] = 100; + } else if (value.min < 0 || value.max < 0) { + neg[i] += value.min; + } else { + pos[i] += value.max; + } + } +} + +function updateMinMax(scale, meta, data) { + var ilen = data.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = scale._parseValue(data[i]); + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { + continue; + } + + scale.min = Math.min(scale.min, value.min); + scale.max = Math.max(scale.max, value.max); + } +} + +var scale_linear = scale_linearbase.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var datasets = chart.data.datasets; + var metasets = me._getMatchingVisibleMetas(); + var hasStacks = opts.stacked; + var stacks = {}; + var ilen = metasets.length; + var i, meta, data, values; + + me.min = Number.POSITIVE_INFINITY; + me.max = Number.NEGATIVE_INFINITY; + + if (hasStacks === undefined) { + for (i = 0; !hasStacks && i < ilen; ++i) { + meta = metasets[i]; + hasStacks = meta.stack !== undefined; + } + } + + for (i = 0; i < ilen; ++i) { + meta = metasets[i]; + data = datasets[meta.index].data; + if (hasStacks) { + stackData(me, stacks, meta, data); + } else { + updateMinMax(me, meta, data); + } + } + + helpers$1.each(stacks, function(stackValues) { + values = stackValues.pos.concat(stackValues.neg); + me.min = Math.min(me.min, helpers$1.min(values)); + me.max = Math.max(me.max, helpers$1.max(values)); + }); + + me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; + me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + var me = this; + var tickFont; + + if (me.isHorizontal()) { + return Math.ceil(me.width / 40); + } + tickFont = helpers$1.options._parseFont(me.options.ticks); + return Math.ceil(me.height / tickFont.lineHeight); + }, + + // Called after the ticks are built. We need + handleDirectionalChanges: function() { + if (!this.isHorizontal()) { + // We are in a vertical orientation. The top value is the highest. So reverse the array + this.ticks.reverse(); + } + }, + + getLabelForIndex: function(index, datasetIndex) { + return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); + }, + + // Utils + getPixelForValue: function(value) { + var me = this; + return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange); + }, + + getValueForPixel: function(pixel) { + return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange; + }, + + getPixelForTick: function(index) { + var ticks = this.ticksAsNumbers; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index]); + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$1 = defaultConfig$1; +scale_linear._defaults = _defaults$1; + +var valueOrDefault$b = helpers$1.valueOrDefault; +var log10 = helpers$1.math.log10; + +/** + * Generate a set of logarithmic ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks$1(generationOptions, dataRange) { + var ticks = []; + + var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); + + var endExp = Math.floor(log10(dataRange.max)); + var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + var exp, significand; + + if (tickVal === 0) { + exp = Math.floor(log10(dataRange.minNotZero)); + significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); + + ticks.push(tickVal); + tickVal = significand * Math.pow(10, exp); + } else { + exp = Math.floor(log10(tickVal)); + significand = Math.floor(tickVal / Math.pow(10, exp)); + } + var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + + do { + ticks.push(tickVal); + + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + precision = exp >= 0 ? 1 : precision; + } + + tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + + var lastTick = valueOrDefault$b(generationOptions.max, tickVal); + ticks.push(lastTick); + + return ticks; +} + +var defaultConfig$2 = { + position: 'left', + + // label settings + ticks: { + callback: core_ticks.formatters.logarithmic + } +}; + +// TODO(v3): change this to positiveOrDefault +function nonNegativeOrDefault(value, defaultValue) { + return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; +} + +var scale_logarithmic = core_scale.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var datasets = chart.data.datasets; + var isHorizontal = me.isHorizontal(); + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + var datasetIndex, meta, value, data, i, ilen; + + // Calculate Range + me.min = Number.POSITIVE_INFINITY; + me.max = Number.NEGATIVE_INFINITY; + me.minNotZero = Number.POSITIVE_INFINITY; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + break; + } + } + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = []; + } + + data = datasets[datasetIndex].data; + for (i = 0, ilen = data.length; i < ilen; i++) { + var values = valuesPerStack[key]; + value = me._parseValue(data[i]); + // invalid, hidden and negative values are ignored + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { + continue; + } + values[i] = values[i] || 0; + values[i] += value.max; + } + } + } + + helpers$1.each(valuesPerStack, function(valuesForType) { + if (valuesForType.length > 0) { + var minVal = helpers$1.min(valuesForType); + var maxVal = helpers$1.max(valuesForType); + me.min = Math.min(me.min, minVal); + me.max = Math.max(me.max, maxVal); + } + }); + + } else { + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + data = datasets[datasetIndex].data; + for (i = 0, ilen = data.length; i < ilen; i++) { + value = me._parseValue(data[i]); + // invalid, hidden and negative values are ignored + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { + continue; + } + + me.min = Math.min(value.min, me.min); + me.max = Math.max(value.max, me.max); + + if (value.min !== 0) { + me.minNotZero = Math.min(value.min, me.minNotZero); + } + } + } + } + } + + me.min = helpers$1.isFinite(me.min) ? me.min : null; + me.max = helpers$1.isFinite(me.max) ? me.max : null; + me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null; + + // Common base implementation to handle ticks.min, ticks.max + this.handleTickRangeOptions(); + }, + + handleTickRangeOptions: function() { + var me = this; + var tickOpts = me.options.ticks; + var DEFAULT_MIN = 1; + var DEFAULT_MAX = 10; + + me.min = nonNegativeOrDefault(tickOpts.min, me.min); + me.max = nonNegativeOrDefault(tickOpts.max, me.max); + + if (me.min === me.max) { + if (me.min !== 0 && me.min !== null) { + me.min = Math.pow(10, Math.floor(log10(me.min)) - 1); + me.max = Math.pow(10, Math.floor(log10(me.max)) + 1); + } else { + me.min = DEFAULT_MIN; + me.max = DEFAULT_MAX; + } + } + if (me.min === null) { + me.min = Math.pow(10, Math.floor(log10(me.max)) - 1); + } + if (me.max === null) { + me.max = me.min !== 0 + ? Math.pow(10, Math.floor(log10(me.min)) + 1) + : DEFAULT_MAX; + } + if (me.minNotZero === null) { + if (me.min > 0) { + me.minNotZero = me.min; + } else if (me.max < 1) { + me.minNotZero = Math.pow(10, Math.floor(log10(me.max))); + } else { + me.minNotZero = DEFAULT_MIN; + } + } + }, + + buildTicks: function() { + var me = this; + var tickOpts = me.options.ticks; + var reverse = !me.isHorizontal(); + + var generationOptions = { + min: nonNegativeOrDefault(tickOpts.min), + max: nonNegativeOrDefault(tickOpts.max) + }; + var ticks = me.ticks = generateTicks$1(generationOptions, me); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + reverse = !reverse; + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + if (reverse) { + ticks.reverse(); + } + }, + + convertTicksToLabels: function() { + this.tickValues = this.ticks.slice(); + + core_scale.prototype.convertTicksToLabels.call(this); + }, + + // Get the correct tooltip label + getLabelForIndex: function(index, datasetIndex) { + return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); + }, + + getPixelForTick: function(index) { + var ticks = this.tickValues; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index]); + }, + + /** + * Returns the value of the first tick. + * @param {number} value - The minimum not zero value. + * @return {number} The first tick value. + * @private + */ + _getFirstTickValue: function(value) { + var exp = Math.floor(log10(value)); + var significand = Math.floor(value / Math.pow(10, exp)); + + return significand * Math.pow(10, exp); + }, + + _configure: function() { + var me = this; + var start = me.min; + var offset = 0; + + core_scale.prototype._configure.call(me); + + if (start === 0) { + start = me._getFirstTickValue(me.minNotZero); + offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length; + } + + me._startValue = log10(start); + me._valueOffset = offset; + me._valueRange = (log10(me.max) - log10(start)) / (1 - offset); + }, + + getPixelForValue: function(value) { + var me = this; + var decimal = 0; + + value = +me.getRightValue(value); + + if (value > me.min && value > 0) { + decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset; + } + return me.getPixelForDecimal(decimal); + }, + + getValueForPixel: function(pixel) { + var me = this; + var decimal = me.getDecimalForPixel(pixel); + return decimal === 0 && me.min === 0 + ? 0 + : Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange); + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$2 = defaultConfig$2; +scale_logarithmic._defaults = _defaults$2; + +var valueOrDefault$c = helpers$1.valueOrDefault; +var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; +var resolve$4 = helpers$1.options.resolve; + +var defaultConfig$3 = { + display: true, + + // Boolean - Whether to animate scaling the chart from the centre + animate: true, + position: 'chartArea', + + angleLines: { + display: true, + color: 'rgba(0,0,0,0.1)', + lineWidth: 1, + borderDash: [], + borderDashOffset: 0.0 + }, + + gridLines: { + circular: false + }, + + // label settings + ticks: { + // Boolean - Show a backdrop to the scale label + showLabelBackdrop: true, + + // String - The colour of the label backdrop + backdropColor: 'rgba(255,255,255,0.75)', + + // Number - The backdrop padding above & below the label in pixels + backdropPaddingY: 2, + + // Number - The backdrop padding to the side of the label in pixels + backdropPaddingX: 2, + + callback: core_ticks.formatters.linear + }, + + pointLabels: { + // Boolean - if true, show point labels + display: true, + + // Number - Point label font size in pixels + fontSize: 10, + + // Function - Used to convert point labels + callback: function(label) { + return label; + } + } +}; + +function getTickBackdropHeight(opts) { + var tickOpts = opts.ticks; + + if (tickOpts.display && opts.display) { + return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; + } + return 0; +} + +function measureLabelSize(ctx, lineHeight, label) { + if (helpers$1.isArray(label)) { + return { + w: helpers$1.longestText(ctx, ctx.font, label), + h: label.length * lineHeight + }; + } + + return { + w: ctx.measureText(label).width, + h: lineHeight + }; +} + +function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size, + end: pos + }; + } + + return { + start: pos, + end: pos + size + }; +} + +/** + * Helper function to fit a radial linear scale with point labels + */ +function fitWithPointLabels(scale) { + + // Right, this is really confusing and there is a lot of maths going on here + // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 + // + // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif + // + // Solution: + // + // We assume the radius of the polygon is half the size of the canvas at first + // at each index we check if the text overlaps. + // + // Where it does, we store that angle and that index. + // + // After finding the largest index and angle we calculate how much we need to remove + // from the shape radius to move the point inwards by that x. + // + // We average the left and right distances to get the maximum shape radius that can fit in the box + // along with labels. + // + // Once we have that, we can find the centre point for the chart, by taking the x text protrusion + // on each side, removing that from the size, halving it and adding the left x protrusion width. + // + // This will mean we have a shape fitted to the canvas, as large as it can be with the labels + // and position it in the most space efficient manner + // + // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif + + var plFont = helpers$1.options._parseFont(scale.options.pointLabels); + + // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. + // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points + var furthestLimits = { + l: 0, + r: scale.width, + t: 0, + b: scale.height - scale.paddingTop + }; + var furthestAngles = {}; + var i, textSize, pointPosition; + + scale.ctx.font = plFont.string; + scale._pointLabelSizes = []; + + var valueCount = scale.chart.data.labels.length; + for (i = 0; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); + textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]); + scale._pointLabelSizes[i] = textSize; + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians) % 360; + var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + + if (hLimits.start < furthestLimits.l) { + furthestLimits.l = hLimits.start; + furthestAngles.l = angleRadians; + } + + if (hLimits.end > furthestLimits.r) { + furthestLimits.r = hLimits.end; + furthestAngles.r = angleRadians; + } + + if (vLimits.start < furthestLimits.t) { + furthestLimits.t = vLimits.start; + furthestAngles.t = angleRadians; + } + + if (vLimits.end > furthestLimits.b) { + furthestLimits.b = vLimits.end; + furthestAngles.b = angleRadians; + } + } + + scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); +} + +function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + + return 'right'; +} + +function fillText(ctx, text, position, lineHeight) { + var y = position.y + lineHeight / 2; + var i, ilen; + + if (helpers$1.isArray(text)) { + for (i = 0, ilen = text.length; i < ilen; ++i) { + ctx.fillText(text[i], position.x, y); + y += lineHeight; + } + } else { + ctx.fillText(text, position.x, y); + } +} + +function adjustPointPositionForLabelHeight(angle, textSize, position) { + if (angle === 90 || angle === 270) { + position.y -= (textSize.h / 2); + } else if (angle > 270 || angle < 90) { + position.y -= textSize.h; + } +} + +function drawPointLabels(scale) { + var ctx = scale.ctx; + var opts = scale.options; + var pointLabelOpts = opts.pointLabels; + var tickBackdropHeight = getTickBackdropHeight(opts); + var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); + var plFont = helpers$1.options._parseFont(pointLabelOpts); + + ctx.save(); + + ctx.font = plFont.string; + ctx.textBaseline = 'middle'; + + for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) { + // Extra pixels out for some label spacing + var extra = (i === 0 ? tickBackdropHeight / 2 : 0); + var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); + + // Keep this in loop since we may support array properties here + var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); + ctx.fillStyle = pointLabelFontColor; + + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians); + ctx.textAlign = getTextAlignForAngle(angle); + adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); + fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight); + } + ctx.restore(); +} + +function drawRadiusLine(scale, gridLineOpts, radius, index) { + var ctx = scale.ctx; + var circular = gridLineOpts.circular; + var valueCount = scale.chart.data.labels.length; + var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); + var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); + var pointPosition; + + if ((!circular && !valueCount) || !lineColor || !lineWidth) { + return; + } + + ctx.save(); + ctx.strokeStyle = lineColor; + ctx.lineWidth = lineWidth; + if (ctx.setLineDash) { + ctx.setLineDash(gridLineOpts.borderDash || []); + ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; + } + + ctx.beginPath(); + if (circular) { + // Draw circular arcs between the points + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); + } else { + // Draw straight lines connecting each index + pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + + for (var i = 1; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + } + ctx.closePath(); + ctx.stroke(); + ctx.restore(); +} + +function numberOrZero(param) { + return helpers$1.isNumber(param) ? param : 0; +} + +var scale_radialLinear = scale_linearbase.extend({ + setDimensions: function() { + var me = this; + + // Set the unconstrained dimension before label rotation + me.width = me.maxWidth; + me.height = me.maxHeight; + me.paddingTop = getTickBackdropHeight(me.options) / 2; + me.xCenter = Math.floor(me.width / 2); + me.yCenter = Math.floor((me.height - me.paddingTop) / 2); + me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var min = Number.POSITIVE_INFINITY; + var max = Number.NEGATIVE_INFINITY; + + helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + min = Math.min(value, min); + max = Math.max(value, max); + }); + } + }); + + me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); + me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); + }, + + convertTicksToLabels: function() { + var me = this; + + scale_linearbase.prototype.convertTicksToLabels.call(me); + + // Point labels + me.pointLabels = me.chart.data.labels.map(function() { + var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me); + return label || label === 0 ? label : ''; + }); + }, + + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + + fit: function() { + var me = this; + var opts = me.options; + + if (opts.display && opts.pointLabels.display) { + fitWithPointLabels(me); + } else { + me.setCenterPoint(0, 0, 0, 0); + } + }, + + /** + * Set radius reductions and determine new radius and center point + * @private + */ + setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { + var me = this; + var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); + var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); + var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); + var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); + + radiusReductionLeft = numberOrZero(radiusReductionLeft); + radiusReductionRight = numberOrZero(radiusReductionRight); + radiusReductionTop = numberOrZero(radiusReductionTop); + radiusReductionBottom = numberOrZero(radiusReductionBottom); + + me.drawingArea = Math.min( + Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), + Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); + me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); + }, + + setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { + var me = this; + var maxRight = me.width - rightMovement - me.drawingArea; + var maxLeft = leftMovement + me.drawingArea; + var maxTop = topMovement + me.drawingArea; + var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; + + me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); + me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); + }, + + getIndexAngle: function(index) { + var chart = this.chart; + var angleMultiplier = 360 / chart.data.labels.length; + var options = chart.options || {}; + var startAngle = options.startAngle || 0; + + // Start from the top instead of right, so remove a quarter of the circle + var angle = (index * angleMultiplier + startAngle) % 360; + + return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360; + }, + + getDistanceFromCenterForValue: function(value) { + var me = this; + + if (helpers$1.isNullOrUndef(value)) { + return NaN; + } + + // Take into account half font size + the yPadding of the top value + var scalingFactor = me.drawingArea / (me.max - me.min); + if (me.options.ticks.reverse) { + return (me.max - value) * scalingFactor; + } + return (value - me.min) * scalingFactor; + }, + + getPointPosition: function(index, distanceFromCenter) { + var me = this; + var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); + return { + x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, + y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter + }; + }, + + getPointPositionForValue: function(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + }, + + getBasePosition: function(index) { + var me = this; + var min = me.min; + var max = me.max; + + return me.getPointPositionForValue(index || 0, + me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0); + }, + + /** + * @private + */ + _drawGrid: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + var gridLineOpts = opts.gridLines; + var angleLineOpts = opts.angleLines; + var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth); + var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color); + var i, offset, position; + + if (opts.pointLabels.display) { + drawPointLabels(me); + } + + if (gridLineOpts.display) { + helpers$1.each(me.ticks, function(label, index) { + if (index !== 0) { + offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + drawRadiusLine(me, gridLineOpts, offset, index); + } + }); + } + + if (angleLineOpts.display && lineWidth && lineColor) { + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = lineColor; + if (ctx.setLineDash) { + ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); + ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); + } + + for (i = me.chart.data.labels.length - 1; i >= 0; i--) { + offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max); + position = me.getPointPosition(i, offset); + ctx.beginPath(); + ctx.moveTo(me.xCenter, me.yCenter); + ctx.lineTo(position.x, position.y); + ctx.stroke(); + } + + ctx.restore(); + } + }, + + /** + * @private + */ + _drawLabels: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + var tickOpts = opts.ticks; + + if (!tickOpts.display) { + return; + } + + var startAngle = me.getIndexAngle(0); + var tickFont = helpers$1.options._parseFont(tickOpts); + var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor); + var offset, width; + + ctx.save(); + ctx.font = tickFont.string; + ctx.translate(me.xCenter, me.yCenter); + ctx.rotate(startAngle); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + helpers$1.each(me.ticks, function(label, index) { + if (index === 0 && !tickOpts.reverse) { + return; + } + + offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + + if (tickOpts.showLabelBackdrop) { + width = ctx.measureText(label).width; + ctx.fillStyle = tickOpts.backdropColor; + + ctx.fillRect( + -width / 2 - tickOpts.backdropPaddingX, + -offset - tickFont.size / 2 - tickOpts.backdropPaddingY, + width + tickOpts.backdropPaddingX * 2, + tickFont.size + tickOpts.backdropPaddingY * 2 + ); + } + + ctx.fillStyle = tickFontColor; + ctx.fillText(label, 0, -offset); + }); + + ctx.restore(); + }, + + /** + * @private + */ + _drawTitle: helpers$1.noop +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$3 = defaultConfig$3; +scale_radialLinear._defaults = _defaults$3; + +var deprecated$1 = helpers$1._deprecated; +var resolve$5 = helpers$1.options.resolve; +var valueOrDefault$d = helpers$1.valueOrDefault; + +// Integer constants are from the ES6 spec. +var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; +var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + +var INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: 1000 + }, + second: { + common: true, + size: 1000, + steps: 60 + }, + minute: { + common: true, + size: 60000, + steps: 60 + }, + hour: { + common: true, + size: 3600000, + steps: 24 + }, + day: { + common: true, + size: 86400000, + steps: 30 + }, + week: { + common: false, + size: 604800000, + steps: 4 + }, + month: { + common: true, + size: 2.628e9, + steps: 12 + }, + quarter: { + common: false, + size: 7.884e9, + steps: 4 + }, + year: { + common: true, + size: 3.154e10 + } +}; + +var UNITS = Object.keys(INTERVALS); + +function sorter(a, b) { + return a - b; +} + +function arrayUnique(items) { + var hash = {}; + var out = []; + var i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + if (!hash[item]) { + hash[item] = true; + out.push(item); + } + } + + return out; +} + +function getMin(options) { + return helpers$1.valueOrDefault(options.time.min, options.ticks.min); +} + +function getMax(options) { + return helpers$1.valueOrDefault(options.time.max, options.ticks.max); +} + +/** + * Returns an array of {time, pos} objects used to interpolate a specific `time` or position + * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is + * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other + * extremity (left + width or top + height). Note that it would be more optimized to directly + * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need + * to create the lookup table. The table ALWAYS contains at least two items: min and max. + * + * @param {number[]} timestamps - timestamps sorted from lowest to highest. + * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min + * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. + * If 'series', timestamps will be positioned at the same distance from each other. In this + * case, only timestamps that break the time linearity are registered, meaning that in the + * best case, all timestamps are linear, the table contains only min and max. + */ +function buildLookupTable(timestamps, min, max, distribution) { + if (distribution === 'linear' || !timestamps.length) { + return [ + {time: min, pos: 0}, + {time: max, pos: 1} + ]; + } + + var table = []; + var items = [min]; + var i, ilen, prev, curr, next; + + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr > min && curr < max) { + items.push(curr); + } + } + + items.push(max); + + for (i = 0, ilen = items.length; i < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + + // only add points that breaks the scale linearity + if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { + table.push({time: curr, pos: i / (ilen - 1)}); + } + } + + return table; +} + +// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ +function lookup(table, key, value) { + var lo = 0; + var hi = table.length - 1; + var mid, i0, i1; + + while (lo >= 0 && lo <= hi) { + mid = (lo + hi) >> 1; + i0 = table[mid - 1] || null; + i1 = table[mid]; + + if (!i0) { + // given value is outside table (before first item) + return {lo: null, hi: i1}; + } else if (i1[key] < value) { + lo = mid + 1; + } else if (i0[key] > value) { + hi = mid - 1; + } else { + return {lo: i0, hi: i1}; + } + } + + // given value is outside table (after last item) + return {lo: i1, hi: null}; +} + +/** + * Linearly interpolates the given source `value` using the table items `skey` values and + * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') + * returns the position for a timestamp equal to 42. If value is out of bounds, values at + * index [0, 1] or [n - 1, n] are used for the interpolation. + */ +function interpolate$1(table, skey, sval, tkey) { + var range = lookup(table, skey, sval); + + // Note: the lookup table ALWAYS contains at least 2 items (min and max) + var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; + var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; + + var span = next[skey] - prev[skey]; + var ratio = span ? (sval - prev[skey]) / span : 0; + var offset = (next[tkey] - prev[tkey]) * ratio; + + return prev[tkey] + offset; +} + +function toTimestamp(scale, input) { + var adapter = scale._adapter; + var options = scale.options.time; + var parser = options.parser; + var format = parser || options.format; + var value = input; + + if (typeof parser === 'function') { + value = parser(value); + } + + // Only parse if its not a timestamp already + if (!helpers$1.isFinite(value)) { + value = typeof format === 'string' + ? adapter.parse(value, format) + : adapter.parse(value); + } + + if (value !== null) { + return +value; + } + + // Labels are in an incompatible format and no `parser` has been provided. + // The user might still use the deprecated `format` option for parsing. + if (!parser && typeof format === 'function') { + value = format(input); + + // `format` could return something else than a timestamp, if so, parse it + if (!helpers$1.isFinite(value)) { + value = adapter.parse(value); + } + } + + return value; +} + +function parse(scale, input) { + if (helpers$1.isNullOrUndef(input)) { + return null; + } + + var options = scale.options.time; + var value = toTimestamp(scale, scale.getRightValue(input)); + if (value === null) { + return value; + } + + if (options.round) { + value = +scale._adapter.startOf(value, options.round); + } + + return value; +} + +/** + * Figures out what unit results in an appropriate number of auto-generated ticks + */ +function determineUnitForAutoTicks(minUnit, min, max, capacity) { + var ilen = UNITS.length; + var i, interval, factor; + + for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + interval = INTERVALS[UNITS[i]]; + factor = interval.steps ? interval.steps : MAX_INTEGER; + + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } + } + + return UNITS[ilen - 1]; +} + +/** + * Figures out what unit to format a set of ticks with + */ +function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { + var i, unit; + + for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { + unit = UNITS[i]; + if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { + return unit; + } + } + + return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; +} + +function determineMajorUnit(unit) { + for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; + } + } +} + +/** + * Generates a maximum of `capacity` timestamps between min and max, rounded to the + * `minor` unit using the given scale time `options`. + * Important: this method can return ticks outside the min and max range, it's the + * responsibility of the calling code to clamp values if needed. + */ +function generate(scale, min, max, capacity) { + var adapter = scale._adapter; + var options = scale.options; + var timeOpts = options.time; + var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); + var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]); + var weekday = minor === 'week' ? timeOpts.isoWeekday : false; + var first = min; + var ticks = []; + var time; + + // For 'week' unit, handle the first day of week option + if (weekday) { + first = +adapter.startOf(first, 'isoWeek', weekday); + } + + // Align first ticks on unit + first = +adapter.startOf(first, weekday ? 'day' : minor); + + // Prevent browser from freezing in case user options request millions of milliseconds + if (adapter.diff(max, min, minor) > 100000 * stepSize) { + throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor; + } + + for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) { + ticks.push(time); + } + + if (time === max || options.bounds === 'ticks') { + ticks.push(time); + } + + return ticks; +} + +/** + * Returns the start and end offsets from edges in the form of {start, end} + * where each value is a relative width to the scale and ranges between 0 and 1. + * They add extra margins on the both sides by scaling down the original scale. + * Offsets are added when the `offset` option is true. + */ +function computeOffsets(table, ticks, min, max, options) { + var start = 0; + var end = 0; + var first, last; + + if (options.offset && ticks.length) { + first = interpolate$1(table, 'time', ticks[0], 'pos'); + if (ticks.length === 1) { + start = 1 - first; + } else { + start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; + } + last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); + if (ticks.length === 1) { + end = last; + } else { + end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; + } + } + + return {start: start, end: end, factor: 1 / (start + 1 + end)}; +} + +function setMajorTicks(scale, ticks, map, majorUnit) { + var adapter = scale._adapter; + var first = +adapter.startOf(ticks[0].value, majorUnit); + var last = ticks[ticks.length - 1].value; + var major, index; + + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index = map[major]; + if (index >= 0) { + ticks[index].major = true; + } + } + return ticks; +} + +function ticksFromTimestamps(scale, values, majorUnit) { + var ticks = []; + var map = {}; + var ilen = values.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = values[i]; + map[value] = i; + + ticks.push({ + value: value, + major: false + }); + } + + // We set the major ticks separately from the above loop because calling startOf for every tick + // is expensive when there is a large number of ticks + return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); +} + +var defaultConfig$4 = { + position: 'bottom', + + /** + * Data distribution along the scale: + * - 'linear': data are spread according to their time (distances can vary), + * - 'series': data are spread at the same distance from each other. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + distribution: 'linear', + + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @see https://github.com/chartjs/Chart.js/pull/4556 + * @since 2.7.0 + */ + bounds: 'data', + + adapters: {}, + time: { + parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment + unit: false, // false == automatic or override with week, month, year, etc. + round: false, // none, or override with week, month, year, etc. + displayFormat: false, // DEPRECATED + isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + autoSkip: false, + + /** + * Ticks generation input values: + * - 'auto': generates "optimal" ticks based on scale size and time options. + * - 'data': generates ticks from data (including labels from data {t|x|y} objects). + * - 'labels': generates ticks from user given `data.labels` values ONLY. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + source: 'auto', + + major: { + enabled: false + } + } +}; + +var scale_time = core_scale.extend({ + initialize: function() { + this.mergeTicksOptions(); + core_scale.prototype.initialize.call(this); + }, + + update: function() { + var me = this; + var options = me.options; + var time = options.time || (options.time = {}); + var adapter = me._adapter = new core_adapters._date(options.adapters.date); + + // DEPRECATIONS: output a message only one time per update + deprecated$1('time scale', time.format, 'time.format', 'time.parser'); + deprecated$1('time scale', time.min, 'time.min', 'ticks.min'); + deprecated$1('time scale', time.max, 'time.max', 'ticks.max'); + + // Backward compatibility: before introducing adapter, `displayFormats` was + // supposed to contain *all* unit/string pairs but this can't be resolved + // when loading the scale (adapters are loaded afterward), so let's populate + // missing formats on update + helpers$1.mergeIf(time.displayFormats, adapter.formats()); + + return core_scale.prototype.update.apply(me, arguments); + }, + + /** + * Allows data to be referenced via 't' attribute + */ + getRightValue: function(rawValue) { + if (rawValue && rawValue.t !== undefined) { + rawValue = rawValue.t; + } + return core_scale.prototype.getRightValue.call(this, rawValue); + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var adapter = me._adapter; + var options = me.options; + var unit = options.time.unit || 'day'; + var min = MAX_INTEGER; + var max = MIN_INTEGER; + var timestamps = []; + var datasets = []; + var labels = []; + var i, j, ilen, jlen, data, timestamp, labelsAdded; + var dataLabels = me._getLabels(); + + for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { + labels.push(parse(me, dataLabels[i])); + } + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + data = chart.data.datasets[i].data; + + // Let's consider that all data have the same format. + if (helpers$1.isObject(data[0])) { + datasets[i] = []; + + for (j = 0, jlen = data.length; j < jlen; ++j) { + timestamp = parse(me, data[j]); + timestamps.push(timestamp); + datasets[i][j] = timestamp; + } + } else { + datasets[i] = labels.slice(0); + if (!labelsAdded) { + timestamps = timestamps.concat(labels); + labelsAdded = true; + } + } + } else { + datasets[i] = []; + } + } + + if (labels.length) { + min = Math.min(min, labels[0]); + max = Math.max(max, labels[labels.length - 1]); + } + + if (timestamps.length) { + timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter); + min = Math.min(min, timestamps[0]); + max = Math.max(max, timestamps[timestamps.length - 1]); + } + + min = parse(me, getMin(options)) || min; + max = parse(me, getMax(options)) || max; + + // In case there is no valid min/max, set limits based on unit time option + min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; + max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; + + // Make sure that max is strictly higher than min (required by the lookup table) + me.min = Math.min(min, max); + me.max = Math.max(min + 1, max); + + // PRIVATE + me._table = []; + me._timestamps = { + data: timestamps, + datasets: datasets, + labels: labels + }; + }, + + buildTicks: function() { + var me = this; + var min = me.min; + var max = me.max; + var options = me.options; + var tickOpts = options.ticks; + var timeOpts = options.time; + var timestamps = me._timestamps; + var ticks = []; + var capacity = me.getLabelCapacity(min); + var source = tickOpts.source; + var distribution = options.distribution; + var i, ilen, timestamp; + + if (source === 'data' || (source === 'auto' && distribution === 'series')) { + timestamps = timestamps.data; + } else if (source === 'labels') { + timestamps = timestamps.labels; + } else { + timestamps = generate(me, min, max, capacity); + } + + if (options.bounds === 'ticks' && timestamps.length) { + min = timestamps[0]; + max = timestamps[timestamps.length - 1]; + } + + // Enforce limits with user min/max options + min = parse(me, getMin(options)) || min; + max = parse(me, getMax(options)) || max; + + // Remove ticks outside the min/max range + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + timestamp = timestamps[i]; + if (timestamp >= min && timestamp <= max) { + ticks.push(timestamp); + } + } + + me.min = min; + me.max = max; + + // PRIVATE + // determineUnitForFormatting relies on the number of ticks so we don't use it when + // autoSkip is enabled because we don't yet know what the final number of ticks will be + me._unit = timeOpts.unit || (tickOpts.autoSkip + ? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity) + : determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max)); + me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined + : determineMajorUnit(me._unit); + me._table = buildLookupTable(me._timestamps.data, min, max, distribution); + me._offsets = computeOffsets(me._table, ticks, min, max, options); + + if (tickOpts.reverse) { + ticks.reverse(); + } + + return ticksFromTimestamps(me, ticks, me._majorUnit); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var adapter = me._adapter; + var data = me.chart.data; + var timeOpts = me.options.time; + var label = data.labels && index < data.labels.length ? data.labels[index] : ''; + var value = data.datasets[datasetIndex].data[index]; + + if (helpers$1.isObject(value)) { + label = me.getRightValue(value); + } + if (timeOpts.tooltipFormat) { + return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); + } + if (typeof label === 'string') { + return label; + } + return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); + }, + + /** + * Function to format an individual tick mark + * @private + */ + tickFormatFunction: function(time, index, ticks, format) { + var me = this; + var adapter = me._adapter; + var options = me.options; + var formats = options.time.displayFormats; + var minorFormat = formats[me._unit]; + var majorUnit = me._majorUnit; + var majorFormat = formats[majorUnit]; + var tick = ticks[index]; + var tickOpts = options.ticks; + var major = majorUnit && majorFormat && tick && tick.major; + var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); + var nestedTickOpts = major ? tickOpts.major : tickOpts.minor; + var formatter = resolve$5([ + nestedTickOpts.callback, + nestedTickOpts.userCallback, + tickOpts.callback, + tickOpts.userCallback + ]); + + return formatter ? formatter(label, index, ticks) : label; + }, + + convertTicksToLabels: function(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); + } + + return labels; + }, + + /** + * @private + */ + getPixelForOffset: function(time) { + var me = this; + var offsets = me._offsets; + var pos = interpolate$1(me._table, 'time', time, 'pos'); + return me.getPixelForDecimal((offsets.start + pos) * offsets.factor); + }, + + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var time = null; + + if (index !== undefined && datasetIndex !== undefined) { + time = me._timestamps.datasets[datasetIndex][index]; + } + + if (time === null) { + time = parse(me, value); + } + + if (time !== null) { + return me.getPixelForOffset(time); + } + }, + + getPixelForTick: function(index) { + var ticks = this.getTicks(); + return index >= 0 && index < ticks.length ? + this.getPixelForOffset(ticks[index].value) : + null; + }, + + getValueForPixel: function(pixel) { + var me = this; + var offsets = me._offsets; + var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + var time = interpolate$1(me._table, 'pos', pos, 'time'); + + // DEPRECATION, we should return time directly + return me._adapter._create(time); + }, + + /** + * @private + */ + _getLabelSize: function(label) { + var me = this; + var ticksOpts = me.options.ticks; + var tickLabelWidth = me.ctx.measureText(label).width; + var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); + var cosRotation = Math.cos(angle); + var sinRotation = Math.sin(angle); + var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize); + + return { + w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation), + h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation) + }; + }, + + /** + * Crude approximation of what the label width might be + * @private + */ + getLabelWidth: function(label) { + return this._getLabelSize(label).w; + }, + + /** + * @private + */ + getLabelCapacity: function(exampleTime) { + var me = this; + var timeOpts = me.options.time; + var displayFormats = timeOpts.displayFormats; + + // pick the longest format (milliseconds) for guestimation + var format = displayFormats[timeOpts.unit] || displayFormats.millisecond; + var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format); + var size = me._getLabelSize(exampleLabel); + var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h); + + if (me.options.offset) { + capacity--; + } + + return capacity > 0 ? capacity : 1; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$4 = defaultConfig$4; +scale_time._defaults = _defaults$4; + +var scales = { + category: scale_category, + linear: scale_linear, + logarithmic: scale_logarithmic, + radialLinear: scale_radialLinear, + time: scale_time +}; + +var moment = createCommonjsModule(function (module, exports) { +(function (global, factory) { + module.exports = factory() ; +}(commonjsGlobal, (function () { + var hookCallback; + + function hooks () { + return hookCallback.apply(null, arguments); + } + + // This is done to register the method called with moment() + // without creating circular dependencies. + function setHookCallback (callback) { + hookCallback = callback; + } + + function isArray(input) { + return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; + } + + function isObject(input) { + // IE8 will treat undefined and null as object if it wasn't for + // input != null + return input != null && Object.prototype.toString.call(input) === '[object Object]'; + } + + function isObjectEmpty(obj) { + if (Object.getOwnPropertyNames) { + return (Object.getOwnPropertyNames(obj).length === 0); + } else { + var k; + for (k in obj) { + if (obj.hasOwnProperty(k)) { + return false; + } + } + return true; + } + } + + function isUndefined(input) { + return input === void 0; + } + + function isNumber(input) { + return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; + } + + function isDate(input) { + return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; + } + + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } + + function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); + } + + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } + + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; + } + + function createUTC (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); + } + + function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso : false, + parsedDateParts : [], + meridiem : null, + rfc2822 : false, + weekdayMismatch : false + }; + } + + function getParsingFlags(m) { + if (m._pf == null) { + m._pf = defaultParsingFlags(); + } + return m._pf; + } + + var some; + if (Array.prototype.some) { + some = Array.prototype.some; + } else { + some = function (fun) { + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t && fun.call(this, t[i], i, t)) { + return true; + } + } + + return false; + }; + } + + function isValid(m) { + if (m._isValid == null) { + var flags = getParsingFlags(m); + var parsedParts = some.call(flags.parsedDateParts, function (i) { + return i != null; + }); + var isNowValid = !isNaN(m._d.getTime()) && + flags.overflow < 0 && + !flags.empty && + !flags.invalidMonth && + !flags.invalidWeekday && + !flags.weekdayMismatch && + !flags.nullInput && + !flags.invalidFormat && + !flags.userInvalidated && + (!flags.meridiem || (flags.meridiem && parsedParts)); + + if (m._strict) { + isNowValid = isNowValid && + flags.charsLeftOver === 0 && + flags.unusedTokens.length === 0 && + flags.bigHour === undefined; + } + + if (Object.isFrozen == null || !Object.isFrozen(m)) { + m._isValid = isNowValid; + } + else { + return isNowValid; + } + } + return m._isValid; + } + + function createInvalid (flags) { + var m = createUTC(NaN); + if (flags != null) { + extend(getParsingFlags(m), flags); + } + else { + getParsingFlags(m).userInvalidated = true; + } + + return m; + } + + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + var momentProperties = hooks.momentProperties = []; + + function copyConfig(to, from) { + var i, prop, val; + + if (!isUndefined(from._isAMomentObject)) { + to._isAMomentObject = from._isAMomentObject; + } + if (!isUndefined(from._i)) { + to._i = from._i; + } + if (!isUndefined(from._f)) { + to._f = from._f; + } + if (!isUndefined(from._l)) { + to._l = from._l; + } + if (!isUndefined(from._strict)) { + to._strict = from._strict; + } + if (!isUndefined(from._tzm)) { + to._tzm = from._tzm; + } + if (!isUndefined(from._isUTC)) { + to._isUTC = from._isUTC; + } + if (!isUndefined(from._offset)) { + to._offset = from._offset; + } + if (!isUndefined(from._pf)) { + to._pf = getParsingFlags(from); + } + if (!isUndefined(from._locale)) { + to._locale = from._locale; + } + + if (momentProperties.length > 0) { + for (i = 0; i < momentProperties.length; i++) { + prop = momentProperties[i]; + val = from[prop]; + if (!isUndefined(val)) { + to[prop] = val; + } + } + } + + return to; + } + + var updateInProgress = false; + + // Moment prototype object + function Moment(config) { + copyConfig(this, config); + this._d = new Date(config._d != null ? config._d.getTime() : NaN); + if (!this.isValid()) { + this._d = new Date(NaN); + } + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + hooks.updateOffset(this); + updateInProgress = false; + } + } + + function isMoment (obj) { + return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); + } + + function absFloor (number) { + if (number < 0) { + // -0 -> 0 + return Math.ceil(number) || 0; + } else { + return Math.floor(number); + } + } + + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + value = absFloor(coercedNumber); + } + + return value; + } + + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } + + function warn(msg) { + if (hooks.suppressDeprecationWarnings === false && + (typeof console !== 'undefined') && console.warn) { + console.warn('Deprecation warning: ' + msg); + } + } + + function deprecate(msg, fn) { + var firstTime = true; + + return extend(function () { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(null, msg); + } + if (firstTime) { + var args = []; + var arg; + for (var i = 0; i < arguments.length; i++) { + arg = ''; + if (typeof arguments[i] === 'object') { + arg += '\n[' + i + '] '; + for (var key in arguments[0]) { + arg += key + ': ' + arguments[0][key] + ', '; + } + arg = arg.slice(0, -2); // Remove trailing comma and space + } else { + arg = arguments[i]; + } + args.push(arg); + } + warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } + + var deprecations = {}; + + function deprecateSimple(name, msg) { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(name, msg); + } + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } + } + + hooks.suppressDeprecationWarnings = false; + hooks.deprecationHandler = null; + + function isFunction(input) { + return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; + } + + function set (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (isFunction(prop)) { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + this._config = config; + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. + // TODO: Remove "ordinalParse" fallback in next major release. + this._dayOfMonthOrdinalParseLenient = new RegExp( + (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + + '|' + (/\d{1,2}/).source); + } + + function mergeConfigs(parentConfig, childConfig) { + var res = extend({}, parentConfig), prop; + for (prop in childConfig) { + if (hasOwnProp(childConfig, prop)) { + if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { + res[prop] = {}; + extend(res[prop], parentConfig[prop]); + extend(res[prop], childConfig[prop]); + } else if (childConfig[prop] != null) { + res[prop] = childConfig[prop]; + } else { + delete res[prop]; + } + } + } + for (prop in parentConfig) { + if (hasOwnProp(parentConfig, prop) && + !hasOwnProp(childConfig, prop) && + isObject(parentConfig[prop])) { + // make sure changes to properties don't modify parent config + res[prop] = extend({}, res[prop]); + } + } + return res; + } + + function Locale(config) { + if (config != null) { + this.set(config); + } + } + + var keys; + + if (Object.keys) { + keys = Object.keys; + } else { + keys = function (obj) { + var i, res = []; + for (i in obj) { + if (hasOwnProp(obj, i)) { + res.push(i); + } + } + return res; + }; + } + + var defaultCalendar = { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }; + + function calendar (key, mom, now) { + var output = this._calendar[key] || this._calendar['sameElse']; + return isFunction(output) ? output.call(mom, now) : output; + } + + var defaultLongDateFormat = { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' + }; + + function longDateFormat (key) { + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + + if (format || !formatUpper) { + return format; + } + + this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + + return this._longDateFormat[key]; + } + + var defaultInvalidDate = 'Invalid date'; + + function invalidDate () { + return this._invalidDate; + } + + var defaultOrdinal = '%d'; + var defaultDayOfMonthOrdinalParse = /\d{1,2}/; + + function ordinal (number) { + return this._ordinal.replace('%d', number); + } + + var defaultRelativeTime = { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + ss : '%d seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }; + + function relativeTime (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (isFunction(output)) ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + } + + function pastFuture (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return isFunction(format) ? format(output) : format.replace(/%s/i, output); + } + + var aliases = {}; + + function addUnitAlias (unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; + } + + function normalizeUnits(units) { + return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; + } + + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + + return normalizedInput; + } + + var priorities = {}; + + function addUnitPriority(unit, priority) { + priorities[unit] = priority; + } + + function getPrioritizedUnits(unitsObj) { + var units = []; + for (var u in unitsObj) { + units.push({unit: u, priority: priorities[u]}); + } + units.sort(function (a, b) { + return a.priority - b.priority; + }); + return units; + } + + function zeroFill(number, targetLength, forceSign) { + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, + sign = number >= 0; + return (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; + } + + var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; + + var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; + + var formatFunctions = {}; + + var formatTokenFunctions = {}; + + // token: 'M' + // padded: ['MM', 2] + // ordinal: 'Mo' + // callback: function () { this.month() + 1 } + function addFormatToken (token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); + }; + } + if (token) { + formatTokenFunctions[token] = func; + } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal(func.apply(this, arguments), token); + }; + } + } + + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); + } + + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = '', i; + for (i = 0; i < length; i++) { + output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } + + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); + + return formatFunctions[format](m); + } + + function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; + } + + var match1 = /\d/; // 0 - 9 + var match2 = /\d\d/; // 00 - 99 + var match3 = /\d{3}/; // 000 - 999 + var match4 = /\d{4}/; // 0000 - 9999 + var match6 = /[+-]?\d{6}/; // -999999 - 999999 + var match1to2 = /\d\d?/; // 0 - 99 + var match3to4 = /\d\d\d\d?/; // 999 - 9999 + var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 + var match1to3 = /\d{1,3}/; // 0 - 999 + var match1to4 = /\d{1,4}/; // 0 - 9999 + var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 + + var matchUnsigned = /\d+/; // 0 - inf + var matchSigned = /[+-]?\d+/; // -inf - inf + + var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z + var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z + + var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + + // any word (or two) characters or numbers including two/three word month in arabic. + // includes scottish gaelic two word and hyphenated months + var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; + + var regexes = {}; + + function addRegexToken (token, regex, strictRegex) { + regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { + return (isStrict && strictRegex) ? strictRegex : regex; + }; + } + + function getParseRegexForToken (token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); + } + + return regexes[token](config._strict, config._locale); + } + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function unescapeFormat(s) { + return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + })); + } + + function regexEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + + var tokens = {}; + + function addParseToken (token, callback) { + var i, func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (isNumber(callback)) { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; + } + } + + function addWeekParseToken (token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); + }); + } + + function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); + } + } + + var YEAR = 0; + var MONTH = 1; + var DATE = 2; + var HOUR = 3; + var MINUTE = 4; + var SECOND = 5; + var MILLISECOND = 6; + var WEEK = 7; + var WEEKDAY = 8; + + // FORMATTING + + addFormatToken('Y', 0, 0, function () { + var y = this.year(); + return y <= 9999 ? '' + y : '+' + y; + }); + + addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; + }); + + addFormatToken(0, ['YYYY', 4], 0, 'year'); + addFormatToken(0, ['YYYYY', 5], 0, 'year'); + addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + + // ALIASES + + addUnitAlias('year', 'y'); + + // PRIORITIES + + addUnitPriority('year', 1); + + // PARSING + + addRegexToken('Y', matchSigned); + addRegexToken('YY', match1to2, match2); + addRegexToken('YYYY', match1to4, match4); + addRegexToken('YYYYY', match1to6, match6); + addRegexToken('YYYYYY', match1to6, match6); + + addParseToken(['YYYYY', 'YYYYYY'], YEAR); + addParseToken('YYYY', function (input, array) { + array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); + }); + addParseToken('YY', function (input, array) { + array[YEAR] = hooks.parseTwoDigitYear(input); + }); + addParseToken('Y', function (input, array) { + array[YEAR] = parseInt(input, 10); + }); + + // HELPERS + + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } + + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } + + // HOOKS + + hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; + + // MOMENTS + + var getSetYear = makeGetSet('FullYear', true); + + function getIsLeapYear () { + return isLeapYear(this.year()); + } + + function makeGetSet (unit, keepTime) { + return function (value) { + if (value != null) { + set$1(this, unit, value); + hooks.updateOffset(this, keepTime); + return this; + } else { + return get(this, unit); + } + }; + } + + function get (mom, unit) { + return mom.isValid() ? + mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; + } + + function set$1 (mom, unit, value) { + if (mom.isValid() && !isNaN(value)) { + if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); + } + else { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } + } + + // MOMENTS + + function stringGet (units) { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](); + } + return this; + } + + + function stringSet (units, value) { + if (typeof units === 'object') { + units = normalizeObjectUnits(units); + var prioritized = getPrioritizedUnits(units); + for (var i = 0; i < prioritized.length; i++) { + this[prioritized[i].unit](units[prioritized[i].unit]); + } + } else { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](value); + } + } + return this; + } + + function mod(n, x) { + return ((n % x) + x) % x; + } + + var indexOf; + + if (Array.prototype.indexOf) { + indexOf = Array.prototype.indexOf; + } else { + indexOf = function (o) { + // I know + var i; + for (i = 0; i < this.length; ++i) { + if (this[i] === o) { + return i; + } + } + return -1; + }; + } + + function daysInMonth(year, month) { + if (isNaN(year) || isNaN(month)) { + return NaN; + } + var modMonth = mod(month, 12); + year += (month - modMonth) / 12; + return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); + } + + // FORMATTING + + addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; + }); + + addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); + }); + + addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); + }); + + // ALIASES + + addUnitAlias('month', 'M'); + + // PRIORITY + + addUnitPriority('month', 8); + + // PARSING + + addRegexToken('M', match1to2); + addRegexToken('MM', match1to2, match2); + addRegexToken('MMM', function (isStrict, locale) { + return locale.monthsShortRegex(isStrict); + }); + addRegexToken('MMMM', function (isStrict, locale) { + return locale.monthsRegex(isStrict); + }); + + addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; + }); + + addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + getParsingFlags(config).invalidMonth = input; + } + }); + + // LOCALES + + var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; + var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); + function localeMonths (m, format) { + if (!m) { + return isArray(this._months) ? this._months : + this._months['standalone']; + } + return isArray(this._months) ? this._months[m.month()] : + this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; + } + + var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); + function localeMonthsShort (m, format) { + if (!m) { + return isArray(this._monthsShort) ? this._monthsShort : + this._monthsShort['standalone']; + } + return isArray(this._monthsShort) ? this._monthsShort[m.month()] : + this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; + } + + function handleStrictParse(monthName, format, strict) { + var i, ii, mom, llc = monthName.toLocaleLowerCase(); + if (!this._monthsParse) { + // this is not used + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + for (i = 0; i < 12; ++i) { + mom = createUTC([2000, i]); + this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); + this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeMonthsParse (monthName, format, strict) { + var i, mom, regex; + + if (this._monthsParseExact) { + return handleStrictParse.call(this, monthName, format, strict); + } + + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + // TODO: add sorting + // Sorting makes sure if one month (or abbr) is a prefix of another + // see sorting in computeMonthsParse + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + } + + // MOMENTS + + function setMonth (mom, value) { + var dayOfMonth; + + if (!mom.isValid()) { + // No op + return mom; + } + + if (typeof value === 'string') { + if (/^\d+$/.test(value)) { + value = toInt(value); + } else { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (!isNumber(value)) { + return mom; + } + } + } + + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } + + function getSetMonth (value) { + if (value != null) { + setMonth(this, value); + hooks.updateOffset(this, true); + return this; + } else { + return get(this, 'Month'); + } + } + + function getDaysInMonth () { + return daysInMonth(this.year(), this.month()); + } + + var defaultMonthsShortRegex = matchWord; + function monthsShortRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsShortStrictRegex; + } else { + return this._monthsShortRegex; + } + } else { + if (!hasOwnProp(this, '_monthsShortRegex')) { + this._monthsShortRegex = defaultMonthsShortRegex; + } + return this._monthsShortStrictRegex && isStrict ? + this._monthsShortStrictRegex : this._monthsShortRegex; + } + } + + var defaultMonthsRegex = matchWord; + function monthsRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsStrictRegex; + } else { + return this._monthsRegex; + } + } else { + if (!hasOwnProp(this, '_monthsRegex')) { + this._monthsRegex = defaultMonthsRegex; + } + return this._monthsStrictRegex && isStrict ? + this._monthsStrictRegex : this._monthsRegex; + } + } + + function computeMonthsParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var shortPieces = [], longPieces = [], mixedPieces = [], + i, mom; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + shortPieces.push(this.monthsShort(mom, '')); + longPieces.push(this.months(mom, '')); + mixedPieces.push(this.months(mom, '')); + mixedPieces.push(this.monthsShort(mom, '')); + } + // Sorting makes sure if one month (or abbr) is a prefix of another it + // will match the longer piece. + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 12; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + } + for (i = 0; i < 24; i++) { + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._monthsShortRegex = this._monthsRegex; + this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + } + + function createDate (y, m, d, h, M, s, ms) { + // can't just apply() to create a date: + // https://stackoverflow.com/q/181348 + var date; + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + date = new Date(y + 400, m, d, h, M, s, ms); + if (isFinite(date.getFullYear())) { + date.setFullYear(y); + } + } else { + date = new Date(y, m, d, h, M, s, ms); + } + + return date; + } + + function createUTCDate (y) { + var date; + // the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + var args = Array.prototype.slice.call(arguments); + // preserve leap years using a full 400 year cycle, then reset + args[0] = y + 400; + date = new Date(Date.UTC.apply(null, args)); + if (isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + } else { + date = new Date(Date.UTC.apply(null, arguments)); + } + + return date; + } + + // start-of-first-week - start-of-year + function firstWeekOffset(year, dow, doy) { + var // first-week day -- which january is always in the first week (4 for iso, 1 for other) + fwd = 7 + dow - doy, + // first-week day local weekday -- which local weekday is fwd + fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; + + return -fwdlw + fwd - 1; + } + + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, dow, doy) { + var localWeekday = (7 + weekday - dow) % 7, + weekOffset = firstWeekOffset(year, dow, doy), + dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, + resYear, resDayOfYear; + + if (dayOfYear <= 0) { + resYear = year - 1; + resDayOfYear = daysInYear(resYear) + dayOfYear; + } else if (dayOfYear > daysInYear(year)) { + resYear = year + 1; + resDayOfYear = dayOfYear - daysInYear(year); + } else { + resYear = year; + resDayOfYear = dayOfYear; + } + + return { + year: resYear, + dayOfYear: resDayOfYear + }; + } + + function weekOfYear(mom, dow, doy) { + var weekOffset = firstWeekOffset(mom.year(), dow, doy), + week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, + resWeek, resYear; + + if (week < 1) { + resYear = mom.year() - 1; + resWeek = week + weeksInYear(resYear, dow, doy); + } else if (week > weeksInYear(mom.year(), dow, doy)) { + resWeek = week - weeksInYear(mom.year(), dow, doy); + resYear = mom.year() + 1; + } else { + resYear = mom.year(); + resWeek = week; + } + + return { + week: resWeek, + year: resYear + }; + } + + function weeksInYear(year, dow, doy) { + var weekOffset = firstWeekOffset(year, dow, doy), + weekOffsetNext = firstWeekOffset(year + 1, dow, doy); + return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; + } + + // FORMATTING + + addFormatToken('w', ['ww', 2], 'wo', 'week'); + addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); + + // ALIASES + + addUnitAlias('week', 'w'); + addUnitAlias('isoWeek', 'W'); + + // PRIORITIES + + addUnitPriority('week', 5); + addUnitPriority('isoWeek', 5); + + // PARSING + + addRegexToken('w', match1to2); + addRegexToken('ww', match1to2, match2); + addRegexToken('W', match1to2); + addRegexToken('WW', match1to2, match2); + + addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { + week[token.substr(0, 1)] = toInt(input); + }); + + // HELPERS + + // LOCALES + + function localeWeek (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + } + + var defaultLocaleWeek = { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 6th is the first week of the year. + }; + + function localeFirstDayOfWeek () { + return this._week.dow; + } + + function localeFirstDayOfYear () { + return this._week.doy; + } + + // MOMENTS + + function getSetWeek (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + function getSetISOWeek (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + // FORMATTING + + addFormatToken('d', 0, 'do', 'day'); + + addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); + }); + + addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); + }); + + addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); + }); + + addFormatToken('e', 0, 0, 'weekday'); + addFormatToken('E', 0, 0, 'isoWeekday'); + + // ALIASES + + addUnitAlias('day', 'd'); + addUnitAlias('weekday', 'e'); + addUnitAlias('isoWeekday', 'E'); + + // PRIORITY + addUnitPriority('day', 11); + addUnitPriority('weekday', 11); + addUnitPriority('isoWeekday', 11); + + // PARSING + + addRegexToken('d', match1to2); + addRegexToken('e', match1to2); + addRegexToken('E', match1to2); + addRegexToken('dd', function (isStrict, locale) { + return locale.weekdaysMinRegex(isStrict); + }); + addRegexToken('ddd', function (isStrict, locale) { + return locale.weekdaysShortRegex(isStrict); + }); + addRegexToken('dddd', function (isStrict, locale) { + return locale.weekdaysRegex(isStrict); + }); + + addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { + var weekday = config._locale.weekdaysParse(input, token, config._strict); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + getParsingFlags(config).invalidWeekday = input; + } + }); + + addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); + }); + + // HELPERS + + function parseWeekday(input, locale) { + if (typeof input !== 'string') { + return input; + } + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; + } + + function parseIsoWeekday(input, locale) { + if (typeof input === 'string') { + return locale.weekdaysParse(input) % 7 || 7; + } + return isNaN(input) ? null : input; + } + + // LOCALES + function shiftWeekdays (ws, n) { + return ws.slice(n, 7).concat(ws.slice(0, n)); + } + + var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); + function localeWeekdays (m, format) { + var weekdays = isArray(this._weekdays) ? this._weekdays : + this._weekdays[(m && m !== true && this._weekdays.isFormat.test(format)) ? 'format' : 'standalone']; + return (m === true) ? shiftWeekdays(weekdays, this._week.dow) + : (m) ? weekdays[m.day()] : weekdays; + } + + var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); + function localeWeekdaysShort (m) { + return (m === true) ? shiftWeekdays(this._weekdaysShort, this._week.dow) + : (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; + } + + var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); + function localeWeekdaysMin (m) { + return (m === true) ? shiftWeekdays(this._weekdaysMin, this._week.dow) + : (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; + } + + function handleStrictParse$1(weekdayName, format, strict) { + var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._shortWeekdaysParse = []; + this._minWeekdaysParse = []; + + for (i = 0; i < 7; ++i) { + mom = createUTC([2000, 1]).day(i); + this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); + this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); + this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeWeekdaysParse (weekdayName, format, strict) { + var i, mom, regex; + + if (this._weekdaysParseExact) { + return handleStrictParse$1.call(this, weekdayName, format, strict); + } + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._minWeekdaysParse = []; + this._shortWeekdaysParse = []; + this._fullWeekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + + mom = createUTC([2000, 1]).day(i); + if (strict && !this._fullWeekdaysParse[i]) { + this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i'); + this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i'); + this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i'); + } + if (!this._weekdaysParse[i]) { + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + } + + // MOMENTS + + function getSetDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + } + + function getSetLocaleDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + } + + function getSetISODayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + + if (input != null) { + var weekday = parseIsoWeekday(input, this.localeData()); + return this.day(this.day() % 7 ? weekday : weekday - 7); + } else { + return this.day() || 7; + } + } + + var defaultWeekdaysRegex = matchWord; + function weekdaysRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysStrictRegex; + } else { + return this._weekdaysRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysRegex')) { + this._weekdaysRegex = defaultWeekdaysRegex; + } + return this._weekdaysStrictRegex && isStrict ? + this._weekdaysStrictRegex : this._weekdaysRegex; + } + } + + var defaultWeekdaysShortRegex = matchWord; + function weekdaysShortRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysShortStrictRegex; + } else { + return this._weekdaysShortRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysShortRegex')) { + this._weekdaysShortRegex = defaultWeekdaysShortRegex; + } + return this._weekdaysShortStrictRegex && isStrict ? + this._weekdaysShortStrictRegex : this._weekdaysShortRegex; + } + } + + var defaultWeekdaysMinRegex = matchWord; + function weekdaysMinRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysMinStrictRegex; + } else { + return this._weekdaysMinRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysMinRegex')) { + this._weekdaysMinRegex = defaultWeekdaysMinRegex; + } + return this._weekdaysMinStrictRegex && isStrict ? + this._weekdaysMinStrictRegex : this._weekdaysMinRegex; + } + } + + + function computeWeekdaysParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], + i, mom, minp, shortp, longp; + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, 1]).day(i); + minp = this.weekdaysMin(mom, ''); + shortp = this.weekdaysShort(mom, ''); + longp = this.weekdays(mom, ''); + minPieces.push(minp); + shortPieces.push(shortp); + longPieces.push(longp); + mixedPieces.push(minp); + mixedPieces.push(shortp); + mixedPieces.push(longp); + } + // Sorting makes sure if one weekday (or abbr) is a prefix of another it + // will match the longer piece. + minPieces.sort(cmpLenRev); + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 7; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._weekdaysShortRegex = this._weekdaysRegex; + this._weekdaysMinRegex = this._weekdaysRegex; + + this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); + } + + // FORMATTING + + function hFormat() { + return this.hours() % 12 || 12; + } + + function kFormat() { + return this.hours() || 24; + } + + addFormatToken('H', ['HH', 2], 0, 'hour'); + addFormatToken('h', ['hh', 2], 0, hFormat); + addFormatToken('k', ['kk', 2], 0, kFormat); + + addFormatToken('hmm', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); + }); + + addFormatToken('hmmss', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); + }); + + addFormatToken('Hmm', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2); + }); + + addFormatToken('Hmmss', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); + }); + + function meridiem (token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); + }); + } + + meridiem('a', true); + meridiem('A', false); + + // ALIASES + + addUnitAlias('hour', 'h'); + + // PRIORITY + addUnitPriority('hour', 13); + + // PARSING + + function matchMeridiem (isStrict, locale) { + return locale._meridiemParse; + } + + addRegexToken('a', matchMeridiem); + addRegexToken('A', matchMeridiem); + addRegexToken('H', match1to2); + addRegexToken('h', match1to2); + addRegexToken('k', match1to2); + addRegexToken('HH', match1to2, match2); + addRegexToken('hh', match1to2, match2); + addRegexToken('kk', match1to2, match2); + + addRegexToken('hmm', match3to4); + addRegexToken('hmmss', match5to6); + addRegexToken('Hmm', match3to4); + addRegexToken('Hmmss', match5to6); + + addParseToken(['H', 'HH'], HOUR); + addParseToken(['k', 'kk'], function (input, array, config) { + var kInput = toInt(input); + array[HOUR] = kInput === 24 ? 0 : kInput; + }); + addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; + }); + addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('Hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + }); + addParseToken('Hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + }); + + // LOCALES + + function localeIsPM (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + } + + var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; + function localeMeridiem (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + } + + + // MOMENTS + + // Setting the hour should keep the time, because the user explicitly + // specified which hour they want. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + var getSetHour = makeGetSet('Hours', true); + + var baseConfig = { + calendar: defaultCalendar, + longDateFormat: defaultLongDateFormat, + invalidDate: defaultInvalidDate, + ordinal: defaultOrdinal, + dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, + relativeTime: defaultRelativeTime, + + months: defaultLocaleMonths, + monthsShort: defaultLocaleMonthsShort, + + week: defaultLocaleWeek, + + weekdays: defaultLocaleWeekdays, + weekdaysMin: defaultLocaleWeekdaysMin, + weekdaysShort: defaultLocaleWeekdaysShort, + + meridiemParse: defaultLocaleMeridiemParse + }; + + // internal storage for locale config files + var locales = {}; + var localeFamilies = {}; + var globalLocale; + + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } + + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return globalLocale; + } + + function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && ('object' !== 'undefined') && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + var aliasedRequire = commonjsRequire; + aliasedRequire('./locale/' + name); + getSetGlobalLocale(oldLocale); + } catch (e) {} + } + return locales[name]; + } + + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + function getSetGlobalLocale (key, values) { + var data; + if (key) { + if (isUndefined(values)) { + data = getLocale(key); + } + else { + data = defineLocale(key, values); + } + + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } + else { + if ((typeof console !== 'undefined') && console.warn) { + //warn user if arguments are passed but the locale could not be set + console.warn('Locale ' + key + ' not found. Did you forget to load it?'); + } + } + } + + return globalLocale._abbr; + } + + function defineLocale (name, config) { + if (config !== null) { + var locale, parentConfig = baseConfig; + config.abbr = name; + if (locales[name] != null) { + deprecateSimple('defineLocaleOverride', + 'use moment.updateLocale(localeName, config) to change ' + + 'an existing locale. moment.defineLocale(localeName, ' + + 'config) should only be used for creating a new locale ' + + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); + parentConfig = locales[name]._config; + } else if (config.parentLocale != null) { + if (locales[config.parentLocale] != null) { + parentConfig = locales[config.parentLocale]._config; + } else { + locale = loadLocale(config.parentLocale); + if (locale != null) { + parentConfig = locale._config; + } else { + if (!localeFamilies[config.parentLocale]) { + localeFamilies[config.parentLocale] = []; + } + localeFamilies[config.parentLocale].push({ + name: name, + config: config + }); + return null; + } + } + } + locales[name] = new Locale(mergeConfigs(parentConfig, config)); + + if (localeFamilies[name]) { + localeFamilies[name].forEach(function (x) { + defineLocale(x.name, x.config); + }); + } + + // backwards compat for now: also set the locale + // make sure we set the locale AFTER all child locales have been + // created, so we won't end up with the child locale set. + getSetGlobalLocale(name); + + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + } + + function updateLocale(name, config) { + if (config != null) { + var locale, tmpLocale, parentConfig = baseConfig; + // MERGE + tmpLocale = loadLocale(name); + if (tmpLocale != null) { + parentConfig = tmpLocale._config; + } + config = mergeConfigs(parentConfig, config); + locale = new Locale(config); + locale.parentLocale = locales[name]; + locales[name] = locale; + + // backwards compat for now: also set the locale + getSetGlobalLocale(name); + } else { + // pass null for config to unupdate, useful for tests + if (locales[name] != null) { + if (locales[name].parentLocale != null) { + locales[name] = locales[name].parentLocale; + } else if (locales[name] != null) { + delete locales[name]; + } + } + } + return locales[name]; + } + + // returns locale data + function getLocale (key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + + if (!key) { + return globalLocale; + } + + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + + return chooseLocale(key); + } + + function listLocales() { + return keys(locales); + } + + function checkOverflow (m) { + var overflow; + var a = m._a; + + if (a && getParsingFlags(m).overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : + a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : + a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : + a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : + a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : + a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : + -1; + + if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } + if (getParsingFlags(m)._overflowWeeks && overflow === -1) { + overflow = WEEK; + } + if (getParsingFlags(m)._overflowWeekday && overflow === -1) { + overflow = WEEKDAY; + } + + getParsingFlags(m).overflow = overflow; + } + + return m; + } + + // Pick the first defined of two or three arguments. + function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; + } + return c; + } + + function currentDateArray(config) { + // hooks is actually the exported moment object + var nowValue = new Date(hooks.now()); + if (config._useUTC) { + return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; + } + return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; + } + + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function configFromArray (config) { + var i, date, input = [], currentDate, expectedWeekday, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear != null) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { + getParsingFlags(config)._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); + expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); + + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } + + // check for mismatching day of week + if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { + getParsingFlags(config).weekdayMismatch = true; + } + } + + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + if (weekday < 1 || weekday > 7) { + weekdayOverflow = true; + } + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + var curWeek = weekOfYear(createLocal(), dow, doy); + + weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); + + // Default to current week. + week = defaults(w.w, curWeek.week); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < 0 || weekday > 6) { + weekdayOverflow = true; + } + } else if (w.e != null) { + // local weekday -- counting starts from beginning of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { + weekdayOverflow = true; + } + } else { + // default to beginning of week + weekday = dow; + } + } + if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { + getParsingFlags(config)._overflowWeeks = true; + } else if (weekdayOverflow != null) { + getParsingFlags(config)._overflowWeekday = true; + } else { + temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } + } + + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; + var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; + + var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; + + var isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], + ['GGGG-[W]WW', /\d{4}-W\d\d/, false], + ['YYYY-DDD', /\d{4}-\d{3}/], + ['YYYY-MM', /\d{4}-\d\d/, false], + ['YYYYYYMMDD', /[+-]\d{10}/], + ['YYYYMMDD', /\d{8}/], + // YYYYMM is NOT allowed by the standard + ['GGGG[W]WWE', /\d{4}W\d{3}/], + ['GGGG[W]WW', /\d{4}W\d{2}/, false], + ['YYYYDDD', /\d{7}/] + ]; + + // iso time formats and regexes + var isoTimes = [ + ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], + ['HH:mm:ss', /\d\d:\d\d:\d\d/], + ['HH:mm', /\d\d:\d\d/], + ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], + ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], + ['HHmmss', /\d\d\d\d\d\d/], + ['HHmm', /\d\d\d\d/], + ['HH', /\d\d/] + ]; + + var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + + // date from iso format + function configFromISO(config) { + var i, l, + string = config._i, + match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), + allowTime, dateFormat, timeFormat, tzFormat; + + if (match) { + getParsingFlags(config).iso = true; + + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(match[1])) { + dateFormat = isoDates[i][0]; + allowTime = isoDates[i][2] !== false; + break; + } + } + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3]) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; + break; + } + } + if (timeFormat == null) { + config._isValid = false; + return; + } + } + if (!allowTime && timeFormat != null) { + config._isValid = false; + return; + } + if (match[4]) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; + } + } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); + configFromStringAndFormat(config); + } else { + config._isValid = false; + } + } + + // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 + var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; + + function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { + var result = [ + untruncateYear(yearStr), + defaultLocaleMonthsShort.indexOf(monthStr), + parseInt(dayStr, 10), + parseInt(hourStr, 10), + parseInt(minuteStr, 10) + ]; + + if (secondStr) { + result.push(parseInt(secondStr, 10)); + } + + return result; + } + + function untruncateYear(yearStr) { + var year = parseInt(yearStr, 10); + if (year <= 49) { + return 2000 + year; + } else if (year <= 999) { + return 1900 + year; + } + return year; + } + + function preprocessRFC2822(s) { + // Remove comments and folding whitespace and replace multiple-spaces with a single space + return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + + function checkWeekday(weekdayStr, parsedInput, config) { + if (weekdayStr) { + // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. + var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), + weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); + if (weekdayProvided !== weekdayActual) { + getParsingFlags(config).weekdayMismatch = true; + config._isValid = false; + return false; + } + } + return true; + } + + var obsOffsets = { + UT: 0, + GMT: 0, + EDT: -4 * 60, + EST: -5 * 60, + CDT: -5 * 60, + CST: -6 * 60, + MDT: -6 * 60, + MST: -7 * 60, + PDT: -7 * 60, + PST: -8 * 60 + }; + + function calculateOffset(obsOffset, militaryOffset, numOffset) { + if (obsOffset) { + return obsOffsets[obsOffset]; + } else if (militaryOffset) { + // the only allowed military tz is Z + return 0; + } else { + var hm = parseInt(numOffset, 10); + var m = hm % 100, h = (hm - m) / 100; + return h * 60 + m; + } + } + + // date and time from ref 2822 format + function configFromRFC2822(config) { + var match = rfc2822.exec(preprocessRFC2822(config._i)); + if (match) { + var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); + if (!checkWeekday(match[1], parsedArray, config)) { + return; + } + + config._a = parsedArray; + config._tzm = calculateOffset(match[8], match[9], match[10]); + + config._d = createUTCDate.apply(null, config._a); + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + + getParsingFlags(config).rfc2822 = true; + } else { + config._isValid = false; + } + } + + // date from iso format or fallback + function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); + + if (matched !== null) { + config._d = new Date(+matched[1]); + return; + } + + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + configFromRFC2822(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + // Final attempt, use Input Fallback + hooks.createFromInputFallback(config); + } + + hooks.createFromInputFallback = deprecate( + 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + + 'discouraged and will be removed in an upcoming major release. Please refer to ' + + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); + + // constant that refers to the ISO standard + hooks.ISO_8601 = function () {}; + + // constant that refers to the RFC 2822 form + hooks.RFC_2822 = function () {}; + + // date from string and format string + function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === hooks.ISO_8601) { + configFromISO(config); + return; + } + if (config._f === hooks.RFC_2822) { + configFromRFC2822(config); + return; + } + config._a = []; + getParsingFlags(config).empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + // console.log('token', token, 'parsedInput', parsedInput, + // 'regex', getParseRegexForToken(token, config)); + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + getParsingFlags(config).unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + getParsingFlags(config).empty = false; + } + else { + getParsingFlags(config).unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + getParsingFlags(config).unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + getParsingFlags(config).unusedInput.push(string); + } + + // clear _12h flag if hour is <= 12 + if (config._a[HOUR] <= 12 && + getParsingFlags(config).bigHour === true && + config._a[HOUR] > 0) { + getParsingFlags(config).bigHour = undefined; + } + + getParsingFlags(config).parsedDateParts = config._a.slice(0); + getParsingFlags(config).meridiem = config._meridiem; + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); + + configFromArray(config); + checkOverflow(config); + } + + + function meridiemFixWrap (locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; + } + } + + // date from string and array of format strings + function configFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + getParsingFlags(config).invalidFormat = true; + config._d = new Date(NaN); + return; + } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); + + if (!isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += getParsingFlags(tempConfig).charsLeftOver; + + //or tokens + currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; + + getParsingFlags(tempConfig).score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } + + extend(config, bestMoment || tempConfig); + } + + function configFromObject(config) { + if (config._d) { + return; + } + + var i = normalizeObjectUnits(config._i); + config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { + return obj && parseInt(obj, 10); + }); + + configFromArray(config); + } + + function createFromConfig (config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; + } + + function prepareConfig (config) { + var input = config._i, + format = config._f; + + config._locale = config._locale || getLocale(config._l); + + if (input === null || (format === undefined && input === '')) { + return createInvalid({nullInput: true}); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isDate(input)) { + config._d = input; + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); + } + + if (!isValid(config)) { + config._d = null; + } + + return config; + } + + function configFromInput(config) { + var input = config._i; + if (isUndefined(input)) { + config._d = new Date(hooks.now()); + } else if (isDate(input)) { + config._d = new Date(input.valueOf()); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (isObject(input)) { + configFromObject(config); + } else if (isNumber(input)) { + // from milliseconds + config._d = new Date(input); + } else { + hooks.createFromInputFallback(config); + } + } + + function createLocalOrUTC (input, format, locale, strict, isUTC) { + var c = {}; + + if (locale === true || locale === false) { + strict = locale; + locale = undefined; + } + + if ((isObject(input) && isObjectEmpty(input)) || + (isArray(input) && input.length === 0)) { + input = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + + return createFromConfig(c); + } + + function createLocal (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); + } + + var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other < this ? this : other; + } else { + return createInvalid(); + } + } + ); + + var prototypeMax = deprecate( + 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other > this ? this : other; + } else { + return createInvalid(); + } + } + ); + + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (!moments[i].isValid() || moments[i][fn](res)) { + res = moments[i]; + } + } + return res; + } + + // TODO: Use [].sort instead? + function min () { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); + } + + function max () { + var args = [].slice.call(arguments, 0); + + return pickBy('isAfter', args); + } + + var now = function () { + return Date.now ? Date.now() : +(new Date()); + }; + + var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; + + function isDurationValid(m) { + for (var key in m) { + if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { + return false; + } + } + + var unitHasDecimal = false; + for (var i = 0; i < ordering.length; ++i) { + if (m[ordering[i]]) { + if (unitHasDecimal) { + return false; // only allow non-integers for smallest unit + } + if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { + unitHasDecimal = true; + } + } + } + + return true; + } + + function isValid$1() { + return this._isValid; + } + + function createInvalid$1() { + return createDuration(NaN); + } + + function Duration (duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || normalizedInput.isoWeek || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + this._isValid = isDurationValid(normalizedInput); + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible to translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._locale = getLocale(); + + this._bubble(); + } + + function isDuration (obj) { + return obj instanceof Duration; + } + + function absRound (number) { + if (number < 0) { + return Math.round(-1 * number) * -1; + } else { + return Math.round(number); + } + } + + // FORMATTING + + function offset (token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(); + var sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); + }); + } + + offset('Z', ':'); + offset('ZZ', ''); + + // PARSING + + addRegexToken('Z', matchShortOffset); + addRegexToken('ZZ', matchShortOffset); + addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(matchShortOffset, input); + }); + + // HELPERS + + // timezone chunker + // '+10:00' > ['10', '00'] + // '-1530' > ['-15', '30'] + var chunkOffset = /([\+\-]|\d\d)/gi; + + function offsetFromString(matcher, string) { + var matches = (string || '').match(matcher); + + if (matches === null) { + return null; + } + + var chunk = matches[matches.length - 1] || []; + var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + var minutes = +(parts[1] * 60) + toInt(parts[2]); + + return minutes === 0 ? + 0 : + parts[0] === '+' ? minutes : -minutes; + } + + // Return a moment from input, that is local/utc/zone equivalent to model. + function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); + // Use low-level api, because this fn is low-level api. + res._d.setTime(res._d.valueOf() + diff); + hooks.updateOffset(res, false); + return res; + } else { + return createLocal(input).local(); + } + } + + function getDateOffset (m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset() / 15) * 15; + } + + // HOOKS + + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + hooks.updateOffset = function () {}; + + // MOMENTS + + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + function getSetOffset (input, keepLocalTime, keepMinutes) { + var offset = this._offset || 0, + localAdjust; + if (!this.isValid()) { + return input != null ? this : NaN; + } + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(matchShortOffset, input); + if (input === null) { + return this; + } + } else if (Math.abs(input) < 16 && !keepMinutes) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addSubtract(this, createDuration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : getDateOffset(this); + } + } + + function getSetZone (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); + + return this; + } else { + return -this.utcOffset(); + } + } + + function setOffsetToUTC (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + } + + function setOffsetToLocal (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); + } + } + return this; + } + + function setOffsetToParsedOffset () { + if (this._tzm != null) { + this.utcOffset(this._tzm, false, true); + } else if (typeof this._i === 'string') { + var tZone = offsetFromString(matchOffset, this._i); + if (tZone != null) { + this.utcOffset(tZone); + } + else { + this.utcOffset(0, true); + } + } + return this; + } + + function hasAlignedHourOffset (input) { + if (!this.isValid()) { + return false; + } + input = input ? createLocal(input).utcOffset() : 0; + + return (this.utcOffset() - input) % 60 === 0; + } + + function isDaylightSavingTime () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); + } + + function isDaylightSavingTimeShifted () { + if (!isUndefined(this._isDSTShifted)) { + return this._isDSTShifted; + } + + var c = {}; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); + this._isDSTShifted = this.isValid() && + compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; + } + + return this._isDSTShifted; + } + + function isLocal () { + return this.isValid() ? !this._isUTC : false; + } + + function isUtcOffset () { + return this.isValid() ? this._isUTC : false; + } + + function isUtc () { + return this.isValid() ? this._isUTC && this._offset === 0 : false; + } + + // ASP.NET json date format regex + var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; + + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + // and further modified to allow for strings containing both week and day + var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; + + function createDuration (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; + + if (isDuration(input)) { + duration = { + ms : input._milliseconds, + d : input._days, + M : input._months + }; + } else if (isNumber(input)) { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : 0, + d : toInt(match[DATE]) * sign, + h : toInt(match[HOUR]) * sign, + m : toInt(match[MINUTE]) * sign, + s : toInt(match[SECOND]) * sign, + ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match + }; + } else if (!!(match = isoRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : parseIso(match[2], sign), + M : parseIso(match[3], sign), + w : parseIso(match[4], sign), + d : parseIso(match[5], sign), + h : parseIso(match[6], sign), + m : parseIso(match[7], sign), + s : parseIso(match[8], sign) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } + + ret = new Duration(duration); + + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + return ret; + } + + createDuration.fn = Duration.prototype; + createDuration.invalid = createInvalid$1; + + function parseIso (inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + } + + function positiveMomentsDifference(base, other) { + var res = {}; + + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } + + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + + return res; + } + + function momentsDifference(base, other) { + var res; + if (!(base.isValid() && other.isValid())) { + return {milliseconds: 0, months: 0}; + } + + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } + + return res; + } + + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); + tmp = val; val = period; period = tmp; + } + + val = typeof val === 'string' ? +val : val; + dur = createDuration(val, period); + addSubtract(this, dur, direction); + return this; + }; + } + + function addSubtract (mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = absRound(duration._days), + months = absRound(duration._months); + + if (!mom.isValid()) { + // No op + return; + } + + updateOffset = updateOffset == null ? true : updateOffset; + + if (months) { + setMonth(mom, get(mom, 'Month') + months * isAdding); + } + if (days) { + set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); + } + if (milliseconds) { + mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); + } + if (updateOffset) { + hooks.updateOffset(mom, days || months); + } + } + + var add = createAdder(1, 'add'); + var subtract = createAdder(-1, 'subtract'); + + function getCalendarFormat(myMoment, now) { + var diff = myMoment.diff(now, 'days', true); + return diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + } + + function calendar$1 (time, formats) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + format = hooks.calendarFormat(this, sod) || 'sameElse'; + + var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); + + return this.format(output || this.localeData().calendar(format, this, createLocal(now))); + } + + function clone () { + return new Moment(this); + } + + function isAfter (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() > localInput.valueOf(); + } else { + return localInput.valueOf() < this.clone().startOf(units).valueOf(); + } + } + + function isBefore (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() < localInput.valueOf(); + } else { + return this.clone().endOf(units).valueOf() < localInput.valueOf(); + } + } + + function isBetween (from, to, units, inclusivity) { + var localFrom = isMoment(from) ? from : createLocal(from), + localTo = isMoment(to) ? to : createLocal(to); + if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { + return false; + } + inclusivity = inclusivity || '()'; + return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && + (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units)); + } + + function isSame (input, units) { + var localInput = isMoment(input) ? input : createLocal(input), + inputMs; + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() === localInput.valueOf(); + } else { + inputMs = localInput.valueOf(); + return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); + } + } + + function isSameOrAfter (input, units) { + return this.isSame(input, units) || this.isAfter(input, units); + } + + function isSameOrBefore (input, units) { + return this.isSame(input, units) || this.isBefore(input, units); + } + + function diff (input, units, asFloat) { + var that, + zoneDelta, + output; + + if (!this.isValid()) { + return NaN; + } + + that = cloneWithOffset(input, this); + + if (!that.isValid()) { + return NaN; + } + + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; + + units = normalizeUnits(units); + + switch (units) { + case 'year': output = monthDiff(this, that) / 12; break; + case 'month': output = monthDiff(this, that); break; + case 'quarter': output = monthDiff(this, that) / 3; break; + case 'second': output = (this - that) / 1e3; break; // 1000 + case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 + case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 + case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst + case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst + default: output = this - that; + } + + return asFloat ? output : absFloor(output); + } + + function monthDiff (a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; + + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } + + //check for negative zero, return zero if negative zero + return -(wholeMonthDiff + adjust) || 0; + } + + hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; + hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; + + function toString () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + } + + function toISOString(keepOffset) { + if (!this.isValid()) { + return null; + } + var utc = keepOffset !== true; + var m = utc ? this.clone().utc() : this; + if (m.year() < 0 || m.year() > 9999) { + return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); + } + if (isFunction(Date.prototype.toISOString)) { + // native implementation is ~50x faster, use it when we can + if (utc) { + return this.toDate().toISOString(); + } else { + return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z')); + } + } + return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); + } + + /** + * Return a human readable representation of a moment that can + * also be evaluated to get a new moment which is the same + * + * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects + */ + function inspect () { + if (!this.isValid()) { + return 'moment.invalid(/* ' + this._i + ' */)'; + } + var func = 'moment'; + var zone = ''; + if (!this.isLocal()) { + func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; + zone = 'Z'; + } + var prefix = '[' + func + '("]'; + var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; + var datetime = '-MM-DD[T]HH:mm:ss.SSS'; + var suffix = zone + '[")]'; + + return this.format(prefix + year + datetime + suffix); + } + + function format (inputString) { + if (!inputString) { + inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; + } + var output = formatMoment(this, inputString); + return this.localeData().postformat(output); + } + + function from (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function fromNow (withoutSuffix) { + return this.from(createLocal(), withoutSuffix); + } + + function to (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function toNow (withoutSuffix) { + return this.to(createLocal(), withoutSuffix); + } + + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + function locale (key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + } + + var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ); + + function localeData () { + return this._locale; + } + + var MS_PER_SECOND = 1000; + var MS_PER_MINUTE = 60 * MS_PER_SECOND; + var MS_PER_HOUR = 60 * MS_PER_MINUTE; + var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; + + // actual modulo - handles negative numbers (for dates before 1970): + function mod$1(dividend, divisor) { + return (dividend % divisor + divisor) % divisor; + } + + function localStartOfDate(y, m, d) { + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return new Date(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return new Date(y, m, d).valueOf(); + } + } + + function utcStartOfDate(y, m, d) { + // Date.UTC remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return Date.UTC(y, m, d); + } + } + + function startOf (units) { + var time; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year(), 0, 1); + break; + case 'quarter': + time = startOfDate(this.year(), this.month() - this.month() % 3, 1); + break; + case 'month': + time = startOfDate(this.year(), this.month(), 1); + break; + case 'week': + time = startOfDate(this.year(), this.month(), this.date() - this.weekday()); + break; + case 'isoWeek': + time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1)); + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date()); + break; + case 'hour': + time = this._d.valueOf(); + time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR); + break; + case 'minute': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_MINUTE); + break; + case 'second': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_SECOND); + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + function endOf (units) { + var time; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year() + 1, 0, 1) - 1; + break; + case 'quarter': + time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1; + break; + case 'month': + time = startOfDate(this.year(), this.month() + 1, 1) - 1; + break; + case 'week': + time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; + break; + case 'isoWeek': + time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1; + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; + break; + case 'hour': + time = this._d.valueOf(); + time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1; + break; + case 'minute': + time = this._d.valueOf(); + time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; + break; + case 'second': + time = this._d.valueOf(); + time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + function valueOf () { + return this._d.valueOf() - ((this._offset || 0) * 60000); + } + + function unix () { + return Math.floor(this.valueOf() / 1000); + } + + function toDate () { + return new Date(this.valueOf()); + } + + function toArray () { + var m = this; + return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; + } + + function toObject () { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() + }; + } + + function toJSON () { + // new Date(NaN).toJSON() === null + return this.isValid() ? this.toISOString() : null; + } + + function isValid$2 () { + return isValid(this); + } + + function parsingFlags () { + return extend({}, getParsingFlags(this)); + } + + function invalidAt () { + return getParsingFlags(this).overflow; + } + + function creationData() { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict + }; + } + + // FORMATTING + + addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; + }); + + addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; + }); + + function addWeekYearFormatToken (token, getter) { + addFormatToken(0, [token, token.length], 0, getter); + } + + addWeekYearFormatToken('gggg', 'weekYear'); + addWeekYearFormatToken('ggggg', 'weekYear'); + addWeekYearFormatToken('GGGG', 'isoWeekYear'); + addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + + // ALIASES + + addUnitAlias('weekYear', 'gg'); + addUnitAlias('isoWeekYear', 'GG'); + + // PRIORITY + + addUnitPriority('weekYear', 1); + addUnitPriority('isoWeekYear', 1); + + + // PARSING + + addRegexToken('G', matchSigned); + addRegexToken('g', matchSigned); + addRegexToken('GG', match1to2, match2); + addRegexToken('gg', match1to2, match2); + addRegexToken('GGGG', match1to4, match4); + addRegexToken('gggg', match1to4, match4); + addRegexToken('GGGGG', match1to6, match6); + addRegexToken('ggggg', match1to6, match6); + + addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { + week[token.substr(0, 2)] = toInt(input); + }); + + addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = hooks.parseTwoDigitYear(input); + }); + + // MOMENTS + + function getSetWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy); + } + + function getSetISOWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, this.isoWeek(), this.isoWeekday(), 1, 4); + } + + function getISOWeeksInYear () { + return weeksInYear(this.year(), 1, 4); + } + + function getWeeksInYear () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + } + + function getSetWeekYearHelper(input, week, weekday, dow, doy) { + var weeksTarget; + if (input == null) { + return weekOfYear(this, dow, doy).year; + } else { + weeksTarget = weeksInYear(input, dow, doy); + if (week > weeksTarget) { + week = weeksTarget; + } + return setWeekAll.call(this, input, week, weekday, dow, doy); + } + } + + function setWeekAll(weekYear, week, weekday, dow, doy) { + var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), + date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); + + this.year(date.getUTCFullYear()); + this.month(date.getUTCMonth()); + this.date(date.getUTCDate()); + return this; + } + + // FORMATTING + + addFormatToken('Q', 0, 'Qo', 'quarter'); + + // ALIASES + + addUnitAlias('quarter', 'Q'); + + // PRIORITY + + addUnitPriority('quarter', 7); + + // PARSING + + addRegexToken('Q', match1); + addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; + }); + + // MOMENTS + + function getSetQuarter (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + } + + // FORMATTING + + addFormatToken('D', ['DD', 2], 'Do', 'date'); + + // ALIASES + + addUnitAlias('date', 'D'); + + // PRIORITY + addUnitPriority('date', 9); + + // PARSING + + addRegexToken('D', match1to2); + addRegexToken('DD', match1to2, match2); + addRegexToken('Do', function (isStrict, locale) { + // TODO: Remove "ordinalParse" fallback in next major release. + return isStrict ? + (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : + locale._dayOfMonthOrdinalParseLenient; + }); + + addParseToken(['D', 'DD'], DATE); + addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0]); + }); + + // MOMENTS + + var getSetDayOfMonth = makeGetSet('Date', true); + + // FORMATTING + + addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + + // ALIASES + + addUnitAlias('dayOfYear', 'DDD'); + + // PRIORITY + addUnitPriority('dayOfYear', 4); + + // PARSING + + addRegexToken('DDD', match1to3); + addRegexToken('DDDD', match3); + addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); + }); + + // HELPERS + + // MOMENTS + + function getSetDayOfYear (input) { + var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + } + + // FORMATTING + + addFormatToken('m', ['mm', 2], 0, 'minute'); + + // ALIASES + + addUnitAlias('minute', 'm'); + + // PRIORITY + + addUnitPriority('minute', 14); + + // PARSING + + addRegexToken('m', match1to2); + addRegexToken('mm', match1to2, match2); + addParseToken(['m', 'mm'], MINUTE); + + // MOMENTS + + var getSetMinute = makeGetSet('Minutes', false); + + // FORMATTING + + addFormatToken('s', ['ss', 2], 0, 'second'); + + // ALIASES + + addUnitAlias('second', 's'); + + // PRIORITY + + addUnitPriority('second', 15); + + // PARSING + + addRegexToken('s', match1to2); + addRegexToken('ss', match1to2, match2); + addParseToken(['s', 'ss'], SECOND); + + // MOMENTS + + var getSetSecond = makeGetSet('Seconds', false); + + // FORMATTING + + addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); + }); + + addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); + }); + + addFormatToken(0, ['SSS', 3], 0, 'millisecond'); + addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; + }); + addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; + }); + addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; + }); + addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; + }); + addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; + }); + addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; + }); + + + // ALIASES + + addUnitAlias('millisecond', 'ms'); + + // PRIORITY + + addUnitPriority('millisecond', 16); + + // PARSING + + addRegexToken('S', match1to3, match1); + addRegexToken('SS', match1to3, match2); + addRegexToken('SSS', match1to3, match3); + + var token; + for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); + } + + function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); + } + + for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); + } + // MOMENTS + + var getSetMillisecond = makeGetSet('Milliseconds', false); + + // FORMATTING + + addFormatToken('z', 0, 0, 'zoneAbbr'); + addFormatToken('zz', 0, 0, 'zoneName'); + + // MOMENTS + + function getZoneAbbr () { + return this._isUTC ? 'UTC' : ''; + } + + function getZoneName () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + } + + var proto = Moment.prototype; + + proto.add = add; + proto.calendar = calendar$1; + proto.clone = clone; + proto.diff = diff; + proto.endOf = endOf; + proto.format = format; + proto.from = from; + proto.fromNow = fromNow; + proto.to = to; + proto.toNow = toNow; + proto.get = stringGet; + proto.invalidAt = invalidAt; + proto.isAfter = isAfter; + proto.isBefore = isBefore; + proto.isBetween = isBetween; + proto.isSame = isSame; + proto.isSameOrAfter = isSameOrAfter; + proto.isSameOrBefore = isSameOrBefore; + proto.isValid = isValid$2; + proto.lang = lang; + proto.locale = locale; + proto.localeData = localeData; + proto.max = prototypeMax; + proto.min = prototypeMin; + proto.parsingFlags = parsingFlags; + proto.set = stringSet; + proto.startOf = startOf; + proto.subtract = subtract; + proto.toArray = toArray; + proto.toObject = toObject; + proto.toDate = toDate; + proto.toISOString = toISOString; + proto.inspect = inspect; + proto.toJSON = toJSON; + proto.toString = toString; + proto.unix = unix; + proto.valueOf = valueOf; + proto.creationData = creationData; + proto.year = getSetYear; + proto.isLeapYear = getIsLeapYear; + proto.weekYear = getSetWeekYear; + proto.isoWeekYear = getSetISOWeekYear; + proto.quarter = proto.quarters = getSetQuarter; + proto.month = getSetMonth; + proto.daysInMonth = getDaysInMonth; + proto.week = proto.weeks = getSetWeek; + proto.isoWeek = proto.isoWeeks = getSetISOWeek; + proto.weeksInYear = getWeeksInYear; + proto.isoWeeksInYear = getISOWeeksInYear; + proto.date = getSetDayOfMonth; + proto.day = proto.days = getSetDayOfWeek; + proto.weekday = getSetLocaleDayOfWeek; + proto.isoWeekday = getSetISODayOfWeek; + proto.dayOfYear = getSetDayOfYear; + proto.hour = proto.hours = getSetHour; + proto.minute = proto.minutes = getSetMinute; + proto.second = proto.seconds = getSetSecond; + proto.millisecond = proto.milliseconds = getSetMillisecond; + proto.utcOffset = getSetOffset; + proto.utc = setOffsetToUTC; + proto.local = setOffsetToLocal; + proto.parseZone = setOffsetToParsedOffset; + proto.hasAlignedHourOffset = hasAlignedHourOffset; + proto.isDST = isDaylightSavingTime; + proto.isLocal = isLocal; + proto.isUtcOffset = isUtcOffset; + proto.isUtc = isUtc; + proto.isUTC = isUtc; + proto.zoneAbbr = getZoneAbbr; + proto.zoneName = getZoneName; + proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); + proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); + proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); + proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); + proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); + + function createUnix (input) { + return createLocal(input * 1000); + } + + function createInZone () { + return createLocal.apply(null, arguments).parseZone(); + } + + function preParsePostFormat (string) { + return string; + } + + var proto$1 = Locale.prototype; + + proto$1.calendar = calendar; + proto$1.longDateFormat = longDateFormat; + proto$1.invalidDate = invalidDate; + proto$1.ordinal = ordinal; + proto$1.preparse = preParsePostFormat; + proto$1.postformat = preParsePostFormat; + proto$1.relativeTime = relativeTime; + proto$1.pastFuture = pastFuture; + proto$1.set = set; + + proto$1.months = localeMonths; + proto$1.monthsShort = localeMonthsShort; + proto$1.monthsParse = localeMonthsParse; + proto$1.monthsRegex = monthsRegex; + proto$1.monthsShortRegex = monthsShortRegex; + proto$1.week = localeWeek; + proto$1.firstDayOfYear = localeFirstDayOfYear; + proto$1.firstDayOfWeek = localeFirstDayOfWeek; + + proto$1.weekdays = localeWeekdays; + proto$1.weekdaysMin = localeWeekdaysMin; + proto$1.weekdaysShort = localeWeekdaysShort; + proto$1.weekdaysParse = localeWeekdaysParse; + + proto$1.weekdaysRegex = weekdaysRegex; + proto$1.weekdaysShortRegex = weekdaysShortRegex; + proto$1.weekdaysMinRegex = weekdaysMinRegex; + + proto$1.isPM = localeIsPM; + proto$1.meridiem = localeMeridiem; + + function get$1 (format, index, field, setter) { + var locale = getLocale(); + var utc = createUTC().set(setter, index); + return locale[field](utc, format); + } + + function listMonthsImpl (format, index, field) { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + + if (index != null) { + return get$1(format, index, field, 'month'); + } + + var i; + var out = []; + for (i = 0; i < 12; i++) { + out[i] = get$1(format, i, field, 'month'); + } + return out; + } + + // () + // (5) + // (fmt, 5) + // (fmt) + // (true) + // (true, 5) + // (true, fmt, 5) + // (true, fmt) + function listWeekdaysImpl (localeSorted, format, index, field) { + if (typeof localeSorted === 'boolean') { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } else { + format = localeSorted; + index = format; + localeSorted = false; + + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } + + var locale = getLocale(), + shift = localeSorted ? locale._week.dow : 0; + + if (index != null) { + return get$1(format, (index + shift) % 7, field, 'day'); + } + + var i; + var out = []; + for (i = 0; i < 7; i++) { + out[i] = get$1(format, (i + shift) % 7, field, 'day'); + } + return out; + } + + function listMonths (format, index) { + return listMonthsImpl(format, index, 'months'); + } + + function listMonthsShort (format, index) { + return listMonthsImpl(format, index, 'monthsShort'); + } + + function listWeekdays (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); + } + + function listWeekdaysShort (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); + } + + function listWeekdaysMin (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); + } + + getSetGlobalLocale('en', { + dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); + + // Side effect imports + + hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); + hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); + + var mathAbs = Math.abs; + + function abs () { + var data = this._data; + + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); + + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); + + return this; + } + + function addSubtract$1 (duration, input, value, direction) { + var other = createDuration(input, value); + + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; + + return duration._bubble(); + } + + // supports only 2.0-style add(1, 's') or add(duration) + function add$1 (input, value) { + return addSubtract$1(this, input, value, 1); + } + + // supports only 2.0-style subtract(1, 's') or subtract(duration) + function subtract$1 (input, value) { + return addSubtract$1(this, input, value, -1); + } + + function absCeil (number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } + } + + function bubble () { + var milliseconds = this._milliseconds; + var days = this._days; + var months = this._months; + var data = this._data; + var seconds, minutes, hours, years, monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if (!((milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0))) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; + } + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; + + hours = absFloor(minutes / 60); + data.hours = hours % 24; + + days += absFloor(hours / 24); + + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + + return this; + } + + function daysToMonths (days) { + // 400 years have 146097 days (taking into account leap year rules) + // 400 years have 12 months === 4800 + return days * 4800 / 146097; + } + + function monthsToDays (months) { + // the reverse of daysToMonths + return months * 146097 / 4800; + } + + function as (units) { + if (!this.isValid()) { + return NaN; + } + var days; + var months; + var milliseconds = this._milliseconds; + + units = normalizeUnits(units); + + if (units === 'month' || units === 'quarter' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + switch (units) { + case 'month': return months; + case 'quarter': return months / 3; + case 'year': return months / 12; + } + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(monthsToDays(this._months)); + switch (units) { + case 'week' : return days / 7 + milliseconds / 6048e5; + case 'day' : return days + milliseconds / 864e5; + case 'hour' : return days * 24 + milliseconds / 36e5; + case 'minute' : return days * 1440 + milliseconds / 6e4; + case 'second' : return days * 86400 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 864e5) + milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + } + + // TODO: Use this.as('ms')? + function valueOf$1 () { + if (!this.isValid()) { + return NaN; + } + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); + } + + function makeAs (alias) { + return function () { + return this.as(alias); + }; + } + + var asMilliseconds = makeAs('ms'); + var asSeconds = makeAs('s'); + var asMinutes = makeAs('m'); + var asHours = makeAs('h'); + var asDays = makeAs('d'); + var asWeeks = makeAs('w'); + var asMonths = makeAs('M'); + var asQuarters = makeAs('Q'); + var asYears = makeAs('y'); + + function clone$1 () { + return createDuration(this); + } + + function get$2 (units) { + units = normalizeUnits(units); + return this.isValid() ? this[units + 's']() : NaN; + } + + function makeGetter(name) { + return function () { + return this.isValid() ? this._data[name] : NaN; + }; + } + + var milliseconds = makeGetter('milliseconds'); + var seconds = makeGetter('seconds'); + var minutes = makeGetter('minutes'); + var hours = makeGetter('hours'); + var days = makeGetter('days'); + var months = makeGetter('months'); + var years = makeGetter('years'); + + function weeks () { + return absFloor(this.days() / 7); + } + + var round = Math.round; + var thresholds = { + ss: 44, // a few seconds to seconds + s : 45, // seconds to minute + m : 45, // minutes to hour + h : 22, // hours to day + d : 26, // days to month + M : 11 // months to year + }; + + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } + + function relativeTime$1 (posNegDuration, withoutSuffix, locale) { + var duration = createDuration(posNegDuration).abs(); + var seconds = round(duration.as('s')); + var minutes = round(duration.as('m')); + var hours = round(duration.as('h')); + var days = round(duration.as('d')); + var months = round(duration.as('M')); + var years = round(duration.as('y')); + + var a = seconds <= thresholds.ss && ['s', seconds] || + seconds < thresholds.s && ['ss', seconds] || + minutes <= 1 && ['m'] || + minutes < thresholds.m && ['mm', minutes] || + hours <= 1 && ['h'] || + hours < thresholds.h && ['hh', hours] || + days <= 1 && ['d'] || + days < thresholds.d && ['dd', days] || + months <= 1 && ['M'] || + months < thresholds.M && ['MM', months] || + years <= 1 && ['y'] || ['yy', years]; + + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); + } + + // This function allows you to set the rounding function for relative time strings + function getSetRelativeTimeRounding (roundingFunction) { + if (roundingFunction === undefined) { + return round; + } + if (typeof(roundingFunction) === 'function') { + round = roundingFunction; + return true; + } + return false; + } + + // This function allows you to set a threshold for relative time strings + function getSetRelativeTimeThreshold (threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + if (threshold === 's') { + thresholds.ss = limit - 1; + } + return true; + } + + function humanize (withSuffix) { + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var locale = this.localeData(); + var output = relativeTime$1(this, !withSuffix, locale); + + if (withSuffix) { + output = locale.pastFuture(+this, output); + } + + return locale.postformat(output); + } + + var abs$1 = Math.abs; + + function sign(x) { + return ((x > 0) - (x < 0)) || +x; + } + + function toISOString$1() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var seconds = abs$1(this._milliseconds) / 1000; + var days = abs$1(this._days); + var months = abs$1(this._months); + var minutes, hours, years; + + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var Y = years; + var M = months; + var D = days; + var h = hours; + var m = minutes; + var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; + var total = this.asSeconds(); + + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } + + var totalSign = total < 0 ? '-' : ''; + var ymSign = sign(this._months) !== sign(total) ? '-' : ''; + var daysSign = sign(this._days) !== sign(total) ? '-' : ''; + var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; + + return totalSign + 'P' + + (Y ? ymSign + Y + 'Y' : '') + + (M ? ymSign + M + 'M' : '') + + (D ? daysSign + D + 'D' : '') + + ((h || m || s) ? 'T' : '') + + (h ? hmsSign + h + 'H' : '') + + (m ? hmsSign + m + 'M' : '') + + (s ? hmsSign + s + 'S' : ''); + } + + var proto$2 = Duration.prototype; + + proto$2.isValid = isValid$1; + proto$2.abs = abs; + proto$2.add = add$1; + proto$2.subtract = subtract$1; + proto$2.as = as; + proto$2.asMilliseconds = asMilliseconds; + proto$2.asSeconds = asSeconds; + proto$2.asMinutes = asMinutes; + proto$2.asHours = asHours; + proto$2.asDays = asDays; + proto$2.asWeeks = asWeeks; + proto$2.asMonths = asMonths; + proto$2.asQuarters = asQuarters; + proto$2.asYears = asYears; + proto$2.valueOf = valueOf$1; + proto$2._bubble = bubble; + proto$2.clone = clone$1; + proto$2.get = get$2; + proto$2.milliseconds = milliseconds; + proto$2.seconds = seconds; + proto$2.minutes = minutes; + proto$2.hours = hours; + proto$2.days = days; + proto$2.weeks = weeks; + proto$2.months = months; + proto$2.years = years; + proto$2.humanize = humanize; + proto$2.toISOString = toISOString$1; + proto$2.toString = toISOString$1; + proto$2.toJSON = toISOString$1; + proto$2.locale = locale; + proto$2.localeData = localeData; + + proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); + proto$2.lang = lang; + + // Side effect imports + + // FORMATTING + + addFormatToken('X', 0, 0, 'unix'); + addFormatToken('x', 0, 0, 'valueOf'); + + // PARSING + + addRegexToken('x', matchSigned); + addRegexToken('X', matchTimestamp); + addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input, 10) * 1000); + }); + addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); + }); + + // Side effect imports + + + hooks.version = '2.24.0'; + + setHookCallback(createLocal); + + hooks.fn = proto; + hooks.min = min; + hooks.max = max; + hooks.now = now; + hooks.utc = createUTC; + hooks.unix = createUnix; + hooks.months = listMonths; + hooks.isDate = isDate; + hooks.locale = getSetGlobalLocale; + hooks.invalid = createInvalid; + hooks.duration = createDuration; + hooks.isMoment = isMoment; + hooks.weekdays = listWeekdays; + hooks.parseZone = createInZone; + hooks.localeData = getLocale; + hooks.isDuration = isDuration; + hooks.monthsShort = listMonthsShort; + hooks.weekdaysMin = listWeekdaysMin; + hooks.defineLocale = defineLocale; + hooks.updateLocale = updateLocale; + hooks.locales = listLocales; + hooks.weekdaysShort = listWeekdaysShort; + hooks.normalizeUnits = normalizeUnits; + hooks.relativeTimeRounding = getSetRelativeTimeRounding; + hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; + hooks.calendarFormat = getCalendarFormat; + hooks.prototype = proto; + + // currently HTML5 input type only supports 24-hour formats + hooks.HTML5_FMT = { + DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" /> + DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" /> + DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" /> + DATE: 'YYYY-MM-DD', // <input type="date" /> + TIME: 'HH:mm', // <input type="time" /> + TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" /> + TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" /> + WEEK: 'GGGG-[W]WW', // <input type="week" /> + MONTH: 'YYYY-MM' // <input type="month" /> + }; + + return hooks; + +}))); +}); + +var FORMATS = { + datetime: 'MMM D, YYYY, h:mm:ss a', + millisecond: 'h:mm:ss.SSS a', + second: 'h:mm:ss a', + minute: 'h:mm a', + hour: 'hA', + day: 'MMM D', + week: 'll', + month: 'MMM YYYY', + quarter: '[Q]Q - YYYY', + year: 'YYYY' +}; + +core_adapters._date.override(typeof moment === 'function' ? { + _id: 'moment', // DEBUG ONLY + + formats: function() { + return FORMATS; + }, + + parse: function(value, format) { + if (typeof value === 'string' && typeof format === 'string') { + value = moment(value, format); + } else if (!(value instanceof moment)) { + value = moment(value); + } + return value.isValid() ? value.valueOf() : null; + }, + + format: function(time, format) { + return moment(time).format(format); + }, + + add: function(time, amount, unit) { + return moment(time).add(amount, unit).valueOf(); + }, + + diff: function(max, min, unit) { + return moment(max).diff(moment(min), unit); + }, + + startOf: function(time, unit, weekday) { + time = moment(time); + if (unit === 'isoWeek') { + return time.isoWeekday(weekday).valueOf(); + } + return time.startOf(unit).valueOf(); + }, + + endOf: function(time, unit) { + return moment(time).endOf(unit).valueOf(); + }, + + // DEPRECATIONS + + /** + * Provided for backward compatibility with scale.getValueForPixel(). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(time) { + return moment(time); + }, +} : {}); + +core_defaults._set('global', { + plugins: { + filler: { + propagate: true + } + } +}); + +var mappers = { + dataset: function(source) { + var index = source.fill; + var chart = source.chart; + var meta = chart.getDatasetMeta(index); + var visible = meta && chart.isDatasetVisible(index); + var points = (visible && meta.dataset._children) || []; + var length = points.length || 0; + + return !length ? null : function(point, i) { + return (i < length && points[i]._view) || null; + }; + }, + + boundary: function(source) { + var boundary = source.boundary; + var x = boundary ? boundary.x : null; + var y = boundary ? boundary.y : null; + + if (helpers$1.isArray(boundary)) { + return function(point, i) { + return boundary[i]; + }; + } + + return function(point) { + return { + x: x === null ? point.x : x, + y: y === null ? point.y : y, + }; + }; + } +}; + +// @todo if (fill[0] === '#') +function decodeFill(el, index, count) { + var model = el._model || {}; + var fill = model.fill; + var target; + + if (fill === undefined) { + fill = !!model.backgroundColor; + } + + if (fill === false || fill === null) { + return false; + } + + if (fill === true) { + return 'origin'; + } + + target = parseFloat(fill, 10); + if (isFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + + if (target === index || target < 0 || target >= count) { + return false; + } + + return target; + } + + switch (fill) { + // compatibility + case 'bottom': + return 'start'; + case 'top': + return 'end'; + case 'zero': + return 'origin'; + // supported boundaries + case 'origin': + case 'start': + case 'end': + return fill; + // invalid fill values + default: + return false; + } +} + +function computeLinearBoundary(source) { + var model = source.el._model || {}; + var scale = source.el._scale || {}; + var fill = source.fill; + var target = null; + var horizontal; + + if (isFinite(fill)) { + return null; + } + + // Backward compatibility: until v3, we still need to support boundary values set on + // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and + // controllers might still use it (e.g. the Smith chart). + + if (fill === 'start') { + target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; + } else if (fill === 'end') { + target = model.scaleTop === undefined ? scale.top : model.scaleTop; + } else if (model.scaleZero !== undefined) { + target = model.scaleZero; + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + + if (target !== undefined && target !== null) { + if (target.x !== undefined && target.y !== undefined) { + return target; + } + + if (helpers$1.isFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + } + + return null; +} + +function computeCircularBoundary(source) { + var scale = source.el._scale; + var options = scale.options; + var length = scale.chart.data.labels.length; + var fill = source.fill; + var target = []; + var start, end, center, i, point; + + if (!length) { + return null; + } + + start = options.ticks.reverse ? scale.max : scale.min; + end = options.ticks.reverse ? scale.min : scale.max; + center = scale.getPointPositionForValue(0, start); + for (i = 0; i < length; ++i) { + point = fill === 'start' || fill === 'end' + ? scale.getPointPositionForValue(i, fill === 'start' ? start : end) + : scale.getBasePosition(i); + if (options.gridLines.circular) { + point.cx = center.x; + point.cy = center.y; + point.angle = scale.getIndexAngle(i) - Math.PI / 2; + } + target.push(point); + } + return target; +} + +function computeBoundary(source) { + var scale = source.el._scale || {}; + + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); +} + +function resolveTarget(sources, index, propagate) { + var source = sources[index]; + var fill = source.fill; + var visited = [index]; + var target; + + if (!propagate) { + return fill; + } + + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isFinite(fill)) { + return fill; + } + + target = sources[fill]; + if (!target) { + return false; + } + + if (target.visible) { + return fill; + } + + visited.push(fill); + fill = target.fill; + } + + return false; +} + +function createMapper(source) { + var fill = source.fill; + var type = 'dataset'; + + if (fill === false) { + return null; + } + + if (!isFinite(fill)) { + type = 'boundary'; + } + + return mappers[type](source); +} + +function isDrawable(point) { + return point && !point.skip; +} + +function drawArea(ctx, curve0, curve1, len0, len1) { + var i, cx, cy, r; + + if (!len0 || !len1) { + return; + } + + // building first area curve (normal) + ctx.moveTo(curve0[0].x, curve0[0].y); + for (i = 1; i < len0; ++i) { + helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); + } + + if (curve1[0].angle !== undefined) { + cx = curve1[0].cx; + cy = curve1[0].cy; + r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2)); + for (i = len1 - 1; i > 0; --i) { + ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true); + } + return; + } + + // joining the two area curves + ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); + + // building opposite area curve (reverse) + for (i = len1 - 1; i > 0; --i) { + helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); + } +} + +function doFill(ctx, points, mapper, view, color, loop) { + var count = points.length; + var span = view.spanGaps; + var curve0 = []; + var curve1 = []; + var len0 = 0; + var len1 = 0; + var i, ilen, index, p0, p1, d0, d1, loopOffset; + + ctx.beginPath(); + + for (i = 0, ilen = count; i < ilen; ++i) { + index = i % count; + p0 = points[index]._view; + p1 = mapper(p0, index, view); + d0 = isDrawable(p0); + d1 = isDrawable(p1); + + if (loop && loopOffset === undefined && d0) { + loopOffset = i + 1; + ilen = count + loopOffset; + } + + if (d0 && d1) { + len0 = curve0.push(p0); + len1 = curve1.push(p1); + } else if (len0 && len1) { + if (!span) { + drawArea(ctx, curve0, curve1, len0, len1); + len0 = len1 = 0; + curve0 = []; + curve1 = []; + } else { + if (d0) { + curve0.push(p0); + } + if (d1) { + curve1.push(p1); + } + } + } + } + + drawArea(ctx, curve0, curve1, len0, len1); + + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); +} + +var plugin_filler = { + id: 'filler', + + afterDatasetsUpdate: function(chart, options) { + var count = (chart.data.datasets || []).length; + var propagate = options.propagate; + var sources = []; + var meta, i, el, source; + + for (i = 0; i < count; ++i) { + meta = chart.getDatasetMeta(i); + el = meta.dataset; + source = null; + + if (el && el._model && el instanceof elements.Line) { + source = { + visible: chart.isDatasetVisible(i), + fill: decodeFill(el, i, count), + chart: chart, + el: el + }; + } + + meta.$filler = source; + sources.push(source); + } + + for (i = 0; i < count; ++i) { + source = sources[i]; + if (!source) { + continue; + } + + source.fill = resolveTarget(sources, i, propagate); + source.boundary = computeBoundary(source); + source.mapper = createMapper(source); + } + }, + + beforeDatasetsDraw: function(chart) { + var metasets = chart._getSortedVisibleDatasetMetas(); + var ctx = chart.ctx; + var meta, i, el, view, points, mapper, color; + + for (i = metasets.length - 1; i >= 0; --i) { + meta = metasets[i].$filler; + + if (!meta || !meta.visible) { + continue; + } + + el = meta.el; + view = el._view; + points = el._children || []; + mapper = meta.mapper; + color = view.backgroundColor || core_defaults.global.defaultColor; + + if (mapper && color && points.length) { + helpers$1.canvas.clipArea(ctx, chart.chartArea); + doFill(ctx, points, mapper, view, color, el._loop); + helpers$1.canvas.unclipArea(ctx); + } + } + } +}; + +var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter; +var noop$1 = helpers$1.noop; +var valueOrDefault$e = helpers$1.valueOrDefault; + +core_defaults._set('global', { + legend: { + display: true, + position: 'top', + align: 'center', + fullWidth: true, + reverse: false, + weight: 1000, + + // a callback that will handle + onClick: function(e, legendItem) { + var index = legendItem.datasetIndex; + var ci = this.chart; + var meta = ci.getDatasetMeta(index); + + // See controller.isDatasetVisible comment + meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; + + // We hid a dataset ... rerender the chart + ci.update(); + }, + + onHover: null, + onLeave: null, + + labels: { + boxWidth: 40, + padding: 10, + // Generates labels shown in the legend + // Valid properties to return: + // text : text to display + // fillStyle : fill of coloured box + // strokeStyle: stroke of coloured box + // hidden : if this legend item refers to a hidden item + // lineCap : cap style for line + // lineDash + // lineDashOffset : + // lineJoin : + // lineWidth : + generateLabels: function(chart) { + var datasets = chart.data.datasets; + var options = chart.options.legend || {}; + var usePointStyle = options.labels && options.labels.usePointStyle; + + return chart._getSortedDatasetMetas().map(function(meta) { + var style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + + return { + text: datasets[meta.index].label, + fillStyle: style.backgroundColor, + hidden: !chart.isDatasetVisible(meta.index), + lineCap: style.borderCapStyle, + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: style.borderWidth, + strokeStyle: style.borderColor, + pointStyle: style.pointStyle, + rotation: style.rotation, + + // Below is extra data used for toggling the datasets + datasetIndex: meta.index + }; + }, this); + } + } + }, + + legendCallback: function(chart) { + var list = document.createElement('ul'); + var datasets = chart.data.datasets; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + + for (i = 0, ilen = datasets.length; i < ilen; i++) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[i].backgroundColor; + if (datasets[i].label) { + listItem.appendChild(document.createTextNode(datasets[i].label)); + } + } + + return list.outerHTML; + } +}); + +/** + * Helper function to get the box width based on the usePointStyle option + * @param {object} labelopts - the label options on the legend + * @param {number} fontSize - the label font size + * @return {number} width of the color box area + */ +function getBoxWidth(labelOpts, fontSize) { + return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? + fontSize : + labelOpts.boxWidth; +} + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Legend = core_element.extend({ + + initialize: function(config) { + var me = this; + helpers$1.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + + /** + * @private + */ + me._hoveredItem = null; + + // Are we in doughnut mode which has a different data type + me.doughnutMode = false; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + // Any function defined here is inherited by all legend types. + // Any function can be extended by the legend type + + beforeUpdate: noop$1, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + }, + afterUpdate: noop$1, + + // + + beforeSetDimensions: noop$1, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$1, + + // + + beforeBuildLabels: noop$1, + buildLabels: function() { + var me = this; + var labelOpts = me.options.labels || {}; + var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; + + if (labelOpts.filter) { + legendItems = legendItems.filter(function(item) { + return labelOpts.filter(item, me.chart.data); + }); + } + + if (me.options.reverse) { + legendItems.reverse(); + } + + me.legendItems = legendItems; + }, + afterBuildLabels: noop$1, + + // + + beforeFit: noop$1, + fit: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var display = opts.display; + + var ctx = me.ctx; + + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + + // Reset hit boxes + var hitboxes = me.legendHitBoxes = []; + + var minSize = me.minSize; + var isHorizontal = me.isHorizontal(); + + if (isHorizontal) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = display ? 10 : 0; + } else { + minSize.width = display ? 10 : 0; + minSize.height = me.maxHeight; // fill all the height + } + + // Increase sizes here + if (!display) { + me.width = minSize.width = me.height = minSize.height = 0; + return; + } + ctx.font = labelFont.string; + + if (isHorizontal) { + // Labels + + // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one + var lineWidths = me.lineWidths = [0]; + var totalHeight = 0; + + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { + totalHeight += fontSize + labelOpts.padding; + lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; + } + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: width, + height: fontSize + }; + + lineWidths[lineWidths.length - 1] += width + labelOpts.padding; + }); + + minSize.height += totalHeight; + + } else { + var vPadding = labelOpts.padding; + var columnWidths = me.columnWidths = []; + var columnHeights = me.columnHeights = []; + var totalWidth = labelOpts.padding; + var currentColWidth = 0; + var currentColHeight = 0; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + // If too tall, go to new column + if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) { + totalWidth += currentColWidth + labelOpts.padding; + columnWidths.push(currentColWidth); // previous column width + columnHeights.push(currentColHeight); + currentColWidth = 0; + currentColHeight = 0; + } + + // Get max width + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += fontSize + vPadding; + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: itemWidth, + height: fontSize + }; + }); + + totalWidth += currentColWidth; + columnWidths.push(currentColWidth); + columnHeights.push(currentColHeight); + minSize.width += totalWidth; + } + + me.width = minSize.width; + me.height = minSize.height; + }, + afterFit: noop$1, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + + // Actually draw the legend on the canvas + draw: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; + var lineDefault = globalDefaults.elements.line; + var legendHeight = me.height; + var columnHeights = me.columnHeights; + var legendWidth = me.width; + var lineWidths = me.lineWidths; + + if (!opts.display) { + return; + } + + var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width); + var ctx = me.ctx; + var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor); + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + var cursor; + + // Canvas setup + ctx.textAlign = rtlHelper.textAlign('left'); + ctx.textBaseline = 'middle'; + ctx.lineWidth = 0.5; + ctx.strokeStyle = fontColor; // for strikethrough effect + ctx.fillStyle = fontColor; // render in correct colour + ctx.font = labelFont.string; + + var boxWidth = getBoxWidth(labelOpts, fontSize); + var hitboxes = me.legendHitBoxes; + + // current position + var drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0) { + return; + } + + // Set the ctx for the box + ctx.save(); + + var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth); + ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor); + ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle); + ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset); + ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor); + + if (ctx.setLineDash) { + // IE 9 and 10 do not support line dash + ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash)); + } + + if (labelOpts && labelOpts.usePointStyle) { + // Recalculate x and y for drawPoint() because its expecting + // x and y to be center of figure (instead of top left) + var radius = boxWidth * Math.SQRT2 / 2; + var centerX = rtlHelper.xPlus(x, boxWidth / 2); + var centerY = y + fontSize / 2; + + // Draw pointStyle as legend symbol + helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); + } else { + // Draw box as legend symbol + ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + if (lineWidth !== 0) { + ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + } + } + + ctx.restore(); + }; + + var fillText = function(x, y, legendItem, textWidth) { + var halfFontSize = fontSize / 2; + var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); + var yMiddle = y + halfFontSize; + + ctx.fillText(legendItem.text, xLeft, yMiddle); + + if (legendItem.hidden) { + // Strikethrough the text if hidden + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.moveTo(xLeft, yMiddle); + ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle); + ctx.stroke(); + } + }; + + var alignmentOffset = function(dimension, blockSize) { + switch (opts.align) { + case 'start': + return labelOpts.padding; + case 'end': + return dimension - blockSize; + default: // center + return (dimension - blockSize + labelOpts.padding) / 2; + } + }; + + // Horizontal + var isHorizontal = me.isHorizontal(); + if (isHorizontal) { + cursor = { + x: me.left + alignmentOffset(legendWidth, lineWidths[0]), + y: me.top + labelOpts.padding, + line: 0 + }; + } else { + cursor = { + x: me.left + labelOpts.padding, + y: me.top + alignmentOffset(legendHeight, columnHeights[0]), + line: 0 + }; + } + + helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection); + + var itemHeight = fontSize + labelOpts.padding; + helpers$1.each(me.legendItems, function(legendItem, i) { + var textWidth = ctx.measureText(legendItem.text).width; + var width = boxWidth + (fontSize / 2) + textWidth; + var x = cursor.x; + var y = cursor.y; + + rtlHelper.setWidth(me.minSize.width); + + // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) + // instead of me.right and me.bottom because me.width and me.height + // may have been changed since me.minSize was calculated + if (isHorizontal) { + if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { + y = cursor.y += itemHeight; + cursor.line++; + x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]); + } + } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { + x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; + cursor.line++; + y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]); + } + + var realX = rtlHelper.x(x); + + drawLegendBox(realX, y, legendItem); + + hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width); + hitboxes[i].top = y; + + // Fill the actual label + fillText(realX, y, legendItem, textWidth); + + if (isHorizontal) { + cursor.x += width + labelOpts.padding; + } else { + cursor.y += itemHeight; + } + }); + + helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection); + }, + + /** + * @private + */ + _getLegendItemAt: function(x, y) { + var me = this; + var i, hitBox, lh; + + if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { + // See if we are touching one of the dataset boxes + lh = me.legendHitBoxes; + for (i = 0; i < lh.length; ++i) { + hitBox = lh[i]; + + if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { + // Touching an element + return me.legendItems[i]; + } + } + } + + return null; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + */ + handleEvent: function(e) { + var me = this; + var opts = me.options; + var type = e.type === 'mouseup' ? 'click' : e.type; + var hoveredItem; + + if (type === 'mousemove') { + if (!opts.onHover && !opts.onLeave) { + return; + } + } else if (type === 'click') { + if (!opts.onClick) { + return; + } + } else { + return; + } + + // Chart event already has relative position in it + hoveredItem = me._getLegendItemAt(e.x, e.y); + + if (type === 'click') { + if (hoveredItem && opts.onClick) { + // use e.native for backwards compatibility + opts.onClick.call(me, e.native, hoveredItem); + } + } else { + if (opts.onLeave && hoveredItem !== me._hoveredItem) { + if (me._hoveredItem) { + opts.onLeave.call(me, e.native, me._hoveredItem); + } + me._hoveredItem = hoveredItem; + } + + if (opts.onHover && hoveredItem) { + // use e.native for backwards compatibility + opts.onHover.call(me, e.native, hoveredItem); + } + } + } +}); + +function createNewLegendAndAttach(chart, legendOpts) { + var legend = new Legend({ + ctx: chart.ctx, + options: legendOpts, + chart: chart + }); + + core_layouts.configure(chart, legend, legendOpts); + core_layouts.addBox(chart, legend); + chart.legend = legend; +} + +var plugin_legend = { + id: 'legend', + + /** + * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making + * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Legend, + + beforeInit: function(chart) { + var legendOpts = chart.options.legend; + + if (legendOpts) { + createNewLegendAndAttach(chart, legendOpts); + } + }, + + beforeUpdate: function(chart) { + var legendOpts = chart.options.legend; + var legend = chart.legend; + + if (legendOpts) { + helpers$1.mergeIf(legendOpts, core_defaults.global.legend); + + if (legend) { + core_layouts.configure(chart, legend, legendOpts); + legend.options = legendOpts; + } else { + createNewLegendAndAttach(chart, legendOpts); + } + } else if (legend) { + core_layouts.removeBox(chart, legend); + delete chart.legend; + } + }, + + afterEvent: function(chart, e) { + var legend = chart.legend; + if (legend) { + legend.handleEvent(e); + } + } +}; + +var noop$2 = helpers$1.noop; + +core_defaults._set('global', { + title: { + display: false, + fontStyle: 'bold', + fullWidth: true, + padding: 10, + position: 'top', + text: '', + weight: 2000 // by default greater than legend (1000) to be above + } +}); + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Title = core_element.extend({ + initialize: function(config) { + var me = this; + helpers$1.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + + beforeUpdate: noop$2, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: noop$2, + + // + + beforeSetDimensions: noop$2, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$2, + + // + + beforeBuildLabels: noop$2, + buildLabels: noop$2, + afterBuildLabels: noop$2, + + // + + beforeFit: noop$2, + fit: function() { + var me = this; + var opts = me.options; + var minSize = me.minSize = {}; + var isHorizontal = me.isHorizontal(); + var lineCount, textSize; + + if (!opts.display) { + me.width = minSize.width = me.height = minSize.height = 0; + return; + } + + lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; + textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2; + + me.width = minSize.width = isHorizontal ? me.maxWidth : textSize; + me.height = minSize.height = isHorizontal ? textSize : me.maxHeight; + }, + afterFit: noop$2, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + + // Actually draw the title block on the canvas + draw: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + + if (!opts.display) { + return; + } + + var fontOpts = helpers$1.options._parseFont(opts); + var lineHeight = fontOpts.lineHeight; + var offset = lineHeight / 2 + opts.padding; + var rotation = 0; + var top = me.top; + var left = me.left; + var bottom = me.bottom; + var right = me.right; + var maxWidth, titleX, titleY; + + ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour + ctx.font = fontOpts.string; + + // Horizontal + if (me.isHorizontal()) { + titleX = left + ((right - left) / 2); // midpoint of the width + titleY = top + offset; + maxWidth = right - left; + } else { + titleX = opts.position === 'left' ? left + offset : right - offset; + titleY = top + ((bottom - top) / 2); + maxWidth = bottom - top; + rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); + } + + ctx.save(); + ctx.translate(titleX, titleY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + var text = opts.text; + if (helpers$1.isArray(text)) { + var y = 0; + for (var i = 0; i < text.length; ++i) { + ctx.fillText(text[i], 0, y, maxWidth); + y += lineHeight; + } + } else { + ctx.fillText(text, 0, 0, maxWidth); + } + + ctx.restore(); + } +}); + +function createNewTitleBlockAndAttach(chart, titleOpts) { + var title = new Title({ + ctx: chart.ctx, + options: titleOpts, + chart: chart + }); + + core_layouts.configure(chart, title, titleOpts); + core_layouts.addBox(chart, title); + chart.titleBlock = title; +} + +var plugin_title = { + id: 'title', + + /** + * Backward compatibility: since 2.1.5, the title is registered as a plugin, making + * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Title, + + beforeInit: function(chart) { + var titleOpts = chart.options.title; + + if (titleOpts) { + createNewTitleBlockAndAttach(chart, titleOpts); + } + }, + + beforeUpdate: function(chart) { + var titleOpts = chart.options.title; + var titleBlock = chart.titleBlock; + + if (titleOpts) { + helpers$1.mergeIf(titleOpts, core_defaults.global.title); + + if (titleBlock) { + core_layouts.configure(chart, titleBlock, titleOpts); + titleBlock.options = titleOpts; + } else { + createNewTitleBlockAndAttach(chart, titleOpts); + } + } else if (titleBlock) { + core_layouts.removeBox(chart, titleBlock); + delete chart.titleBlock; + } + } +}; + +var plugins = {}; +var filler = plugin_filler; +var legend = plugin_legend; +var title = plugin_title; +plugins.filler = filler; +plugins.legend = legend; +plugins.title = title; + +/** + * @namespace Chart + */ + + +core_controller.helpers = helpers$1; + +// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! +core_helpers(); + +core_controller._adapters = core_adapters; +core_controller.Animation = core_animation; +core_controller.animationService = core_animations; +core_controller.controllers = controllers; +core_controller.DatasetController = core_datasetController; +core_controller.defaults = core_defaults; +core_controller.Element = core_element; +core_controller.elements = elements; +core_controller.Interaction = core_interaction; +core_controller.layouts = core_layouts; +core_controller.platform = platform; +core_controller.plugins = core_plugins; +core_controller.Scale = core_scale; +core_controller.scaleService = core_scaleService; +core_controller.Ticks = core_ticks; +core_controller.Tooltip = core_tooltip; + +// Register built-in scales + +core_controller.helpers.each(scales, function(scale, type) { + core_controller.scaleService.registerScaleType(type, scale, scale._defaults); +}); + +// Load to register built-in adapters (as side effects) + + +// Loading built-in plugins + +for (var k in plugins) { + if (plugins.hasOwnProperty(k)) { + core_controller.plugins.register(plugins[k]); + } +} + +core_controller.platform.initialize(); + +var src = core_controller; +if (typeof window !== 'undefined') { + window.Chart = core_controller; +} + +// DEPRECATIONS + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Chart + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +core_controller.Chart = core_controller; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Legend + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Legend = plugins.legend._element; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Title + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Title = plugins.title._element; + +/** + * Provided for backward compatibility, use Chart.plugins instead + * @namespace Chart.pluginService + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.pluginService = core_controller.plugins; + +/** + * Provided for backward compatibility, inheriting from Chart.PlugingBase has no + * effect, instead simply create/register plugins via plain JavaScript objects. + * @interface Chart.PluginBase + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ +core_controller.PluginBase = core_controller.Element.extend({}); + +/** + * Provided for backward compatibility, use Chart.helpers.canvas instead. + * @namespace Chart.canvasHelpers + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +core_controller.canvasHelpers = core_controller.helpers.canvas; + +/** + * Provided for backward compatibility, use Chart.layouts instead. + * @namespace Chart.layoutService + * @deprecated since version 2.7.3 + * @todo remove at version 3 + * @private + */ +core_controller.layoutService = core_controller.layouts; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.LinearScaleBase + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +core_controller.LinearScaleBase = scale_linearbase; + +/** + * Provided for backward compatibility, instead we should create a new Chart + * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ +core_controller.helpers.each( + [ + 'Bar', + 'Bubble', + 'Doughnut', + 'Line', + 'PolarArea', + 'Radar', + 'Scatter' + ], + function(klass) { + core_controller[klass] = function(ctx, cfg) { + return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { + type: klass.charAt(0).toLowerCase() + klass.slice(1) + })); + }; + } +); + +return src; + +}))); diff --git a/lib/web/chartjs/Chart.bundle.min.js b/lib/web/chartjs/Chart.bundle.min.js new file mode 100644 index 0000000000000..55d9eb03f821a --- /dev/null +++ b/lib/web/chartjs/Chart.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Chart.js v2.9.3 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Chart=e()}(this,(function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function t(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function e(t,e){return t(e={exports:{}},e.exports),e.exports}var n={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},i=e((function(t){var e={};for(var i in n)n.hasOwnProperty(i)&&(e[n[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=e[t];if(i)return i;var a,r,o,s=1/0;for(var l in n)if(n.hasOwnProperty(l)){var u=n[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return n[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));i.rgb,i.hsl,i.hsv,i.hwb,i.cmyk,i.xyz,i.lab,i.lch,i.hex,i.keyword,i.ansi16,i.ansi256,i.hcg,i.apple,i.gray;function a(t){var e=function(){for(var t={},e=Object.keys(i),n=e.length,a=0;a<n;a++)t[e[a]]={distance:-1,parent:null};return t}(),n=[t];for(e[t].distance=0;n.length;)for(var a=n.pop(),r=Object.keys(i[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,n.unshift(l))}return e}function r(t,e){return function(n){return e(t(n))}}function o(t,e){for(var n=[e[t].parent,t],a=i[e[t].parent][t],o=e[t].parent;e[o].parent;)n.unshift(e[o].parent),a=r(i[e[o].parent][o],a),o=e[o].parent;return a.conversion=n,a}var s={};Object.keys(i).forEach((function(t){s[t]={},Object.defineProperty(s[t],"channels",{value:i[t].channels}),Object.defineProperty(s[t],"labels",{value:i[t].labels});var e=function(t){for(var e=a(t),n={},i=Object.keys(e),r=i.length,s=0;s<r;s++){var l=i[s];null!==e[l].parent&&(n[l]=o(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];s[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),s[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var l=s,u={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},d={getRgba:h,getHsla:c,getRgb:function(t){var e=h(t);return e&&e.slice(0,3)},getHsl:function(t){var e=c(t);return e&&e.slice(0,3)},getHwb:f,getAlpha:function(t){var e=h(t);if(e)return e[3];if(e=c(t))return e[3];if(e=f(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+b(t[0])+b(t[1])+b(t[2])+(e>=0&&e<1?b(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:g,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return m(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:m,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return y[t.slice(0,3)]}};function h(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=u[i[1]]))return}for(r=0;r<e.length;r++)e[r]=v(e[r],0,255);return n=n||0==n?v(n,0,1):1,e[3]=n,e}}function c(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function f(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function g(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function m(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function v(t,e,n){return Math.min(Math.max(e,t),n)}function b(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var y={};for(var x in u)y[u[x]]=x;var _=function(t){return t instanceof _?t:this instanceof _?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=d.getRgba(t))?this.setValues("rgb",e):(e=d.getHsla(t))?this.setValues("hsl",e):(e=d.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new _(t);var e};_.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return d.hexString(this.values.rgb)},rgbString:function(){return d.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return d.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return d.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return d.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return d.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return d.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return d.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new _,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},_.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},_.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},_.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},_.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,s=1;if(this.valid=!0,"alpha"===t)s=e;else if(e.length)a[t]=e.slice(0,t.length),s=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];s=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];s=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===s?a.alpha:s)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=l[t][d](a[t]));return!0},_.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},_.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=_);var w,k=_,M={noop:function(){},uid:(w=0,function(){return w++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return M.valueOrDefault(M.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(M.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(M.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!M.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(M.isArray(t))return t.map(M.clone);if(M.isObject(t)){for(var e={},n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=M.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){var a=e[t],r=n[t];M.isObject(a)&&M.isObject(r)?M.merge(a,r,i):e[t]=M.clone(r)},_mergerIf:function(t,e,n){var i=e[t],a=n[t];M.isObject(i)&&M.isObject(a)?M.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=M.clone(a))},merge:function(t,e,n){var i,a,r,o,s,l=M.isArray(e)?e:[e],u=l.length;if(!M.isObject(t))return t;for(i=(n=n||{}).merger||M._merger,a=0;a<u;++a)if(e=l[a],M.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return M.merge(t,e,{merger:M._mergerIf})},extend:Object.assign||function(t){return M.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=M.inherits,t&&M.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},S=M;M.callCallback=M.callback,M.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},M.getValueOrDefault=M.valueOrDefault,M.getValueAtIndexOrDefault=M.valueAtIndexOrDefault;var D={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-D.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*D.easeInBounce(2*t):.5*D.easeOutBounce(2*t-1)+.5}},C={effects:D};S.easingEffects=D;var P=Math.PI,T=P/180,O=2*P,A=P/2,F=P/4,I=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-P,-A),t.arc(u,l,o,-A,0),t.arc(u,d,o,0,A),t.arc(s,d,o,A,P)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-A,A),t.arc(s,l,o,A,P+A)):l<d?(t.arc(s,l,o,-P,0),t.arc(s,d,o,0,P)):t.arc(s,l,o,-P,P),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*T;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,O),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+F)*u,l=Math.sin(h+F)*u,t.arc(i-s,a-l,d,h-P,h-A),t.arc(i+l,a-s,d,h-A,h),t.arc(i+s,a+l,d,h,h+A),t.arc(i-l,a+s,d,h+A,h+P),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=F;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=F;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=F,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},R=L;S.clear=L.clear,S.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var N={_set:function(t,e){return S.merge(this[t]||(this[t]={}),e)}};N._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var W=N,Y=S.valueOrDefault;var z={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return S.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=W.global,n=Y(t.fontSize,e.defaultFontSize),i={family:Y(t.fontFamily,e.defaultFontFamily),lineHeight:S.options.toLineHeight(Y(t.lineHeight,e.defaultLineHeight),n),size:n,style:Y(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||S.isNullOrUndef(t.size)||S.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&S.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},E={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},V=E;S.log10=E.log10;var H=S,B=C,j=R,U=z,G=V,q={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};H.easing=B,H.canvas=j,H.options=U,H.math=G,H.rtl=q;var Z=function(t){H.extend(this,t),this.initialize.apply(this,arguments)};H.extend(Z.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=H.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=k(l)).valid&&(c=k(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(H.isFinite(l)&&H.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=H.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return H.isNumber(this._model.x)&&H.isNumber(this._model.y)}}),Z.extend=H.inherits;var $=Z,X=$.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),K=X;Object.defineProperty(X.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(X.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),W._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:H.noop,onComplete:H.noop}});var J={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=H.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=H.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),H.callback(t.render,[e,t],e),H.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(H.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},Q=H.options.resolve,tt=["push","pop","shift","splice","unshift"];function et(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(tt.forEach((function(e){delete t[e]})),delete t._chartjs)}}var nt=function(t,e){this.initialize(t,e)};H.extend(nt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&et(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&et(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),tt.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return H.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=H.merge({},[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&H._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:H.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=Q([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},m={cacheable:!i};if(i=i||{},H.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=Q([i[l],d[l],h[l]],g,e,m);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=Q([i[l],d[c[l]],d[l],h[l]],g,e,m);return m.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){H.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=H.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=Q([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=Q([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=Q([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),nt.extend=H.inherits;var it=nt,at=2*Math.PI;function rt(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function ot(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+at,rt(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=at,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+at,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+at),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&rt(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}W._set("global",{elements:{arc:{backgroundColor:W.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var st=$.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=H.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=at;for(;a>s;)a-=at;for(;a<o;)a+=at;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/at)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+at,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%at}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&ot(e,n,a),e.restore()}}),lt=H.valueOrDefault,ut=W.global.defaultColor;W._set("global",{elements:{line:{tension:.4,backgroundColor:ut,borderWidth:3,borderColor:ut,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var dt=$.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=W.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=H.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=lt(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=lt(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?H.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):H.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),ht=H.valueOrDefault,ct=W.global.defaultColor;function ft(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}W._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ct,borderColor:ct,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var gt=$.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ft,inXRange:ft,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=W.global,u=l.defaultColor;e.skip||(void 0===t||H.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=ht(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,H.canvas.drawPoint(n,i,r,o,s,a))}}),mt=W.global.defaultColor;function pt(t){return t&&void 0!==t.width}function vt(t){var e,n,i,a,r;return pt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function bt(t,e,n){return t===e?n:t===n?e:t}function yt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=bt(e,"left","right")):t.base<t.y&&(e=bt(e,"bottom","top")),n[e]=!0,n):n}(t);return H.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&vt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}W._set("global",{elements:{rectangle:{backgroundColor:mt,borderColor:mt,borderSkipped:"bottom",borderWidth:0}}});var _t=$.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=vt(t),n=e.right-e.left,i=e.bottom-e.top,a=yt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),wt={},kt=st,Mt=dt,St=gt,Dt=_t;wt.Arc=kt,wt.Line=Mt,wt.Point=St,wt.Rectangle=Dt;var Ct=H._deprecated,Pt=H.valueOrDefault;function Tt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=H.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return H.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}W._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),W._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Ot=it.extend({dataElementType:wt.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;it.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},H.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),m=h._parseValue(f[t].data[e]),p=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,y=void 0===m.start?0:m.max>=0&&m.min>=0?m.min:m.max,x=void 0===m.start?m.end:m.max>=0&&m.min>=0?m.max-m.min:m.min-m.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(m.min<0&&r<0||m.max>=0&&r>0)&&(y+=r));return o=h.getPixelForValue(y),l=(s=h.getPixelForValue(y+x))-o,void 0!==p&&Math.abs(l)<p&&(l=p,s=x>=0&&!c||x<0&&c?o-p:o+p),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):Tt(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(Pt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(H.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}H.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=H.extend({},it.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Pt(n.barPercentage,e.barPercentage),e.barThickness=Pt(n.barThickness,e.barThickness),e.categoryPercentage=Pt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Pt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Pt(i.minBarLength,e.minBarLength),e}}),At=H.valueOrDefault,Ft=H.options.resolve;W._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var It=it.extend({dataElementType:wt.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;H.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=At(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=At(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=At(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=it.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=H.extend({},s)),s.radius=Ft([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=H.valueOrDefault,Rt=Math.PI,Nt=2*Rt,Wt=Rt/2;W._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-Wt,circumference:Nt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return H.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Yt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,m=l.cutoutPercentage/100||0,p=l.circumference,v=r._getRingWeight(r.index);if(p<Nt){var b=l.rotation%Nt,y=(b+=b>=Rt?-Nt:b<-Rt?Nt:0)+p,x=Math.cos(b),_=Math.sin(b),w=Math.cos(y),k=Math.sin(y),M=b<=0&&y>=0||y>=Nt,S=b<=Wt&&y>=Wt||y>=Nt+Wt,D=b<=-Wt&&y>=-Wt||y>=Rt+Wt,C=b===-Rt||y>=Rt?-1:Math.min(x,x*m,w,w*m),P=D?-1:Math.min(_,_*m,k,k*m),T=M?1:Math.max(x,x*m,w,w*m),O=S?1:Math.max(_,_*m,k,k*m);u=(T-C)/2,d=(O-P)/2,h=-(T+C)/2,c=-(O+P)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*m,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/Nt),g=n&&s.animateScale?0:i.innerRadius,m=n&&s.animateScale?0:i.outerRadius,p=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:p.backgroundColor,borderColor:p.borderColor,borderWidth:p.borderWidth,borderAlign:p.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:m,innerRadius:g,label:H.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return H.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Nt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});W._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),W._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var zt=Ot.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Et=H.valueOrDefault,Vt=H.options.resolve,Ht=H.canvas._isPointInArea;function Bt(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function jt(t,e,n){var i=n/2,a=Bt(t,i),r=Bt(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function Ut(t){var e,n,i,a;return H.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}W._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Gt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Et(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Et(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=it.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Et(n.spanGaps,a.spanGaps),o.tension=Et(n.lineTension,r.tension),o.steppedLine=Vt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=Ut(Et(n.clip,jt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)H.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=H.splineCurve(H.previousItem(l,t)._model,n,H.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Ht(n,s)&&(t>0&&Ht(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Ht(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,H.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),H.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Et(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Et(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Et(n.hoverBorderWidth,n.borderWidth),e.radius=Et(n.hoverRadius,n.radius)}}),qt=H.options.resolve;W._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Zt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],m=g+(t.hidden?0:i._angles[e]),p=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?p:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:m,label:H.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return H.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor,a=H.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return qt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});W._set("pie",H.clone(W.doughnut)),W._set("pie",{cutoutPercentage:0});var $t=Yt,Xt=H.valueOrDefault;W._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var Kt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,linkScales:H.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Xt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=it.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Xt(e.spanGaps,n.spanGaps),i.tension=Xt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=H.splineCurve(H.previousItem(o,t,!0)._model,n,H.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Xt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Xt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Xt(n.hoverBorderWidth,n.borderWidth),e.radius=Xt(n.hoverRadius,n.radius)}});W._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),W._set("global",{datasets:{scatter:{showLine:!1}}});var Jt={bar:Ot,bubble:It,doughnut:Yt,horizontalBar:zt,line:Gt,polarArea:Zt,pie:$t,radar:Kt,scatter:Gt};function Qt(t,e){return t.native?{x:t.x,y:t.y}:H.getRelativePosition(t,e)}function te(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function ee(t,e){var n=[];return te(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ne(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return te(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ie(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ae(t,e,n){var i=Qt(e,t);n.axis=n.axis||"x";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var re={modes:{single:function(t,e){var n=Qt(e,t),i=[];return te(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ae,index:ae,dataset:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ae(t,e,{intersect:!1})},point:function(t,e){return ee(t,Qt(e,t))},nearest:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis);return ne(t,i,n.intersect,a)},x:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},oe=H.extend;function se(t,e){return H.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function ue(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function de(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-ue(o,t,"left","right"),a=e.outerHeight-ue(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function he(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function ce(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,he(r.horizontal,e)),de(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&ce(u,e,n)||l}function fe(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}W._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ge,me={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=H.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=le(se(e,"left"),!0),i=le(se(e,"right")),a=le(se(e,"top"),!0),r=le(se(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:se(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=oe({maxPadding:oe({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),ce(l,h,d),ce(u,h,d)&&ce(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),fe(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,fe(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},H.each(s.chartArea,(function(e){var n=e.box;oe(n,t.chartArea),n.update(h.w,h.h)}))}}},pe=(ge=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&ge.default||ge,ve="$chartjs",be="chartjs-size-monitor",ye="chartjs-render-monitor",xe="chartjs-render-animation",_e=["animationstart","webkitAnimationStart"],we={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=H.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var Me=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Se(t,e,n){t.addEventListener(e,n,Me)}function De(t,e,n){t.removeEventListener(e,n,Me)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Te(t,e,n){var i,a,r,o,s=t[ve]||(t[ve]={}),l=s.resizer=function(t){var e=Pe(be),n=Pe(be+"-expand"),i=Pe(be+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Se(n,"scroll",a.bind(n,"expand")),Se(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Ce("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,H.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[ve]||(t[ve]={}),i=n.renderProxy=function(t){t.animationName===xe&&e()};H.each(_e,(function(e){Se(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(ye)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function Oe(t){var e=t[ve]||{},n=e.resizer;delete e.resizer,function(t){var e=t[ve]||{},n=e.renderProxy;n&&(H.each(_e,(function(e){De(t,e,n)})),delete e.renderProxy),t.classList.remove(ye)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Ae={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[ve]||(t[ve]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[ve]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=ke(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=ke(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[ve]){var n=e[ve].initial;["height","width"].forEach((function(t){var i=n[t];H.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),H.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[ve]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[ve]||(n[ve]={});Se(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=we[t.type]||t.type,i=H.getRelativePosition(t,e);return Ce(n,e,i.x,i.y,t)}(e,t))})}else Te(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[ve]||{}).proxies||{})[t.id+"_"+e];a&&De(i,e,a)}else Oe(i)}};H.addEvent=Se,H.removeEvent=De;var Fe=Ae._enabled?Ae:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Ie=H.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Fe);W._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=H.clone(W.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Re={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=H.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?H.merge({},[W.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=H.extend(this.defaults[t],e))},addScalesToLayout:function(t){H.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,me.addBox(t,e)}))}},Ne=H.valueOrDefault,We=H.rtl.getRtlAdapter;W._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:H.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:H.noop,beforeBody:H.noop,beforeLabel:H.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),H.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:H.noop,afterBody:H.noop,beforeFooter:H.noop,footer:H.noop,afterFooter:H.noop}}});var Ye={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=H.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function ze(t,e){return e&&(H.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ee(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Ve(t){var e=W.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Ne(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Ne(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Ne(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Ne(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Ne(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Ne(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Ne(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Ne(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Ne(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function He(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Be(t){return ze([],Ee(t))}var je=$.extend({initialize:function(){this._model=Ve(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=ze(o,Ee(i)),o=ze(o,Ee(a)),o=ze(o,Ee(r))},getBeforeBody:function(){return Be(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return H.each(t,(function(t){var r={before:[],lines:[],after:[]};ze(r.before,Ee(i.beforeLabel.call(n,t,e))),ze(r.lines,i.label.call(n,t,e)),ze(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return Be(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=ze(r,Ee(n)),r=ze(r,Ee(i)),r=ze(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=Ve(c),m=h._active,p=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},y={width:f.width,height:f.height},x={x:f.caretX,y:f.caretY};if(m.length){g.opacity=1;var _=[],w=[];x=Ye[c.position].call(h,m,h._eventPosition);var k=[];for(e=0,n=m.length;e<n;++e)k.push((i=m[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(k=k.filter((function(t){return c.filter(t,p)}))),c.itemSort&&(k=k.sort((function(t,e){return c.itemSort(t,e,p)}))),H.each(k,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),w.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(k,p),g.beforeBody=h.getBeforeBody(k,p),g.body=h.getBody(k,p),g.afterBody=h.getAfterBody(k,p),g.footer=h.getFooter(k,p),g.x=x.x,g.y=x.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=w,g.dataPoints=k,y=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=H.fontString(u,e._titleFontStyle,e._titleFontFamily),H.each(e.title,f),n.font=H.fontString(d,e._bodyFontStyle,e._bodyFontFamily),H.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,H.each(r,(function(t){H.each(t.before,f),H.each(t.lines,f),H.each(t.after,f)})),c=0,n.font=H.fontString(h,e._footerFontStyle,e._footerFontFamily),H.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,y,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,y),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=y.width,g.height=y.height,g.caretX=x.x,g.caretY=x.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,m=e.width,p=e.height;if("center"===c)s=g+p/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+m)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+m-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+p)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=We(e.rtl,e.x,e.width);for(t.x=He(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=H.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,m=e.displayColors,p=0,v=m?He(e,"left"):0,b=We(e.rtl,e.x,e.width),y=function(e){n.fillText(e,b.x(t.x+p),t.y+h/2),t.y+=h+c},x=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=H.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=He(e,x),n.fillStyle=e.bodyFontColor,H.each(e.beforeBody,y),p=m&&"right"!==x?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,H.each(i.before,y),l=0,d=(o=i.lines).length;l<d;++l){if(m){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}y(o[l])}H.each(i.after,y)}p=0,H.each(e.afterBody,y),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=We(e.rtl,e.x,e.width);for(t.x=He(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=H.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,H.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),H.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!H.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ue=Ye,Ge=je;Ge.positioners=Ue;var qe=H.valueOrDefault;function Ze(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=qe(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?H.merge(e[t][a],[Re.getScaleDefaults(r),o]):H.merge(e[t][a],o)}else H._merger(t,e,n,i)}})}function $e(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ze(a,r):"scale"===t?e[t]=H.merge(a,[Re.getScaleDefaults(r.type),r]):H._merger(t,e,n,i)}})}function Xe(t){var e=t.options;H.each(t.scales,(function(e){me.removeBox(t,e)})),e=$e(W.global,W[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ke(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(H.findIndex(t,a)>=0);return i}function Je(t){return"top"===t||"bottom"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}W._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};H.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=$e(W.global,W[t.type],t.options||{}),t}(e);var i=Ie.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=H.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),H.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return H.canvas.clear(this),this},stop:function(){return J.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(H.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:H.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",H.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;H.each(e.xAxes,(function(t,n){t.id||(t.id=Ke(e.xAxes,"x-axis-",n))})),H.each(e.yAxes,(function(t,n){t.id||(t.id=Ke(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),H.each(i,(function(e){var i=e.options,r=i.id,o=qe(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Re.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),H.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Re.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=Jt[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;H.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Xe(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&H.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Qe("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(me.update(this,this.width,this.height),t._layers=[],H.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=qe(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),H.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new K({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=H.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});J.addAnimation(e,o,i,a)}else e.draw(),r(new K({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),H.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Qe("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return re.modes.single(this,t)},getElementsAtEvent:function(t){return re.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return re.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=re.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return re.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),H.canvas.clear(n),Ie.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete tn.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ge({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};H.each(t.options.events,(function(i){Ie.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Ie.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,H.each(e,(function(e,n){Ie.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),H.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!H.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),tn.instances={};var en=tn;tn.Controller=tn,tn.types={},H.configMerge=$e,H.scaleMerge=Ze;function nn(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function an(t){this.options=t||{}}H.extend(an.prototype,{formats:nn,parse:nn,format:nn,add:nn,diff:nn,startOf:nn,endOf:nn,_create:function(t){return t}}),an.override=function(t){H.extend(an.prototype,t)};var rn={_date:an},on={formatters:{values:function(t){return H.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=H.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=H.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(H.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},sn=H.isArray,ln=H.isNullOrUndef,un=H.valueOrDefault,dn=H.valueAtIndexOrDefault;function hn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function cn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,m,p,v=n.length,b=[],y=[],x=[];for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,ln(s)||sn(s)){if(sn(s))for(r=0,o=s.length;r<o;++r)g=s[r],ln(g)||sn(g)||(c=H.measureText(t,d.data,d.gc,c,g),f+=h)}else c=H.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),y.push(f),x.push(h/2)}function _(t){return{width:b[t]||0,height:y[t]||0,offset:x[t]||0}}return function(t,e){H.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),m=b.indexOf(Math.max.apply(null,b)),p=y.indexOf(Math.max.apply(null,y)),{first:_(0),last:_(v-1),widest:_(m),highest:_(p)}}function fn(t){return t.drawTicks?t.tickMarkLength:0}function gn(t){var e,n;return t.display?(e=H.options._parseFont(t),n=H.options.toPadding(t.padding),e.lineHeight+n.height):0}function mn(t,e){return H.extend(H.options._parseFont({fontFamily:un(e.fontFamily,t.fontFamily),fontSize:un(e.fontSize,t.fontSize),fontStyle:un(e.fontStyle,t.fontStyle),lineHeight:un(e.lineHeight,t.lineHeight)}),{color:H.options.resolve([e.fontColor,t.fontColor,W.global.defaultFontColor])})}function pn(t){var e=mn(t,t.minor);return{minor:e,major:t.major.enabled?mn(t,t.major):e}}function vn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function bn(t,e,n,i){var a,r,o,s,l=un(n,0),u=Math.min(un(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}W._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:on.formatters.values,minor:{},major:{}}});var yn=$.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){H.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=H.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){H.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){H.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){H.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){H.callback(this.options.beforeDataLimits,[this])},determineDataLimits:H.noop,afterDataLimits:function(){H.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){H.callback(this.options.beforeBuildTicks,[this])},buildTicks:H.noop,afterBuildTicks:function(t){var e=this;return sn(t)&&t.length?H.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=H.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){H.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){H.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){H.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-fn(l.gridLines)-u.padding-gn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=H.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=fn(o)+gn(r)),u?s&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,m=h.highest,p=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,y=H.toRadians(t.labelRotation),x=Math.cos(y),_=Math.sin(y),w=_*g.width+x*(m.height-(b?m.offset:0))+(b?0:p);e.height=Math.min(t.maxHeight,e.height+w+v);var k,M,S=t.getPixelForTick(0)-t.left,D=t.right-t.getPixelForTick(t.getTicks().length-1);b?(k=l?x*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):x*f.width+_*f.offset):(k=c.width/2,M=f.width/2),t.paddingLeft=Math.max((k-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-D)*t.width/(t.width-D),0)+3}else{var C=a.mirror?0:g.width+v+p;e.width=Math.min(t.maxWidth,e.width+C),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ln(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=cn(t.ctx,pn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return sn(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:H.noop,getPixelForValue:H.noop,getValueForPixel:H.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),vn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=H.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)bn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,bn(t,i,H.isNullOrUndef(a)?0:d-a,d),bn(t,i,h,H.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this.options.ticks,e=H.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,m,p,v,b=this,y=b.chart,x=b.options,_=x.gridLines,w=x.position,k=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,D=S.length+(k?1:0),C=fn(_),P=[],T=_.drawBorder?dn(_.lineWidth,0,0):0,O=T/2,A=H._alignPixel,F=function(t){return A(y,t,T)};for("top"===w?(e=F(b.bottom),s=b.bottom-C,u=e-O,h=F(t.top)+O,f=t.bottom):"bottom"===w?(e=F(b.top),h=t.top,f=F(t.bottom)-O,s=e+O,u=b.top+C):"left"===w?(e=F(b.right),o=b.right-C,l=e-O,d=F(t.left)+O,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-O,o=e+O,l=b.left+C),n=0;n<D;++n)i=S[n]||{},ln(i.label)&&n<S.length||(n===b.zeroLineIndex&&x.offset===k?(g=_.zeroLineWidth,m=_.zeroLineColor,p=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=dn(_.lineWidth,n,1),m=dn(_.color,n,"rgba(0,0,0,0.1)"),p=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=hn(b,i._index||n,k))&&(r=A(y,a,g),M?o=l=d=c=r:s=u=h=f=r,P.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:m,borderDash:p,borderDashOffset:v})));return P.ticksLength=D,P.borderValue=e,P},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,m=f.position,p=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,y=pn(g),x=g.padding,_=fn(f.gridLines),w=-H.toRadians(c.labelRotation),k=[];for("top"===m?(r=c.bottom-_-x,o=w?"left":"center"):"bottom"===m?(r=c.top+_+x,o=w?"right":"center"):"left"===m?(a=c.right-(p?0:_)-x,o=p?"left":"right"):(a=c.left+(p?0:_)+x,o=p?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,ln(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?y.major:y.minor).lineHeight,d=sn(i)?i.length:1,v?(a=s,h="top"===m?((w?1:.5)-d)*u:(w?0:.5)*u):(r=s,h=(1-d)*u/2),k.push({x:a,y:r,rotation:w,label:i,font:l,textOffset:h,textAlign:o}));return k},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=H._alignPixel,h=n.drawBorder?dn(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,m,p,v=h,b=dn(n.lineWidth,c.ticksLength-1,1),y=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,m=p=y):(m=d(u,e.top,v)-v/2,p=d(u,e.bottom,b)+b/2,f=g=y),l.lineWidth=h,l.strokeStyle=dn(n.color,0),l.beginPath(),l.moveTo(f,m),l.lineTo(g,p),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,sn(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=un(i.fontColor,W.global.defaultFontColor),s=H.options._parseFont(i),l=H.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});yn.prototype._draw=yn.prototype.draw;var xn=yn,_n=H.isNullOrUndef,wn=xn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=H.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};wn._defaults=kn;var Mn=H.noop,Sn=H.isNullOrUndef;var Dn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=H.sign(t.min),i=H.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:H.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,m=H.niceNum((g-f)/u/l)*l;if(m<1e-14&&Sn(d)&&Sn(h))return[f,g];(r=Math.ceil(g/m)-Math.floor(f/m))>u&&(m=H.niceNum(r*m/u/l)*l),s||Sn(c)?n=Math.pow(10,H._decimalPlaces(m)):(n=Math.pow(10,c),m=Math.ceil(m*n)/n),i=Math.floor(f/m)*m,a=Math.ceil(g/m)*m,s&&(!Sn(d)&&H.almostWhole(d/m,m/1e3)&&(i=d),!Sn(h)&&H.almostWhole(h/m,m/1e3)&&(a=h)),r=(a-i)/m,r=H.almostEquals(r,Math.round(r),m/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var p=1;p<r;++p)o.push(Math.round((i+p*m)*n)/n);return o.push(Sn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=H.max(a),t.min=H.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),xn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;xn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Cn={position:"left",ticks:{callback:on.formatters.linear}};function Pn(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function Tn(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var On=Dn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?Pn(a,u,e,n):Tn(a,e,n);H.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,H.min(i)),a.max=Math.max(a.max,H.max(i))})),a.min=H.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=H.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=H.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),An=Cn;On._defaults=An;var Fn=H.valueOrDefault,In=H.math.log10;var Ln={position:"left",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return H.isFinite(t)&&t>=0?t:e}var Nn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var m=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(m[a]=m[a]||0,m[a]+=n.max)}}H.each(f,(function(t){if(t.length>0){var e=H.min(t),n=H.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=H.isFinite(o.min)?o.min:null,o.max=H.isFinite(o.max)?o.max:null,o.minNotZero=H.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=Rn(e.min,t.min),t.max=Rn(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(In(t.min))-1),t.max=Math.pow(10,Math.floor(In(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(In(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(In(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(In(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(In(e.min)))),o=Math.floor(In(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(In(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(In(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=Fn(t.max,r);return a.push(u),a}(i,t);t.max=H.max(a),t.min=H.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),xn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(In(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,W.global.defaultFontSize)/t._length),t._startValue=In(e),t._valueOffset=n,t._valueRange=(In(t.max)-In(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(In(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Wn=Ln;Nn._defaults=Wn;var Yn=H.valueOrDefault,zn=H.valueAtIndexOrDefault,En=H.options.resolve,Vn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Yn(e.fontSize,W.global.defaultFontSize)+2*e.backdropPaddingY:0}function Bn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(H.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Gn(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function qn(t){return H.isNumber(t)?t:0}var Zn=Dn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;H.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);H.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Dn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=H.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=H.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=H.isArray(u)?{w:H.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=H.toDegrees(h)%360,f=Bn(c,i.x,n.w,0,180),g=Bn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=qn(a),r=qn(r),o=qn(o),s=qn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(H.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Yn(s.lineWidth,o.lineWidth),u=Yn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=H.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=zn(i.fontColor,s,W.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=H.toDegrees(h);e.textAlign=jn(c),Gn(c,t._pointLabelSizes[s],u),Un(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&H.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=zn(e.color,i-1),u=zn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(En([s.borderDash,o.borderDash,[]])),a.lineDashOffset=En([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=H.options._parseFont(n),s=Yn(n.fontColor,W.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",H.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:H.noop}),$n=Vn;Zn._defaults=$n;var Xn=H._deprecated,Kn=H.options.resolve,Jn=H.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return H.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return H.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function si(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),H.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),H.isFinite(o)||(o=n.parse(o))),o)}function li(t,e){if(H.isNullOrUndef(e))return null;var n=t.options.time,i=si(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function ui(t,e,n,i){var a,r,o,s=ni.length;for(a=ni.indexOf(t);a<s-1;++a)if(o=(r=ei[ni[a]]).steps?r.steps:ti,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ni[a];return ni[s-1]}function di(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var hi=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Xn("time scale",n.format,"time.format","time.parser"),Xn("time scale",n.min,"time.min","ticks.min"),Xn("time scale",n.max,"time.max","ticks.max"),H.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=ti,f=Qn,g=[],m=[],p=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)p.push(li(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,H.isObject(a[0]))for(m[t]=[],e=0,i=a.length;e<i;++e)r=li(s,a[e]),g.push(r),m[t][e]=r;else m[t]=p.slice(0),o||(g=g.concat(p),o=!0);else m[t]=[];p.length&&(c=Math.min(c,p[0]),f=Math.max(f,p[p.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ii):g.sort(ii),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=li(s,ai(d))||c,f=li(s,ri(d))||f,c=c===ti?+u.startOf(Date.now(),h):c,f=f===Qn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:m,labels:p}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||ui(s.minUnit,e,n,i),u=Kn([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=li(i,ai(o))||a,r=li(i,ri(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?ui(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;e<n;++e)if(ei[ni[e]].common)return ni[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=oi(t,"time",e[0],"pos"),s=1===e.length?1-r:(oi(t,"time",e[1],"pos")-r)/2,o=oi(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-oi(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),di(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return H.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(si(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(si(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,m=Kn([g.callback,g.userCallback,h.callback,h.userCallback]);return m?m(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=oi(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=li(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=oi(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=H.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=Jn(e.fontSize,W.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,di(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),ci={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};hi._defaults=ci;var fi={category:wn,linear:On,logarithmic:Nn,radialLinear:Zn,time:hi},gi=e((function(e,n){e.exports=function(){var n,i;function a(){return n.apply(null,arguments)}function r(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function l(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function d(t,e){var n,i=[];for(n=0;n<t.length;++n)i.push(e(t[n],n));return i}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function c(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function f(t,e,n,i){return Ie(t,e,n,i,!0).utc()}function g(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function m(t){if(null==t._isValid){var e=g(t),n=i.call(e.parsedDateParts,(function(t){return null!=t})),a=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(a=a&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return a;t._isValid=a}return t._isValid}function p(t){var e=f(NaN);return null!=t?c(g(e),t):g(e).userInvalidated=!0,e}i=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,i=0;i<n;i++)if(i in e&&t.call(this,e[i],i,e))return!0;return!1};var v=a.momentProperties=[];function b(t,e){var n,i,a;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=g(e)),s(e._locale)||(t._locale=e._locale),v.length>0)for(n=0;n<v.length;n++)s(a=e[i=v[n]])||(t[i]=a);return t}var y=!1;function x(t){b(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===y&&(y=!0,a.updateOffset(this),y=!1)}function _(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function w(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function k(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=w(e)),n}function M(t,e,n){var i,a=Math.min(t.length,e.length),r=Math.abs(t.length-e.length),o=0;for(i=0;i<a;i++)(n&&t[i]!==e[i]||!n&&k(t[i])!==k(e[i]))&&o++;return o+r}function S(t){!1===a.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function D(t,e){var n=!0;return c((function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,t),n){for(var i,r=[],o=0;o<arguments.length;o++){if(i="","object"==typeof arguments[o]){for(var s in i+="\n["+o+"] ",arguments[0])i+=s+": "+arguments[0][s]+", ";i=i.slice(0,-2)}else i=arguments[o];r.push(i)}S(t+"\nArguments: "+Array.prototype.slice.call(r).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var C,P={};function T(t,e){null!=a.deprecationHandler&&a.deprecationHandler(t,e),P[t]||(S(e),P[t]=!0)}function O(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function A(t,e){var n,i=c({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(i[n]={},c(i[n],t[n]),c(i[n],e[n])):null!=e[n]?i[n]=e[n]:delete i[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(i[n]=c({},i[n]));return i}function F(t){null!=t&&this.set(t)}a.suppressDeprecationWarnings=!1,a.deprecationHandler=null,C=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var I={};function L(t,e){var n=t.toLowerCase();I[n]=I[n+"s"]=I[e]=t}function R(t){return"string"==typeof t?I[t]||I[t.toLowerCase()]:void 0}function N(t){var e,n,i={};for(n in t)h(t,n)&&(e=R(n))&&(i[e]=t[n]);return i}var W={};function Y(t,e){W[t]=e}function z(t,e,n){var i=""+Math.abs(t),a=e-i.length;return(t>=0?n?"+":"":"-")+Math.pow(10,Math.max(0,a)).toString().substr(1)+i}var E=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,V=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,H={},B={};function j(t,e,n,i){var a=i;"string"==typeof i&&(a=function(){return this[i]()}),t&&(B[t]=a),e&&(B[e[0]]=function(){return z(a.apply(this,arguments),e[1],e[2])}),n&&(B[n]=function(){return this.localeData().ordinal(a.apply(this,arguments),t)})}function U(t,e){return t.isValid()?(e=G(e,t.localeData()),H[e]=H[e]||function(t){var e,n,i,a=t.match(E);for(e=0,n=a.length;e<n;e++)B[a[e]]?a[e]=B[a[e]]:a[e]=(i=a[e]).match(/\[[\s\S]/)?i.replace(/^\[|\]$/g,""):i.replace(/\\/g,"");return function(e){var i,r="";for(i=0;i<n;i++)r+=O(a[i])?a[i].call(e,t):a[i];return r}}(e),H[e](t)):t.localeData().invalidDate()}function G(t,e){var n=5;function i(t){return e.longDateFormat(t)||t}for(V.lastIndex=0;n>=0&&V.test(t);)t=t.replace(V,i),V.lastIndex=0,n-=1;return t}var q=/\d/,Z=/\d\d/,$=/\d{3}/,X=/\d{4}/,K=/[+-]?\d{6}/,J=/\d\d?/,Q=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,it=/[+-]?\d{1,6}/,at=/\d+/,rt=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,lt=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function dt(t,e,n){ut[t]=O(e)?e:function(t,i){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ct(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,i,a){return e||n||i||a}))))}function ct(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var ft={};function gt(t,e){var n,i=e;for("string"==typeof t&&(t=[t]),l(e)&&(i=function(t,n){n[e]=k(t)}),n=0;n<t.length;n++)ft[t[n]]=i}function mt(t,e){gt(t,(function(t,n,i,a){i._w=i._w||{},e(t,i._w,i,a)}))}function pt(t,e,n){null!=e&&h(ft,t)&&ft[t](e,n._a,n,t)}var vt=0,bt=1,yt=2,xt=3,_t=4,wt=5,kt=6,Mt=7,St=8;function Dt(t){return Ct(t)?366:365}function Ct(t){return t%4==0&&t%100!=0||t%400==0}j("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),j(0,["YY",2],0,(function(){return this.year()%100})),j(0,["YYYY",4],0,"year"),j(0,["YYYYY",5],0,"year"),j(0,["YYYYYY",6,!0],0,"year"),L("year","y"),Y("year",1),dt("Y",rt),dt("YY",J,Z),dt("YYYY",nt,X),dt("YYYYY",it,K),dt("YYYYYY",it,K),gt(["YYYYY","YYYYYY"],vt),gt("YYYY",(function(t,e){e[vt]=2===t.length?a.parseTwoDigitYear(t):k(t)})),gt("YY",(function(t,e){e[vt]=a.parseTwoDigitYear(t)})),gt("Y",(function(t,e){e[vt]=parseInt(t,10)})),a.parseTwoDigitYear=function(t){return k(t)+(k(t)>68?1900:2e3)};var Pt,Tt=Ot("FullYear",!0);function Ot(t,e){return function(n){return null!=n?(Ft(this,t,n),a.updateOffset(this,e),this):At(this,t)}}function At(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function Ft(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&Ct(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),It(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function It(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=function(t,e){return(t%e+e)%e}(e,12);return t+=(e-n)/12,1===n?Ct(t)?29:28:31-n%7%2}Pt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},j("M",["MM",2],"Mo",(function(){return this.month()+1})),j("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),j("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),Y("month",8),dt("M",J),dt("MM",J,Z),dt("MMM",(function(t,e){return e.monthsShortRegex(t)})),dt("MMMM",(function(t,e){return e.monthsRegex(t)})),gt(["M","MM"],(function(t,e){e[bt]=k(t)-1})),gt(["MMM","MMMM"],(function(t,e,n,i){var a=n._locale.monthsParse(t,i,n._strict);null!=a?e[bt]=a:g(n).invalidMonth=t}));var Lt=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Rt="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Nt="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Wt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],i=0;i<12;++i)r=f([2e3,i]),this._shortMonthsParse[i]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[i]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null}function Yt(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=k(e);else if(!l(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),It(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function zt(t){return null!=t?(Yt(this,t),a.updateOffset(this,!0),this):At(this,"Month")}var Et=lt,Vt=lt;function Ht(){function t(t,e){return e.length-t.length}var e,n,i=[],a=[],r=[];for(e=0;e<12;e++)n=f([2e3,e]),i.push(this.monthsShort(n,"")),a.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(i.sort(t),a.sort(t),r.sort(t),e=0;e<12;e++)i[e]=ct(i[e]),a[e]=ct(a[e]);for(e=0;e<24;e++)r[e]=ct(r[e]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+i.join("|")+")","i")}function Bt(t,e,n,i,a,r,o){var s;return t<100&&t>=0?(s=new Date(t+400,e,n,i,a,r,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,i,a,r,o),s}function jt(t){var e;if(t<100&&t>=0){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Ut(t,e,n){var i=7+e-n;return-(7+jt(t,0,i).getUTCDay()-e)%7+i-1}function Gt(t,e,n,i,a){var r,o,s=1+7*(e-1)+(7+n-i)%7+Ut(t,i,a);return s<=0?o=Dt(r=t-1)+s:s>Dt(t)?(r=t+1,o=s-Dt(t)):(r=t,o=s),{year:r,dayOfYear:o}}function qt(t,e,n){var i,a,r=Ut(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?i=o+Zt(a=t.year()-1,e,n):o>Zt(t.year(),e,n)?(i=o-Zt(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function Zt(t,e,n){var i=Ut(t,e,n),a=Ut(t+1,e,n);return(Dt(t)-i+a)/7}function $t(t,e){return t.slice(e,7).concat(t.slice(0,e))}j("w",["ww",2],"wo","week"),j("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),Y("week",5),Y("isoWeek",5),dt("w",J),dt("ww",J,Z),dt("W",J),dt("WW",J,Z),mt(["w","ww","W","WW"],(function(t,e,n,i){e[i.substr(0,1)]=k(t)})),j("d",0,"do","day"),j("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),j("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),j("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),j("e",0,0,"weekday"),j("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),Y("day",11),Y("weekday",11),Y("isoWeekday",11),dt("d",J),dt("e",J),dt("E",J),dt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),dt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),dt("dddd",(function(t,e){return e.weekdaysRegex(t)})),mt(["dd","ddd","dddd"],(function(t,e,n,i){var a=n._locale.weekdaysParse(t,i,n._strict);null!=a?e.d=a:g(n).invalidWeekday=t})),mt(["d","e","E"],(function(t,e,n,i){e[i]=k(t)}));var Xt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Kt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Jt="Su_Mo_Tu_We_Th_Fr_Sa".split("_");function Qt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=f([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null}var te=lt,ee=lt,ne=lt;function ie(){function t(t,e){return e.length-t.length}var e,n,i,a,r,o=[],s=[],l=[],u=[];for(e=0;e<7;e++)n=f([2e3,1]).day(e),i=this.weekdaysMin(n,""),a=this.weekdaysShort(n,""),r=this.weekdays(n,""),o.push(i),s.push(a),l.push(r),u.push(i),u.push(a),u.push(r);for(o.sort(t),s.sort(t),l.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ct(s[e]),l[e]=ct(l[e]),u[e]=ct(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function ae(){return this.hours()%12||12}function re(t,e){j(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function oe(t,e){return e._meridiemParse}j("H",["HH",2],0,"hour"),j("h",["hh",2],0,ae),j("k",["kk",2],0,(function(){return this.hours()||24})),j("hmm",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)})),j("hmmss",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)+z(this.seconds(),2)})),j("Hmm",0,0,(function(){return""+this.hours()+z(this.minutes(),2)})),j("Hmmss",0,0,(function(){return""+this.hours()+z(this.minutes(),2)+z(this.seconds(),2)})),re("a",!0),re("A",!1),L("hour","h"),Y("hour",13),dt("a",oe),dt("A",oe),dt("H",J),dt("h",J),dt("k",J),dt("HH",J,Z),dt("hh",J,Z),dt("kk",J,Z),dt("hmm",Q),dt("hmmss",tt),dt("Hmm",Q),dt("Hmmss",tt),gt(["H","HH"],xt),gt(["k","kk"],(function(t,e,n){var i=k(t);e[xt]=24===i?0:i})),gt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),gt(["h","hh"],(function(t,e,n){e[xt]=k(t),g(n).bigHour=!0})),gt("hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i)),g(n).bigHour=!0})),gt("hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a)),g(n).bigHour=!0})),gt("Hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i))})),gt("Hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a))}));var se,le=Ot("Hours",!0),ue={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Rt,monthsShort:Nt,week:{dow:0,doy:6},weekdays:Xt,weekdaysMin:Jt,weekdaysShort:Kt,meridiemParse:/[ap]\.?m?\.?/i},de={},he={};function ce(t){return t?t.toLowerCase().replace("_","-"):t}function fe(n){var i=null;if(!de[n]&&e&&e.exports)try{i=se._abbr,t(),ge(i)}catch(t){}return de[n]}function ge(t,e){var n;return t&&((n=s(e)?pe(t):me(t,e))?se=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),se._abbr}function me(t,e){if(null!==e){var n,i=ue;if(e.abbr=t,null!=de[t])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=de[t]._config;else if(null!=e.parentLocale)if(null!=de[e.parentLocale])i=de[e.parentLocale]._config;else{if(null==(n=fe(e.parentLocale)))return he[e.parentLocale]||(he[e.parentLocale]=[]),he[e.parentLocale].push({name:t,config:e}),null;i=n._config}return de[t]=new F(A(i,e)),he[t]&&he[t].forEach((function(t){me(t.name,t.config)})),ge(t),de[t]}return delete de[t],null}function pe(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return se;if(!r(t)){if(e=fe(t))return e;t=[t]}return function(t){for(var e,n,i,a,r=0;r<t.length;){for(e=(a=ce(t[r]).split("-")).length,n=(n=ce(t[r+1]))?n.split("-"):null;e>0;){if(i=fe(a.slice(0,e).join("-")))return i;if(n&&n.length>=e&&M(a,n,!0)>=e-1)break;e--}r++}return se}(t)}function ve(t){var e,n=t._a;return n&&-2===g(t).overflow&&(e=n[bt]<0||n[bt]>11?bt:n[yt]<1||n[yt]>It(n[vt],n[bt])?yt:n[xt]<0||n[xt]>24||24===n[xt]&&(0!==n[_t]||0!==n[wt]||0!==n[kt])?xt:n[_t]<0||n[_t]>59?_t:n[wt]<0||n[wt]>59?wt:n[kt]<0||n[kt]>999?kt:-1,g(t)._overflowDayOfYear&&(e<vt||e>yt)&&(e=yt),g(t)._overflowWeeks&&-1===e&&(e=Mt),g(t)._overflowWeekday&&-1===e&&(e=St),g(t).overflow=e),t}function be(t,e,n){return null!=t?t:null!=e?e:n}function ye(t){var e,n,i,r,o,s=[];if(!t._d){for(i=function(t){var e=new Date(a.now());return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}(t),t._w&&null==t._a[yt]&&null==t._a[bt]&&function(t){var e,n,i,a,r,o,s,l;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)r=1,o=4,n=be(e.GG,t._a[vt],qt(Le(),1,4).year),i=be(e.W,1),((a=be(e.E,1))<1||a>7)&&(l=!0);else{r=t._locale._week.dow,o=t._locale._week.doy;var u=qt(Le(),r,o);n=be(e.gg,t._a[vt],u.year),i=be(e.w,u.week),null!=e.d?((a=e.d)<0||a>6)&&(l=!0):null!=e.e?(a=e.e+r,(e.e<0||e.e>6)&&(l=!0)):a=r}i<1||i>Zt(n,r,o)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(s=Gt(n,i,a,r,o),t._a[vt]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=be(t._a[vt],i[vt]),(t._dayOfYear>Dt(o)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),n=jt(o,0,t._dayOfYear),t._a[bt]=n.getUTCMonth(),t._a[yt]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=i[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[xt]&&0===t._a[_t]&&0===t._a[wt]&&0===t._a[kt]&&(t._nextDay=!0,t._a[xt]=0),t._d=(t._useUTC?jt:Bt).apply(null,s),r=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[xt]=24),t._w&&void 0!==t._w.d&&t._w.d!==r&&(g(t).weekdayMismatch=!0)}}var xe=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_e=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,we=/Z|[+-]\d\d(?::?\d\d)?/,ke=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Me=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Se=/^\/?Date\((\-?\d+)/i;function De(t){var e,n,i,a,r,o,s=t._i,l=xe.exec(s)||_e.exec(s);if(l){for(g(t).iso=!0,e=0,n=ke.length;e<n;e++)if(ke[e][1].exec(l[1])){a=ke[e][0],i=!1!==ke[e][2];break}if(null==a)return void(t._isValid=!1);if(l[3]){for(e=0,n=Me.length;e<n;e++)if(Me[e][1].exec(l[3])){r=(l[2]||" ")+Me[e][0];break}if(null==r)return void(t._isValid=!1)}if(!i&&null!=r)return void(t._isValid=!1);if(l[4]){if(!we.exec(l[4]))return void(t._isValid=!1);o="Z"}t._f=a+(r||"")+(o||""),Ae(t)}else t._isValid=!1}var Ce=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function Pe(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}var Te={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Oe(t){var e,n,i,a,r,o,s,l=Ce.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(l){var u=(e=l[4],n=l[3],i=l[2],a=l[5],r=l[6],o=l[7],s=[Pe(e),Nt.indexOf(n),parseInt(i,10),parseInt(a,10),parseInt(r,10)],o&&s.push(parseInt(o,10)),s);if(!function(t,e,n){return!t||Kt.indexOf(t)===new Date(e[0],e[1],e[2]).getDay()||(g(n).weekdayMismatch=!0,n._isValid=!1,!1)}(l[1],u,t))return;t._a=u,t._tzm=function(t,e,n){if(t)return Te[t];if(e)return 0;var i=parseInt(n,10),a=i%100;return(i-a)/100*60+a}(l[8],l[9],l[10]),t._d=jt.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),g(t).rfc2822=!0}else t._isValid=!1}function Ae(t){if(t._f!==a.ISO_8601)if(t._f!==a.RFC_2822){t._a=[],g(t).empty=!0;var e,n,i,r,o,s=""+t._i,l=s.length,u=0;for(i=G(t._f,t._locale).match(E)||[],e=0;e<i.length;e++)r=i[e],(n=(s.match(ht(r,t))||[])[0])&&((o=s.substr(0,s.indexOf(n))).length>0&&g(t).unusedInput.push(o),s=s.slice(s.indexOf(n)+n.length),u+=n.length),B[r]?(n?g(t).empty=!1:g(t).unusedTokens.push(r),pt(r,n,t)):t._strict&&!n&&g(t).unusedTokens.push(r);g(t).charsLeftOver=l-u,s.length>0&&g(t).unusedInput.push(s),t._a[xt]<=12&&!0===g(t).bigHour&&t._a[xt]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[xt]=function(t,e,n){var i;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?((i=t.isPM(n))&&e<12&&(e+=12),i||12!==e||(e=0),e):e}(t._locale,t._a[xt],t._meridiem),ye(t),ve(t)}else Oe(t);else De(t)}function Fe(t){var e=t._i,n=t._f;return t._locale=t._locale||pe(t._l),null===e||void 0===n&&""===e?p({nullInput:!0}):("string"==typeof e&&(t._i=e=t._locale.preparse(e)),_(e)?new x(ve(e)):(u(e)?t._d=e:r(n)?function(t){var e,n,i,a,r;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(a=0;a<t._f.length;a++)r=0,e=b({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[a],Ae(e),m(e)&&(r+=g(e).charsLeftOver,r+=10*g(e).unusedTokens.length,g(e).score=r,(null==i||r<i)&&(i=r,n=e));c(t,n||e)}(t):n?Ae(t):function(t){var e=t._i;s(e)?t._d=new Date(a.now()):u(e)?t._d=new Date(e.valueOf()):"string"==typeof e?function(t){var e=Se.exec(t._i);null===e?(De(t),!1===t._isValid&&(delete t._isValid,Oe(t),!1===t._isValid&&(delete t._isValid,a.createFromInputFallback(t)))):t._d=new Date(+e[1])}(t):r(e)?(t._a=d(e.slice(0),(function(t){return parseInt(t,10)})),ye(t)):o(e)?function(t){if(!t._d){var e=N(t._i);t._a=d([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),ye(t)}}(t):l(e)?t._d=new Date(e):a.createFromInputFallback(t)}(t),m(t)||(t._d=null),t))}function Ie(t,e,n,i,a){var s,l={};return!0!==n&&!1!==n||(i=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||r(t)&&0===t.length)&&(t=void 0),l._isAMomentObject=!0,l._useUTC=l._isUTC=a,l._l=n,l._i=t,l._f=e,l._strict=i,(s=new x(ve(Fe(l))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function Le(t,e,n,i){return Ie(t,e,n,i,!1)}a.createFromInputFallback=D("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),a.ISO_8601=function(){},a.RFC_2822=function(){};var Re=D("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:p()})),Ne=D("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t>this?this:t:p()}));function We(t,e){var n,i;if(1===e.length&&r(e[0])&&(e=e[0]),!e.length)return Le();for(n=e[0],i=1;i<e.length;++i)e[i].isValid()&&!e[i][t](n)||(n=e[i]);return n}var Ye=["year","quarter","month","week","day","hour","minute","second","millisecond"];function ze(t){var e=N(t),n=e.year||0,i=e.quarter||0,a=e.month||0,r=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,l=e.minute||0,u=e.second||0,d=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===Pt.call(Ye,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,i=0;i<Ye.length;++i)if(t[Ye[i]]){if(n)return!1;parseFloat(t[Ye[i]])!==k(t[Ye[i]])&&(n=!0)}return!0}(e),this._milliseconds=+d+1e3*u+6e4*l+1e3*s*60*60,this._days=+o+7*r,this._months=+a+3*i+12*n,this._data={},this._locale=pe(),this._bubble()}function Ee(t){return t instanceof ze}function Ve(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function He(t,e){j(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+z(~~(t/60),2)+e+z(~~t%60,2)}))}He("Z",":"),He("ZZ",""),dt("Z",st),dt("ZZ",st),gt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=je(st,t)}));var Be=/([\+\-]|\d\d)/gi;function je(t,e){var n=(e||"").match(t);if(null===n)return null;var i=((n[n.length-1]||[])+"").match(Be)||["-",0,0],a=60*i[1]+k(i[2]);return 0===a?0:"+"===i[0]?a:-a}function Ue(t,e){var n,i;return e._isUTC?(n=e.clone(),i=(_(t)||u(t)?t.valueOf():Le(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+i),a.updateOffset(n,!1),n):Le(t).local()}function Ge(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function qe(){return!!this.isValid()&&this._isUTC&&0===this._offset}a.updateOffset=function(){};var Ze=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,$e=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Xe(t,e){var n,i,a,r,o,s,u=t,d=null;return Ee(t)?u={ms:t._milliseconds,d:t._days,M:t._months}:l(t)?(u={},e?u[e]=t:u.milliseconds=t):(d=Ze.exec(t))?(n="-"===d[1]?-1:1,u={y:0,d:k(d[yt])*n,h:k(d[xt])*n,m:k(d[_t])*n,s:k(d[wt])*n,ms:k(Ve(1e3*d[kt]))*n}):(d=$e.exec(t))?(n="-"===d[1]?-1:1,u={y:Ke(d[2],n),M:Ke(d[3],n),w:Ke(d[4],n),d:Ke(d[5],n),h:Ke(d[6],n),m:Ke(d[7],n),s:Ke(d[8],n)}):null==u?u={}:"object"==typeof u&&("from"in u||"to"in u)&&(r=Le(u.from),o=Le(u.to),a=r.isValid()&&o.isValid()?(o=Ue(o,r),r.isBefore(o)?s=Je(r,o):((s=Je(o,r)).milliseconds=-s.milliseconds,s.months=-s.months),s):{milliseconds:0,months:0},(u={}).ms=a.milliseconds,u.M=a.months),i=new ze(u),Ee(t)&&h(t,"_locale")&&(i._locale=t._locale),i}function Ke(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Je(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function Qe(t,e){return function(n,i){var a;return null===i||isNaN(+i)||(T(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),a=n,n=i,i=a),tn(this,Xe(n="string"==typeof n?+n:n,i),t),this}}function tn(t,e,n,i){var r=e._milliseconds,o=Ve(e._days),s=Ve(e._months);t.isValid()&&(i=null==i||i,s&&Yt(t,At(t,"Month")+s*n),o&&Ft(t,"Date",At(t,"Date")+o*n),r&&t._d.setTime(t._d.valueOf()+r*n),i&&a.updateOffset(t,o||s))}Xe.fn=ze.prototype,Xe.invalid=function(){return Xe(NaN)};var en=Qe(1,"add"),nn=Qe(-1,"subtract");function an(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),i=t.clone().add(n,"months");return-(n+(e-i<0?(e-i)/(i-t.clone().add(n-1,"months")):(e-i)/(t.clone().add(n+1,"months")-i)))||0}function rn(t){var e;return void 0===t?this._locale._abbr:(null!=(e=pe(t))&&(this._locale=e),this)}a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var on=D("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function sn(){return this._locale}var ln=1e3,un=60*ln,dn=60*un,hn=3506328*dn;function cn(t,e){return(t%e+e)%e}function fn(t,e,n){return t<100&&t>=0?new Date(t+400,e,n)-hn:new Date(t,e,n).valueOf()}function gn(t,e,n){return t<100&&t>=0?Date.UTC(t+400,e,n)-hn:Date.UTC(t,e,n)}function mn(t,e){j(0,[t,t.length],0,e)}function pn(t,e,n,i,a){var r;return null==t?qt(this,i,a).year:(e>(r=Zt(t,i,a))&&(e=r),vn.call(this,t,e,n,i,a))}function vn(t,e,n,i,a){var r=Gt(t,e,n,i,a),o=jt(r.year,0,r.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}j(0,["gg",2],0,(function(){return this.weekYear()%100})),j(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),mn("gggg","weekYear"),mn("ggggg","weekYear"),mn("GGGG","isoWeekYear"),mn("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),Y("weekYear",1),Y("isoWeekYear",1),dt("G",rt),dt("g",rt),dt("GG",J,Z),dt("gg",J,Z),dt("GGGG",nt,X),dt("gggg",nt,X),dt("GGGGG",it,K),dt("ggggg",it,K),mt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,i){e[i.substr(0,2)]=k(t)})),mt(["gg","GG"],(function(t,e,n,i){e[i]=a.parseTwoDigitYear(t)})),j("Q",0,"Qo","quarter"),L("quarter","Q"),Y("quarter",7),dt("Q",q),gt("Q",(function(t,e){e[bt]=3*(k(t)-1)})),j("D",["DD",2],"Do","date"),L("date","D"),Y("date",9),dt("D",J),dt("DD",J,Z),dt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),gt(["D","DD"],yt),gt("Do",(function(t,e){e[yt]=k(t.match(J)[0])}));var bn=Ot("Date",!0);j("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),Y("dayOfYear",4),dt("DDD",et),dt("DDDD",$),gt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=k(t)})),j("m",["mm",2],0,"minute"),L("minute","m"),Y("minute",14),dt("m",J),dt("mm",J,Z),gt(["m","mm"],_t);var yn=Ot("Minutes",!1);j("s",["ss",2],0,"second"),L("second","s"),Y("second",15),dt("s",J),dt("ss",J,Z),gt(["s","ss"],wt);var xn,_n=Ot("Seconds",!1);for(j("S",0,0,(function(){return~~(this.millisecond()/100)})),j(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),j(0,["SSS",3],0,"millisecond"),j(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),j(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),j(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),j(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),j(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),j(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),Y("millisecond",16),dt("S",et,q),dt("SS",et,Z),dt("SSS",et,$),xn="SSSS";xn.length<=9;xn+="S")dt(xn,at);function wn(t,e){e[kt]=k(1e3*("0."+t))}for(xn="S";xn.length<=9;xn+="S")gt(xn,wn);var kn=Ot("Milliseconds",!1);j("z",0,0,"zoneAbbr"),j("zz",0,0,"zoneName");var Mn=x.prototype;function Sn(t){return t}Mn.add=en,Mn.calendar=function(t,e){var n=t||Le(),i=Ue(n,this).startOf("day"),r=a.calendarFormat(this,i)||"sameElse",o=e&&(O(e[r])?e[r].call(this,n):e[r]);return this.format(o||this.localeData().calendar(r,this,Le(n)))},Mn.clone=function(){return new x(this)},Mn.diff=function(t,e,n){var i,a,r;if(!this.isValid())return NaN;if(!(i=Ue(t,this)).isValid())return NaN;switch(a=6e4*(i.utcOffset()-this.utcOffset()),e=R(e)){case"year":r=an(this,i)/12;break;case"month":r=an(this,i);break;case"quarter":r=an(this,i)/3;break;case"second":r=(this-i)/1e3;break;case"minute":r=(this-i)/6e4;break;case"hour":r=(this-i)/36e5;break;case"day":r=(this-i-a)/864e5;break;case"week":r=(this-i-a)/6048e5;break;default:r=this-i}return n?r:w(r)},Mn.endOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=dn-cn(e+(this._isUTC?0:this.utcOffset()*un),dn)-1;break;case"minute":e=this._d.valueOf(),e+=un-cn(e,un)-1;break;case"second":e=this._d.valueOf(),e+=ln-cn(e,ln)-1}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.format=function(t){t||(t=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var e=U(this,t);return this.localeData().postformat(e)},Mn.from=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.fromNow=function(t){return this.from(Le(),t)},Mn.to=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.toNow=function(t){return this.to(Le(),t)},Mn.get=function(t){return O(this[t=R(t)])?this[t]():this},Mn.invalidAt=function(){return g(this).overflow},Mn.isAfter=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},Mn.isBefore=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},Mn.isBetween=function(t,e,n,i){var a=_(t)?t:Le(t),r=_(e)?e:Le(e);return!!(this.isValid()&&a.isValid()&&r.isValid())&&("("===(i=i||"()")[0]?this.isAfter(a,n):!this.isBefore(a,n))&&(")"===i[1]?this.isBefore(r,n):!this.isAfter(r,n))},Mn.isSame=function(t,e){var n,i=_(t)?t:Le(t);return!(!this.isValid()||!i.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()===i.valueOf():(n=i.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},Mn.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},Mn.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},Mn.isValid=function(){return m(this)},Mn.lang=on,Mn.locale=rn,Mn.localeData=sn,Mn.max=Ne,Mn.min=Re,Mn.parsingFlags=function(){return c({},g(this))},Mn.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:W[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=N(t)),i=0;i<n.length;i++)this[n[i].unit](t[n[i].unit]);else if(O(this[t=R(t)]))return this[t](e);return this},Mn.startOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=cn(e+(this._isUTC?0:this.utcOffset()*un),dn);break;case"minute":e=this._d.valueOf(),e-=cn(e,un);break;case"second":e=this._d.valueOf(),e-=cn(e,ln)}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.subtract=nn,Mn.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},Mn.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},Mn.toDate=function(){return new Date(this.valueOf())},Mn.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||n.year()>9999?U(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):O(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",U(n,"Z")):U(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},Mn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',i=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",a=e+'[")]';return this.format(n+i+"-MM-DD[T]HH:mm:ss.SSS"+a)},Mn.toJSON=function(){return this.isValid()?this.toISOString():null},Mn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Mn.unix=function(){return Math.floor(this.valueOf()/1e3)},Mn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Mn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Mn.year=Tt,Mn.isLeapYear=function(){return Ct(this.year())},Mn.weekYear=function(t){return pn.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Mn.isoWeekYear=function(t){return pn.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Mn.quarter=Mn.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Mn.month=zt,Mn.daysInMonth=function(){return It(this.year(),this.month())},Mn.week=Mn.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},Mn.isoWeek=Mn.isoWeeks=function(t){var e=qt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},Mn.weeksInYear=function(){var t=this.localeData()._week;return Zt(this.year(),t.dow,t.doy)},Mn.isoWeeksInYear=function(){return Zt(this.year(),1,4)},Mn.date=bn,Mn.day=Mn.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=function(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}(t,this.localeData()),this.add(t-e,"d")):e},Mn.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},Mn.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=function(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7},Mn.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},Mn.hour=Mn.hours=le,Mn.minute=Mn.minutes=yn,Mn.second=Mn.seconds=_n,Mn.millisecond=Mn.milliseconds=kn,Mn.utcOffset=function(t,e,n){var i,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=je(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(i=Ge(this)),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),r!==t&&(!e||this._changeInProgress?tn(this,Xe(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Ge(this)},Mn.utc=function(t){return this.utcOffset(0,t)},Mn.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ge(this),"m")),this},Mn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=je(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Mn.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Le(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Mn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Mn.isUtc=qe,Mn.isUTC=qe,Mn.zoneAbbr=function(){return this._isUTC?"UTC":""},Mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Mn.dates=D("dates accessor is deprecated. Use date instead.",bn),Mn.months=D("months accessor is deprecated. Use month instead",zt),Mn.years=D("years accessor is deprecated. Use year instead",Tt),Mn.zone=D("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),Mn.isDSTShifted=D("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(b(t,this),(t=Fe(t))._a){var e=t._isUTC?f(t._a):Le(t._a);this._isDSTShifted=this.isValid()&&M(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}));var Dn=F.prototype;function Cn(t,e,n,i){var a=pe(),r=f().set(i,e);return a[n](r,t)}function Pn(t,e,n){if(l(t)&&(e=t,t=void 0),t=t||"",null!=e)return Cn(t,e,n,"month");var i,a=[];for(i=0;i<12;i++)a[i]=Cn(t,i,n,"month");return a}function Tn(t,e,n,i){"boolean"==typeof t?(l(e)&&(n=e,e=void 0),e=e||""):(n=e=t,t=!1,l(e)&&(n=e,e=void 0),e=e||"");var a,r=pe(),o=t?r._week.dow:0;if(null!=n)return Cn(e,(n+o)%7,i,"day");var s=[];for(a=0;a<7;a++)s[a]=Cn(e,(a+o)%7,i,"day");return s}Dn.calendar=function(t,e,n){var i=this._calendar[t]||this._calendar.sameElse;return O(i)?i.call(e,n):i},Dn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},Dn.invalidDate=function(){return this._invalidDate},Dn.ordinal=function(t){return this._ordinal.replace("%d",t)},Dn.preparse=Sn,Dn.postformat=Sn,Dn.relativeTime=function(t,e,n,i){var a=this._relativeTime[n];return O(a)?a(t,e,n,i):a.replace(/%d/i,t)},Dn.pastFuture=function(t,e){var n=this._relativeTime[t>0?"future":"past"];return O(n)?n(e):n.replace(/%s/i,e)},Dn.set=function(t){var e,n;for(n in t)O(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Dn.months=function(t,e){return t?r(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Lt).test(e)?"format":"standalone"][t.month()]:r(this._months)?this._months:this._months.standalone},Dn.monthsShort=function(t,e){return t?r(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Lt.test(e)?"format":"standalone"][t.month()]:r(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Dn.monthsParse=function(t,e,n){var i,a,r;if(this._monthsParseExact)return Wt.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),i=0;i<12;i++){if(a=f([2e3,i]),n&&!this._longMonthsParse[i]&&(this._longMonthsParse[i]=new RegExp("^"+this.months(a,"").replace(".","")+"$","i"),this._shortMonthsParse[i]=new RegExp("^"+this.monthsShort(a,"").replace(".","")+"$","i")),n||this._monthsParse[i]||(r="^"+this.months(a,"")+"|^"+this.monthsShort(a,""),this._monthsParse[i]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[i].test(t))return i;if(n&&"MMM"===e&&this._shortMonthsParse[i].test(t))return i;if(!n&&this._monthsParse[i].test(t))return i}},Dn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Vt),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Dn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Et),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Dn.week=function(t){return qt(t,this._week.dow,this._week.doy).week},Dn.firstDayOfYear=function(){return this._week.doy},Dn.firstDayOfWeek=function(){return this._week.dow},Dn.weekdays=function(t,e){var n=r(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?$t(n,this._week.dow):t?n[t.day()]:n},Dn.weekdaysMin=function(t){return!0===t?$t(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},Dn.weekdaysShort=function(t){return!0===t?$t(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},Dn.weekdaysParse=function(t,e,n){var i,a,r;if(this._weekdaysParseExact)return Qt.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=f([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].test(t))return i;if(!n&&this._weekdaysParse[i].test(t))return i}},Dn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=te),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Dn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ee),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Dn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=ne),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Dn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},Dn.meridiem=function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},ge("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===k(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),a.lang=D("moment.lang is deprecated. Use moment.locale instead.",ge),a.langData=D("moment.langData is deprecated. Use moment.localeData instead.",pe);var On=Math.abs;function An(t,e,n,i){var a=Xe(e,n);return t._milliseconds+=i*a._milliseconds,t._days+=i*a._days,t._months+=i*a._months,t._bubble()}function Fn(t){return t<0?Math.floor(t):Math.ceil(t)}function In(t){return 4800*t/146097}function Ln(t){return 146097*t/4800}function Rn(t){return function(){return this.as(t)}}var Nn=Rn("ms"),Wn=Rn("s"),Yn=Rn("m"),zn=Rn("h"),En=Rn("d"),Vn=Rn("w"),Hn=Rn("M"),Bn=Rn("Q"),jn=Rn("y");function Un(t){return function(){return this.isValid()?this._data[t]:NaN}}var Gn=Un("milliseconds"),qn=Un("seconds"),Zn=Un("minutes"),$n=Un("hours"),Xn=Un("days"),Kn=Un("months"),Jn=Un("years"),Qn=Math.round,ti={ss:44,s:45,m:45,h:22,d:26,M:11};function ei(t,e,n,i,a){return a.relativeTime(e||1,!!n,t,i)}var ni=Math.abs;function ii(t){return(t>0)-(t<0)||+t}function ai(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=ni(this._milliseconds)/1e3,i=ni(this._days),a=ni(this._months);t=w(n/60),e=w(t/60),n%=60,t%=60;var r=w(a/12),o=a%=12,s=i,l=e,u=t,d=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var c=h<0?"-":"",f=ii(this._months)!==ii(h)?"-":"",g=ii(this._days)!==ii(h)?"-":"",m=ii(this._milliseconds)!==ii(h)?"-":"";return c+"P"+(r?f+r+"Y":"")+(o?f+o+"M":"")+(s?g+s+"D":"")+(l||u||d?"T":"")+(l?m+l+"H":"")+(u?m+u+"M":"")+(d?m+d+"S":"")}var ri=ze.prototype;return ri.isValid=function(){return this._isValid},ri.abs=function(){var t=this._data;return this._milliseconds=On(this._milliseconds),this._days=On(this._days),this._months=On(this._months),t.milliseconds=On(t.milliseconds),t.seconds=On(t.seconds),t.minutes=On(t.minutes),t.hours=On(t.hours),t.months=On(t.months),t.years=On(t.years),this},ri.add=function(t,e){return An(this,t,e,1)},ri.subtract=function(t,e){return An(this,t,e,-1)},ri.as=function(t){if(!this.isValid())return NaN;var e,n,i=this._milliseconds;if("month"===(t=R(t))||"quarter"===t||"year"===t)switch(e=this._days+i/864e5,n=this._months+In(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(Ln(this._months)),t){case"week":return e/7+i/6048e5;case"day":return e+i/864e5;case"hour":return 24*e+i/36e5;case"minute":return 1440*e+i/6e4;case"second":return 86400*e+i/1e3;case"millisecond":return Math.floor(864e5*e)+i;default:throw new Error("Unknown unit "+t)}},ri.asMilliseconds=Nn,ri.asSeconds=Wn,ri.asMinutes=Yn,ri.asHours=zn,ri.asDays=En,ri.asWeeks=Vn,ri.asMonths=Hn,ri.asQuarters=Bn,ri.asYears=jn,ri.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},ri._bubble=function(){var t,e,n,i,a,r=this._milliseconds,o=this._days,s=this._months,l=this._data;return r>=0&&o>=0&&s>=0||r<=0&&o<=0&&s<=0||(r+=864e5*Fn(Ln(s)+o),o=0,s=0),l.milliseconds=r%1e3,t=w(r/1e3),l.seconds=t%60,e=w(t/60),l.minutes=e%60,n=w(e/60),l.hours=n%24,o+=w(n/24),a=w(In(o)),s+=a,o-=Fn(Ln(a)),i=w(s/12),s%=12,l.days=o,l.months=s,l.years=i,this},ri.clone=function(){return Xe(this)},ri.get=function(t){return t=R(t),this.isValid()?this[t+"s"]():NaN},ri.milliseconds=Gn,ri.seconds=qn,ri.minutes=Zn,ri.hours=$n,ri.days=Xn,ri.weeks=function(){return w(this.days()/7)},ri.months=Kn,ri.years=Jn,ri.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e=this.localeData(),n=function(t,e,n){var i=Xe(t).abs(),a=Qn(i.as("s")),r=Qn(i.as("m")),o=Qn(i.as("h")),s=Qn(i.as("d")),l=Qn(i.as("M")),u=Qn(i.as("y")),d=a<=ti.ss&&["s",a]||a<ti.s&&["ss",a]||r<=1&&["m"]||r<ti.m&&["mm",r]||o<=1&&["h"]||o<ti.h&&["hh",o]||s<=1&&["d"]||s<ti.d&&["dd",s]||l<=1&&["M"]||l<ti.M&&["MM",l]||u<=1&&["y"]||["yy",u];return d[2]=e,d[3]=+t>0,d[4]=n,ei.apply(null,d)}(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)},ri.toISOString=ai,ri.toString=ai,ri.toJSON=ai,ri.locale=rn,ri.localeData=sn,ri.toIsoString=D("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ai),ri.lang=on,j("X",0,0,"unix"),j("x",0,0,"valueOf"),dt("x",rt),dt("X",/[+-]?\d+(\.\d{1,3})?/),gt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),gt("x",(function(t,e,n){n._d=new Date(k(t))})),a.version="2.24.0",n=Le,a.fn=Mn,a.min=function(){return We("isBefore",[].slice.call(arguments,0))},a.max=function(){return We("isAfter",[].slice.call(arguments,0))},a.now=function(){return Date.now?Date.now():+new Date},a.utc=f,a.unix=function(t){return Le(1e3*t)},a.months=function(t,e){return Pn(t,e,"months")},a.isDate=u,a.locale=ge,a.invalid=p,a.duration=Xe,a.isMoment=_,a.weekdays=function(t,e,n){return Tn(t,e,n,"weekdays")},a.parseZone=function(){return Le.apply(null,arguments).parseZone()},a.localeData=pe,a.isDuration=Ee,a.monthsShort=function(t,e){return Pn(t,e,"monthsShort")},a.weekdaysMin=function(t,e,n){return Tn(t,e,n,"weekdaysMin")},a.defineLocale=me,a.updateLocale=function(t,e){if(null!=e){var n,i,a=ue;null!=(i=fe(t))&&(a=i._config),e=A(a,e),(n=new F(e)).parentLocale=de[t],de[t]=n,ge(t)}else null!=de[t]&&(null!=de[t].parentLocale?de[t]=de[t].parentLocale:null!=de[t]&&delete de[t]);return de[t]},a.locales=function(){return C(de)},a.weekdaysShort=function(t,e,n){return Tn(t,e,n,"weekdaysShort")},a.normalizeUnits=R,a.relativeTimeRounding=function(t){return void 0===t?Qn:"function"==typeof t&&(Qn=t,!0)},a.relativeTimeThreshold=function(t,e){return void 0!==ti[t]&&(void 0===e?ti[t]:(ti[t]=e,"s"===t&&(ti.ss=e-1),!0))},a.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},a.prototype=Mn,a.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},a}()})),mi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};rn._date.override("function"==typeof gi?{_id:"moment",formats:function(){return mi},parse:function(t,e){return"string"==typeof t&&"string"==typeof e?t=gi(t,e):t instanceof gi||(t=gi(t)),t.isValid()?t.valueOf():null},format:function(t,e){return gi(t).format(e)},add:function(t,e,n){return gi(t).add(e,n).valueOf()},diff:function(t,e,n){return gi(t).diff(gi(e),n)},startOf:function(t,e,n){return t=gi(t),"isoWeek"===e?t.isoWeekday(n).valueOf():t.startOf(e).valueOf()},endOf:function(t,e){return gi(t).endOf(e).valueOf()},_create:function(t){return gi(t)}}:{}),W._set("global",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return H.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function vi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function bi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(H.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function yi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function xi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),pi[n](t))}function _i(t){return t&&!t.skip}function wi(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)H.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)H.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,m=i.spanGaps,p=[],v=[],b=0,y=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=_i(u),c=_i(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=p.push(u),y=v.push(d)):b&&y&&(m?(h&&p.push(u),c&&v.push(d)):(wi(t,p,v,b,y),b=y=0,p=[],v=[]));wi(t,p,v,b,y),t.closePath(),t.fillStyle=a,t.fill()}var Mi={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof wt.Line&&(r={visible:t.isDatasetVisible(i),fill:vi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=yi(l,i,s),r.boundary=bi(r),r.mapper=xi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||W.global.defaultColor,o&&s&&r.length&&(H.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,s,i._loop),H.canvas.unclipArea(u)))}},Si=H.rtl.getRtlAdapter,Di=H.noop,Ci=H.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}W._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Ti=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Di,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Di,beforeSetDimensions:Di,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Di,beforeBuildLabels:Di,buildLabels:function(){var t=this,e=t.options.labels||{},n=H.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Di,beforeFit:Di,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=H.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],m=n.padding,p=0,v=0;H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(m+=p+n.padding,f.push(p),g.push(v),p=0,v=0),p=Math.max(p,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),m+=p,f.push(p),g.push(v),l.width+=m}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Di,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=W.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=Si(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=H.options._parseFont(n),m=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var p=Pi(n,m),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},y=t.isHorizontal();d=y?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},H.rtl.overrideTextDirection(t.ctx,e.textDirection);var x=m+n.padding;H.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=p+m/2+f,_=d.x,w=d.y;h.setWidth(t.minSize.width),y?i>0&&_+g+n.padding>t.left+t.minSize.width&&(w=d.y+=x,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&w+x>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,w=d.y=t.top+b(o,s[d.line]));var k=h.x(_);!function(t,e,i){if(!(isNaN(p)||p<=0)){c.save();var o=Ci(i.lineWidth,r.borderWidth);if(c.fillStyle=Ci(i.fillStyle,a),c.lineCap=Ci(i.lineCap,r.borderCapStyle),c.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Ci(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=p*Math.SQRT2/2,l=h.xPlus(t,p/2),u=e+m/2;H.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,p),e,p,m),0!==o&&c.strokeRect(h.leftForLtr(t,p),e,p,m);c.restore()}}(k,w,e),v[i].left=h.leftForLtr(k,v[i].width),v[i].top=w,function(t,e,n,i){var a=m/2,r=h.xPlus(t,p+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(k,w,e,f),y?d.x+=g+n.padding:d.y+=x})),H.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Oi(t,e){var n=new Ti({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.legend=n}var Ai={id:"legend",_element:Ti,beforeInit:function(t){var e=t.options.legend;e&&Oi(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(H.mergeIf(e,W.global.legend),n?(me.configure(t,n,e),n.options=e):Oi(t,e)):n&&(me.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Fi=H.noop;W._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Fi,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Fi,beforeSetDimensions:Fi,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Fi,beforeBuildLabels:Fi,buildLabels:Fi,afterBuildLabels:Fi,beforeFit:Fi,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(H.isArray(n.text)?n.text.length:1)*H.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Fi,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=H.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=H.valueOrDefault(n.fontColor,W.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(H.isArray(g))for(var m=0,p=0;p<g.length;++p)e.fillText(g[p],0,m,i),m+=s;else e.fillText(g,0,0,i);e.restore()}}});function Li(t,e){var n=new Ii({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.titleBlock=n}var Ri={},Ni=Mi,Wi=Ai,Yi={id:"title",_element:Ii,beforeInit:function(t){var e=t.options.title;e&&Li(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(H.mergeIf(e,W.global.title),n?(me.configure(t,n,e),n.options=e):Li(t,e)):n&&(me.removeBox(t,n),delete t.titleBlock)}};for(var zi in Ri.filler=Ni,Ri.legend=Wi,Ri.title=Yi,en.helpers=H,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=H._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}H.where=function(t,e){if(H.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return H.each(t,(function(t){e(t)&&n.push(t)})),n},H.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},H.findNextWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},H.findPreviousWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},H.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},H.almostEquals=function(t,e,n){return Math.abs(t-e)<n},H.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},H.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},H.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},H.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},H.toRadians=function(t){return t*(Math.PI/180)},H.toDegrees=function(t){return t*(180/Math.PI)},H._decimalPlaces=function(t){if(H.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},H.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},H.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},H.aliasPixel=function(t){return t%2==0?0:.5},H._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},H.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},H.EPSILON=Number.EPSILON||1e-14,H.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(H.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},H.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},H.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},H.niceNum=function(t,e){var n=Math.floor(H.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},H.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},H.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(H.getStyle(r,"padding-left")),u=parseFloat(H.getStyle(r,"padding-top")),d=parseFloat(H.getStyle(r,"padding-right")),h=parseFloat(H.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},H.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},H.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},H._calculatePadding=function(t,e,n){return(e=H.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},H._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},H.getMaximumWidth=function(t){var e=H._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-H._calculatePadding(e,"padding-left",n)-H._calculatePadding(e,"padding-right",n),a=H.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},H.getMaximumHeight=function(t){var e=H._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-H._calculatePadding(e,"padding-top",n)-H._calculatePadding(e,"padding-bottom",n),a=H.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},H.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},H.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},H.fontString=function(t,e,n){return e+" "+t+"px "+n},H.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==H.isArray(u))h=H.measureText(t,a,r,h,u);else if(H.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||H.isArray(d)||(h=H.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},H.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},H.numberOfLabelLines=function(t){var e=1;return H.each(t,(function(t){H.isArray(t)&&t.length>e&&(e=t.length)})),e},H.color=k?function(t){return t instanceof CanvasGradient&&(t=W.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},H.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:H.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=K,en.animationService=J,en.controllers=Jt,en.DatasetController=it,en.defaults=W,en.Element=$,en.elements=wt,en.Interaction=re,en.layouts=me,en.platform=Ie,en.plugins=Le,en.Scale=xn,en.scaleService=Re,en.Ticks=on,en.Tooltip=Ge,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Ri)Ri.hasOwnProperty(zi)&&en.plugins.register(Ri[zi]);en.platform.initialize();var Ei=en;return"undefined"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Ri.legend._element,en.Title=Ri.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Dn,en.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei})); diff --git a/lib/web/chartjs/Chart.css b/lib/web/chartjs/Chart.css new file mode 100644 index 0000000000000..5e749593eeb65 --- /dev/null +++ b/lib/web/chartjs/Chart.css @@ -0,0 +1,47 @@ +/* + * DOM element rendering detection + * https://davidwalsh.name/detect-node-insertion + */ +@keyframes chartjs-render-animation { + from { opacity: 0.99; } + to { opacity: 1; } +} + +.chartjs-render-monitor { + animation: chartjs-render-animation 0.001s; +} + +/* + * DOM element resizing detection + * https://github.com/marcj/css-element-queries + */ +.chartjs-size-monitor, +.chartjs-size-monitor-expand, +.chartjs-size-monitor-shrink { + position: absolute; + direction: ltr; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; + pointer-events: none; + visibility: hidden; + z-index: -1; +} + +.chartjs-size-monitor-expand > div { + position: absolute; + width: 1000000px; + height: 1000000px; + left: 0; + top: 0; +} + +.chartjs-size-monitor-shrink > div { + position: absolute; + width: 200%; + height: 200%; + left: 0; + top: 0; +} diff --git a/lib/web/chartjs/Chart.js b/lib/web/chartjs/Chart.js new file mode 100644 index 0000000000000..e8d937cf82c54 --- /dev/null +++ b/lib/web/chartjs/Chart.js @@ -0,0 +1,16151 @@ +/*! + * Chart.js v2.9.3 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) : +typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) : +(global = global || self, global.Chart = factory(global.moment)); +}(this, (function (moment) { 'use strict'; + +moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment; + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +function getCjsExportFromNamespace (n) { + return n && n['default'] || n; +} + +var colorName = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +var conversions = createCommonjsModule(function (module) { +/* MIT license */ + + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +var reverseKeywords = {}; +for (var key in colorName) { + if (colorName.hasOwnProperty(key)) { + reverseKeywords[colorName[key]] = key; + } +} + +var convert = module.exports = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; + +// hide .channels and .labels properties +for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } + + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } + + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); + } +} + +convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; + + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; + + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); + + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } + + return [ + h * 360, + s * 100, + v * 100 + ]; +}; + +convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +/** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ +function comparativeDistance(x, y) { + return ( + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) + ); +} + +convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } + + var currentClosestDistance = Infinity; + var currentClosestKeyword; + + for (var keyword in colorName) { + if (colorName.hasOwnProperty(keyword)) { + var value = colorName[keyword]; + + // Compute comparative distance + var distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } + + return currentClosestKeyword; +}; + +convert.keyword.rgb = function (keyword) { + return colorName[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; + + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + n = wh + f * (v - wh); // linear interpolation + + var r; + var g; + var b; + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; + + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; + + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b * 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + var color = args % 10; + + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + var colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } + + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + + return [r, g, b]; +}; + +convert.rgb.hcg = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma + 4; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; + + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } + + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; + + var c = s * v; + var f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; + + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; + +convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var v = c + g * (1.0 - c); + var f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; +}; + +convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; +}); +var conversions_1 = conversions.rgb; +var conversions_2 = conversions.hsl; +var conversions_3 = conversions.hsv; +var conversions_4 = conversions.hwb; +var conversions_5 = conversions.cmyk; +var conversions_6 = conversions.xyz; +var conversions_7 = conversions.lab; +var conversions_8 = conversions.lch; +var conversions_9 = conversions.hex; +var conversions_10 = conversions.keyword; +var conversions_11 = conversions.ansi16; +var conversions_12 = conversions.ansi256; +var conversions_13 = conversions.hcg; +var conversions_14 = conversions.apple; +var conversions_15 = conversions.gray; + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + var graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + var models = Object.keys(conversions); + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +var route = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + +var convert = {}; + +var models = Object.keys(conversions); + +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + return fn(args); + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + var result = fn(args); + + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(function (fromModel) { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + var routes = route(fromModel); + var routeModels = Object.keys(routes); + + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +var colorConvert = convert; + +var colorName$1 = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +/* MIT license */ + + +var colorString = { + getRgba: getRgba, + getHsla: getHsla, + getRgb: getRgb, + getHsl: getHsl, + getHwb: getHwb, + getAlpha: getAlpha, + + hexString: hexString, + rgbString: rgbString, + rgbaString: rgbaString, + percentString: percentString, + percentaString: percentaString, + hslString: hslString, + hslaString: hslaString, + hwbString: hwbString, + keyword: keyword +}; + +function getRgba(string) { + if (!string) { + return; + } + var abbr = /^#([a-fA-F0-9]{3,4})$/i, + hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, + rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + keyword = /(\w+)/; + + var rgb = [0, 0, 0], + a = 1, + match = string.match(abbr), + hexAlpha = ""; + if (match) { + match = match[1]; + hexAlpha = match[3]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i] + match[i], 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(hex)) { + hexAlpha = match[2]; + match = match[1]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(rgba)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i + 1]); + } + a = parseFloat(match[4]); + } + else if (match = string.match(per)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); + } + a = parseFloat(match[4]); + } + else if (match = string.match(keyword)) { + if (match[1] == "transparent") { + return [0, 0, 0, 0]; + } + rgb = colorName$1[match[1]]; + if (!rgb) { + return; + } + } + + for (var i = 0; i < rgb.length; i++) { + rgb[i] = scale(rgb[i], 0, 255); + } + if (!a && a != 0) { + a = 1; + } + else { + a = scale(a, 0, 1); + } + rgb[3] = a; + return rgb; +} + +function getHsla(string) { + if (!string) { + return; + } + var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hsl); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + s = scale(parseFloat(match[2]), 0, 100), + l = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, s, l, a]; + } +} + +function getHwb(string) { + if (!string) { + return; + } + var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hwb); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + w = scale(parseFloat(match[2]), 0, 100), + b = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, w, b, a]; + } +} + +function getRgb(string) { + var rgba = getRgba(string); + return rgba && rgba.slice(0, 3); +} + +function getHsl(string) { + var hsla = getHsla(string); + return hsla && hsla.slice(0, 3); +} + +function getAlpha(string) { + var vals = getRgba(string); + if (vals) { + return vals[3]; + } + else if (vals = getHsla(string)) { + return vals[3]; + } + else if (vals = getHwb(string)) { + return vals[3]; + } +} + +// generators +function hexString(rgba, a) { + var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; + return "#" + hexDouble(rgba[0]) + + hexDouble(rgba[1]) + + hexDouble(rgba[2]) + + ( + (a >= 0 && a < 1) + ? hexDouble(Math.round(a * 255)) + : "" + ); +} + +function rgbString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return rgbaString(rgba, alpha); + } + return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; +} + +function rgbaString(rgba, alpha) { + if (alpha === undefined) { + alpha = (rgba[3] !== undefined ? rgba[3] : 1); + } + return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + + ", " + alpha + ")"; +} + +function percentString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return percentaString(rgba, alpha); + } + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + + return "rgb(" + r + "%, " + g + "%, " + b + "%)"; +} + +function percentaString(rgba, alpha) { + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; +} + +function hslString(hsla, alpha) { + if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { + return hslaString(hsla, alpha); + } + return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; +} + +function hslaString(hsla, alpha) { + if (alpha === undefined) { + alpha = (hsla[3] !== undefined ? hsla[3] : 1); + } + return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + + alpha + ")"; +} + +// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax +// (hwb have alpha optional & 1 is default value) +function hwbString(hwb, alpha) { + if (alpha === undefined) { + alpha = (hwb[3] !== undefined ? hwb[3] : 1); + } + return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" + + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; +} + +function keyword(rgb) { + return reverseNames[rgb.slice(0, 3)]; +} + +// helpers +function scale(num, min, max) { + return Math.min(Math.max(min, num), max); +} + +function hexDouble(num) { + var str = num.toString(16).toUpperCase(); + return (str.length < 2) ? "0" + str : str; +} + + +//create a list of reverse color names +var reverseNames = {}; +for (var name in colorName$1) { + reverseNames[colorName$1[name]] = name; +} + +/* MIT license */ + + + +var Color = function (obj) { + if (obj instanceof Color) { + return obj; + } + if (!(this instanceof Color)) { + return new Color(obj); + } + + this.valid = false; + this.values = { + rgb: [0, 0, 0], + hsl: [0, 0, 0], + hsv: [0, 0, 0], + hwb: [0, 0, 0], + cmyk: [0, 0, 0, 0], + alpha: 1 + }; + + // parse Color() argument + var vals; + if (typeof obj === 'string') { + vals = colorString.getRgba(obj); + if (vals) { + this.setValues('rgb', vals); + } else if (vals = colorString.getHsla(obj)) { + this.setValues('hsl', vals); + } else if (vals = colorString.getHwb(obj)) { + this.setValues('hwb', vals); + } + } else if (typeof obj === 'object') { + vals = obj; + if (vals.r !== undefined || vals.red !== undefined) { + this.setValues('rgb', vals); + } else if (vals.l !== undefined || vals.lightness !== undefined) { + this.setValues('hsl', vals); + } else if (vals.v !== undefined || vals.value !== undefined) { + this.setValues('hsv', vals); + } else if (vals.w !== undefined || vals.whiteness !== undefined) { + this.setValues('hwb', vals); + } else if (vals.c !== undefined || vals.cyan !== undefined) { + this.setValues('cmyk', vals); + } + } +}; + +Color.prototype = { + isValid: function () { + return this.valid; + }, + rgb: function () { + return this.setSpace('rgb', arguments); + }, + hsl: function () { + return this.setSpace('hsl', arguments); + }, + hsv: function () { + return this.setSpace('hsv', arguments); + }, + hwb: function () { + return this.setSpace('hwb', arguments); + }, + cmyk: function () { + return this.setSpace('cmyk', arguments); + }, + + rgbArray: function () { + return this.values.rgb; + }, + hslArray: function () { + return this.values.hsl; + }, + hsvArray: function () { + return this.values.hsv; + }, + hwbArray: function () { + var values = this.values; + if (values.alpha !== 1) { + return values.hwb.concat([values.alpha]); + } + return values.hwb; + }, + cmykArray: function () { + return this.values.cmyk; + }, + rgbaArray: function () { + var values = this.values; + return values.rgb.concat([values.alpha]); + }, + hslaArray: function () { + var values = this.values; + return values.hsl.concat([values.alpha]); + }, + alpha: function (val) { + if (val === undefined) { + return this.values.alpha; + } + this.setValues('alpha', val); + return this; + }, + + red: function (val) { + return this.setChannel('rgb', 0, val); + }, + green: function (val) { + return this.setChannel('rgb', 1, val); + }, + blue: function (val) { + return this.setChannel('rgb', 2, val); + }, + hue: function (val) { + if (val) { + val %= 360; + val = val < 0 ? 360 + val : val; + } + return this.setChannel('hsl', 0, val); + }, + saturation: function (val) { + return this.setChannel('hsl', 1, val); + }, + lightness: function (val) { + return this.setChannel('hsl', 2, val); + }, + saturationv: function (val) { + return this.setChannel('hsv', 1, val); + }, + whiteness: function (val) { + return this.setChannel('hwb', 1, val); + }, + blackness: function (val) { + return this.setChannel('hwb', 2, val); + }, + value: function (val) { + return this.setChannel('hsv', 2, val); + }, + cyan: function (val) { + return this.setChannel('cmyk', 0, val); + }, + magenta: function (val) { + return this.setChannel('cmyk', 1, val); + }, + yellow: function (val) { + return this.setChannel('cmyk', 2, val); + }, + black: function (val) { + return this.setChannel('cmyk', 3, val); + }, + + hexString: function () { + return colorString.hexString(this.values.rgb); + }, + rgbString: function () { + return colorString.rgbString(this.values.rgb, this.values.alpha); + }, + rgbaString: function () { + return colorString.rgbaString(this.values.rgb, this.values.alpha); + }, + percentString: function () { + return colorString.percentString(this.values.rgb, this.values.alpha); + }, + hslString: function () { + return colorString.hslString(this.values.hsl, this.values.alpha); + }, + hslaString: function () { + return colorString.hslaString(this.values.hsl, this.values.alpha); + }, + hwbString: function () { + return colorString.hwbString(this.values.hwb, this.values.alpha); + }, + keyword: function () { + return colorString.keyword(this.values.rgb, this.values.alpha); + }, + + rgbNumber: function () { + var rgb = this.values.rgb; + return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; + }, + + luminosity: function () { + // http://www.w3.org/TR/WCAG20/#relativeluminancedef + var rgb = this.values.rgb; + var lum = []; + for (var i = 0; i < rgb.length; i++) { + var chan = rgb[i] / 255; + lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); + } + return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; + }, + + contrast: function (color2) { + // http://www.w3.org/TR/WCAG20/#contrast-ratiodef + var lum1 = this.luminosity(); + var lum2 = color2.luminosity(); + if (lum1 > lum2) { + return (lum1 + 0.05) / (lum2 + 0.05); + } + return (lum2 + 0.05) / (lum1 + 0.05); + }, + + level: function (color2) { + var contrastRatio = this.contrast(color2); + if (contrastRatio >= 7.1) { + return 'AAA'; + } + + return (contrastRatio >= 4.5) ? 'AA' : ''; + }, + + dark: function () { + // YIQ equation from http://24ways.org/2010/calculating-color-contrast + var rgb = this.values.rgb; + var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; + return yiq < 128; + }, + + light: function () { + return !this.dark(); + }, + + negate: function () { + var rgb = []; + for (var i = 0; i < 3; i++) { + rgb[i] = 255 - this.values.rgb[i]; + } + this.setValues('rgb', rgb); + return this; + }, + + lighten: function (ratio) { + var hsl = this.values.hsl; + hsl[2] += hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + darken: function (ratio) { + var hsl = this.values.hsl; + hsl[2] -= hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + saturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] += hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + desaturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] -= hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + whiten: function (ratio) { + var hwb = this.values.hwb; + hwb[1] += hwb[1] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + blacken: function (ratio) { + var hwb = this.values.hwb; + hwb[2] += hwb[2] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + greyscale: function () { + var rgb = this.values.rgb; + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; + this.setValues('rgb', [val, val, val]); + return this; + }, + + clearer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha - (alpha * ratio)); + return this; + }, + + opaquer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha + (alpha * ratio)); + return this; + }, + + rotate: function (degrees) { + var hsl = this.values.hsl; + var hue = (hsl[0] + degrees) % 360; + hsl[0] = hue < 0 ? 360 + hue : hue; + this.setValues('hsl', hsl); + return this; + }, + + /** + * Ported from sass implementation in C + * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 + */ + mix: function (mixinColor, weight) { + var color1 = this; + var color2 = mixinColor; + var p = weight === undefined ? 0.5 : weight; + + var w = 2 * p - 1; + var a = color1.alpha() - color2.alpha(); + + var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + return this + .rgb( + w1 * color1.red() + w2 * color2.red(), + w1 * color1.green() + w2 * color2.green(), + w1 * color1.blue() + w2 * color2.blue() + ) + .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); + }, + + toJSON: function () { + return this.rgb(); + }, + + clone: function () { + // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, + // making the final build way to big to embed in Chart.js. So let's do it manually, + // assuming that values to clone are 1 dimension arrays containing only numbers, + // except 'alpha' which is a number. + var result = new Color(); + var source = this.values; + var target = result.values; + var value, type; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + value = source[prop]; + type = ({}).toString.call(value); + if (type === '[object Array]') { + target[prop] = value.slice(0); + } else if (type === '[object Number]') { + target[prop] = value; + } else { + console.error('unexpected color value:', value); + } + } + } + + return result; + } +}; + +Color.prototype.spaces = { + rgb: ['red', 'green', 'blue'], + hsl: ['hue', 'saturation', 'lightness'], + hsv: ['hue', 'saturation', 'value'], + hwb: ['hue', 'whiteness', 'blackness'], + cmyk: ['cyan', 'magenta', 'yellow', 'black'] +}; + +Color.prototype.maxes = { + rgb: [255, 255, 255], + hsl: [360, 100, 100], + hsv: [360, 100, 100], + hwb: [360, 100, 100], + cmyk: [100, 100, 100, 100] +}; + +Color.prototype.getValues = function (space) { + var values = this.values; + var vals = {}; + + for (var i = 0; i < space.length; i++) { + vals[space.charAt(i)] = values[space][i]; + } + + if (values.alpha !== 1) { + vals.a = values.alpha; + } + + // {r: 255, g: 255, b: 255, a: 0.4} + return vals; +}; + +Color.prototype.setValues = function (space, vals) { + var values = this.values; + var spaces = this.spaces; + var maxes = this.maxes; + var alpha = 1; + var i; + + this.valid = true; + + if (space === 'alpha') { + alpha = vals; + } else if (vals.length) { + // [10, 10, 10] + values[space] = vals.slice(0, space.length); + alpha = vals[space.length]; + } else if (vals[space.charAt(0)] !== undefined) { + // {r: 10, g: 10, b: 10} + for (i = 0; i < space.length; i++) { + values[space][i] = vals[space.charAt(i)]; + } + + alpha = vals.a; + } else if (vals[spaces[space][0]] !== undefined) { + // {red: 10, green: 10, blue: 10} + var chans = spaces[space]; + + for (i = 0; i < space.length; i++) { + values[space][i] = vals[chans[i]]; + } + + alpha = vals.alpha; + } + + values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); + + if (space === 'alpha') { + return false; + } + + var capped; + + // cap values of the space prior converting all values + for (i = 0; i < space.length; i++) { + capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); + values[space][i] = Math.round(capped); + } + + // convert to all the other color spaces + for (var sname in spaces) { + if (sname !== space) { + values[sname] = colorConvert[space][sname](values[space]); + } + } + + return true; +}; + +Color.prototype.setSpace = function (space, args) { + var vals = args[0]; + + if (vals === undefined) { + // color.rgb() + return this.getValues(space); + } + + // color.rgb(10, 10, 10) + if (typeof vals === 'number') { + vals = Array.prototype.slice.call(args); + } + + this.setValues(space, vals); + return this; +}; + +Color.prototype.setChannel = function (space, index, val) { + var svalues = this.values[space]; + if (val === undefined) { + // color.red() + return svalues[index]; + } else if (val === svalues[index]) { + // color.red(color.red()) + return this; + } + + // color.red(100) + svalues[index] = val; + this.setValues(space, svalues); + + return this; +}; + +if (typeof window !== 'undefined') { + window.Color = Color; +} + +var chartjsColor = Color; + +/** + * @namespace Chart.helpers + */ +var helpers = { + /** + * An empty function that can be used, for example, for optional callback. + */ + noop: function() {}, + + /** + * Returns a unique id, sequentially generated from a global variable. + * @returns {number} + * @function + */ + uid: (function() { + var id = 0; + return function() { + return id++; + }; + }()), + + /** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isNullOrUndef: function(value) { + return value === null || typeof value === 'undefined'; + }, + + /** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @function + */ + isArray: function(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + var type = Object.prototype.toString.call(value); + if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { + return true; + } + return false; + }, + + /** + * Returns true if `value` is an object (excluding null), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isObject: function(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; + }, + + /** + * Returns true if `value` is a finite number, else returns false + * @param {*} value - The value to test. + * @returns {boolean} + */ + isFinite: function(value) { + return (typeof value === 'number' || value instanceof Number) && isFinite(value); + }, + + /** + * Returns `value` if defined, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is undefined. + * @returns {*} + */ + valueOrDefault: function(value, defaultValue) { + return typeof value === 'undefined' ? defaultValue : value; + }, + + /** + * Returns value at the given `index` in array if defined, else returns `defaultValue`. + * @param {Array} value - The array to lookup for value at `index`. + * @param {number} index - The index in `value` to lookup for value. + * @param {*} defaultValue - The value to return if `value[index]` is undefined. + * @returns {*} + */ + valueAtIndexOrDefault: function(value, index, defaultValue) { + return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); + }, + + /** + * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the + * value returned by `fn`. If `fn` is not a function, this method returns undefined. + * @param {function} fn - The function to call. + * @param {Array|undefined|null} args - The arguments with which `fn` should be called. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @returns {*} + */ + callback: function(fn, args, thisArg) { + if (fn && typeof fn.call === 'function') { + return fn.apply(thisArg, args); + } + }, + + /** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param {object|Array} loopable - The object or array to be iterated. + * @param {function} fn - The function to call for each item. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @param {boolean} [reverse] - If true, iterates backward on the loopable. + */ + each: function(loopable, fn, thisArg, reverse) { + var i, len, keys; + if (helpers.isArray(loopable)) { + len = loopable.length; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[i], i); + } + } + } else if (helpers.isObject(loopable)) { + keys = Object.keys(loopable); + len = keys.length; + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[keys[i]], keys[i]); + } + } + }, + + /** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @see https://stackoverflow.com/a/14853974 + * @param {Array} a0 - The array to compare + * @param {Array} a1 - The array to compare + * @returns {boolean} + */ + arrayEquals: function(a0, a1) { + var i, ilen, v0, v1; + + if (!a0 || !a1 || a0.length !== a1.length) { + return false; + } + + for (i = 0, ilen = a0.length; i < ilen; ++i) { + v0 = a0[i]; + v1 = a1[i]; + + if (v0 instanceof Array && v1 instanceof Array) { + if (!helpers.arrayEquals(v0, v1)) { + return false; + } + } else if (v0 !== v1) { + // NOTE: two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + + return true; + }, + + /** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param {*} source - The value to clone. + * @returns {*} + */ + clone: function(source) { + if (helpers.isArray(source)) { + return source.map(helpers.clone); + } + + if (helpers.isObject(source)) { + var target = {}; + var keys = Object.keys(source); + var klen = keys.length; + var k = 0; + + for (; k < klen; ++k) { + target[keys[k]] = helpers.clone(source[keys[k]]); + } + + return target; + } + + return source; + }, + + /** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. + * @private + */ + _merger: function(key, target, source, options) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.merge(tval, sval, options); + } else { + target[key] = helpers.clone(sval); + } + }, + + /** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ + _mergerIf: function(key, target, source) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.mergeIf(tval, sval); + } else if (!target.hasOwnProperty(key)) { + target[key] = helpers.clone(sval); + } + }, + + /** + * Recursively deep copies `source` properties into `target` with the given `options`. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @param {object} [options] - Merging options: + * @param {function} [options.merger] - The merge method (key, target, source, options) + * @returns {object} The `target` object. + */ + merge: function(target, source, options) { + var sources = helpers.isArray(source) ? source : [source]; + var ilen = sources.length; + var merge, i, keys, klen, k; + + if (!helpers.isObject(target)) { + return target; + } + + options = options || {}; + merge = options.merger || helpers._merger; + + for (i = 0; i < ilen; ++i) { + source = sources[i]; + if (!helpers.isObject(source)) { + continue; + } + + keys = Object.keys(source); + for (k = 0, klen = keys.length; k < klen; ++k) { + merge(keys[k], target, source, options); + } + } + + return target; + }, + + /** + * Recursively deep copies `source` properties into `target` *only* if not defined in target. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @returns {object} The `target` object. + */ + mergeIf: function(target, source) { + return helpers.merge(target, source, {merger: helpers._mergerIf}); + }, + + /** + * Applies the contents of two or more objects together into the first object. + * @param {object} target - The target object in which all objects are merged into. + * @param {object} arg1 - Object containing additional properties to merge in target. + * @param {object} argN - Additional objects containing properties to merge in target. + * @returns {object} The `target` object. + */ + extend: Object.assign || function(target) { + return helpers.merge(target, [].slice.call(arguments, 1), { + merger: function(key, dst, src) { + dst[key] = src[key]; + } + }); + }, + + /** + * Basic javascript inheritance based on the model created in Backbone.js + */ + inherits: function(extensions) { + var me = this; + var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { + return me.apply(this, arguments); + }; + + var Surrogate = function() { + this.constructor = ChartElement; + }; + + Surrogate.prototype = me.prototype; + ChartElement.prototype = new Surrogate(); + ChartElement.extend = helpers.inherits; + + if (extensions) { + helpers.extend(ChartElement.prototype, extensions); + } + + ChartElement.__super__ = me.prototype; + return ChartElement; + }, + + _deprecated: function(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + + '" is deprecated. Please use "' + current + '" instead'); + } + } +}; + +var helpers_core = helpers; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.callback instead. + * @function Chart.helpers.callCallback + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +helpers.callCallback = helpers.callback; + +/** + * Provided for backward compatibility, use Array.prototype.indexOf instead. + * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ + * @function Chart.helpers.indexOf + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.indexOf = function(array, item, fromIndex) { + return Array.prototype.indexOf.call(array, item, fromIndex); +}; + +/** + * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. + * @function Chart.helpers.getValueOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueOrDefault = helpers.valueOrDefault; + +/** + * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. + * @function Chart.helpers.getValueAtIndexOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easingEffects + * @see http://www.robertpenner.com/easing/ + */ +var effects = { + linear: function(t) { + return t; + }, + + easeInQuad: function(t) { + return t * t; + }, + + easeOutQuad: function(t) { + return -t * (t - 2); + }, + + easeInOutQuad: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t; + } + return -0.5 * ((--t) * (t - 2) - 1); + }, + + easeInCubic: function(t) { + return t * t * t; + }, + + easeOutCubic: function(t) { + return (t = t - 1) * t * t + 1; + }, + + easeInOutCubic: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t; + } + return 0.5 * ((t -= 2) * t * t + 2); + }, + + easeInQuart: function(t) { + return t * t * t * t; + }, + + easeOutQuart: function(t) { + return -((t = t - 1) * t * t * t - 1); + }, + + easeInOutQuart: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t; + } + return -0.5 * ((t -= 2) * t * t * t - 2); + }, + + easeInQuint: function(t) { + return t * t * t * t * t; + }, + + easeOutQuint: function(t) { + return (t = t - 1) * t * t * t * t + 1; + }, + + easeInOutQuint: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t * t; + } + return 0.5 * ((t -= 2) * t * t * t * t + 2); + }, + + easeInSine: function(t) { + return -Math.cos(t * (Math.PI / 2)) + 1; + }, + + easeOutSine: function(t) { + return Math.sin(t * (Math.PI / 2)); + }, + + easeInOutSine: function(t) { + return -0.5 * (Math.cos(Math.PI * t) - 1); + }, + + easeInExpo: function(t) { + return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); + }, + + easeOutExpo: function(t) { + return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; + }, + + easeInOutExpo: function(t) { + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if ((t /= 0.5) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + }, + + easeInCirc: function(t) { + if (t >= 1) { + return t; + } + return -(Math.sqrt(1 - t * t) - 1); + }, + + easeOutCirc: function(t) { + return Math.sqrt(1 - (t = t - 1) * t); + }, + + easeInOutCirc: function(t) { + if ((t /= 0.5) < 1) { + return -0.5 * (Math.sqrt(1 - t * t) - 1); + } + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }, + + easeInElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + }, + + easeOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; + }, + + easeInOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if ((t /= 0.5) === 2) { + return 1; + } + if (!p) { + p = 0.45; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + if (t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + easeInBack: function(t) { + var s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + + easeOutBack: function(t) { + var s = 1.70158; + return (t = t - 1) * t * ((s + 1) * t + s) + 1; + }, + + easeInOutBack: function(t) { + var s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }, + + easeInBounce: function(t) { + return 1 - effects.easeOutBounce(1 - t); + }, + + easeOutBounce: function(t) { + if (t < (1 / 2.75)) { + return 7.5625 * t * t; + } + if (t < (2 / 2.75)) { + return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; + } + if (t < (2.5 / 2.75)) { + return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; + } + return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; + }, + + easeInOutBounce: function(t) { + if (t < 0.5) { + return effects.easeInBounce(t * 2) * 0.5; + } + return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; + } +}; + +var helpers_easing = { + effects: effects +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.easing.effects instead. + * @function Chart.helpers.easingEffects + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.easingEffects = effects; + +var PI = Math.PI; +var RAD_PER_DEG = PI / 180; +var DOUBLE_PI = PI * 2; +var HALF_PI = PI / 2; +var QUARTER_PI = PI / 4; +var TWO_THIRDS_PI = PI * 2 / 3; + +/** + * @namespace Chart.helpers.canvas + */ +var exports$1 = { + /** + * Clears the entire canvas associated to the given `chart`. + * @param {Chart} chart - The chart for which to clear the canvas. + */ + clear: function(chart) { + chart.ctx.clearRect(0, 0, chart.width, chart.height); + }, + + /** + * Creates a "path" for a rectangle with rounded corners at position (x, y) with a + * given size (width, height) and the same `radius` for all corners. + * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. + * @param {number} x - The x axis of the coordinate for the rectangle starting point. + * @param {number} y - The y axis of the coordinate for the rectangle starting point. + * @param {number} width - The rectangle's width. + * @param {number} height - The rectangle's height. + * @param {number} radius - The rounded amount (in pixels) for the four corners. + * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? + */ + roundedRect: function(ctx, x, y, width, height, radius) { + if (radius) { + var r = Math.min(radius, height / 2, width / 2); + var left = x + r; + var top = y + r; + var right = x + width - r; + var bottom = y + height - r; + + ctx.moveTo(x, top); + if (left < right && top < bottom) { + ctx.arc(left, top, r, -PI, -HALF_PI); + ctx.arc(right, top, r, -HALF_PI, 0); + ctx.arc(right, bottom, r, 0, HALF_PI); + ctx.arc(left, bottom, r, HALF_PI, PI); + } else if (left < right) { + ctx.moveTo(left, y); + ctx.arc(right, top, r, -HALF_PI, HALF_PI); + ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); + } else if (top < bottom) { + ctx.arc(left, top, r, -PI, 0); + ctx.arc(left, bottom, r, 0, PI); + } else { + ctx.arc(left, top, r, -PI, PI); + } + ctx.closePath(); + ctx.moveTo(x, y); + } else { + ctx.rect(x, y, width, height); + } + }, + + drawPoint: function(ctx, style, radius, x, y, rotation) { + var type, xOffset, yOffset, size, cornerRadius; + var rad = (rotation || 0) * RAD_PER_DEG; + + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + ctx.beginPath(); + + switch (style) { + // Default includes circle + default: + ctx.arc(x, y, radius, 0, DOUBLE_PI); + ctx.closePath(); + break; + case 'triangle': + ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + // NOTE: the rounded rect implementation changed to use `arc` instead of + // `quadraticCurveTo` since it generates better results when rect is + // almost a circle. 0.516 (instead of 0.5) produces results with visually + // closer proportion to the previous impl and it is inscribed in the + // circle with `radius`. For more details, see the following PRs: + // https://github.com/chartjs/Chart.js/issues/5597 + // https://github.com/chartjs/Chart.js/issues/5858 + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + yOffset = Math.sin(rad + QUARTER_PI) * size; + ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + ctx.rect(x - size, y - size, 2 * size, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ + case 'rectRot': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + yOffset, y - xOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.lineTo(x - yOffset, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + /* falls through */ + case 'cross': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'star': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + rad += QUARTER_PI; + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'line': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case 'dash': + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); + break; + } + + ctx.fill(); + ctx.stroke(); + }, + + /** + * Returns true if the point is inside the rectangle + * @param {object} point - The point to test + * @param {object} area - The rectangle + * @returns {boolean} + * @private + */ + _isPointInArea: function(point, area) { + var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. + + return point.x > area.left - epsilon && point.x < area.right + epsilon && + point.y > area.top - epsilon && point.y < area.bottom + epsilon; + }, + + clipArea: function(ctx, area) { + ctx.save(); + ctx.beginPath(); + ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); + ctx.clip(); + }, + + unclipArea: function(ctx) { + ctx.restore(); + }, + + lineTo: function(ctx, previous, target, flip) { + var stepped = target.steppedLine; + if (stepped) { + if (stepped === 'middle') { + var midpoint = (previous.x + target.x) / 2.0; + ctx.lineTo(midpoint, flip ? target.y : previous.y); + ctx.lineTo(midpoint, flip ? previous.y : target.y); + } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { + ctx.lineTo(previous.x, target.y); + } else { + ctx.lineTo(target.x, previous.y); + } + ctx.lineTo(target.x, target.y); + return; + } + + if (!target.tension) { + ctx.lineTo(target.x, target.y); + return; + } + + ctx.bezierCurveTo( + flip ? previous.controlPointPreviousX : previous.controlPointNextX, + flip ? previous.controlPointPreviousY : previous.controlPointNextY, + flip ? target.controlPointNextX : target.controlPointPreviousX, + flip ? target.controlPointNextY : target.controlPointPreviousY, + target.x, + target.y); + } +}; + +var helpers_canvas = exports$1; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. + * @namespace Chart.helpers.clear + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.clear = exports$1.clear; + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. + * @namespace Chart.helpers.drawRoundedRectangle + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.drawRoundedRectangle = function(ctx) { + ctx.beginPath(); + exports$1.roundedRect.apply(exports$1, arguments); +}; + +var defaults = { + /** + * @private + */ + _set: function(scope, values) { + return helpers_core.merge(this[scope] || (this[scope] = {}), values); + } +}; + +// TODO(v3): remove 'global' from namespace. all default are global and +// there's inconsistency around which options are under 'global' +defaults._set('global', { + defaultColor: 'rgba(0,0,0,0.1)', + defaultFontColor: '#666', + defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + defaultFontSize: 12, + defaultFontStyle: 'normal', + defaultLineHeight: 1.2, + showLines: true +}); + +var core_defaults = defaults; + +var valueOrDefault = helpers_core.valueOrDefault; + +/** + * Converts the given font object into a CSS font string. + * @param {object} font - A font object. + * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font + * @private + */ +function toFontString(font) { + if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { + return null; + } + + return (font.style ? font.style + ' ' : '') + + (font.weight ? font.weight + ' ' : '') + + font.size + 'px ' + + font.family; +} + +/** + * @alias Chart.helpers.options + * @namespace + */ +var helpers_options = { + /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param {number} size - The font size (in pixels) used to resolve relative `value`. + * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height + * @since 2.7.0 + */ + toLineHeight: function(value, size) { + var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); + if (!matches || matches[1] === 'normal') { + return size * 1.2; + } + + value = +matches[2]; + + switch (matches[3]) { + case 'px': + return value; + case '%': + value /= 100; + break; + } + + return size * value; + }, + + /** + * Converts the given value into a padding object with pre-computed width/height. + * @param {number|object} value - If a number, set the value to all TRBL component, + * else, if and object, use defined properties and sets undefined ones to 0. + * @returns {object} The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ + toPadding: function(value) { + var t, r, b, l; + + if (helpers_core.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + top: t, + right: r, + bottom: b, + left: l, + height: t + b, + width: l + r + }; + }, + + /** + * Parses font options and returns the font object. + * @param {object} options - A object that contains font options to be parsed. + * @return {object} The font object. + * @todo Support font.* options and renamed to toFont(). + * @private + */ + _parseFont: function(options) { + var globalDefaults = core_defaults.global; + var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); + var font = { + family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), + lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), + size: size, + style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), + weight: null, + string: '' + }; + + font.string = toFontString(font); + return font; + }, + + /** + * Evaluates the given `inputs` sequentially and returns the first defined value. + * @param {Array} inputs - An array of values, falling back to the last value. + * @param {object} [context] - If defined and the current value is a function, the value + * is called with `context` as first argument and the result becomes the new input. + * @param {number} [index] - If defined and the current value is an array, the value + * at `index` become the new input. + * @param {object} [info] - object to return information about resolution in + * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. + * @since 2.7.0 + */ + resolve: function(inputs, context, index, info) { + var cacheable = true; + var i, ilen, value; + + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + cacheable = false; + } + if (index !== undefined && helpers_core.isArray(value)) { + value = value[index]; + cacheable = false; + } + if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } + return value; + } + } + } +}; + +/** + * @alias Chart.helpers.math + * @namespace + */ +var exports$2 = { + /** + * Returns an array of factors sorted from 1 to sqrt(value) + * @private + */ + _factorize: function(value) { + var result = []; + var sqrt = Math.sqrt(value); + var i; + + for (i = 1; i < sqrt; i++) { + if (value % i === 0) { + result.push(i); + result.push(value / i); + } + } + if (sqrt === (sqrt | 0)) { // if value is a square number + result.push(sqrt); + } + + result.sort(function(a, b) { + return a - b; + }).pop(); + return result; + }, + + log10: Math.log10 || function(x) { + var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. + // Check for whole powers of 10, + // which due to floating point rounding error should be corrected. + var powerOf10 = Math.round(exponent); + var isPowerOf10 = x === Math.pow(10, powerOf10); + + return isPowerOf10 ? powerOf10 : exponent; + } +}; + +var helpers_math = exports$2; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.math.log10 instead. + * @namespace Chart.helpers.log10 + * @deprecated since version 2.9.0 + * @todo remove at version 3 + * @private + */ +helpers_core.log10 = exports$2.log10; + +var getRtlAdapter = function(rectX, width) { + return { + x: function(x) { + return rectX + rectX + width - x; + }, + setWidth: function(w) { + width = w; + }, + textAlign: function(align) { + if (align === 'center') { + return align; + } + return align === 'right' ? 'left' : 'right'; + }, + xPlus: function(x, value) { + return x - value; + }, + leftForLtr: function(x, itemWidth) { + return x - itemWidth; + }, + }; +}; + +var getLtrAdapter = function() { + return { + x: function(x) { + return x; + }, + setWidth: function(w) { // eslint-disable-line no-unused-vars + }, + textAlign: function(align) { + return align; + }, + xPlus: function(x, value) { + return x + value; + }, + leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars + return x; + }, + }; +}; + +var getAdapter = function(rtl, rectX, width) { + return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter(); +}; + +var overrideTextDirection = function(ctx, direction) { + var style, original; + if (direction === 'ltr' || direction === 'rtl') { + style = ctx.canvas.style; + original = [ + style.getPropertyValue('direction'), + style.getPropertyPriority('direction'), + ]; + + style.setProperty('direction', direction, 'important'); + ctx.prevTextDirection = original; + } +}; + +var restoreTextDirection = function(ctx) { + var original = ctx.prevTextDirection; + if (original !== undefined) { + delete ctx.prevTextDirection; + ctx.canvas.style.setProperty('direction', original[0], original[1]); + } +}; + +var helpers_rtl = { + getRtlAdapter: getAdapter, + overrideTextDirection: overrideTextDirection, + restoreTextDirection: restoreTextDirection, +}; + +var helpers$1 = helpers_core; +var easing = helpers_easing; +var canvas = helpers_canvas; +var options = helpers_options; +var math = helpers_math; +var rtl = helpers_rtl; +helpers$1.easing = easing; +helpers$1.canvas = canvas; +helpers$1.options = options; +helpers$1.math = math; +helpers$1.rtl = rtl; + +function interpolate(start, view, model, ease) { + var keys = Object.keys(model); + var i, ilen, key, actual, origin, target, type, c0, c1; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + + target = model[key]; + + // if a value is added to the model after pivot() has been called, the view + // doesn't contain it, so let's initialize the view to the target value. + if (!view.hasOwnProperty(key)) { + view[key] = target; + } + + actual = view[key]; + + if (actual === target || key[0] === '_') { + continue; + } + + if (!start.hasOwnProperty(key)) { + start[key] = actual; + } + + origin = start[key]; + + type = typeof target; + + if (type === typeof origin) { + if (type === 'string') { + c0 = chartjsColor(origin); + if (c0.valid) { + c1 = chartjsColor(target); + if (c1.valid) { + view[key] = c1.mix(c0, ease).rgbString(); + continue; + } + } + } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { + view[key] = origin + (target - origin) * ease; + continue; + } + } + + view[key] = target; + } +} + +var Element = function(configuration) { + helpers$1.extend(this, configuration); + this.initialize.apply(this, arguments); +}; + +helpers$1.extend(Element.prototype, { + _type: undefined, + + initialize: function() { + this.hidden = false; + }, + + pivot: function() { + var me = this; + if (!me._view) { + me._view = helpers$1.extend({}, me._model); + } + me._start = {}; + return me; + }, + + transition: function(ease) { + var me = this; + var model = me._model; + var start = me._start; + var view = me._view; + + // No animation -> No Transition + if (!model || ease === 1) { + me._view = helpers$1.extend({}, model); + me._start = null; + return me; + } + + if (!view) { + view = me._view = {}; + } + + if (!start) { + start = me._start = {}; + } + + interpolate(start, view, model, ease); + + return me; + }, + + tooltipPosition: function() { + return { + x: this._model.x, + y: this._model.y + }; + }, + + hasValue: function() { + return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); + } +}); + +Element.extend = helpers$1.inherits; + +var core_element = Element; + +var exports$3 = core_element.extend({ + chart: null, // the animation associated chart instance + currentStep: 0, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null, // user specified callback to fire when the animation finishes +}); + +var core_animation = exports$3; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.Animation instead + * @prop Chart.Animation#animationObject + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$3.prototype, 'animationObject', { + get: function() { + return this; + } +}); + +/** + * Provided for backward compatibility, use Chart.Animation#chart instead + * @prop Chart.Animation#chartInstance + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$3.prototype, 'chartInstance', { + get: function() { + return this.chart; + }, + set: function(value) { + this.chart = value; + } +}); + +core_defaults._set('global', { + animation: { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers$1.noop, + onComplete: helpers$1.noop + } +}); + +var core_animations = { + animations: [], + request: null, + + /** + * @param {Chart} chart - The chart to animate. + * @param {Chart.Animation} animation - The animation that we will animate. + * @param {number} duration - The animation duration in ms. + * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions + */ + addAnimation: function(chart, animation, duration, lazy) { + var animations = this.animations; + var i, ilen; + + animation.chart = chart; + animation.startTime = Date.now(); + animation.duration = duration; + + if (!lazy) { + chart.animating = true; + } + + for (i = 0, ilen = animations.length; i < ilen; ++i) { + if (animations[i].chart === chart) { + animations[i] = animation; + return; + } + } + + animations.push(animation); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (animations.length === 1) { + this.requestAnimationFrame(); + } + }, + + cancelAnimation: function(chart) { + var index = helpers$1.findIndex(this.animations, function(animation) { + return animation.chart === chart; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chart.animating = false; + } + }, + + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers$1.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + + /** + * @private + */ + startDigest: function() { + var me = this; + + me.advance(); + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + }, + + /** + * @private + */ + advance: function() { + var animations = this.animations; + var animation, chart, numSteps, nextStep; + var i = 0; + + // 1 animation per chart, so we are looping charts here + while (i < animations.length) { + animation = animations[i]; + chart = animation.chart; + numSteps = animation.numSteps; + + // Make sure that currentStep starts at 1 + // https://github.com/chartjs/Chart.js/issues/6104 + nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; + animation.currentStep = Math.min(nextStep, numSteps); + + helpers$1.callback(animation.render, [chart, animation], chart); + helpers$1.callback(animation.onAnimationProgress, [animation], chart); + + if (animation.currentStep >= numSteps) { + helpers$1.callback(animation.onAnimationComplete, [animation], chart); + chart.animating = false; + animations.splice(i, 1); + } else { + ++i; + } + } + } +}; + +var resolve = helpers$1.options.resolve; + +var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; + +/** + * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', + * 'unshift') and notify the listener AFTER the array has been altered. Listeners are + * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. + */ +function listenArrayEvents(array, listener) { + if (array._chartjs) { + array._chartjs.listeners.push(listener); + return; + } + + Object.defineProperty(array, '_chartjs', { + configurable: true, + enumerable: false, + value: { + listeners: [listener] + } + }); + + arrayEvents.forEach(function(key) { + var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); + var base = array[key]; + + Object.defineProperty(array, key, { + configurable: true, + enumerable: false, + value: function() { + var args = Array.prototype.slice.call(arguments); + var res = base.apply(this, args); + + helpers$1.each(array._chartjs.listeners, function(object) { + if (typeof object[method] === 'function') { + object[method].apply(object, args); + } + }); + + return res; + } + }); + }); +} + +/** + * Removes the given array event listener and cleanup extra attached properties (such as + * the _chartjs stub and overridden methods) if array doesn't have any more listeners. + */ +function unlistenArrayEvents(array, listener) { + var stub = array._chartjs; + if (!stub) { + return; + } + + var listeners = stub.listeners; + var index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + + if (listeners.length > 0) { + return; + } + + arrayEvents.forEach(function(key) { + delete array[key]; + }); + + delete array._chartjs; +} + +// Base class for all dataset controllers (line, bar, etc) +var DatasetController = function(chart, datasetIndex) { + this.initialize(chart, datasetIndex); +}; + +helpers$1.extend(DatasetController.prototype, { + + /** + * Element type used to generate a meta dataset (e.g. Chart.element.Line). + * @type {Chart.core.element} + */ + datasetElementType: null, + + /** + * Element type used to generate a meta data (e.g. Chart.element.Point). + * @type {Chart.core.element} + */ + dataElementType: null, + + /** + * Dataset element option keys to be resolved in _resolveDatasetElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth' + ], + + /** + * Data element option keys to be resolved in _resolveDataElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'pointStyle' + ], + + initialize: function(chart, datasetIndex) { + var me = this; + me.chart = chart; + me.index = datasetIndex; + me.linkScales(); + me.addElements(); + me._type = me.getMeta().type; + }, + + updateIndex: function(datasetIndex) { + this.index = datasetIndex; + }, + + linkScales: function() { + var me = this; + var meta = me.getMeta(); + var chart = me.chart; + var scales = chart.scales; + var dataset = me.getDataset(); + var scalesOpts = chart.options.scales; + + if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) { + meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id; + } + if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) { + meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id; + } + }, + + getDataset: function() { + return this.chart.data.datasets[this.index]; + }, + + getMeta: function() { + return this.chart.getDatasetMeta(this.index); + }, + + getScaleForId: function(scaleID) { + return this.chart.scales[scaleID]; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().yAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getValueScale: function() { + return this.getScaleForId(this._getValueScaleId()); + }, + + /** + * @private + */ + _getIndexScale: function() { + return this.getScaleForId(this._getIndexScaleId()); + }, + + reset: function() { + this._update(true); + }, + + /** + * @private + */ + destroy: function() { + if (this._data) { + unlistenArrayEvents(this._data, this); + } + }, + + createMetaDataset: function() { + var me = this; + var type = me.datasetElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index + }); + }, + + createMetaData: function(index) { + var me = this; + var type = me.dataElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index, + _index: index + }); + }, + + addElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data || []; + var metaData = meta.data; + var i, ilen; + + for (i = 0, ilen = data.length; i < ilen; ++i) { + metaData[i] = metaData[i] || me.createMetaData(i); + } + + meta.dataset = meta.dataset || me.createMetaDataset(); + }, + + addElementAndReset: function(index) { + var element = this.createMetaData(index); + this.getMeta().data.splice(index, 0, element); + this.updateElement(element, index, true); + }, + + buildOrUpdateElements: function() { + var me = this; + var dataset = me.getDataset(); + var data = dataset.data || (dataset.data = []); + + // In order to correctly handle data addition/deletion animation (an thus simulate + // real-time charts), we need to monitor these data modifications and synchronize + // the internal meta data accordingly. + if (me._data !== data) { + if (me._data) { + // This case happens when the user replaced the data array instance. + unlistenArrayEvents(me._data, me); + } + + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, me); + } + me._data = data; + } + + // Re-sync meta data in case the user replaced the data array or if we missed + // any updates and so make sure that we handle number of datapoints changing. + me.resyncElements(); + }, + + /** + * Returns the merged user-supplied and default dataset-level options + * @private + */ + _configure: function() { + var me = this; + me._config = helpers$1.merge({}, [ + me.chart.options.datasets[me._type], + me.getDataset(), + ], { + merger: function(key, target, source) { + if (key !== '_meta' && key !== 'data') { + helpers$1._merger(key, target, source); + } + } + }); + }, + + _update: function(reset) { + var me = this; + me._configure(); + me._cachedDataOpts = null; + me.update(reset); + }, + + update: helpers$1.noop, + + transition: function(easingValue) { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + for (; i < ilen; ++i) { + elements[i].transition(easingValue); + } + + if (meta.dataset) { + meta.dataset.transition(easingValue); + } + }, + + draw: function() { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + if (meta.dataset) { + meta.dataset.draw(); + } + + for (; i < ilen; ++i) { + elements[i].draw(); + } + }, + + /** + * Returns a set of predefined style properties that should be used to represent the dataset + * or the data if the index is specified + * @param {number} index - data index + * @return {IStyleInterface} style object + */ + getStyle: function(index) { + var me = this; + var meta = me.getMeta(); + var dataset = meta.dataset; + var style; + + me._configure(); + if (dataset && index === undefined) { + style = me._resolveDatasetElementOptions(dataset || {}); + } else { + index = index || 0; + style = me._resolveDataElementOptions(meta.data[index] || {}, index); + } + + if (style.fill === false || style.fill === null) { + style.backgroundColor = style.borderColor; + } + + return style; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function(element, hover) { + var me = this; + var chart = me.chart; + var datasetOpts = me._config; + var custom = element.custom || {}; + var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; + var elementOptions = me._datasetElementOptions; + var values = {}; + var i, ilen, key, readKey; + + // Scriptable options + var context = { + chart: chart, + dataset: me.getDataset(), + datasetIndex: me.index, + hover: hover + }; + + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key; + values[key] = resolve([ + custom[readKey], + datasetOpts[readKey], + options[readKey] + ], context); + } + + return values; + }, + + /** + * @private + */ + _resolveDataElementOptions: function(element, index) { + var me = this; + var custom = element && element.custom; + var cached = me._cachedDataOpts; + if (cached && !custom) { + return cached; + } + var chart = me.chart; + var datasetOpts = me._config; + var options = chart.options.elements[me.dataElementType.prototype._type] || {}; + var elementOptions = me._dataElementOptions; + var values = {}; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: me.getDataset(), + datasetIndex: me.index + }; + + // `resolve` sets cacheable to `false` if any option is indexed or scripted + var info = {cacheable: !custom}; + + var keys, i, ilen, key; + + custom = custom || {}; + + if (helpers$1.isArray(elementOptions)) { + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + values[key] = resolve([ + custom[key], + datasetOpts[key], + options[key] + ], context, index, info); + } + } else { + keys = Object.keys(elementOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + datasetOpts[elementOptions[key]], + datasetOpts[key], + options[key] + ], context, index, info); + } + } + + if (info.cacheable) { + me._cachedDataOpts = Object.freeze(values); + } + + return values; + }, + + removeHoverStyle: function(element) { + helpers$1.merge(element._model, element.$previousStyle || {}); + delete element.$previousStyle; + }, + + setHoverStyle: function(element) { + var dataset = this.chart.data.datasets[element._datasetIndex]; + var index = element._index; + var custom = element.custom || {}; + var model = element._model; + var getHoverColor = helpers$1.getHoverColor; + + element.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth + }; + + model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); + model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); + model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); + }, + + /** + * @private + */ + _removeDatasetHoverStyle: function() { + var element = this.getMeta().dataset; + + if (element) { + this.removeHoverStyle(element); + } + }, + + /** + * @private + */ + _setDatasetHoverStyle: function() { + var element = this.getMeta().dataset; + var prev = {}; + var i, ilen, key, keys, hoverOptions, model; + + if (!element) { + return; + } + + model = element._model; + hoverOptions = this._resolveDatasetElementOptions(element, true); + + keys = Object.keys(hoverOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + prev[key] = model[key]; + model[key] = hoverOptions[key]; + } + + element.$previousStyle = prev; + }, + + /** + * @private + */ + resyncElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var numMeta = meta.data.length; + var numData = data.length; + + if (numData < numMeta) { + meta.data.splice(numData, numMeta - numData); + } else if (numData > numMeta) { + me.insertElements(numMeta, numData - numMeta); + } + }, + + /** + * @private + */ + insertElements: function(start, count) { + for (var i = 0; i < count; ++i) { + this.addElementAndReset(start + i); + } + }, + + /** + * @private + */ + onDataPush: function() { + var count = arguments.length; + this.insertElements(this.getDataset().data.length - count, count); + }, + + /** + * @private + */ + onDataPop: function() { + this.getMeta().data.pop(); + }, + + /** + * @private + */ + onDataShift: function() { + this.getMeta().data.shift(); + }, + + /** + * @private + */ + onDataSplice: function(start, count) { + this.getMeta().data.splice(start, count); + this.insertElements(start, arguments.length - 2); + }, + + /** + * @private + */ + onDataUnshift: function() { + this.insertElements(0, arguments.length); + } +}); + +DatasetController.extend = helpers$1.inherits; + +var core_datasetController = DatasetController; + +var TAU = Math.PI * 2; + +core_defaults._set('global', { + elements: { + arc: { + backgroundColor: core_defaults.global.defaultColor, + borderColor: '#fff', + borderWidth: 2, + borderAlign: 'center' + } + } +}); + +function clipArc(ctx, arc) { + var startAngle = arc.startAngle; + var endAngle = arc.endAngle; + var pixelMargin = arc.pixelMargin; + var angleMargin = pixelMargin / arc.outerRadius; + var x = arc.x; + var y = arc.y; + + // Draw an inner border by cliping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + ctx.beginPath(); + ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin); + if (arc.innerRadius > pixelMargin) { + angleMargin = pixelMargin / arc.innerRadius; + ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true); + } else { + ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2); + } + ctx.closePath(); + ctx.clip(); +} + +function drawFullCircleBorders(ctx, vm, arc, inner) { + var endAngle = arc.endAngle; + var i; + + if (inner) { + arc.endAngle = arc.startAngle + TAU; + clipArc(ctx, arc); + arc.endAngle = endAngle; + if (arc.endAngle === arc.startAngle && arc.fullCircles) { + arc.endAngle += TAU; + arc.fullCircles--; + } + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } +} + +function drawBorder(ctx, vm, arc) { + var inner = vm.borderAlign === 'inner'; + + if (inner) { + ctx.lineWidth = vm.borderWidth * 2; + ctx.lineJoin = 'round'; + } else { + ctx.lineWidth = vm.borderWidth; + ctx.lineJoin = 'bevel'; + } + + if (arc.fullCircles) { + drawFullCircleBorders(ctx, vm, arc, inner); + } + + if (inner) { + clipArc(ctx, arc); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + ctx.stroke(); +} + +var element_arc = core_element.extend({ + _type: 'arc', + + inLabelRange: function(mouseX) { + var vm = this._view; + + if (vm) { + return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); + } + return false; + }, + + inRange: function(chartX, chartY) { + var vm = this._view; + + if (vm) { + var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); + var angle = pointRelativePosition.angle; + var distance = pointRelativePosition.distance; + + // Sanitise angle range + var startAngle = vm.startAngle; + var endAngle = vm.endAngle; + while (endAngle < startAngle) { + endAngle += TAU; + } + while (angle > endAngle) { + angle -= TAU; + } + while (angle < startAngle) { + angle += TAU; + } + + // Check if within the range of the open/close angle + var betweenAngles = (angle >= startAngle && angle <= endAngle); + var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); + + return (betweenAngles && withinRadius); + } + return false; + }, + + getCenterPoint: function() { + var vm = this._view; + var halfAngle = (vm.startAngle + vm.endAngle) / 2; + var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; + return { + x: vm.x + Math.cos(halfAngle) * halfRadius, + y: vm.y + Math.sin(halfAngle) * halfRadius + }; + }, + + getArea: function() { + var vm = this._view; + return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); + }, + + tooltipPosition: function() { + var vm = this._view; + var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); + var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; + + return { + x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), + y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) + }; + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; + var arc = { + x: vm.x, + y: vm.y, + innerRadius: vm.innerRadius, + outerRadius: Math.max(vm.outerRadius - pixelMargin, 0), + pixelMargin: pixelMargin, + startAngle: vm.startAngle, + endAngle: vm.endAngle, + fullCircles: Math.floor(vm.circumference / TAU) + }; + var i; + + ctx.save(); + + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + + if (arc.fullCircles) { + arc.endAngle = arc.startAngle + TAU; + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.fill(); + } + arc.endAngle = arc.startAngle + vm.circumference % TAU; + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + ctx.fill(); + + if (vm.borderWidth) { + drawBorder(ctx, vm, arc); + } + + ctx.restore(); + } +}); + +var valueOrDefault$1 = helpers$1.valueOrDefault; + +var defaultColor = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + line: { + tension: 0.4, + backgroundColor: defaultColor, + borderWidth: 3, + borderColor: defaultColor, + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + capBezierPoints: true, + fill: true, // do we fill in the area between the line and its base axis + } + } +}); + +var element_line = core_element.extend({ + _type: 'line', + + draw: function() { + var me = this; + var vm = me._view; + var ctx = me._chart.ctx; + var spanGaps = vm.spanGaps; + var points = me._children.slice(); // clone array + var globalDefaults = core_defaults.global; + var globalOptionLineElements = globalDefaults.elements.line; + var lastDrawnIndex = -1; + var closePath = me._loop; + var index, previous, currentVM; + + if (!points.length) { + return; + } + + if (me._loop) { + for (index = 0; index < points.length; ++index) { + previous = helpers$1.previousItem(points, index); + // If the line has an open path, shift the point array + if (!points[index]._view.skip && previous._view.skip) { + points = points.slice(index).concat(points.slice(0, index)); + closePath = spanGaps; + break; + } + } + // If the line has a close path, add the first point again + if (closePath) { + points.push(points[0]); + } + } + + ctx.save(); + + // Stroke Line Options + ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; + + // IE 9 and 10 do not support line dash + if (ctx.setLineDash) { + ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); + } + + ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); + ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; + ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); + ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; + + // Stroke Line + ctx.beginPath(); + + // First point moves to it's starting position no matter what + currentVM = points[0]._view; + if (!currentVM.skip) { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = 0; + } + + for (index = 1; index < points.length; ++index) { + currentVM = points[index]._view; + previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex]; + + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + helpers$1.canvas.lineTo(ctx, previous._view, currentVM); + } + lastDrawnIndex = index; + } + } + + if (closePath) { + ctx.closePath(); + } + + ctx.stroke(); + ctx.restore(); + } +}); + +var valueOrDefault$2 = helpers$1.valueOrDefault; + +var defaultColor$1 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + point: { + radius: 3, + pointStyle: 'circle', + backgroundColor: defaultColor$1, + borderColor: defaultColor$1, + borderWidth: 1, + // Hover + hitRadius: 1, + hoverRadius: 4, + hoverBorderWidth: 1 + } + } +}); + +function xRange(mouseX) { + var vm = this._view; + return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; +} + +function yRange(mouseY) { + var vm = this._view; + return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; +} + +var element_point = core_element.extend({ + _type: 'point', + + inRange: function(mouseX, mouseY) { + var vm = this._view; + return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; + }, + + inLabelRange: xRange, + inXRange: xRange, + inYRange: yRange, + + getCenterPoint: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + }, + + getArea: function() { + return Math.PI * Math.pow(this._view.radius, 2); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y, + padding: vm.radius + vm.borderWidth + }; + }, + + draw: function(chartArea) { + var vm = this._view; + var ctx = this._chart.ctx; + var pointStyle = vm.pointStyle; + var rotation = vm.rotation; + var radius = vm.radius; + var x = vm.x; + var y = vm.y; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow + + if (vm.skip) { + return; + } + + // Clipping for Points. + if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { + ctx.strokeStyle = vm.borderColor || defaultColor; + ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); + ctx.fillStyle = vm.backgroundColor || defaultColor; + helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); + } + } +}); + +var defaultColor$2 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + rectangle: { + backgroundColor: defaultColor$2, + borderColor: defaultColor$2, + borderSkipped: 'bottom', + borderWidth: 0 + } + } +}); + +function isVertical(vm) { + return vm && vm.width !== undefined; +} + +/** + * Helper function to get the bounds of the bar regardless of the orientation + * @param bar {Chart.Element.Rectangle} the bar + * @return {Bounds} bounds of the bar + * @private + */ +function getBarBounds(vm) { + var x1, x2, y1, y2, half; + + if (isVertical(vm)) { + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; + y1 = Math.min(vm.y, vm.base); + y2 = Math.max(vm.y, vm.base); + } else { + half = vm.height / 2; + x1 = Math.min(vm.x, vm.base); + x2 = Math.max(vm.x, vm.base); + y1 = vm.y - half; + y2 = vm.y + half; + } + + return { + left: x1, + top: y1, + right: x2, + bottom: y2 + }; +} + +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} + +function parseBorderSkipped(vm) { + var edge = vm.borderSkipped; + var res = {}; + + if (!edge) { + return res; + } + + if (vm.horizontal) { + if (vm.base > vm.x) { + edge = swap(edge, 'left', 'right'); + } + } else if (vm.base < vm.y) { + edge = swap(edge, 'bottom', 'top'); + } + + res[edge] = true; + return res; +} + +function parseBorderWidth(vm, maxW, maxH) { + var value = vm.borderWidth; + var skip = parseBorderSkipped(vm); + var t, r, b, l; + + if (helpers$1.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, + r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, + b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, + l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l + }; +} + +function boundingRects(vm) { + var bounds = getBarBounds(vm); + var width = bounds.right - bounds.left; + var height = bounds.bottom - bounds.top; + var border = parseBorderWidth(vm, width / 2, height / 2); + + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b + } + }; +} + +function inRange(vm, x, y) { + var skipX = x === null; + var skipY = y === null; + var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); + + return bounds + && (skipX || x >= bounds.left && x <= bounds.right) + && (skipY || y >= bounds.top && y <= bounds.bottom); +} + +var element_rectangle = core_element.extend({ + _type: 'rectangle', + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var rects = boundingRects(vm); + var outer = rects.outer; + var inner = rects.inner; + + ctx.fillStyle = vm.backgroundColor; + ctx.fillRect(outer.x, outer.y, outer.w, outer.h); + + if (outer.w === inner.w && outer.h === inner.h) { + return; + } + + ctx.save(); + ctx.beginPath(); + ctx.rect(outer.x, outer.y, outer.w, outer.h); + ctx.clip(); + ctx.fillStyle = vm.borderColor; + ctx.rect(inner.x, inner.y, inner.w, inner.h); + ctx.fill('evenodd'); + ctx.restore(); + }, + + height: function() { + var vm = this._view; + return vm.base - vm.y; + }, + + inRange: function(mouseX, mouseY) { + return inRange(this._view, mouseX, mouseY); + }, + + inLabelRange: function(mouseX, mouseY) { + var vm = this._view; + return isVertical(vm) + ? inRange(vm, mouseX, null) + : inRange(vm, null, mouseY); + }, + + inXRange: function(mouseX) { + return inRange(this._view, mouseX, null); + }, + + inYRange: function(mouseY) { + return inRange(this._view, null, mouseY); + }, + + getCenterPoint: function() { + var vm = this._view; + var x, y; + if (isVertical(vm)) { + x = vm.x; + y = (vm.y + vm.base) / 2; + } else { + x = (vm.x + vm.base) / 2; + y = vm.y; + } + + return {x: x, y: y}; + }, + + getArea: function() { + var vm = this._view; + + return isVertical(vm) + ? vm.width * Math.abs(vm.y - vm.base) + : vm.height * Math.abs(vm.x - vm.base); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + } +}); + +var elements = {}; +var Arc = element_arc; +var Line = element_line; +var Point = element_point; +var Rectangle = element_rectangle; +elements.Arc = Arc; +elements.Line = Line; +elements.Point = Point; +elements.Rectangle = Rectangle; + +var deprecated = helpers$1._deprecated; +var valueOrDefault$3 = helpers$1.valueOrDefault; + +core_defaults._set('bar', { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + offset: true, + gridLines: { + offsetGridLines: true + } + }], + + yAxes: [{ + type: 'linear' + }] + } +}); + +core_defaults._set('global', { + datasets: { + bar: { + categoryPercentage: 0.8, + barPercentage: 0.9 + } + } +}); + +/** + * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. + * @private + */ +function computeMinSampleSize(scale, pixels) { + var min = scale._length; + var prev, curr, i, ilen; + + for (i = 1, ilen = pixels.length; i < ilen; ++i) { + min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); + } + + for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) { + curr = scale.getPixelForTick(i); + min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min; + prev = curr; + } + + return min; +} + +/** + * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, + * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This + * mode currently always generates bars equally sized (until we introduce scriptable options?). + * @private + */ +function computeFitCategoryTraits(index, ruler, options) { + var thickness = options.barThickness; + var count = ruler.stackCount; + var curr = ruler.pixels[index]; + var min = helpers$1.isNullOrUndef(thickness) + ? computeMinSampleSize(ruler.scale, ruler.pixels) + : -1; + var size, ratio; + + if (helpers$1.isNullOrUndef(thickness)) { + size = min * options.categoryPercentage; + ratio = options.barPercentage; + } else { + // When bar thickness is enforced, category and bar percentages are ignored. + // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') + // and deprecate barPercentage since this value is ignored when thickness is absolute. + size = thickness * count; + ratio = 1; + } + + return { + chunk: size / count, + ratio: ratio, + start: curr - (size / 2) + }; +} + +/** + * Computes an "optimal" category that globally arranges bars side by side (no gap when + * percentage options are 1), based on the previous and following categories. This mode + * generates bars with different widths when data are not evenly spaced. + * @private + */ +function computeFlexCategoryTraits(index, ruler, options) { + var pixels = ruler.pixels; + var curr = pixels[index]; + var prev = index > 0 ? pixels[index - 1] : null; + var next = index < pixels.length - 1 ? pixels[index + 1] : null; + var percent = options.categoryPercentage; + var start, size; + + if (prev === null) { + // first data: its size is double based on the next point or, + // if it's also the last data, we use the scale size. + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); + } + + if (next === null) { + // last data: its size is also double based on the previous point. + next = curr + curr - prev; + } + + start = curr - (curr - Math.min(prev, next)) / 2 * percent; + size = Math.abs(next - prev) / 2 * percent; + + return { + chunk: size / ruler.stackCount, + ratio: options.barPercentage, + start: start + }; +} + +var controller_bar = core_datasetController.extend({ + + dataElementType: elements.Rectangle, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderSkipped', + 'borderWidth', + 'barPercentage', + 'barThickness', + 'categoryPercentage', + 'maxBarThickness', + 'minBarLength' + ], + + initialize: function() { + var me = this; + var meta, scaleOpts; + + core_datasetController.prototype.initialize.apply(me, arguments); + + meta = me.getMeta(); + meta.stack = me.getDataset().stack; + meta.bar = true; + + scaleOpts = me._getIndexScale().options; + deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage'); + deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness'); + deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage'); + deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength'); + deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness'); + }, + + update: function(reset) { + var me = this; + var rects = me.getMeta().data; + var i, ilen; + + me._ruler = me.getRuler(); + + for (i = 0, ilen = rects.length; i < ilen; ++i) { + me.updateElement(rects[i], i, reset); + } + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var dataset = me.getDataset(); + var options = me._resolveDataElementOptions(rectangle, index); + + rectangle._xScale = me.getScaleForId(meta.xAxisID); + rectangle._yScale = me.getScaleForId(meta.yAxisID); + rectangle._datasetIndex = me.index; + rectangle._index = index; + rectangle._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderSkipped: options.borderSkipped, + borderWidth: options.borderWidth, + datasetLabel: dataset.label, + label: me.chart.data.labels[index] + }; + + if (helpers$1.isArray(dataset.data[index])) { + rectangle._model.borderSkipped = null; + } + + me._updateElementGeometry(rectangle, index, reset, options); + + rectangle.pivot(); + }, + + /** + * @private + */ + _updateElementGeometry: function(rectangle, index, reset, options) { + var me = this; + var model = rectangle._model; + var vscale = me._getValueScale(); + var base = vscale.getBasePixel(); + var horizontal = vscale.isHorizontal(); + var ruler = me._ruler || me.getRuler(); + var vpixels = me.calculateBarValuePixels(me.index, index, options); + var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options); + + model.horizontal = horizontal; + model.base = reset ? base : vpixels.base; + model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; + model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; + model.height = horizontal ? ipixels.size : undefined; + model.width = horizontal ? undefined : ipixels.size; + }, + + /** + * Returns the stacks based on groups and bar visibility. + * @param {number} [last] - The dataset index + * @returns {string[]} The list of stack IDs + * @private + */ + _getStacks: function(last) { + var me = this; + var scale = me._getIndexScale(); + var metasets = scale._getMatchingVisibleMetas(me._type); + var stacked = scale.options.stacked; + var ilen = metasets.length; + var stacks = []; + var i, meta; + + for (i = 0; i < ilen; ++i) { + meta = metasets[i]; + // stacked | meta.stack + // | found | not found | undefined + // false | x | x | x + // true | | x | + // undefined | | x | x + if (stacked === false || stacks.indexOf(meta.stack) === -1 || + (stacked === undefined && meta.stack === undefined)) { + stacks.push(meta.stack); + } + if (meta.index === last) { + break; + } + } + + return stacks; + }, + + /** + * Returns the effective number of stacks based on groups and bar visibility. + * @private + */ + getStackCount: function() { + return this._getStacks().length; + }, + + /** + * Returns the stack index for the given dataset based on groups and bar visibility. + * @param {number} [datasetIndex] - The dataset index + * @param {string} [name] - The stack name to find + * @returns {number} The stack index + * @private + */ + getStackIndex: function(datasetIndex, name) { + var stacks = this._getStacks(datasetIndex); + var index = (name !== undefined) + ? stacks.indexOf(name) + : -1; // indexOf returns -1 if element is not present + + return (index === -1) + ? stacks.length - 1 + : index; + }, + + /** + * @private + */ + getRuler: function() { + var me = this; + var scale = me._getIndexScale(); + var pixels = []; + var i, ilen; + + for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { + pixels.push(scale.getPixelForValue(null, i, me.index)); + } + + return { + pixels: pixels, + start: scale._startPixel, + end: scale._endPixel, + stackCount: me.getStackCount(), + scale: scale + }; + }, + + /** + * Note: pixel values are not clamped to the scale area. + * @private + */ + calculateBarValuePixels: function(datasetIndex, index, options) { + var me = this; + var chart = me.chart; + var scale = me._getValueScale(); + var isHorizontal = scale.isHorizontal(); + var datasets = chart.data.datasets; + var metasets = scale._getMatchingVisibleMetas(me._type); + var value = scale._parseValue(datasets[datasetIndex].data[index]); + var minBarLength = options.minBarLength; + var stacked = scale.options.stacked; + var stack = me.getMeta().stack; + var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; + var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; + var ilen = metasets.length; + var i, imeta, ivalue, base, head, size, stackLength; + + if (stacked || (stacked === undefined && stack !== undefined)) { + for (i = 0; i < ilen; ++i) { + imeta = metasets[i]; + + if (imeta.index === datasetIndex) { + break; + } + + if (imeta.stack === stack) { + stackLength = scale._parseValue(datasets[imeta.index].data[index]); + ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; + + if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { + start += ivalue; + } + } + } + } + + base = scale.getPixelForValue(start); + head = scale.getPixelForValue(start + length); + size = head - base; + + if (minBarLength !== undefined && Math.abs(size) < minBarLength) { + size = minBarLength; + if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { + head = base - minBarLength; + } else { + head = base + minBarLength; + } + } + + return { + size: size, + base: base, + head: head, + center: head + size / 2 + }; + }, + + /** + * @private + */ + calculateBarIndexPixels: function(datasetIndex, index, ruler, options) { + var me = this; + var range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options) + : computeFitCategoryTraits(index, ruler, options); + + var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); + var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + var size = Math.min( + valueOrDefault$3(options.maxBarThickness, Infinity), + range.chunk * range.ratio); + + return { + base: center - size / 2, + head: center + size / 2, + center: center, + size: size + }; + }, + + draw: function() { + var me = this; + var chart = me.chart; + var scale = me._getValueScale(); + var rects = me.getMeta().data; + var dataset = me.getDataset(); + var ilen = rects.length; + var i = 0; + + helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); + + for (; i < ilen; ++i) { + var val = scale._parseValue(dataset.data[i]); + if (!isNaN(val.min) && !isNaN(val.max)) { + rects[i].draw(); + } + } + + helpers$1.canvas.unclipArea(chart.ctx); + }, + + /** + * @private + */ + _resolveDataElementOptions: function() { + var me = this; + var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments)); + var indexOpts = me._getIndexScale().options; + var valueOpts = me._getValueScale().options; + + values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage); + values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness); + values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage); + values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness); + values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength); + + return values; + } + +}); + +var valueOrDefault$4 = helpers$1.valueOrDefault; +var resolve$1 = helpers$1.options.resolve; + +core_defaults._set('bubble', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + var dataPoint = data.datasets[item.datasetIndex].data[item.index]; + return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; + } + } + } +}); + +var controller_bubble = core_datasetController.extend({ + /** + * @protected + */ + dataElementType: elements.Point, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle', + 'rotation' + ], + + /** + * @protected + */ + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var points = meta.data; + + // Update Points + helpers$1.each(points, function(point, index) { + me.updateElement(point, index, reset); + }); + }, + + /** + * @protected + */ + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var options = me._resolveDataElementOptions(point, index); + var data = me.getDataset().data[index]; + var dsIndex = me.index; + + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); + + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = dsIndex; + point._index = index; + point._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + hitRadius: options.hitRadius, + pointStyle: options.pointStyle, + rotation: options.rotation, + radius: reset ? 0 : options.radius, + skip: custom.skip || isNaN(x) || isNaN(y), + x: x, + y: y, + }; + + point.pivot(); + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); + model.radius = options.radius + options.hoverRadius; + }, + + /** + * @private + */ + _resolveDataElementOptions: function(point, index) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var custom = point.custom || {}; + var data = dataset.data[index] || {}; + var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments); + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + // In case values were cached (and thus frozen), we need to clone the values + if (me._cachedDataOpts === values) { + values = helpers$1.extend({}, values); + } + + // Custom radius resolution + values.radius = resolve$1([ + custom.radius, + data.r, + me._config.radius, + chart.options.elements.point.radius + ], context, index); + + return values; + } +}); + +var valueOrDefault$5 = helpers$1.valueOrDefault; + +var PI$1 = Math.PI; +var DOUBLE_PI$1 = PI$1 * 2; +var HALF_PI$1 = PI$1 / 2; + +core_defaults._set('doughnut', { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var list = document.createElement('ul'); + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + if (datasets.length) { + for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; + if (labels[i]) { + listItem.appendChild(document.createTextNode(labels[i])); + } + } + } + + return list.outerHTML; + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var style = meta.controller.getStyle(i); + + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: -HALF_PI$1, + + // The total circumference of the chart. + circumference: DOUBLE_PI$1, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + var dataLabel = data.labels[tooltipItem.index]; + var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + + if (helpers$1.isArray(dataLabel)) { + // show value on first line of multiline label + // need to clone because we are changing the value + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + + return dataLabel; + } + } + } +}); + +var controller_doughnut = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly + getRingIndex: function(datasetIndex) { + var ringIndex = 0; + + for (var j = 0; j < datasetIndex; ++j) { + if (this.chart.isDatasetVisible(j)) { + ++ringIndex; + } + } + + return ringIndex; + }, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var ratioX = 1; + var ratioY = 1; + var offsetX = 0; + var offsetY = 0; + var meta = me.getMeta(); + var arcs = meta.data; + var cutout = opts.cutoutPercentage / 100 || 0; + var circumference = opts.circumference; + var chartWeight = me._getRingWeight(me.index); + var maxWidth, maxHeight, i, ilen; + + // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc + if (circumference < DOUBLE_PI$1) { + var startAngle = opts.rotation % DOUBLE_PI$1; + startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0; + var endAngle = startAngle + circumference; + var startX = Math.cos(startAngle); + var startY = Math.sin(startAngle); + var endX = Math.cos(endAngle); + var endY = Math.sin(endAngle); + var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1; + var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1; + var contains180 = startAngle === -PI$1 || endAngle >= PI$1; + var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1; + var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout); + var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout); + var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout); + var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout); + ratioX = (maxX - minX) / 2; + ratioY = (maxY - minY) / 2; + offsetX = -(maxX + minX) / 2; + offsetY = -(maxY + minY) / 2; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); + } + + chart.borderWidth = me.getMaxBorderWidth(); + maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX; + maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY; + chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); + chart.innerRadius = Math.max(chart.outerRadius * cutout, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); + chart.offsetX = offsetX * chart.outerRadius; + chart.offsetY = offsetY * chart.outerRadius; + + meta.total = me.calculateTotal(); + + me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); + me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + me.updateElement(arcs[i], i, reset); + } + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var animationOpts = opts.animation; + var centerX = (chartArea.left + chartArea.right) / 2; + var centerY = (chartArea.top + chartArea.bottom) / 2; + var startAngle = opts.rotation; // non reset case handled later + var endAngle = opts.rotation; // non reset case handled later + var dataset = me.getDataset(); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1); + var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; + var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX + chart.offsetX, + y: centerY + chart.offsetY, + startAngle: startAngle, + endAngle: endAngle, + circumference: circumference, + outerRadius: outerRadius, + innerRadius: innerRadius, + label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) + } + }); + + var model = arc._model; + + // Set correct angles if not resetting + if (!reset || !animationOpts.animateRotate) { + if (index === 0) { + model.startAngle = opts.rotation; + } else { + model.startAngle = me.getMeta().data[index - 1]._model.endAngle; + } + + model.endAngle = model.startAngle + model.circumference; + } + + arc.pivot(); + }, + + calculateTotal: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var total = 0; + var value; + + helpers$1.each(meta.data, function(element, index) { + value = dataset.data[index]; + if (!isNaN(value) && !element.hidden) { + total += Math.abs(value); + } + }); + + /* if (total === 0) { + total = NaN; + }*/ + + return total; + }, + + calculateCircumference: function(value) { + var total = this.getMeta().total; + if (total > 0 && !isNaN(value)) { + return DOUBLE_PI$1 * (Math.abs(value) / total); + } + return 0; + }, + + // gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function(arcs) { + var me = this; + var max = 0; + var chart = me.chart; + var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; + + if (!arcs) { + // Find the outmost visible dataset + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + arcs = meta.data; + if (i !== me.index) { + controller = meta.controller; + } + break; + } + } + } + + if (!arcs) { + return 0; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arc = arcs[i]; + if (controller) { + controller._configure(); + options = controller._resolveDataElementOptions(arc, i); + } else { + options = arc._options; + } + if (options.borderAlign !== 'inner') { + borderWidth = options.borderWidth; + hoverWidth = options.hoverBorderWidth; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + } + return max; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly + * @private + */ + _getRingWeightOffset: function(datasetIndex) { + var ringWeightOffset = 0; + + for (var i = 0; i < datasetIndex; ++i) { + if (this.chart.isDatasetVisible(i)) { + ringWeightOffset += this._getRingWeight(i); + } + } + + return ringWeightOffset; + }, + + /** + * @private + */ + _getRingWeight: function(dataSetIndex) { + return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0); + }, + + /** + * Returns the sum of all visibile data set weights. This value can be 0. + * @private + */ + _getVisibleDatasetWeightTotal: function() { + return this._getRingWeightOffset(this.chart.data.datasets.length); + } +}); + +core_defaults._set('horizontalBar', { + hover: { + mode: 'index', + axis: 'y' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + + yAxes: [{ + type: 'category', + position: 'left', + offset: true, + gridLines: { + offsetGridLines: true + } + }] + }, + + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + + tooltips: { + mode: 'index', + axis: 'y' + } +}); + +core_defaults._set('global', { + datasets: { + horizontalBar: { + categoryPercentage: 0.8, + barPercentage: 0.9 + } + } +}); + +var controller_horizontalBar = controller_bar.extend({ + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().yAxisID; + } +}); + +var valueOrDefault$6 = helpers$1.valueOrDefault; +var resolve$2 = helpers$1.options.resolve; +var isPointInArea = helpers$1.canvas._isPointInArea; + +core_defaults._set('line', { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } +}); + +function scaleClip(scale, halfBorderWidth) { + var tickOpts = scale && scale.options.ticks || {}; + var reverse = tickOpts.reverse; + var min = tickOpts.min === undefined ? halfBorderWidth : 0; + var max = tickOpts.max === undefined ? halfBorderWidth : 0; + return { + start: reverse ? max : min, + end: reverse ? min : max + }; +} + +function defaultClip(xScale, yScale, borderWidth) { + var halfBorderWidth = borderWidth / 2; + var x = scaleClip(xScale, halfBorderWidth); + var y = scaleClip(yScale, halfBorderWidth); + + return { + top: y.end, + right: x.end, + bottom: y.start, + left: x.start + }; +} + +function toClip(value) { + var t, r, b, l; + + if (helpers$1.isObject(value)) { + t = value.top; + r = value.right; + b = value.bottom; + l = value.left; + } else { + t = r = b = l = value; + } + + return { + top: t, + right: r, + bottom: b, + left: l + }; +} + + +var controller_line = core_datasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth', + 'cubicInterpolationMode', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var options = me.chart.options; + var config = me._config; + var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines); + var i, ilen; + + me._xScale = me.getScaleForId(meta.xAxisID); + me._yScale = me.getScaleForId(meta.yAxisID); + + // Update Line + if (showLine) { + // Compatibility: If the properties are defined with only the old name, use those values + if (config.tension !== undefined && config.lineTension === undefined) { + config.lineTension = config.tension; + } + + // Utility + line._scale = me._yScale; + line._datasetIndex = me.index; + // Data + line._children = points; + // Model + line._model = me._resolveDatasetElementOptions(line); + + line.pivot(); + } + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + if (showLine && line._model.tension !== 0) { + me.updateBezierControlPoints(); + } + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var dataset = me.getDataset(); + var datasetIndex = me.index; + var value = dataset.data[index]; + var xScale = me._xScale; + var yScale = me._yScale; + var lineModel = meta.dataset._model; + var x, y; + + var options = me._resolveDataElementOptions(point, index); + + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); + y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); + + // Utility + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = datasetIndex; + point._index = index; + + // Desired view properties + point._model = { + x: x, + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), + steppedLine: lineModel ? lineModel.steppedLine : false, + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function(element) { + var me = this; + var config = me._config; + var custom = element.custom || {}; + var options = me.chart.options; + var lineOptions = options.elements.line; + var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); + + // The default behavior of lines is to break at null values, according + // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 + // This option gives lines the ability to span gaps + values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps); + values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension); + values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]); + values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth))); + + return values; + }, + + calculatePointY: function(value, index, datasetIndex) { + var me = this; + var chart = me.chart; + var yScale = me._yScale; + var sumPos = 0; + var sumNeg = 0; + var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen; + + if (yScale.options.stacked) { + rightValue = +yScale.getRightValue(value); + metasets = chart._getSortedVisibleDatasetMetas(); + ilen = metasets.length; + + for (i = 0; i < ilen; ++i) { + dsMeta = metasets[i]; + if (dsMeta.index === datasetIndex) { + break; + } + + ds = chart.data.datasets[dsMeta.index]; + if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) { + stackedRightValue = +yScale.getRightValue(ds.data[index]); + if (stackedRightValue < 0) { + sumNeg += stackedRightValue || 0; + } else { + sumPos += stackedRightValue || 0; + } + } + } + + if (rightValue < 0) { + return yScale.getPixelForValue(sumNeg + rightValue); + } + return yScale.getPixelForValue(sumPos + rightValue); + } + return yScale.getPixelForValue(value); + }, + + updateBezierControlPoints: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var lineModel = meta.dataset._model; + var area = chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (lineModel.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + if (lineModel.cubicInterpolationMode === 'monotone') { + helpers$1.splineCurveMonotone(points); + } else { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i)._model, + model, + helpers$1.nextItem(points, i)._model, + lineModel.tension + ); + model.controlPointPreviousX = controlPoints.previous.x; + model.controlPointPreviousY = controlPoints.previous.y; + model.controlPointNextX = controlPoints.next.x; + model.controlPointNextY = controlPoints.next.y; + } + } + + if (chart.options.elements.line.capBezierPoints) { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + if (isPointInArea(model, area)) { + if (i > 0 && isPointInArea(points[i - 1]._model, area)) { + model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); + model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); + } + if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { + model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); + model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); + } + } + } + } + }, + + draw: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var points = meta.data || []; + var area = chart.chartArea; + var canvas = chart.canvas; + var i = 0; + var ilen = points.length; + var clip; + + if (me._showLine) { + clip = meta.dataset._model.clip; + + helpers$1.canvas.clipArea(chart.ctx, { + left: clip.left === false ? 0 : area.left - clip.left, + right: clip.right === false ? canvas.width : area.right + clip.right, + top: clip.top === false ? 0 : area.top - clip.top, + bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom + }); + + meta.dataset.draw(); + + helpers$1.canvas.unclipArea(chart.ctx); + } + + // Draw the points + for (; i < ilen; ++i) { + points[i].draw(area); + } + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$6(options.hoverRadius, options.radius); + }, +}); + +var resolve$3 = helpers$1.options.resolve; + +core_defaults._set('polarArea', { + scale: { + type: 'radialLinear', + angleLines: { + display: false + }, + gridLines: { + circular: true + }, + pointLabels: { + display: false + }, + ticks: { + beginAtZero: true + } + }, + + // Boolean - Whether to animate the rotation of the chart + animation: { + animateRotate: true, + animateScale: true + }, + + startAngle: -0.5 * Math.PI, + legendCallback: function(chart) { + var list = document.createElement('ul'); + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + if (datasets.length) { + for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; + if (labels[i]) { + listItem.appendChild(document.createTextNode(labels[i])); + } + } + } + + return list.outerHTML; + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var style = meta.controller.getStyle(i); + + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(item, data) { + return data.labels[item.index] + ': ' + item.yLabel; + } + } + } +}); + +var controller_polarArea = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + + /** + * @private + */ + _getIndexScaleId: function() { + return this.chart.scale.id; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.chart.scale.id; + }, + + update: function(reset) { + var me = this; + var dataset = me.getDataset(); + var meta = me.getMeta(); + var start = me.chart.options.startAngle || 0; + var starts = me._starts = []; + var angles = me._angles = []; + var arcs = meta.data; + var i, ilen, angle; + + me._updateRadius(); + + meta.count = me.countVisibleElements(); + + for (i = 0, ilen = dataset.data.length; i < ilen; i++) { + starts[i] = start; + angle = me._computeAngle(i); + angles[i] = angle; + start += angle; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); + me.updateElement(arcs[i], i, reset); + } + }, + + /** + * @private + */ + _updateRadius: function() { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + + chart.outerRadius = Math.max(minSize / 2, 0); + chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); + me.innerRadius = me.outerRadius - chart.radiusLength; + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var opts = chart.options; + var animationOpts = opts.animation; + var scale = chart.scale; + var labels = chart.data.labels; + + var centerX = scale.xCenter; + var centerY = scale.yCenter; + + // var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; + var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var startAngle = me._starts[index]; + var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); + + var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius: reset ? resetRadius : distance, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, + label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) + } + }); + + arc.pivot(); + }, + + countVisibleElements: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var count = 0; + + helpers$1.each(meta.data, function(element, index) { + if (!isNaN(dataset.data[index]) && !element.hidden) { + count++; + } + }); + + return count; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + var valueOrDefault = helpers$1.valueOrDefault; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * @private + */ + _computeAngle: function(index) { + var me = this; + var count = this.getMeta().count; + var dataset = me.getDataset(); + var meta = me.getMeta(); + + if (isNaN(dataset.data[index]) || meta.data[index].hidden) { + return 0; + } + + // Scriptable options + var context = { + chart: me.chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + return resolve$3([ + me.chart.options.elements.arc.angle, + (2 * Math.PI) / count + ], context, index); + } +}); + +core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); +core_defaults._set('pie', { + cutoutPercentage: 0 +}); + +// Pie charts are Doughnut chart with different defaults +var controller_pie = controller_doughnut; + +var valueOrDefault$7 = helpers$1.valueOrDefault; + +core_defaults._set('radar', { + spanGaps: false, + scale: { + type: 'radialLinear' + }, + elements: { + line: { + fill: 'start', + tension: 0 // no bezier in radar + } + } +}); + +var controller_radar = core_datasetController.extend({ + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + linkScales: helpers$1.noop, + + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderCapStyle', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.chart.scale.id; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.chart.scale.id; + }, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var scale = me.chart.scale; + var config = me._config; + var i, ilen; + + // Compatibility: If the properties are defined with only the old name, use those values + if (config.tension !== undefined && config.lineTension === undefined) { + config.lineTension = config.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + line._loop = true; + // Model + line._model = me._resolveDatasetElementOptions(line); + + line.pivot(); + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + // Update bezier control points + me.updateBezierControlPoints(); + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var custom = point.custom || {}; + var dataset = me.getDataset(); + var scale = me.chart.scale; + var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); + var options = me._resolveDataElementOptions(point, index); + var lineModel = me.getMeta().dataset._model; + var x = reset ? scale.xCenter : pointPosition.x; + var y = reset ? scale.yCenter : pointPosition.y; + + // Utility + point._scale = scale; + point._options = options; + point._datasetIndex = me.index; + point._index = index; + + // Desired view properties + point._model = { + x: x, // value not used in dataset scale, but we want a consistent API between scales + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0), + + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function() { + var me = this; + var config = me._config; + var options = me.chart.options; + var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); + + values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps); + values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension); + + return values; + }, + + updateBezierControlPoints: function() { + var me = this; + var meta = me.getMeta(); + var area = me.chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (meta.dataset._model.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i, true)._model, + model, + helpers$1.nextItem(points, i, true)._model, + model.tension + ); + + // Prevent the bezier going outside of the bounds of the graph + model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); + model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); + model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); + model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); + } + }, + + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$7(options.hoverRadius, options.radius); + } +}); + +core_defaults._set('scatter', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + id: 'x-axis-1', // need an ID so datasets can reference the scale + type: 'linear', // scatter should not use a category axis + position: 'bottom' + }], + yAxes: [{ + id: 'y-axis-1', + type: 'linear', + position: 'left' + }] + }, + + tooltips: { + callbacks: { + title: function() { + return ''; // doesn't make sense for scatter since data are formatted as a point + }, + label: function(item) { + return '(' + item.xLabel + ', ' + item.yLabel + ')'; + } + } + } +}); + +core_defaults._set('global', { + datasets: { + scatter: { + showLine: false + } + } +}); + +// Scatter charts use line controllers +var controller_scatter = controller_line; + +// NOTE export a map in which the key represents the controller type, not +// the class, and so must be CamelCase in order to be correctly retrieved +// by the controller in core.controller.js (`controllers[meta.type]`). + +var controllers = { + bar: controller_bar, + bubble: controller_bubble, + doughnut: controller_doughnut, + horizontalBar: controller_horizontalBar, + line: controller_line, + polarArea: controller_polarArea, + pie: controller_pie, + radar: controller_radar, + scatter: controller_scatter +}; + +/** + * Helper function to get relative position for an event + * @param {Event|IEvent} event - The event to get the position for + * @param {Chart} chart - The chart + * @returns {object} the event position + */ +function getRelativePosition(e, chart) { + if (e.native) { + return { + x: e.x, + y: e.y + }; + } + + return helpers$1.getRelativePosition(e, chart); +} + +/** + * Helper function to traverse all of the visible elements in the chart + * @param {Chart} chart - the chart + * @param {function} handler - the callback to execute for each visible item + */ +function parseVisibleItems(chart, handler) { + var metasets = chart._getSortedVisibleDatasetMetas(); + var metadata, i, j, ilen, jlen, element; + + for (i = 0, ilen = metasets.length; i < ilen; ++i) { + metadata = metasets[i].data; + for (j = 0, jlen = metadata.length; j < jlen; ++j) { + element = metadata[j]; + if (!element._view.skip) { + handler(element); + } + } + } +} + +/** + * Helper function to get the items that intersect the event position + * @param {ChartElement[]} items - elements to filter + * @param {object} position - the point to be nearest to + * @return {ChartElement[]} the nearest items + */ +function getIntersectItems(chart, position) { + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + } + }); + + return elements; +} + +/** + * Helper function to get the items nearest to the event position considering all visible items in teh chart + * @param {Chart} chart - the chart to look at elements from + * @param {object} position - the point to be nearest to + * @param {boolean} intersect - if true, only consider items that intersect the position + * @param {function} distanceMetric - function to provide the distance between points + * @return {ChartElement[]} the nearest items + */ +function getNearestItems(chart, position, intersect, distanceMetric) { + var minDistance = Number.POSITIVE_INFINITY; + var nearestItems = []; + + parseVisibleItems(chart, function(element) { + if (intersect && !element.inRange(position.x, position.y)) { + return; + } + + var center = element.getCenterPoint(); + var distance = distanceMetric(position, center); + if (distance < minDistance) { + nearestItems = [element]; + minDistance = distance; + } else if (distance === minDistance) { + // Can have multiple items at the same distance in which case we sort by size + nearestItems.push(element); + } + }); + + return nearestItems; +} + +/** + * Get a distance metric function for two points based on the + * axis mode setting + * @param {string} axis - the axis mode. x|y|xy + */ +function getDistanceMetricForAxis(axis) { + var useX = axis.indexOf('x') !== -1; + var useY = axis.indexOf('y') !== -1; + + return function(pt1, pt2) { + var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; +} + +function indexMode(chart, e, options) { + var position = getRelativePosition(e, chart); + // Default axis for index mode is 'x' to match old behaviour + options.axis = options.axis || 'x'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + var elements = []; + + if (!items.length) { + return []; + } + + chart._getSortedVisibleDatasetMetas().forEach(function(meta) { + var element = meta.data[items[0]._index]; + + // don't count items that are skipped (null data) + if (element && !element._view.skip) { + elements.push(element); + } + }); + + return elements; +} + +/** + * @interface IInteractionOptions + */ +/** + * If true, only consider items that intersect the point + * @name IInterfaceOptions#boolean + * @type Boolean + */ + +/** + * Contains interaction related functions + * @namespace Chart.Interaction + */ +var core_interaction = { + // Helper function for different modes + modes: { + single: function(chart, e) { + var position = getRelativePosition(e, chart); + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + return elements; + } + }); + + return elements.slice(0, 1); + }, + + /** + * @function Chart.Interaction.modes.label + * @deprecated since version 2.4.0 + * @todo remove at version 3 + * @private + */ + label: indexMode, + + /** + * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item + * @function Chart.Interaction.modes.index + * @since v2.4.0 + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + index: indexMode, + + /** + * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect is false, we find the nearest item and return the items in that dataset + * @function Chart.Interaction.modes.dataset + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + dataset: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + + if (items.length > 0) { + items = chart.getDatasetMeta(items[0]._datasetIndex).data; + } + + return items; + }, + + /** + * @function Chart.Interaction.modes.x-axis + * @deprecated since version 2.4.0. Use index mode and intersect == true + * @todo remove at version 3 + * @private + */ + 'x-axis': function(chart, e) { + return indexMode(chart, e, {intersect: false}); + }, + + /** + * Point mode returns all elements that hit test based on the event position + * of the event + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + point: function(chart, e) { + var position = getRelativePosition(e, chart); + return getIntersectItems(chart, position); + }, + + /** + * nearest mode returns the element closest to the point + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + nearest: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + return getNearestItems(chart, position, options.intersect, distanceMetric); + }, + + /** + * x mode returns the elements that hit-test at the current x coordinate + * @function Chart.Interaction.modes.x + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + x: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inXRange(position.x)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + }, + + /** + * y mode returns the elements that hit-test at the current y coordinate + * @function Chart.Interaction.modes.y + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + y: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inYRange(position.y)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + } + } +}; + +var extend = helpers$1.extend; + +function filterByPosition(array, position) { + return helpers$1.where(array, function(v) { + return v.pos === position; + }); +} + +function sortByWeight(array, reverse) { + return array.sort(function(a, b) { + var v0 = reverse ? b : a; + var v1 = reverse ? a : b; + return v0.weight === v1.weight ? + v0.index - v1.index : + v0.weight - v1.weight; + }); +} + +function wrapBoxes(boxes) { + var layoutBoxes = []; + var i, ilen, box; + + for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) { + box = boxes[i]; + layoutBoxes.push({ + index: i, + box: box, + pos: box.position, + horizontal: box.isHorizontal(), + weight: box.weight + }); + } + return layoutBoxes; +} + +function setLayoutDims(layouts, params) { + var i, ilen, layout; + for (i = 0, ilen = layouts.length; i < ilen; ++i) { + layout = layouts[i]; + // store width used instead of chartArea.w in fitBoxes + layout.width = layout.horizontal + ? layout.box.fullWidth && params.availableWidth + : params.vBoxMaxWidth; + // store height used instead of chartArea.h in fitBoxes + layout.height = layout.horizontal && params.hBoxMaxHeight; + } +} + +function buildLayoutBoxes(boxes) { + var layoutBoxes = wrapBoxes(boxes); + var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true); + var right = sortByWeight(filterByPosition(layoutBoxes, 'right')); + var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true); + var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom')); + + return { + leftAndTop: left.concat(top), + rightAndBottom: right.concat(bottom), + chartArea: filterByPosition(layoutBoxes, 'chartArea'), + vertical: left.concat(right), + horizontal: top.concat(bottom) + }; +} + +function getCombinedMax(maxPadding, chartArea, a, b) { + return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]); +} + +function updateDims(chartArea, params, layout) { + var box = layout.box; + var maxPadding = chartArea.maxPadding; + var newWidth, newHeight; + + if (layout.size) { + // this layout was already counted for, lets first reduce old size + chartArea[layout.pos] -= layout.size; + } + layout.size = layout.horizontal ? box.height : box.width; + chartArea[layout.pos] += layout.size; + + if (box.getPadding) { + var boxPadding = box.getPadding(); + maxPadding.top = Math.max(maxPadding.top, boxPadding.top); + maxPadding.left = Math.max(maxPadding.left, boxPadding.left); + maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom); + maxPadding.right = Math.max(maxPadding.right, boxPadding.right); + } + + newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'); + newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'); + + if (newWidth !== chartArea.w || newHeight !== chartArea.h) { + chartArea.w = newWidth; + chartArea.h = newHeight; + + // return true if chart area changed in layout's direction + return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h; + } +} + +function handleMaxPadding(chartArea) { + var maxPadding = chartArea.maxPadding; + + function updatePos(pos) { + var change = Math.max(maxPadding[pos] - chartArea[pos], 0); + chartArea[pos] += change; + return change; + } + chartArea.y += updatePos('top'); + chartArea.x += updatePos('left'); + updatePos('right'); + updatePos('bottom'); +} + +function getMargins(horizontal, chartArea) { + var maxPadding = chartArea.maxPadding; + + function marginForPositions(positions) { + var margin = {left: 0, top: 0, right: 0, bottom: 0}; + positions.forEach(function(pos) { + margin[pos] = Math.max(chartArea[pos], maxPadding[pos]); + }); + return margin; + } + + return horizontal + ? marginForPositions(['left', 'right']) + : marginForPositions(['top', 'bottom']); +} + +function fitBoxes(boxes, chartArea, params) { + var refitBoxes = []; + var i, ilen, layout, box, refit, changed; + + for (i = 0, ilen = boxes.length; i < ilen; ++i) { + layout = boxes[i]; + box = layout.box; + + box.update( + layout.width || chartArea.w, + layout.height || chartArea.h, + getMargins(layout.horizontal, chartArea) + ); + if (updateDims(chartArea, params, layout)) { + changed = true; + if (refitBoxes.length) { + // Dimensions changed and there were non full width boxes before this + // -> we have to refit those + refit = true; + } + } + if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case + refitBoxes.push(layout); + } + } + + return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed; +} + +function placeBoxes(boxes, chartArea, params) { + var userPadding = params.padding; + var x = chartArea.x; + var y = chartArea.y; + var i, ilen, layout, box; + + for (i = 0, ilen = boxes.length; i < ilen; ++i) { + layout = boxes[i]; + box = layout.box; + if (layout.horizontal) { + box.left = box.fullWidth ? userPadding.left : chartArea.left; + box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w; + box.top = y; + box.bottom = y + box.height; + box.width = box.right - box.left; + y = box.bottom; + } else { + box.left = x; + box.right = x + box.width; + box.top = chartArea.top; + box.bottom = chartArea.top + chartArea.h; + box.height = box.bottom - box.top; + x = box.right; + } + } + + chartArea.x = x; + chartArea.y = y; +} + +core_defaults._set('global', { + layout: { + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } +}); + +/** + * @interface ILayoutItem + * @prop {string} position - The position of the item in the chart layout. Possible values are + * 'left', 'top', 'right', 'bottom', and 'chartArea' + * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area + * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down + * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) + * @prop {function} update - Takes two parameters: width and height. Returns size of item + * @prop {function} getPadding - Returns an object with padding on the edges + * @prop {number} width - Width of item. Must be valid after update() + * @prop {number} height - Height of item. Must be valid after update() + * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update + * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update + * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update + * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update + */ + +// The layout service is very self explanatory. It's responsible for the layout within a chart. +// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need +// It is this service's responsibility of carrying out that layout. +var core_layouts = { + defaults: {}, + + /** + * Register a box to a chart. + * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. + * @param {Chart} chart - the chart to use + * @param {ILayoutItem} item - the item to add to be layed out + */ + addBox: function(chart, item) { + if (!chart.boxes) { + chart.boxes = []; + } + + // initialize item with default values + item.fullWidth = item.fullWidth || false; + item.position = item.position || 'top'; + item.weight = item.weight || 0; + item._layers = item._layers || function() { + return [{ + z: 0, + draw: function() { + item.draw.apply(item, arguments); + } + }]; + }; + + chart.boxes.push(item); + }, + + /** + * Remove a layoutItem from a chart + * @param {Chart} chart - the chart to remove the box from + * @param {ILayoutItem} layoutItem - the item to remove from the layout + */ + removeBox: function(chart, layoutItem) { + var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 1); + } + }, + + /** + * Sets (or updates) options on the given `item`. + * @param {Chart} chart - the chart in which the item lives (or will be added to) + * @param {ILayoutItem} item - the item to configure with the given options + * @param {object} options - the new item options. + */ + configure: function(chart, item, options) { + var props = ['fullWidth', 'position', 'weight']; + var ilen = props.length; + var i = 0; + var prop; + + for (; i < ilen; ++i) { + prop = props[i]; + if (options.hasOwnProperty(prop)) { + item[prop] = options[prop]; + } + } + }, + + /** + * Fits boxes of the given chart into the given size by having each box measure itself + * then running a fitting algorithm + * @param {Chart} chart - the chart + * @param {number} width - the width to fit into + * @param {number} height - the height to fit into + */ + update: function(chart, width, height) { + if (!chart) { + return; + } + + var layoutOptions = chart.options.layout || {}; + var padding = helpers$1.options.toPadding(layoutOptions.padding); + + var availableWidth = width - padding.width; + var availableHeight = height - padding.height; + var boxes = buildLayoutBoxes(chart.boxes); + var verticalBoxes = boxes.vertical; + var horizontalBoxes = boxes.horizontal; + + // Essentially we now have any number of boxes on each of the 4 sides. + // Our canvas looks like the following. + // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and + // B1 is the bottom axis + // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays + // These locations are single-box locations only, when trying to register a chartArea location that is already taken, + // an error will be thrown. + // + // |----------------------------------------------------| + // | T1 (Full Width) | + // |----------------------------------------------------| + // | | | T2 | | + // | |----|-------------------------------------|----| + // | | | C1 | | C2 | | + // | | |----| |----| | + // | | | | | + // | L1 | L2 | ChartArea (C0) | R1 | + // | | | | | + // | | |----| |----| | + // | | | C3 | | C4 | | + // | |----|-------------------------------------|----| + // | | | B1 | | + // |----------------------------------------------------| + // | B2 (Full Width) | + // |----------------------------------------------------| + // + + var params = Object.freeze({ + outerWidth: width, + outerHeight: height, + padding: padding, + availableWidth: availableWidth, + vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length, + hBoxMaxHeight: availableHeight / 2 + }); + var chartArea = extend({ + maxPadding: extend({}, padding), + w: availableWidth, + h: availableHeight, + x: padding.left, + y: padding.top + }, padding); + + setLayoutDims(verticalBoxes.concat(horizontalBoxes), params); + + // First fit vertical boxes + fitBoxes(verticalBoxes, chartArea, params); + + // Then fit horizontal boxes + if (fitBoxes(horizontalBoxes, chartArea, params)) { + // if the area changed, re-fit vertical boxes + fitBoxes(verticalBoxes, chartArea, params); + } + + handleMaxPadding(chartArea); + + // Finally place the boxes to correct coordinates + placeBoxes(boxes.leftAndTop, chartArea, params); + + // Move to opposite side of chart + chartArea.x += chartArea.w; + chartArea.y += chartArea.h; + + placeBoxes(boxes.rightAndBottom, chartArea, params); + + chart.chartArea = { + left: chartArea.left, + top: chartArea.top, + right: chartArea.left + chartArea.w, + bottom: chartArea.top + chartArea.h + }; + + // Finally update boxes in chartArea (radial scale for example) + helpers$1.each(boxes.chartArea, function(layout) { + var box = layout.box; + extend(box, chart.chartArea); + box.update(chartArea.w, chartArea.h); + }); + } +}; + +/** + * Platform fallback implementation (minimal). + * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 + */ + +var platform_basic = { + acquireContext: function(item) { + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + return item && item.getContext('2d') || null; + } +}; + +var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; + +var platform_dom$1 = /*#__PURE__*/Object.freeze({ +__proto__: null, +'default': platform_dom +}); + +var stylesheet = getCjsExportFromNamespace(platform_dom$1); + +var EXPANDO_KEY = '$chartjs'; +var CSS_PREFIX = 'chartjs-'; +var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; +var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; +var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; +var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; + +/** + * DOM event types -> Chart.js event types. + * Note: only events with different types are mapped. + * @see https://developer.mozilla.org/en-US/docs/Web/Events + */ +var EVENT_TYPES = { + touchstart: 'mousedown', + touchmove: 'mousemove', + touchend: 'mouseup', + pointerenter: 'mouseenter', + pointerdown: 'mousedown', + pointermove: 'mousemove', + pointerup: 'mouseup', + pointerleave: 'mouseout', + pointerout: 'mouseout' +}; + +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns {number} Size in pixels or undefined if unknown. + */ +function readUsedSize(element, property) { + var value = helpers$1.getStyle(element, property); + var matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? Number(matches[1]) : undefined; +} + +/** + * Initializes the canvas style and render size without modifying the canvas display size, + * since responsiveness is handled by the controller.resize() method. The config is used + * to determine the aspect ratio to apply in case no explicit height has been specified. + */ +function initCanvas(canvas, config) { + var style = canvas.style; + + // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it + // returns null or '' if no explicit value has been set to the canvas attribute. + var renderHeight = canvas.getAttribute('height'); + var renderWidth = canvas.getAttribute('width'); + + // Chart.js modifies some canvas values that we want to restore on destroy + canvas[EXPANDO_KEY] = { + initial: { + height: renderHeight, + width: renderWidth, + style: { + display: style.display, + height: style.height, + width: style.width + } + } + }; + + // Force canvas to display as block to avoid extra space caused by inline + // elements, which would interfere with the responsive resize process. + // https://github.com/chartjs/Chart.js/issues/2538 + style.display = style.display || 'block'; + + if (renderWidth === null || renderWidth === '') { + var displayWidth = readUsedSize(canvas, 'width'); + if (displayWidth !== undefined) { + canvas.width = displayWidth; + } + } + + if (renderHeight === null || renderHeight === '') { + if (canvas.style.height === '') { + // If no explicit render height and style height, let's apply the aspect ratio, + // which one can be specified by the user but also by charts as default option + // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. + canvas.height = canvas.width / (config.options.aspectRatio || 2); + } else { + var displayHeight = readUsedSize(canvas, 'height'); + if (displayWidth !== undefined) { + canvas.height = displayHeight; + } + } + } + + return canvas; +} + +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ +var supportsEventListenerOptions = (function() { + var supports = false; + try { + var options = Object.defineProperty({}, 'passive', { + // eslint-disable-next-line getter-return + get: function() { + supports = true; + } + }); + window.addEventListener('e', null, options); + } catch (e) { + // continue regardless of error + } + return supports; +}()); + +// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. +// https://github.com/chartjs/Chart.js/issues/4287 +var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; + +function addListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} + +function removeListener(node, type, listener) { + node.removeEventListener(type, listener, eventListenerOptions); +} + +function createEvent(type, chart, x, y, nativeEvent) { + return { + type: type, + chart: chart, + native: nativeEvent || null, + x: x !== undefined ? x : null, + y: y !== undefined ? y : null, + }; +} + +function fromNativeEvent(event, chart) { + var type = EVENT_TYPES[event.type] || event.type; + var pos = helpers$1.getRelativePosition(event, chart); + return createEvent(type, chart, pos.x, pos.y, event); +} + +function throttled(fn, thisArg) { + var ticking = false; + var args = []; + + return function() { + args = Array.prototype.slice.call(arguments); + thisArg = thisArg || this; + + if (!ticking) { + ticking = true; + helpers$1.requestAnimFrame.call(window, function() { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} + +function createDiv(cls) { + var el = document.createElement('div'); + el.className = cls || ''; + return el; +} + +// Implementation based on https://github.com/marcj/css-element-queries +function createResizer(handler) { + var maxSize = 1000000; + + // NOTE(SB) Don't use innerHTML because it could be considered unsafe. + // https://github.com/chartjs/Chart.js/issues/5902 + var resizer = createDiv(CSS_SIZE_MONITOR); + var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); + var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); + + expand.appendChild(createDiv()); + shrink.appendChild(createDiv()); + + resizer.appendChild(expand); + resizer.appendChild(shrink); + resizer._reset = function() { + expand.scrollLeft = maxSize; + expand.scrollTop = maxSize; + shrink.scrollLeft = maxSize; + shrink.scrollTop = maxSize; + }; + + var onScroll = function() { + resizer._reset(); + handler(); + }; + + addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); + addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); + + return resizer; +} + +// https://davidwalsh.name/detect-node-insertion +function watchForRender(node, handler) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + var proxy = expando.renderProxy = function(e) { + if (e.animationName === CSS_RENDER_ANIMATION) { + handler(); + } + }; + + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + addListener(node, type, proxy); + }); + + // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class + // is removed then added back immediately (same animation frame?). Accessing the + // `offsetParent` property will force a reflow and re-evaluate the CSS animation. + // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics + // https://github.com/chartjs/Chart.js/issues/4737 + expando.reflow = !!node.offsetParent; + + node.classList.add(CSS_RENDER_MONITOR); +} + +function unwatchForRender(node) { + var expando = node[EXPANDO_KEY] || {}; + var proxy = expando.renderProxy; + + if (proxy) { + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + removeListener(node, type, proxy); + }); + + delete expando.renderProxy; + } + + node.classList.remove(CSS_RENDER_MONITOR); +} + +function addResizeListener(node, listener, chart) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + + // Let's keep track of this added resizer and thus avoid DOM query when removing it. + var resizer = expando.resizer = createResizer(throttled(function() { + if (expando.resizer) { + var container = chart.options.maintainAspectRatio && node.parentNode; + var w = container ? container.clientWidth : 0; + listener(createEvent('resize', chart)); + if (container && container.clientWidth < w && chart.canvas) { + // If the container size shrank during chart resize, let's assume + // scrollbar appeared. So we resize again with the scrollbar visible - + // effectively making chart smaller and the scrollbar hidden again. + // Because we are inside `throttled`, and currently `ticking`, scroll + // events are ignored during this whole 2 resize process. + // If we assumed wrong and something else happened, we are resizing + // twice in a frame (potential performance issue) + listener(createEvent('resize', chart)); + } + } + })); + + // The resizer needs to be attached to the node parent, so we first need to be + // sure that `node` is attached to the DOM before injecting the resizer element. + watchForRender(node, function() { + if (expando.resizer) { + var container = node.parentNode; + if (container && container !== resizer.parentNode) { + container.insertBefore(resizer, container.firstChild); + } + + // The container size might have changed, let's reset the resizer state. + resizer._reset(); + } + }); +} + +function removeResizeListener(node) { + var expando = node[EXPANDO_KEY] || {}; + var resizer = expando.resizer; + + delete expando.resizer; + unwatchForRender(node); + + if (resizer && resizer.parentNode) { + resizer.parentNode.removeChild(resizer); + } +} + +/** + * Injects CSS styles inline if the styles are not already present. + * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>. + * @param {string} css - the CSS to be injected. + */ +function injectCSS(rootNode, css) { + // https://stackoverflow.com/q/3922139 + var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {}); + if (!expando.containsStyles) { + expando.containsStyles = true; + css = '/* Chart.js */\n' + css; + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.appendChild(document.createTextNode(css)); + rootNode.appendChild(style); + } +} + +var platform_dom$2 = { + /** + * When `true`, prevents the automatic injection of the stylesheet required to + * correctly detect when the chart is added to the DOM and then resized. This + * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) + * to be manually imported to make this library compatible with any CSP. + * See https://github.com/chartjs/Chart.js/issues/5208 + */ + disableCSSInjection: false, + + /** + * This property holds whether this platform is enabled for the current environment. + * Currently used by platform.js to select the proper implementation. + * @private + */ + _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', + + /** + * Initializes resources that depend on platform options. + * @param {HTMLCanvasElement} canvas - The Canvas element. + * @private + */ + _ensureLoaded: function(canvas) { + if (!this.disableCSSInjection) { + // If the canvas is in a shadow DOM, then the styles must also be inserted + // into the same shadow DOM. + // https://github.com/chartjs/Chart.js/issues/5763 + var root = canvas.getRootNode ? canvas.getRootNode() : document; + var targetNode = root.host ? root : document.head; + injectCSS(targetNode, stylesheet); + } + }, + + acquireContext: function(item, config) { + if (typeof item === 'string') { + item = document.getElementById(item); + } else if (item.length) { + // Support for array based queries (such as jQuery) + item = item[0]; + } + + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item && item.getContext && item.getContext('2d'); + + // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is + // inside an iframe or when running in a protected environment. We could guess the + // types from their toString() value but let's keep things flexible and assume it's + // a sufficient condition if the item has a context2D which has item as `canvas`. + // https://github.com/chartjs/Chart.js/issues/3887 + // https://github.com/chartjs/Chart.js/issues/4102 + // https://github.com/chartjs/Chart.js/issues/4152 + if (context && context.canvas === item) { + // Load platform resources on first chart creation, to make it possible to + // import the library before setting platform options. + this._ensureLoaded(item); + initCanvas(item, config); + return context; + } + + return null; + }, + + releaseContext: function(context) { + var canvas = context.canvas; + if (!canvas[EXPANDO_KEY]) { + return; + } + + var initial = canvas[EXPANDO_KEY].initial; + ['height', 'width'].forEach(function(prop) { + var value = initial[prop]; + if (helpers$1.isNullOrUndef(value)) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + + helpers$1.each(initial.style || {}, function(value, key) { + canvas.style[key] = value; + }); + + // The canvas render size might have been changed (and thus the state stack discarded), + // we can't use save() and restore() to restore the initial state. So make sure that at + // least the canvas context is reset to the default state by setting the canvas width. + // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html + // eslint-disable-next-line no-self-assign + canvas.width = canvas.width; + + delete canvas[EXPANDO_KEY]; + }, + + addEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + addResizeListener(canvas, listener, chart); + return; + } + + var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); + var proxies = expando.proxies || (expando.proxies = {}); + var proxy = proxies[chart.id + '_' + type] = function(event) { + listener(fromNativeEvent(event, chart)); + }; + + addListener(canvas, type, proxy); + }, + + removeEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + removeResizeListener(canvas); + return; + } + + var expando = listener[EXPANDO_KEY] || {}; + var proxies = expando.proxies || {}; + var proxy = proxies[chart.id + '_' + type]; + if (!proxy) { + return; + } + + removeListener(canvas, type, proxy); + } +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use EventTarget.addEventListener instead. + * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + * @function Chart.helpers.addEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.addEvent = addListener; + +/** + * Provided for backward compatibility, use EventTarget.removeEventListener instead. + * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener + * @function Chart.helpers.removeEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.removeEvent = removeListener; + +// @TODO Make possible to select another platform at build time. +var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; + +/** + * @namespace Chart.platform + * @see https://chartjs.gitbooks.io/proposals/content/Platform.html + * @since 2.4.0 + */ +var platform = helpers$1.extend({ + /** + * @since 2.7.0 + */ + initialize: function() {}, + + /** + * Called at chart construction time, returns a context2d instance implementing + * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. + * @param {*} item - The native item from which to acquire context (platform specific) + * @param {object} options - The chart options + * @returns {CanvasRenderingContext2D} context2d instance + */ + acquireContext: function() {}, + + /** + * Called at chart destruction time, releases any resources associated to the context + * previously returned by the acquireContext() method. + * @param {CanvasRenderingContext2D} context - The context2d instance + * @returns {boolean} true if the method succeeded, else false + */ + releaseContext: function() {}, + + /** + * Registers the specified listener on the given chart. + * @param {Chart} chart - Chart from which to listen for event + * @param {string} type - The ({@link IEvent}) type to listen for + * @param {function} listener - Receives a notification (an object that implements + * the {@link IEvent} interface) when an event of the specified type occurs. + */ + addEventListener: function() {}, + + /** + * Removes the specified listener previously registered with addEventListener. + * @param {Chart} chart - Chart from which to remove the listener + * @param {string} type - The ({@link IEvent}) type to remove + * @param {function} listener - The listener function to remove from the event target. + */ + removeEventListener: function() {} + +}, implementation); + +core_defaults._set('global', { + plugins: {} +}); + +/** + * The plugin service singleton + * @namespace Chart.plugins + * @since 2.1.0 + */ +var core_plugins = { + /** + * Globally registered plugins. + * @private + */ + _plugins: [], + + /** + * This identifier is used to invalidate the descriptors cache attached to each chart + * when a global plugin is registered or unregistered. In this case, the cache ID is + * incremented and descriptors are regenerated during following API calls. + * @private + */ + _cacheId: 0, + + /** + * Registers the given plugin(s) if not already registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + register: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + if (p.indexOf(plugin) === -1) { + p.push(plugin); + } + }); + + this._cacheId++; + }, + + /** + * Unregisters the given plugin(s) only if registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + unregister: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + var idx = p.indexOf(plugin); + if (idx !== -1) { + p.splice(idx, 1); + } + }); + + this._cacheId++; + }, + + /** + * Remove all registered plugins. + * @since 2.1.5 + */ + clear: function() { + this._plugins = []; + this._cacheId++; + }, + + /** + * Returns the number of registered plugins? + * @returns {number} + * @since 2.1.5 + */ + count: function() { + return this._plugins.length; + }, + + /** + * Returns all registered plugin instances. + * @returns {IPlugin[]} array of plugin objects. + * @since 2.1.5 + */ + getAll: function() { + return this._plugins; + }, + + /** + * Calls enabled plugins for `chart` on the specified hook and with the given args. + * This method immediately returns as soon as a plugin explicitly returns false. The + * returned value can be used, for instance, to interrupt the current action. + * @param {Chart} chart - The chart instance for which plugins should be called. + * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). + * @param {Array} [args] - Extra arguments to apply to the hook call. + * @returns {boolean} false if any of the plugins return false, else returns true. + */ + notify: function(chart, hook, args) { + var descriptors = this.descriptors(chart); + var ilen = descriptors.length; + var i, descriptor, plugin, params, method; + + for (i = 0; i < ilen; ++i) { + descriptor = descriptors[i]; + plugin = descriptor.plugin; + method = plugin[hook]; + if (typeof method === 'function') { + params = [chart].concat(args || []); + params.push(descriptor.options); + if (method.apply(plugin, params) === false) { + return false; + } + } + } + + return true; + }, + + /** + * Returns descriptors of enabled plugins for the given chart. + * @returns {object[]} [{ plugin, options }] + * @private + */ + descriptors: function(chart) { + var cache = chart.$plugins || (chart.$plugins = {}); + if (cache.id === this._cacheId) { + return cache.descriptors; + } + + var plugins = []; + var descriptors = []; + var config = (chart && chart.config) || {}; + var options = (config.options && config.options.plugins) || {}; + + this._plugins.concat(config.plugins || []).forEach(function(plugin) { + var idx = plugins.indexOf(plugin); + if (idx !== -1) { + return; + } + + var id = plugin.id; + var opts = options[id]; + if (opts === false) { + return; + } + + if (opts === true) { + opts = helpers$1.clone(core_defaults.global.plugins[id]); + } + + plugins.push(plugin); + descriptors.push({ + plugin: plugin, + options: opts || {} + }); + }); + + cache.descriptors = descriptors; + cache.id = this._cacheId; + return descriptors; + }, + + /** + * Invalidates cache for the given chart: descriptors hold a reference on plugin option, + * but in some cases, this reference can be changed by the user when updating options. + * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + * @private + */ + _invalidate: function(chart) { + delete chart.$plugins; + } +}; + +var core_scaleService = { + // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then + // use the new chart options to grab the correct scale + constructors: {}, + // Use a registration function so that we can move to an ES6 map when we no longer need to support + // old browsers + + // Scale config defaults + defaults: {}, + registerScaleType: function(type, scaleConstructor, scaleDefaults) { + this.constructors[type] = scaleConstructor; + this.defaults[type] = helpers$1.clone(scaleDefaults); + }, + getScaleConstructor: function(type) { + return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; + }, + getScaleDefaults: function(type) { + // Return the scale defaults merged with the global settings so that we always use the latest ones + return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; + }, + updateScaleDefaults: function(type, additions) { + var me = this; + if (me.defaults.hasOwnProperty(type)) { + me.defaults[type] = helpers$1.extend(me.defaults[type], additions); + } + }, + addScalesToLayout: function(chart) { + // Adds each scale to the chart.boxes array to be sized accordingly + helpers$1.each(chart.scales, function(scale) { + // Set ILayoutItem parameters for backwards compatibility + scale.fullWidth = scale.options.fullWidth; + scale.position = scale.options.position; + scale.weight = scale.options.weight; + core_layouts.addBox(chart, scale); + }); + } +}; + +var valueOrDefault$8 = helpers$1.valueOrDefault; +var getRtlHelper = helpers$1.rtl.getRtlAdapter; + +core_defaults._set('global', { + tooltips: { + enabled: true, + custom: null, + mode: 'nearest', + position: 'average', + intersect: true, + backgroundColor: 'rgba(0,0,0,0.8)', + titleFontStyle: 'bold', + titleSpacing: 2, + titleMarginBottom: 6, + titleFontColor: '#fff', + titleAlign: 'left', + bodySpacing: 2, + bodyFontColor: '#fff', + bodyAlign: 'left', + footerFontStyle: 'bold', + footerSpacing: 2, + footerMarginTop: 6, + footerFontColor: '#fff', + footerAlign: 'left', + yPadding: 6, + xPadding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + multiKeyBackground: '#fff', + displayColors: true, + borderColor: 'rgba(0,0,0,0)', + borderWidth: 0, + callbacks: { + // Args are: (tooltipItems, data) + beforeTitle: helpers$1.noop, + title: function(tooltipItems, data) { + var title = ''; + var labels = data.labels; + var labelCount = labels ? labels.length : 0; + + if (tooltipItems.length > 0) { + var item = tooltipItems[0]; + if (item.label) { + title = item.label; + } else if (item.xLabel) { + title = item.xLabel; + } else if (labelCount > 0 && item.index < labelCount) { + title = labels[item.index]; + } + } + + return title; + }, + afterTitle: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeBody: helpers$1.noop, + + // Args are: (tooltipItem, data) + beforeLabel: helpers$1.noop, + label: function(tooltipItem, data) { + var label = data.datasets[tooltipItem.datasetIndex].label || ''; + + if (label) { + label += ': '; + } + if (!helpers$1.isNullOrUndef(tooltipItem.value)) { + label += tooltipItem.value; + } else { + label += tooltipItem.yLabel; + } + return label; + }, + labelColor: function(tooltipItem, chart) { + var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); + var activeElement = meta.data[tooltipItem.index]; + var view = activeElement._view; + return { + borderColor: view.borderColor, + backgroundColor: view.backgroundColor + }; + }, + labelTextColor: function() { + return this._options.bodyFontColor; + }, + afterLabel: helpers$1.noop, + + // Args are: (tooltipItems, data) + afterBody: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeFooter: helpers$1.noop, + footer: helpers$1.noop, + afterFooter: helpers$1.noop + } + } +}); + +var positioners = { + /** + * Average mode places the tooltip at the average position of the elements shown + * @function Chart.Tooltip.positioners.average + * @param elements {ChartElement[]} the elements being displayed in the tooltip + * @returns {object} tooltip position + */ + average: function(elements) { + if (!elements.length) { + return false; + } + + var i, len; + var x = 0; + var y = 0; + var count = 0; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var pos = el.tooltipPosition(); + x += pos.x; + y += pos.y; + ++count; + } + } + + return { + x: x / count, + y: y / count + }; + }, + + /** + * Gets the tooltip position nearest of the item nearest to the event position + * @function Chart.Tooltip.positioners.nearest + * @param elements {Chart.Element[]} the tooltip elements + * @param eventPosition {object} the position of the event in canvas coordinates + * @returns {object} the tooltip position + */ + nearest: function(elements, eventPosition) { + var x = eventPosition.x; + var y = eventPosition.y; + var minDistance = Number.POSITIVE_INFINITY; + var i, len, nearestElement; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var center = el.getCenterPoint(); + var d = helpers$1.distanceBetweenPoints(eventPosition, center); + + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + + if (nearestElement) { + var tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + + return { + x: x, + y: y + }; + } +}; + +// Helper to push or concat based on if the 2nd parameter is an array or not +function pushOrConcat(base, toPush) { + if (toPush) { + if (helpers$1.isArray(toPush)) { + // base = base.concat(toPush); + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + + return base; +} + +/** + * Returns array of strings split by newline + * @param {string} value - The value to split by newline. + * @returns {string[]} value if newline present - Returned from String split() method + * @function + */ +function splitNewlines(str) { + if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { + return str.split('\n'); + } + return str; +} + + +/** + * Private helper to create a tooltip item model + * @param element - the chart element (point, arc, bar) to create the tooltip item for + * @return new tooltip item + */ +function createTooltipItem(element) { + var xScale = element._xScale; + var yScale = element._yScale || element._scale; // handle radar || polarArea charts + var index = element._index; + var datasetIndex = element._datasetIndex; + var controller = element._chart.getDatasetMeta(datasetIndex).controller; + var indexScale = controller._getIndexScale(); + var valueScale = controller._getValueScale(); + + return { + xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', + yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', + label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', + value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', + index: index, + datasetIndex: datasetIndex, + x: element._model.x, + y: element._model.y + }; +} + +/** + * Helper to get the reset model for the tooltip + * @param tooltipOpts {object} the tooltip options + */ +function getBaseModel(tooltipOpts) { + var globalDefaults = core_defaults.global; + + return { + // Positioning + xPadding: tooltipOpts.xPadding, + yPadding: tooltipOpts.yPadding, + xAlign: tooltipOpts.xAlign, + yAlign: tooltipOpts.yAlign, + + // Drawing direction and text direction + rtl: tooltipOpts.rtl, + textDirection: tooltipOpts.textDirection, + + // Body + bodyFontColor: tooltipOpts.bodyFontColor, + _bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), + _bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), + _bodyAlign: tooltipOpts.bodyAlign, + bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), + bodySpacing: tooltipOpts.bodySpacing, + + // Title + titleFontColor: tooltipOpts.titleFontColor, + _titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), + _titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), + titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), + _titleAlign: tooltipOpts.titleAlign, + titleSpacing: tooltipOpts.titleSpacing, + titleMarginBottom: tooltipOpts.titleMarginBottom, + + // Footer + footerFontColor: tooltipOpts.footerFontColor, + _footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), + _footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), + footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), + _footerAlign: tooltipOpts.footerAlign, + footerSpacing: tooltipOpts.footerSpacing, + footerMarginTop: tooltipOpts.footerMarginTop, + + // Appearance + caretSize: tooltipOpts.caretSize, + cornerRadius: tooltipOpts.cornerRadius, + backgroundColor: tooltipOpts.backgroundColor, + opacity: 0, + legendColorBackground: tooltipOpts.multiKeyBackground, + displayColors: tooltipOpts.displayColors, + borderColor: tooltipOpts.borderColor, + borderWidth: tooltipOpts.borderWidth + }; +} + +/** + * Get the size of the tooltip + */ +function getTooltipSize(tooltip, model) { + var ctx = tooltip._chart.ctx; + + var height = model.yPadding * 2; // Tooltip Padding + var width = 0; + + // Count of all lines in the body + var body = model.body; + var combinedBodyLength = body.reduce(function(count, bodyItem) { + return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; + }, 0); + combinedBodyLength += model.beforeBody.length + model.afterBody.length; + + var titleLineCount = model.title.length; + var footerLineCount = model.footer.length; + var titleFontSize = model.titleFontSize; + var bodyFontSize = model.bodyFontSize; + var footerFontSize = model.footerFontSize; + + height += titleLineCount * titleFontSize; // Title Lines + height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing + height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin + height += combinedBodyLength * bodyFontSize; // Body Lines + height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing + height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin + height += footerLineCount * (footerFontSize); // Footer Lines + height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing + + // Title width + var widthPadding = 0; + var maxLineWidth = function(line) { + width = Math.max(width, ctx.measureText(line).width + widthPadding); + }; + + ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); + helpers$1.each(model.title, maxLineWidth); + + // Body width + ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); + helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); + + // Body lines may include some extra width due to the color box + widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; + helpers$1.each(body, function(bodyItem) { + helpers$1.each(bodyItem.before, maxLineWidth); + helpers$1.each(bodyItem.lines, maxLineWidth); + helpers$1.each(bodyItem.after, maxLineWidth); + }); + + // Reset back to 0 + widthPadding = 0; + + // Footer width + ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); + helpers$1.each(model.footer, maxLineWidth); + + // Add padding + width += 2 * model.xPadding; + + return { + width: width, + height: height + }; +} + +/** + * Helper to get the alignment of a tooltip given the size + */ +function determineAlignment(tooltip, size) { + var model = tooltip._model; + var chart = tooltip._chart; + var chartArea = tooltip._chart.chartArea; + var xAlign = 'center'; + var yAlign = 'center'; + + if (model.y < size.height) { + yAlign = 'top'; + } else if (model.y > (chart.height - size.height)) { + yAlign = 'bottom'; + } + + var lf, rf; // functions to determine left, right alignment + var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart + var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges + var midX = (chartArea.left + chartArea.right) / 2; + var midY = (chartArea.top + chartArea.bottom) / 2; + + if (yAlign === 'center') { + lf = function(x) { + return x <= midX; + }; + rf = function(x) { + return x > midX; + }; + } else { + lf = function(x) { + return x <= (size.width / 2); + }; + rf = function(x) { + return x >= (chart.width - (size.width / 2)); + }; + } + + olf = function(x) { + return x + size.width + model.caretSize + model.caretPadding > chart.width; + }; + orf = function(x) { + return x - size.width - model.caretSize - model.caretPadding < 0; + }; + yf = function(y) { + return y <= midY ? 'top' : 'bottom'; + }; + + if (lf(model.x)) { + xAlign = 'left'; + + // Is tooltip too wide and goes over the right side of the chart.? + if (olf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } else if (rf(model.x)) { + xAlign = 'right'; + + // Is tooltip too wide and goes outside left edge of canvas? + if (orf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } + + var opts = tooltip._options; + return { + xAlign: opts.xAlign ? opts.xAlign : xAlign, + yAlign: opts.yAlign ? opts.yAlign : yAlign + }; +} + +/** + * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment + */ +function getBackgroundPoint(vm, size, alignment, chart) { + // Background Position + var x = vm.x; + var y = vm.y; + + var caretSize = vm.caretSize; + var caretPadding = vm.caretPadding; + var cornerRadius = vm.cornerRadius; + var xAlign = alignment.xAlign; + var yAlign = alignment.yAlign; + var paddingAndSize = caretSize + caretPadding; + var radiusAndPadding = cornerRadius + caretPadding; + + if (xAlign === 'right') { + x -= size.width; + } else if (xAlign === 'center') { + x -= (size.width / 2); + if (x + size.width > chart.width) { + x = chart.width - size.width; + } + if (x < 0) { + x = 0; + } + } + + if (yAlign === 'top') { + y += paddingAndSize; + } else if (yAlign === 'bottom') { + y -= size.height + paddingAndSize; + } else { + y -= (size.height / 2); + } + + if (yAlign === 'center') { + if (xAlign === 'left') { + x += paddingAndSize; + } else if (xAlign === 'right') { + x -= paddingAndSize; + } + } else if (xAlign === 'left') { + x -= radiusAndPadding; + } else if (xAlign === 'right') { + x += radiusAndPadding; + } + + return { + x: x, + y: y + }; +} + +function getAlignedX(vm, align) { + return align === 'center' + ? vm.x + vm.width / 2 + : align === 'right' + ? vm.x + vm.width - vm.xPadding + : vm.x + vm.xPadding; +} + +/** + * Helper to build before and after body lines + */ +function getBeforeAfterBodyLines(callback) { + return pushOrConcat([], splitNewlines(callback)); +} + +var exports$4 = core_element.extend({ + initialize: function() { + this._model = getBaseModel(this._options); + this._lastActive = []; + }, + + // Get the title + // Args are: (tooltipItem, data) + getTitle: function() { + var me = this; + var opts = me._options; + var callbacks = opts.callbacks; + + var beforeTitle = callbacks.beforeTitle.apply(me, arguments); + var title = callbacks.title.apply(me, arguments); + var afterTitle = callbacks.afterTitle.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeTitle)); + lines = pushOrConcat(lines, splitNewlines(title)); + lines = pushOrConcat(lines, splitNewlines(afterTitle)); + + return lines; + }, + + // Args are: (tooltipItem, data) + getBeforeBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); + }, + + // Args are: (tooltipItem, data) + getBody: function(tooltipItems, data) { + var me = this; + var callbacks = me._options.callbacks; + var bodyItems = []; + + helpers$1.each(tooltipItems, function(tooltipItem) { + var bodyItem = { + before: [], + lines: [], + after: [] + }; + pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); + pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); + pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); + + bodyItems.push(bodyItem); + }); + + return bodyItems; + }, + + // Args are: (tooltipItem, data) + getAfterBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); + }, + + // Get the footer and beforeFooter and afterFooter lines + // Args are: (tooltipItem, data) + getFooter: function() { + var me = this; + var callbacks = me._options.callbacks; + + var beforeFooter = callbacks.beforeFooter.apply(me, arguments); + var footer = callbacks.footer.apply(me, arguments); + var afterFooter = callbacks.afterFooter.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeFooter)); + lines = pushOrConcat(lines, splitNewlines(footer)); + lines = pushOrConcat(lines, splitNewlines(afterFooter)); + + return lines; + }, + + update: function(changed) { + var me = this; + var opts = me._options; + + // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition + // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time + // which breaks any animations. + var existingModel = me._model; + var model = me._model = getBaseModel(opts); + var active = me._active; + + var data = me._data; + + // In the case where active.length === 0 we need to keep these at existing values for good animations + var alignment = { + xAlign: existingModel.xAlign, + yAlign: existingModel.yAlign + }; + var backgroundPoint = { + x: existingModel.x, + y: existingModel.y + }; + var tooltipSize = { + width: existingModel.width, + height: existingModel.height + }; + var tooltipPosition = { + x: existingModel.caretX, + y: existingModel.caretY + }; + + var i, len; + + if (active.length) { + model.opacity = 1; + + var labelColors = []; + var labelTextColors = []; + tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); + + var tooltipItems = []; + for (i = 0, len = active.length; i < len; ++i) { + tooltipItems.push(createTooltipItem(active[i])); + } + + // If the user provided a filter function, use it to modify the tooltip items + if (opts.filter) { + tooltipItems = tooltipItems.filter(function(a) { + return opts.filter(a, data); + }); + } + + // If the user provided a sorting function, use it to modify the tooltip items + if (opts.itemSort) { + tooltipItems = tooltipItems.sort(function(a, b) { + return opts.itemSort(a, b, data); + }); + } + + // Determine colors for boxes + helpers$1.each(tooltipItems, function(tooltipItem) { + labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); + labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); + }); + + + // Build the Text Lines + model.title = me.getTitle(tooltipItems, data); + model.beforeBody = me.getBeforeBody(tooltipItems, data); + model.body = me.getBody(tooltipItems, data); + model.afterBody = me.getAfterBody(tooltipItems, data); + model.footer = me.getFooter(tooltipItems, data); + + // Initial positioning and colors + model.x = tooltipPosition.x; + model.y = tooltipPosition.y; + model.caretPadding = opts.caretPadding; + model.labelColors = labelColors; + model.labelTextColors = labelTextColors; + + // data points + model.dataPoints = tooltipItems; + + // We need to determine alignment of the tooltip + tooltipSize = getTooltipSize(this, model); + alignment = determineAlignment(this, tooltipSize); + // Final Size and Position + backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); + } else { + model.opacity = 0; + } + + model.xAlign = alignment.xAlign; + model.yAlign = alignment.yAlign; + model.x = backgroundPoint.x; + model.y = backgroundPoint.y; + model.width = tooltipSize.width; + model.height = tooltipSize.height; + + // Point where the caret on the tooltip points to + model.caretX = tooltipPosition.x; + model.caretY = tooltipPosition.y; + + me._model = model; + + if (changed && opts.custom) { + opts.custom.call(me, model); + } + + return me; + }, + + drawCaret: function(tooltipPoint, size) { + var ctx = this._chart.ctx; + var vm = this._view; + var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); + + ctx.lineTo(caretPosition.x1, caretPosition.y1); + ctx.lineTo(caretPosition.x2, caretPosition.y2); + ctx.lineTo(caretPosition.x3, caretPosition.y3); + }, + getCaretPosition: function(tooltipPoint, size, vm) { + var x1, x2, x3, y1, y2, y3; + var caretSize = vm.caretSize; + var cornerRadius = vm.cornerRadius; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var ptX = tooltipPoint.x; + var ptY = tooltipPoint.y; + var width = size.width; + var height = size.height; + + if (yAlign === 'center') { + y2 = ptY + (height / 2); + + if (xAlign === 'left') { + x1 = ptX; + x2 = x1 - caretSize; + x3 = x1; + + y1 = y2 + caretSize; + y3 = y2 - caretSize; + } else { + x1 = ptX + width; + x2 = x1 + caretSize; + x3 = x1; + + y1 = y2 - caretSize; + y3 = y2 + caretSize; + } + } else { + if (xAlign === 'left') { + x2 = ptX + cornerRadius + (caretSize); + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else if (xAlign === 'right') { + x2 = ptX + width - cornerRadius - caretSize; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else { + x2 = vm.caretX; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } + if (yAlign === 'top') { + y1 = ptY; + y2 = y1 - caretSize; + y3 = y1; + } else { + y1 = ptY + height; + y2 = y1 + caretSize; + y3 = y1; + // invert drawing order + var tmp = x3; + x3 = x1; + x1 = tmp; + } + } + return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; + }, + + drawTitle: function(pt, vm, ctx) { + var title = vm.title; + var length = title.length; + var titleFontSize, titleSpacing, i; + + if (length) { + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + pt.x = getAlignedX(vm, vm._titleAlign); + + ctx.textAlign = rtlHelper.textAlign(vm._titleAlign); + ctx.textBaseline = 'middle'; + + titleFontSize = vm.titleFontSize; + titleSpacing = vm.titleSpacing; + + ctx.fillStyle = vm.titleFontColor; + ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); + + for (i = 0; i < length; ++i) { + ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2); + pt.y += titleFontSize + titleSpacing; // Line Height and spacing + + if (i + 1 === length) { + pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing + } + } + } + }, + + drawBody: function(pt, vm, ctx) { + var bodyFontSize = vm.bodyFontSize; + var bodySpacing = vm.bodySpacing; + var bodyAlign = vm._bodyAlign; + var body = vm.body; + var drawColorBoxes = vm.displayColors; + var xLinePadding = 0; + var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; + + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + var fillLineOfText = function(line) { + ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2); + pt.y += bodyFontSize + bodySpacing; + }; + + var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen; + var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); + + ctx.textAlign = bodyAlign; + ctx.textBaseline = 'middle'; + ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); + + pt.x = getAlignedX(vm, bodyAlignForCalculation); + + // Before body lines + ctx.fillStyle = vm.bodyFontColor; + helpers$1.each(vm.beforeBody, fillLineOfText); + + xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right' + ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) + : 0; + + // Draw body lines now + for (i = 0, ilen = body.length; i < ilen; ++i) { + bodyItem = body[i]; + textColor = vm.labelTextColors[i]; + labelColors = vm.labelColors[i]; + + ctx.fillStyle = textColor; + helpers$1.each(bodyItem.before, fillLineOfText); + + lines = bodyItem.lines; + for (j = 0, jlen = lines.length; j < jlen; ++j) { + // Draw Legend-like boxes if needed + if (drawColorBoxes) { + var rtlColorX = rtlHelper.x(colorX); + + // Fill a white rect so that colours merge nicely if the opacity is < 1 + ctx.fillStyle = vm.legendColorBackground; + ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); + + // Border + ctx.lineWidth = 1; + ctx.strokeStyle = labelColors.borderColor; + ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); + + // Inner square + ctx.fillStyle = labelColors.backgroundColor; + ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); + ctx.fillStyle = textColor; + } + + fillLineOfText(lines[j]); + } + + helpers$1.each(bodyItem.after, fillLineOfText); + } + + // Reset back to 0 for after body + xLinePadding = 0; + + // After body lines + helpers$1.each(vm.afterBody, fillLineOfText); + pt.y -= bodySpacing; // Remove last body spacing + }, + + drawFooter: function(pt, vm, ctx) { + var footer = vm.footer; + var length = footer.length; + var footerFontSize, i; + + if (length) { + var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); + + pt.x = getAlignedX(vm, vm._footerAlign); + pt.y += vm.footerMarginTop; + + ctx.textAlign = rtlHelper.textAlign(vm._footerAlign); + ctx.textBaseline = 'middle'; + + footerFontSize = vm.footerFontSize; + + ctx.fillStyle = vm.footerFontColor; + ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); + + for (i = 0; i < length; ++i) { + ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2); + pt.y += footerFontSize + vm.footerSpacing; + } + } + }, + + drawBackground: function(pt, vm, ctx, tooltipSize) { + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var x = pt.x; + var y = pt.y; + var width = tooltipSize.width; + var height = tooltipSize.height; + var radius = vm.cornerRadius; + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + if (yAlign === 'top') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + if (yAlign === 'center' && xAlign === 'right') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + if (yAlign === 'bottom') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + if (yAlign === 'center' && xAlign === 'left') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + + ctx.fill(); + + if (vm.borderWidth > 0) { + ctx.stroke(); + } + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + + if (vm.opacity === 0) { + return; + } + + var tooltipSize = { + width: vm.width, + height: vm.height + }; + var pt = { + x: vm.x, + y: vm.y + }; + + // IE11/Edge does not like very small opacities, so snap to 0 + var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; + + // Truthy/falsey value for empty tooltip + var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; + + if (this._options.enabled && hasTooltipContent) { + ctx.save(); + ctx.globalAlpha = opacity; + + // Draw Background + this.drawBackground(pt, vm, ctx, tooltipSize); + + // Draw Title, Body, and Footer + pt.y += vm.yPadding; + + helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection); + + // Titles + this.drawTitle(pt, vm, ctx); + + // Body + this.drawBody(pt, vm, ctx); + + // Footer + this.drawFooter(pt, vm, ctx); + + helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection); + + ctx.restore(); + } + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + * @returns {boolean} true if the tooltip changed + */ + handleEvent: function(e) { + var me = this; + var options = me._options; + var changed = false; + + me._lastActive = me._lastActive || []; + + // Find Active Elements for tooltips + if (e.type === 'mouseout') { + me._active = []; + } else { + me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); + if (options.reverse) { + me._active.reverse(); + } + } + + // Remember Last Actives + changed = !helpers$1.arrayEquals(me._active, me._lastActive); + + // Only handle target event on tooltip change + if (changed) { + me._lastActive = me._active; + + if (options.enabled || options.custom) { + me._eventPosition = { + x: e.x, + y: e.y + }; + + me.update(true); + me.pivot(); + } + } + + return changed; + } +}); + +/** + * @namespace Chart.Tooltip.positioners + */ +var positioners_1 = positioners; + +var core_tooltip = exports$4; +core_tooltip.positioners = positioners_1; + +var valueOrDefault$9 = helpers$1.valueOrDefault; + +core_defaults._set('global', { + elements: {}, + events: [ + 'mousemove', + 'mouseout', + 'click', + 'touchstart', + 'touchmove' + ], + hover: { + onHover: null, + mode: 'nearest', + intersect: true, + animationDuration: 400 + }, + onClick: null, + maintainAspectRatio: true, + responsive: true, + responsiveAnimationDuration: 0 +}); + +/** + * Recursively merge the given config objects representing the `scales` option + * by incorporating scale defaults in `xAxes` and `yAxes` array items, then + * returns a deep copy of the result, thus doesn't alter inputs. + */ +function mergeScaleConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + if (key === 'xAxes' || key === 'yAxes') { + var slen = source[key].length; + var i, type, scale; + + if (!target[key]) { + target[key] = []; + } + + for (i = 0; i < slen; ++i) { + scale = source[key][i]; + type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear'); + + if (i >= target[key].length) { + target[key].push({}); + } + + if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { + // new/untyped scale or type changed: let's apply the new defaults + // then merge source scale to correctly overwrite the defaults. + helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); + } else { + // scales type are the same + helpers$1.merge(target[key][i], scale); + } + } + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +/** + * Recursively merge the given config objects as the root options by handling + * default scale options for the `scales` and `scale` properties, then returns + * a deep copy of the result, thus doesn't alter inputs. + */ +function mergeConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + var tval = target[key] || {}; + var sval = source[key]; + + if (key === 'scales') { + // scale config merging is complex. Add our own function here for that + target[key] = mergeScaleConfig(tval, sval); + } else if (key === 'scale') { + // used in polar area & radar charts since there is only one scale + target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +function initConfig(config) { + config = config || {}; + + // Do NOT use mergeConfig for the data object because this method merges arrays + // and so would change references to labels and datasets, preventing data updates. + var data = config.data = config.data || {}; + data.datasets = data.datasets || []; + data.labels = data.labels || []; + + config.options = mergeConfig( + core_defaults.global, + core_defaults[config.type], + config.options || {}); + + return config; +} + +function updateConfig(chart) { + var newOptions = chart.options; + + helpers$1.each(chart.scales, function(scale) { + core_layouts.removeBox(chart, scale); + }); + + newOptions = mergeConfig( + core_defaults.global, + core_defaults[chart.config.type], + newOptions); + + chart.options = chart.config.options = newOptions; + chart.ensureScalesHaveIDs(); + chart.buildOrUpdateScales(); + + // Tooltip + chart.tooltip._options = newOptions.tooltips; + chart.tooltip.initialize(); +} + +function nextAvailableScaleId(axesOpts, prefix, index) { + var id; + var hasId = function(obj) { + return obj.id === id; + }; + + do { + id = prefix + index++; + } while (helpers$1.findIndex(axesOpts, hasId) >= 0); + + return id; +} + +function positionIsHorizontal(position) { + return position === 'top' || position === 'bottom'; +} + +function compare2Level(l1, l2) { + return function(a, b) { + return a[l1] === b[l1] + ? a[l2] - b[l2] + : a[l1] - b[l1]; + }; +} + +var Chart = function(item, config) { + this.construct(item, config); + return this; +}; + +helpers$1.extend(Chart.prototype, /** @lends Chart */ { + /** + * @private + */ + construct: function(item, config) { + var me = this; + + config = initConfig(config); + + var context = platform.acquireContext(item, config); + var canvas = context && context.canvas; + var height = canvas && canvas.height; + var width = canvas && canvas.width; + + me.id = helpers$1.uid(); + me.ctx = context; + me.canvas = canvas; + me.config = config; + me.width = width; + me.height = height; + me.aspectRatio = height ? width / height : null; + me.options = config.options; + me._bufferedRender = false; + me._layers = []; + + /** + * Provided for backward compatibility, Chart and Chart.Controller have been merged, + * the "instance" still need to be defined since it might be called from plugins. + * @prop Chart#chart + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ + me.chart = me; + me.controller = me; // chart.chart.controller #inception + + // Add the chart instance to the global namespace + Chart.instances[me.id] = me; + + // Define alias to the config data: `chart.data === chart.config.data` + Object.defineProperty(me, 'data', { + get: function() { + return me.config.data; + }, + set: function(value) { + me.config.data = value; + } + }); + + if (!context || !canvas) { + // The given item is not a compatible context2d element, let's return before finalizing + // the chart initialization but after setting basic chart / controller properties that + // can help to figure out that the chart is not valid (e.g chart.canvas !== null); + // https://github.com/chartjs/Chart.js/issues/2807 + console.error("Failed to create chart: can't acquire context from the given item"); + return; + } + + me.initialize(); + me.update(); + }, + + /** + * @private + */ + initialize: function() { + var me = this; + + // Before init plugin notification + core_plugins.notify(me, 'beforeInit'); + + helpers$1.retinaScale(me, me.options.devicePixelRatio); + + me.bindEvents(); + + if (me.options.responsive) { + // Initial resize before chart draws (must be silent to preserve initial animations). + me.resize(true); + } + + me.initToolTip(); + + // After init plugin notification + core_plugins.notify(me, 'afterInit'); + + return me; + }, + + clear: function() { + helpers$1.canvas.clear(this); + return this; + }, + + stop: function() { + // Stops any current animation loop occurring + core_animations.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var options = me.options; + var canvas = me.canvas; + var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; + + // the canvas render width and height will be casted to integers so make sure that + // the canvas display style uses the same integer values to avoid blurring effect. + + // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed + var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); + var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); + + if (me.width === newWidth && me.height === newHeight) { + return; + } + + canvas.width = me.width = newWidth; + canvas.height = me.height = newHeight; + canvas.style.width = newWidth + 'px'; + canvas.style.height = newHeight + 'px'; + + helpers$1.retinaScale(me, options.devicePixelRatio); + + if (!silent) { + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + core_plugins.notify(me, 'resize', [newSize]); + + // Notify of resize + if (options.onResize) { + options.onResize(me, newSize); + } + + me.stop(); + me.update({ + duration: options.responsiveAnimationDuration + }); + } + }, + + ensureScalesHaveIDs: function() { + var options = this.options; + var scalesOptions = options.scales || {}; + var scaleOptions = options.scale; + + helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { + if (!xAxisOptions.id) { + xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index); + } + }); + + helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { + if (!yAxisOptions.id) { + yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index); + } + }); + + if (scaleOptions) { + scaleOptions.id = scaleOptions.id || 'scale'; + } + }, + + /** + * Builds a map of scale ID to scale object for future lookup. + */ + buildOrUpdateScales: function() { + var me = this; + var options = me.options; + var scales = me.scales || {}; + var items = []; + var updated = Object.keys(scales).reduce(function(obj, id) { + obj[id] = false; + return obj; + }, {}); + + if (options.scales) { + items = items.concat( + (options.scales.xAxes || []).map(function(xAxisOptions) { + return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; + }) + ); + } + + if (options.scale) { + items.push({ + options: options.scale, + dtype: 'radialLinear', + isDefault: true, + dposition: 'chartArea' + }); + } + + helpers$1.each(items, function(item) { + var scaleOptions = item.options; + var id = scaleOptions.id; + var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype); + + if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { + scaleOptions.position = item.dposition; + } + + updated[id] = true; + var scale = null; + if (id in scales && scales[id].type === scaleType) { + scale = scales[id]; + scale.options = scaleOptions; + scale.ctx = me.ctx; + scale.chart = me; + } else { + var scaleClass = core_scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + scale = new scaleClass({ + id: id, + type: scaleType, + options: scaleOptions, + ctx: me.ctx, + chart: me + }); + scales[scale.id] = scale; + } + + scale.mergeTicksOptions(); + + // TODO(SB): I think we should be able to remove this custom case (options.scale) + // and consider it as a regular scale part of the "scales"" map only! This would + // make the logic easier and remove some useless? custom code. + if (item.isDefault) { + me.scale = scale; + } + }); + // clear up discarded scales + helpers$1.each(updated, function(hasUpdated, id) { + if (!hasUpdated) { + delete scales[id]; + } + }); + + me.scales = scales; + + core_scaleService.addScalesToLayout(this); + }, + + buildOrUpdateControllers: function() { + var me = this; + var newControllers = []; + var datasets = me.data.datasets; + var i, ilen; + + for (i = 0, ilen = datasets.length; i < ilen; i++) { + var dataset = datasets[i]; + var meta = me.getDatasetMeta(i); + var type = dataset.type || me.config.type; + + if (meta.type && meta.type !== type) { + me.destroyDatasetMeta(i); + meta = me.getDatasetMeta(i); + } + meta.type = type; + meta.order = dataset.order || 0; + meta.index = i; + + if (meta.controller) { + meta.controller.updateIndex(i); + meta.controller.linkScales(); + } else { + var ControllerClass = controllers[meta.type]; + if (ControllerClass === undefined) { + throw new Error('"' + meta.type + '" is not a chart type.'); + } + + meta.controller = new ControllerClass(me, i); + newControllers.push(meta.controller); + } + } + + return newControllers; + }, + + /** + * Reset the elements of all datasets + * @private + */ + resetElements: function() { + var me = this; + helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.reset(); + }, me); + }, + + /** + * Resets the chart back to it's state before the initial animation + */ + reset: function() { + this.resetElements(); + this.tooltip.initialize(); + }, + + update: function(config) { + var me = this; + var i, ilen; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + updateConfig(me); + + // plugins options references might have change, let's invalidate the cache + // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + core_plugins._invalidate(me); + + if (core_plugins.notify(me, 'beforeUpdate') === false) { + return; + } + + // In case the entire data object changed + me.tooltip._data = me.data; + + // Make sure dataset controllers are updated and new controllers are reset + var newControllers = me.buildOrUpdateControllers(); + + // Make sure all dataset controllers have correct meta data counts + for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) { + me.getDatasetMeta(i).controller.buildOrUpdateElements(); + } + + me.updateLayout(); + + // Can only reset the new controllers after the scales have been updated + if (me.options.animation && me.options.animation.duration) { + helpers$1.each(newControllers, function(controller) { + controller.reset(); + }); + } + + me.updateDatasets(); + + // Need to reset tooltip in case it is displayed with elements that are removed + // after update. + me.tooltip.initialize(); + + // Last active contains items that were previously in the tooltip. + // When we reset the tooltip, we need to clear it + me.lastActive = []; + + // Do this before render so that any plugins that need final scale updates can use it + core_plugins.notify(me, 'afterUpdate'); + + me._layers.sort(compare2Level('z', '_idx')); + + if (me._bufferedRender) { + me._bufferedRequest = { + duration: config.duration, + easing: config.easing, + lazy: config.lazy + }; + } else { + me.render(config); + } + }, + + /** + * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` + * hook, in which case, plugins will not be called on `afterLayout`. + * @private + */ + updateLayout: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeLayout') === false) { + return; + } + + core_layouts.update(this, this.width, this.height); + + me._layers = []; + helpers$1.each(me.boxes, function(box) { + // _configure is called twice, once in core.scale.update and once here. + // Here the boxes are fully updated and at their final positions. + if (box._configure) { + box._configure(); + } + me._layers.push.apply(me._layers, box._layers()); + }, me); + + me._layers.forEach(function(item, index) { + item._idx = index; + }); + + /** + * Provided for backward compatibility, use `afterLayout` instead. + * @method IPlugin#afterScaleUpdate + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ + core_plugins.notify(me, 'afterScaleUpdate'); + core_plugins.notify(me, 'afterLayout'); + }, + + /** + * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` + * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. + * @private + */ + updateDatasets: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { + return; + } + + for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.updateDataset(i); + } + + core_plugins.notify(me, 'afterDatasetsUpdate'); + }, + + /** + * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` + * hook, in which case, plugins will not be called on `afterDatasetUpdate`. + * @private + */ + updateDataset: function(index) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index + }; + + if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { + return; + } + + meta.controller._update(); + + core_plugins.notify(me, 'afterDatasetUpdate', [args]); + }, + + render: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + var animationOptions = me.options.animation; + var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration); + var lazy = config.lazy; + + if (core_plugins.notify(me, 'beforeRender') === false) { + return; + } + + var onComplete = function(animation) { + core_plugins.notify(me, 'afterRender'); + helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); + }; + + if (animationOptions && duration) { + var animation = new core_animation({ + numSteps: duration / 16.66, // 60 fps + easing: config.easing || animationOptions.easing, + + render: function(chart, animationObject) { + var easingFunction = helpers$1.easing.effects[animationObject.easing]; + var currentStep = animationObject.currentStep; + var stepDecimal = currentStep / animationObject.numSteps; + + chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); + }, + + onAnimationProgress: animationOptions.onProgress, + onAnimationComplete: onComplete + }); + + core_animations.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + + // See https://github.com/chartjs/Chart.js/issues/3781 + onComplete(new core_animation({numSteps: 0, chart: me})); + } + + return me; + }, + + draw: function(easingValue) { + var me = this; + var i, layers; + + me.clear(); + + if (helpers$1.isNullOrUndef(easingValue)) { + easingValue = 1; + } + + me.transition(easingValue); + + if (me.width <= 0 || me.height <= 0) { + return; + } + + if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { + return; + } + + // Because of plugin hooks (before/afterDatasetsDraw), datasets can't + // currently be part of layers. Instead, we draw + // layers <= 0 before(default, backward compat), and the rest after + layers = me._layers; + for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { + layers[i].draw(me.chartArea); + } + + me.drawDatasets(easingValue); + + // Rest of layers + for (; i < layers.length; ++i) { + layers[i].draw(me.chartArea); + } + + me._drawTooltip(easingValue); + + core_plugins.notify(me, 'afterDraw', [easingValue]); + }, + + /** + * @private + */ + transition: function(easingValue) { + var me = this; + + for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { + if (me.isDatasetVisible(i)) { + me.getDatasetMeta(i).controller.transition(easingValue); + } + } + + me.tooltip.transition(easingValue); + }, + + /** + * @private + */ + _getSortedDatasetMetas: function(filterVisible) { + var me = this; + var datasets = me.data.datasets || []; + var result = []; + var i, ilen; + + for (i = 0, ilen = datasets.length; i < ilen; ++i) { + if (!filterVisible || me.isDatasetVisible(i)) { + result.push(me.getDatasetMeta(i)); + } + } + + result.sort(compare2Level('order', 'index')); + + return result; + }, + + /** + * @private + */ + _getSortedVisibleDatasetMetas: function() { + return this._getSortedDatasetMetas(true); + }, + + /** + * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` + * hook, in which case, plugins will not be called on `afterDatasetsDraw`. + * @private + */ + drawDatasets: function(easingValue) { + var me = this; + var metasets, i; + + if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { + return; + } + + metasets = me._getSortedVisibleDatasetMetas(); + for (i = metasets.length - 1; i >= 0; --i) { + me.drawDataset(metasets[i], easingValue); + } + + core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); + }, + + /** + * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` + * hook, in which case, plugins will not be called on `afterDatasetDraw`. + * @private + */ + drawDataset: function(meta, easingValue) { + var me = this; + var args = { + meta: meta, + index: meta.index, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { + return; + } + + meta.controller.draw(easingValue); + + core_plugins.notify(me, 'afterDatasetDraw', [args]); + }, + + /** + * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` + * hook, in which case, plugins will not be called on `afterTooltipDraw`. + * @private + */ + _drawTooltip: function(easingValue) { + var me = this; + var tooltip = me.tooltip; + var args = { + tooltip: tooltip, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { + return; + } + + tooltip.draw(); + + core_plugins.notify(me, 'afterTooltipDraw', [args]); + }, + + /** + * Get the single element that was clicked on + * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw + */ + getElementAtEvent: function(e) { + return core_interaction.modes.single(this, e); + }, + + getElementsAtEvent: function(e) { + return core_interaction.modes.label(this, e, {intersect: true}); + }, + + getElementsAtXAxis: function(e) { + return core_interaction.modes['x-axis'](this, e, {intersect: true}); + }, + + getElementsAtEventForMode: function(e, mode, options) { + var method = core_interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options); + } + + return []; + }, + + getDatasetAtEvent: function(e) { + return core_interaction.modes.dataset(this, e, {intersect: true}); + }, + + getDatasetMeta: function(datasetIndex) { + var me = this; + var dataset = me.data.datasets[datasetIndex]; + if (!dataset._meta) { + dataset._meta = {}; + } + + var meta = dataset._meta[me.id]; + if (!meta) { + meta = dataset._meta[me.id] = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, // See isDatasetVisible() comment + xAxisID: null, + yAxisID: null, + order: dataset.order || 0, + index: datasetIndex + }; + } + + return meta; + }, + + getVisibleDatasetCount: function() { + var count = 0; + for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + if (this.isDatasetVisible(i)) { + count++; + } + } + return count; + }, + + isDatasetVisible: function(datasetIndex) { + var meta = this.getDatasetMeta(datasetIndex); + + // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, + // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. + return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; + }, + + generateLegend: function() { + return this.options.legendCallback(this); + }, + + /** + * @private + */ + destroyDatasetMeta: function(datasetIndex) { + var id = this.id; + var dataset = this.data.datasets[datasetIndex]; + var meta = dataset._meta && dataset._meta[id]; + + if (meta) { + meta.controller.destroy(); + delete dataset._meta[id]; + } + }, + + destroy: function() { + var me = this; + var canvas = me.canvas; + var i, ilen; + + me.stop(); + + // dataset controllers need to cleanup associated data + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.destroyDatasetMeta(i); + } + + if (canvas) { + me.unbindEvents(); + helpers$1.canvas.clear(me); + platform.releaseContext(me.ctx); + me.canvas = null; + me.ctx = null; + } + + core_plugins.notify(me, 'destroy'); + + delete Chart.instances[me.id]; + }, + + toBase64Image: function() { + return this.canvas.toDataURL.apply(this.canvas, arguments); + }, + + initToolTip: function() { + var me = this; + me.tooltip = new core_tooltip({ + _chart: me, + _chartInstance: me, // deprecated, backward compatibility + _data: me.data, + _options: me.options.tooltips + }, me); + }, + + /** + * @private + */ + bindEvents: function() { + var me = this; + var listeners = me._listeners = {}; + var listener = function() { + me.eventHandler.apply(me, arguments); + }; + + helpers$1.each(me.options.events, function(type) { + platform.addEventListener(me, type, listener); + listeners[type] = listener; + }); + + // Elements used to detect size change should not be injected for non responsive charts. + // See https://github.com/chartjs/Chart.js/issues/2210 + if (me.options.responsive) { + listener = function() { + me.resize(); + }; + + platform.addEventListener(me, 'resize', listener); + listeners.resize = listener; + } + }, + + /** + * @private + */ + unbindEvents: function() { + var me = this; + var listeners = me._listeners; + if (!listeners) { + return; + } + + delete me._listeners; + helpers$1.each(listeners, function(listener, type) { + platform.removeEventListener(me, type, listener); + }); + }, + + updateHoverStyle: function(elements, mode, enabled) { + var prefix = enabled ? 'set' : 'remove'; + var element, i, ilen; + + for (i = 0, ilen = elements.length; i < ilen; ++i) { + element = elements[i]; + if (element) { + this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element); + } + } + + if (mode === 'dataset') { + this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle'](); + } + }, + + /** + * @private + */ + eventHandler: function(e) { + var me = this; + var tooltip = me.tooltip; + + if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { + return; + } + + // Buffer any update calls so that renders do not occur + me._bufferedRender = true; + me._bufferedRequest = null; + + var changed = me.handleEvent(e); + // for smooth tooltip animations issue #4989 + // the tooltip should be the source of change + // Animation check workaround: + // tooltip._start will be null when tooltip isn't animating + if (tooltip) { + changed = tooltip._start + ? tooltip.handleEvent(e) + : changed | tooltip.handleEvent(e); + } + + core_plugins.notify(me, 'afterEvent', [e]); + + var bufferedRequest = me._bufferedRequest; + if (bufferedRequest) { + // If we have an update that was triggered, we need to do a normal render + me.render(bufferedRequest); + } else if (changed && !me.animating) { + // If entering, leaving, or changing elements, animate the change via pivot + me.stop(); + + // We only need to render at this point. Updating will cause scales to be + // recomputed generating flicker & using more memory than necessary. + me.render({ + duration: me.options.hover.animationDuration, + lazy: true + }); + } + + me._bufferedRender = false; + me._bufferedRequest = null; + + return me; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event the event to handle + * @return {boolean} true if the chart needs to re-render + */ + handleEvent: function(e) { + var me = this; + var options = me.options || {}; + var hoverOptions = options.hover; + var changed = false; + + me.lastActive = me.lastActive || []; + + // Find Active Elements for hover and tooltips + if (e.type === 'mouseout') { + me.active = []; + } else { + me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); + } + + // Invoke onHover hook + // Need to call with native event here to not break backwards compatibility + helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); + + if (e.type === 'mouseup' || e.type === 'click') { + if (options.onClick) { + // Use e.native here for backwards compatibility + options.onClick.call(me, e.native, me.active); + } + } + + // Remove styling for last active (even if it may still be active) + if (me.lastActive.length) { + me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); + } + + // Built in hover styling + if (me.active.length && hoverOptions.mode) { + me.updateHoverStyle(me.active, hoverOptions.mode, true); + } + + changed = !helpers$1.arrayEquals(me.active, me.lastActive); + + // Remember Last Actives + me.lastActive = me.active; + + return changed; + } +}); + +/** + * NOTE(SB) We actually don't use this container anymore but we need to keep it + * for backward compatibility. Though, it can still be useful for plugins that + * would need to work on multiple charts?! + */ +Chart.instances = {}; + +var core_controller = Chart; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart instead. + * @class Chart.Controller + * @deprecated since version 2.6 + * @todo remove at version 3 + * @private + */ +Chart.Controller = Chart; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +Chart.types = {}; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.configMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.configMerge = mergeConfig; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.scaleMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.scaleMerge = mergeScaleConfig; + +var core_helpers = function() { + + // -- Basic js utility methods + + helpers$1.where = function(collection, filterCallback) { + if (helpers$1.isArray(collection) && Array.prototype.filter) { + return collection.filter(filterCallback); + } + var filtered = []; + + helpers$1.each(collection, function(item) { + if (filterCallback(item)) { + filtered.push(item); + } + }); + + return filtered; + }; + helpers$1.findIndex = Array.prototype.findIndex ? + function(array, callback, scope) { + return array.findIndex(callback, scope); + } : + function(array, callback, scope) { + scope = scope === undefined ? array : scope; + for (var i = 0, ilen = array.length; i < ilen; ++i) { + if (callback.call(scope, array[i], i, array)) { + return i; + } + } + return -1; + }; + helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to start of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = -1; + } + for (var i = startIndex + 1; i < arrayToSearch.length; i++) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to end of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = arrayToSearch.length; + } + for (var i = startIndex - 1; i >= 0; i--) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + + // -- Math methods + helpers$1.isNumber = function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + helpers$1.almostEquals = function(x, y, epsilon) { + return Math.abs(x - y) < epsilon; + }; + helpers$1.almostWhole = function(x, epsilon) { + var rounded = Math.round(x); + return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x); + }; + helpers$1.max = function(array) { + return array.reduce(function(max, value) { + if (!isNaN(value)) { + return Math.max(max, value); + } + return max; + }, Number.NEGATIVE_INFINITY); + }; + helpers$1.min = function(array) { + return array.reduce(function(min, value) { + if (!isNaN(value)) { + return Math.min(min, value); + } + return min; + }, Number.POSITIVE_INFINITY); + }; + helpers$1.sign = Math.sign ? + function(x) { + return Math.sign(x); + } : + function(x) { + x = +x; // convert to a number + if (x === 0 || isNaN(x)) { + return x; + } + return x > 0 ? 1 : -1; + }; + helpers$1.toRadians = function(degrees) { + return degrees * (Math.PI / 180); + }; + helpers$1.toDegrees = function(radians) { + return radians * (180 / Math.PI); + }; + + /** + * Returns the number of decimal places + * i.e. the number of digits after the decimal point, of the value of this Number. + * @param {number} x - A number. + * @returns {number} The number of decimal places. + * @private + */ + helpers$1._decimalPlaces = function(x) { + if (!helpers$1.isFinite(x)) { + return; + } + var e = 1; + var p = 0; + while (Math.round(x * e) / e !== x) { + e *= 10; + p++; + } + return p; + }; + + // Gets the angle from vertical upright to the point about a centre. + helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { + var distanceFromXCenter = anglePoint.x - centrePoint.x; + var distanceFromYCenter = anglePoint.y - centrePoint.y; + var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + + var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); + + if (angle < (-0.5 * Math.PI)) { + angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + + return { + angle: angle, + distance: radialDistanceFromCenter + }; + }; + helpers$1.distanceBetweenPoints = function(pt1, pt2) { + return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + }; + + /** + * Provided for backward compatibility, not available anymore + * @function Chart.helpers.aliasPixel + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ + helpers$1.aliasPixel = function(pixelWidth) { + return (pixelWidth % 2 === 0) ? 0 : 0.5; + }; + + /** + * Returns the aligned pixel value to avoid anti-aliasing blur + * @param {Chart} chart - The chart instance. + * @param {number} pixel - A pixel value. + * @param {number} width - The width of the element. + * @returns {number} The aligned pixel value. + * @private + */ + helpers$1._alignPixel = function(chart, pixel, width) { + var devicePixelRatio = chart.currentDevicePixelRatio; + var halfWidth = width / 2; + return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; + }; + + helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + + // This function must also respect "skipped" points + + var previous = firstPoint.skip ? middlePoint : firstPoint; + var current = middlePoint; + var next = afterPoint.skip ? middlePoint : afterPoint; + + var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); + var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); + + var s01 = d01 / (d01 + d12); + var s12 = d12 / (d01 + d12); + + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + + var fa = t * s01; // scaling factor for triangle Ta + var fb = t * s12; + + return { + previous: { + x: current.x - fa * (next.x - previous.x), + y: current.y - fa * (next.y - previous.y) + }, + next: { + x: current.x + fb * (next.x - previous.x), + y: current.y + fb * (next.y - previous.y) + } + }; + }; + helpers$1.EPSILON = Number.EPSILON || 1e-14; + helpers$1.splineCurveMonotone = function(points) { + // This function calculates Bézier control points in a similar way than |splineCurve|, + // but preserves monotonicity of the provided data and ensures no local extremums are added + // between the dataset discrete points due to the interpolation. + // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + var pointsWithTangents = (points || []).map(function(point) { + return { + model: point._model, + deltaK: 0, + mK: 0 + }; + }); + + // Calculate slopes (deltaK) and initialize tangents (mK) + var pointsLen = pointsWithTangents.length; + var i, pointBefore, pointCurrent, pointAfter; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointAfter && !pointAfter.model.skip) { + var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); + + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; + } + + if (!pointBefore || pointBefore.model.skip) { + pointCurrent.mK = pointCurrent.deltaK; + } else if (!pointAfter || pointAfter.model.skip) { + pointCurrent.mK = pointBefore.deltaK; + } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { + pointCurrent.mK = 0; + } else { + pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; + } + } + + // Adjust tangents to ensure monotonic properties + var alphaK, betaK, tauK, squaredMagnitude; + for (i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointsWithTangents[i]; + pointAfter = pointsWithTangents[i + 1]; + if (pointCurrent.model.skip || pointAfter.model.skip) { + continue; + } + + if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { + pointCurrent.mK = pointAfter.mK = 0; + continue; + } + + alphaK = pointCurrent.mK / pointCurrent.deltaK; + betaK = pointAfter.mK / pointCurrent.deltaK; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) { + continue; + } + + tauK = 3 / Math.sqrt(squaredMagnitude); + pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; + pointAfter.mK = betaK * tauK * pointCurrent.deltaK; + } + + // Compute control points + var deltaX; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointBefore && !pointBefore.model.skip) { + deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; + pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; + pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; + } + if (pointAfter && !pointAfter.model.skip) { + deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; + pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; + pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; + } + } + }; + helpers$1.nextItem = function(collection, index, loop) { + if (loop) { + return index >= collection.length - 1 ? collection[0] : collection[index + 1]; + } + return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; + }; + helpers$1.previousItem = function(collection, index, loop) { + if (loop) { + return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; + } + return index <= 0 ? collection[0] : collection[index - 1]; + }; + // Implementation of the nice number algorithm used in determining where axis labels will go + helpers$1.niceNum = function(range, round) { + var exponent = Math.floor(helpers$1.log10(range)); + var fraction = range / Math.pow(10, exponent); + var niceFraction; + + if (round) { + if (fraction < 1.5) { + niceFraction = 1; + } else if (fraction < 3) { + niceFraction = 2; + } else if (fraction < 7) { + niceFraction = 5; + } else { + niceFraction = 10; + } + } else if (fraction <= 1.0) { + niceFraction = 1; + } else if (fraction <= 2) { + niceFraction = 2; + } else if (fraction <= 5) { + niceFraction = 5; + } else { + niceFraction = 10; + } + + return niceFraction * Math.pow(10, exponent); + }; + // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + helpers$1.requestAnimFrame = (function() { + if (typeof window === 'undefined') { + return function(callback) { + callback(); + }; + } + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + }()); + // -- DOM methods + helpers$1.getRelativePosition = function(evt, chart) { + var mouseX, mouseY; + var e = evt.originalEvent || evt; + var canvas = evt.target || evt.srcElement; + var boundingRect = canvas.getBoundingClientRect(); + + var touches = e.touches; + if (touches && touches.length > 0) { + mouseX = touches[0].clientX; + mouseY = touches[0].clientY; + + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + + // Scale mouse coordinates into canvas coordinates + // by following the pattern laid out by 'jerryj' in the comments of + // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ + var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); + var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); + var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); + var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); + var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; + var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; + + // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However + // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here + mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); + mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); + + return { + x: mouseX, + y: mouseY + }; + + }; + + // Private helper function to convert max-width/max-height values that may be percentages into a number + function parseMaxStyle(styleValue, node, parentProperty) { + var valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + + return valueInPixels; + } + + /** + * Returns if the given value contains an effective constraint. + * @private + */ + function isConstrainedValue(value) { + return value !== undefined && value !== null && value !== 'none'; + } + + /** + * Returns the max width or height of the given DOM node in a cross-browser compatible fashion + * @param {HTMLElement} domNode - the node to check the constraint on + * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') + * @param {string} percentageProperty - property of parent to use when calculating width as a percentage + * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} + */ + function getConstraintDimension(domNode, maxStyle, percentageProperty) { + var view = document.defaultView; + var parentNode = helpers$1._getParentNode(domNode); + var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; + var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; + var hasCNode = isConstrainedValue(constrainedNode); + var hasCContainer = isConstrainedValue(constrainedContainer); + var infinity = Number.POSITIVE_INFINITY; + + if (hasCNode || hasCContainer) { + return Math.min( + hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, + hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); + } + + return 'none'; + } + // returns Number or undefined if no constraint + helpers$1.getConstraintWidth = function(domNode) { + return getConstraintDimension(domNode, 'max-width', 'clientWidth'); + }; + // returns Number or undefined if no constraint + helpers$1.getConstraintHeight = function(domNode) { + return getConstraintDimension(domNode, 'max-height', 'clientHeight'); + }; + /** + * @private + */ + helpers$1._calculatePadding = function(container, padding, parentDimension) { + padding = helpers$1.getStyle(container, padding); + + return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); + }; + /** + * @private + */ + helpers$1._getParentNode = function(domNode) { + var parent = domNode.parentNode; + if (parent && parent.toString() === '[object ShadowRoot]') { + parent = parent.host; + } + return parent; + }; + helpers$1.getMaximumWidth = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientWidth; + } + + var clientWidth = container.clientWidth; + var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); + var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); + + var w = clientWidth - paddingLeft - paddingRight; + var cw = helpers$1.getConstraintWidth(domNode); + return isNaN(cw) ? w : Math.min(w, cw); + }; + helpers$1.getMaximumHeight = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientHeight; + } + + var clientHeight = container.clientHeight; + var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); + var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); + + var h = clientHeight - paddingTop - paddingBottom; + var ch = helpers$1.getConstraintHeight(domNode); + return isNaN(ch) ? h : Math.min(h, ch); + }; + helpers$1.getStyle = function(el, property) { + return el.currentStyle ? + el.currentStyle[property] : + document.defaultView.getComputedStyle(el, null).getPropertyValue(property); + }; + helpers$1.retinaScale = function(chart, forceRatio) { + var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; + if (pixelRatio === 1) { + return; + } + + var canvas = chart.canvas; + var height = chart.height; + var width = chart.width; + + canvas.height = height * pixelRatio; + canvas.width = width * pixelRatio; + chart.ctx.scale(pixelRatio, pixelRatio); + + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + if (!canvas.style.height && !canvas.style.width) { + canvas.style.height = height + 'px'; + canvas.style.width = width + 'px'; + } + }; + // -- Canvas methods + helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; + }; + helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + var data = cache.data = cache.data || {}; + var gc = cache.garbageCollect = cache.garbageCollect || []; + + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + + ctx.font = font; + var longest = 0; + var ilen = arrayOfThings.length; + var i, j, jlen, thing, nestedThing; + for (i = 0; i < ilen; i++) { + thing = arrayOfThings[i]; + + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { + longest = helpers$1.measureText(ctx, data, gc, longest, thing); + } else if (helpers$1.isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + for (j = 0, jlen = thing.length; j < jlen; j++) { + nestedThing = thing[j]; + // Undefined strings and arrays should not be measured + if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { + longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); + } + } + } + } + + var gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + return longest; + }; + helpers$1.measureText = function(ctx, data, gc, longest, string) { + var textWidth = data[string]; + if (!textWidth) { + textWidth = data[string] = ctx.measureText(string).width; + gc.push(string); + } + if (textWidth > longest) { + longest = textWidth; + } + return longest; + }; + + /** + * @deprecated + */ + helpers$1.numberOfLabelLines = function(arrayOfThings) { + var numberOfLines = 1; + helpers$1.each(arrayOfThings, function(thing) { + if (helpers$1.isArray(thing)) { + if (thing.length > numberOfLines) { + numberOfLines = thing.length; + } + } + }); + return numberOfLines; + }; + + helpers$1.color = !chartjsColor ? + function(value) { + console.error('Color.js not found!'); + return value; + } : + function(value) { + /* global CanvasGradient */ + if (value instanceof CanvasGradient) { + value = core_defaults.global.defaultColor; + } + + return chartjsColor(value); + }; + + helpers$1.getHoverColor = function(colorValue) { + /* global CanvasPattern */ + return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? + colorValue : + helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); + }; +}; + +function abstract() { + throw new Error( + 'This method is not implemented: either no adapter can ' + + 'be found or an incomplete integration was provided.' + ); +} + +/** + * Date adapter (current used by the time scale) + * @namespace Chart._adapters._date + * @memberof Chart._adapters + * @private + */ + +/** + * Currently supported unit string values. + * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} + * @memberof Chart._adapters._date + * @name Unit + */ + +/** + * @class + */ +function DateAdapter(options) { + this.options = options || {}; +} + +helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { + /** + * Returns a map of time formats for the supported formatting units defined + * in Unit as well as 'datetime' representing a detailed date/time string. + * @returns {{string: string}} + */ + formats: abstract, + + /** + * Parses the given `value` and return the associated timestamp. + * @param {any} value - the value to parse (usually comes from the data) + * @param {string} [format] - the expected data format + * @returns {(number|null)} + * @function + */ + parse: abstract, + + /** + * Returns the formatted date in the specified `format` for a given `timestamp`. + * @param {number} timestamp - the timestamp to format + * @param {string} format - the date/time token + * @return {string} + * @function + */ + format: abstract, + + /** + * Adds the specified `amount` of `unit` to the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {number} amount - the amount to add + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + add: abstract, + + /** + * Returns the number of `unit` between the given timestamps. + * @param {number} max - the input timestamp (reference) + * @param {number} min - the timestamp to substract + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + diff: abstract, + + /** + * Returns start of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @param {number} [weekday] - the ISO day of the week with 1 being Monday + * and 7 being Sunday (only needed if param *unit* is `isoWeek`). + * @function + */ + startOf: abstract, + + /** + * Returns end of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @function + */ + endOf: abstract, + + // DEPRECATIONS + + /** + * Provided for backward compatibility for scale.getValueForPixel(), + * this method should be overridden only by the moment adapter. + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(value) { + return value; + } +}); + +DateAdapter.override = function(members) { + helpers$1.extend(DateAdapter.prototype, members); +}; + +var _date = DateAdapter; + +var core_adapters = { + _date: _date +}; + +/** + * Namespace to hold static tick generation functions + * @namespace Chart.Ticks + */ +var core_ticks = { + /** + * Namespace to hold formatters for different types of ticks + * @namespace Chart.Ticks.formatters + */ + formatters: { + /** + * Formatter for value labels + * @method Chart.Ticks.formatters.values + * @param value the value to display + * @return {string|string[]} the label to display + */ + values: function(value) { + return helpers$1.isArray(value) ? value : '' + value; + }, + + /** + * Formatter for linear numeric ticks + * @method Chart.Ticks.formatters.linear + * @param tickValue {number} the value to be formatted + * @param index {number} the position of the tickValue parameter in the ticks array + * @param ticks {number[]} the list of ticks being converted + * @return {string} string representation of the tickValue parameter + */ + linear: function(tickValue, index, ticks) { + // If we have lots of ticks, don't use the ones + var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; + + // If we have a number like 2.5 as the delta, figure out how many decimal places we need + if (Math.abs(delta) > 1) { + if (tickValue !== Math.floor(tickValue)) { + // not an integer + delta = tickValue - Math.floor(tickValue); + } + } + + var logDelta = helpers$1.log10(Math.abs(delta)); + var tickString = ''; + + if (tickValue !== 0) { + var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); + if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation + var logTick = helpers$1.log10(Math.abs(tickValue)); + var numExponential = Math.floor(logTick) - Math.floor(logDelta); + numExponential = Math.max(Math.min(numExponential, 20), 0); + tickString = tickValue.toExponential(numExponential); + } else { + var numDecimal = -1 * Math.floor(logDelta); + numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } + } else { + tickString = '0'; // never show decimal places for 0 + } + + return tickString; + }, + + logarithmic: function(tickValue, index, ticks) { + var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); + + if (tickValue === 0) { + return '0'; + } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { + return tickValue.toExponential(); + } + return ''; + } + } +}; + +var isArray = helpers$1.isArray; +var isNullOrUndef = helpers$1.isNullOrUndef; +var valueOrDefault$a = helpers$1.valueOrDefault; +var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; + +core_defaults._set('scale', { + display: true, + position: 'left', + offset: false, + + // grid line settings + gridLines: { + display: true, + color: 'rgba(0,0,0,0.1)', + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickMarkLength: 10, + zeroLineWidth: 1, + zeroLineColor: 'rgba(0,0,0,0.25)', + zeroLineBorderDash: [], + zeroLineBorderDashOffset: 0.0, + offsetGridLines: false, + borderDash: [], + borderDashOffset: 0.0 + }, + + // scale label + scaleLabel: { + // display property + display: false, + + // actual label + labelString: '', + + // top/bottom padding + padding: { + top: 4, + bottom: 4 + } + }, + + // label settings + ticks: { + beginAtZero: false, + minRotation: 0, + maxRotation: 50, + mirror: false, + padding: 0, + reverse: false, + display: true, + autoSkip: true, + autoSkipPadding: 0, + labelOffset: 0, + // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. + callback: core_ticks.formatters.values, + minor: {}, + major: {} + } +}); + +/** Returns a new array containing numItems from arr */ +function sample(arr, numItems) { + var result = []; + var increment = arr.length / numItems; + var i = 0; + var len = arr.length; + + for (; i < len; i += increment) { + result.push(arr[Math.floor(i)]); + } + return result; +} + +function getPixelForGridLine(scale, index, offsetGridLines) { + var length = scale.getTicks().length; + var validIndex = Math.min(index, length - 1); + var lineValue = scale.getPixelForTick(validIndex); + var start = scale._startPixel; + var end = scale._endPixel; + var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. + var offset; + + if (offsetGridLines) { + if (length === 1) { + offset = Math.max(lineValue - start, end - lineValue); + } else if (index === 0) { + offset = (scale.getPixelForTick(1) - lineValue) / 2; + } else { + offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; + } + lineValue += validIndex < index ? offset : -offset; + + // Return undefined if the pixel is out of the range + if (lineValue < start - epsilon || lineValue > end + epsilon) { + return; + } + } + return lineValue; +} + +function garbageCollect(caches, length) { + helpers$1.each(caches, function(cache) { + var gc = cache.gc; + var gcLen = gc.length / 2; + var i; + if (gcLen > length) { + for (i = 0; i < gcLen; ++i) { + delete cache.data[gc[i]]; + } + gc.splice(0, gcLen); + } + }); +} + +/** + * Returns {width, height, offset} objects for the first, last, widest, highest tick + * labels where offset indicates the anchor point offset from the top in pixels. + */ +function computeLabelSizes(ctx, tickFonts, ticks, caches) { + var length = ticks.length; + var widths = []; + var heights = []; + var offsets = []; + var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest; + + for (i = 0; i < length; ++i) { + label = ticks[i].label; + tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor; + ctx.font = fontString = tickFont.string; + cache = caches[fontString] = caches[fontString] || {data: {}, gc: []}; + lineHeight = tickFont.lineHeight; + width = height = 0; + // Undefined labels and arrays should not be measured + if (!isNullOrUndef(label) && !isArray(label)) { + width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label); + height = lineHeight; + } else if (isArray(label)) { + // if it is an array let's measure each element + for (j = 0, jlen = label.length; j < jlen; ++j) { + nestedLabel = label[j]; + // Undefined labels and arrays should not be measured + if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { + width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel); + height += lineHeight; + } + } + } + widths.push(width); + heights.push(height); + offsets.push(lineHeight / 2); + } + garbageCollect(caches, length); + + widest = widths.indexOf(Math.max.apply(null, widths)); + highest = heights.indexOf(Math.max.apply(null, heights)); + + function valueAt(idx) { + return { + width: widths[idx] || 0, + height: heights[idx] || 0, + offset: offsets[idx] || 0 + }; + } + + return { + first: valueAt(0), + last: valueAt(length - 1), + widest: valueAt(widest), + highest: valueAt(highest) + }; +} + +function getTickMarkLength(options) { + return options.drawTicks ? options.tickMarkLength : 0; +} + +function getScaleLabelHeight(options) { + var font, padding; + + if (!options.display) { + return 0; + } + + font = helpers$1.options._parseFont(options); + padding = helpers$1.options.toPadding(options.padding); + + return font.lineHeight + padding.height; +} + +function parseFontOptions(options, nestedOpts) { + return helpers$1.extend(helpers$1.options._parseFont({ + fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily), + fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize), + fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle), + lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight) + }), { + color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor]) + }); +} + +function parseTickFontOptions(options) { + var minor = parseFontOptions(options, options.minor); + var major = options.major.enabled ? parseFontOptions(options, options.major) : minor; + + return {minor: minor, major: major}; +} + +function nonSkipped(ticksToFilter) { + var filtered = []; + var item, index, len; + for (index = 0, len = ticksToFilter.length; index < len; ++index) { + item = ticksToFilter[index]; + if (typeof item._index !== 'undefined') { + filtered.push(item); + } + } + return filtered; +} + +function getEvenSpacing(arr) { + var len = arr.length; + var i, diff; + + if (len < 2) { + return false; + } + + for (diff = arr[0], i = 1; i < len; ++i) { + if (arr[i] - arr[i - 1] !== diff) { + return false; + } + } + return diff; +} + +function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) { + var evenMajorSpacing = getEvenSpacing(majorIndices); + var spacing = (ticks.length - 1) / ticksLimit; + var factors, factor, i, ilen; + + // If the major ticks are evenly spaced apart, place the minor ticks + // so that they divide the major ticks into even chunks + if (!evenMajorSpacing) { + return Math.max(spacing, 1); + } + + factors = helpers$1.math._factorize(evenMajorSpacing); + for (i = 0, ilen = factors.length - 1; i < ilen; i++) { + factor = factors[i]; + if (factor > spacing) { + return factor; + } + } + return Math.max(spacing, 1); +} + +function getMajorIndices(ticks) { + var result = []; + var i, ilen; + for (i = 0, ilen = ticks.length; i < ilen; i++) { + if (ticks[i].major) { + result.push(i); + } + } + return result; +} + +function skipMajors(ticks, majorIndices, spacing) { + var count = 0; + var next = majorIndices[0]; + var i, tick; + + spacing = Math.ceil(spacing); + for (i = 0; i < ticks.length; i++) { + tick = ticks[i]; + if (i === next) { + tick._index = i; + count++; + next = majorIndices[count * spacing]; + } else { + delete tick.label; + } + } +} + +function skip(ticks, spacing, majorStart, majorEnd) { + var start = valueOrDefault$a(majorStart, 0); + var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length); + var count = 0; + var length, i, tick, next; + + spacing = Math.ceil(spacing); + if (majorEnd) { + length = majorEnd - majorStart; + spacing = length / Math.floor(length / spacing); + } + + next = start; + + while (next < 0) { + count++; + next = Math.round(start + count * spacing); + } + + for (i = Math.max(start, 0); i < end; i++) { + tick = ticks[i]; + if (i === next) { + tick._index = i; + count++; + next = Math.round(start + count * spacing); + } else { + delete tick.label; + } + } +} + +var Scale = core_element.extend({ + + zeroLineIndex: 0, + + /** + * Get the padding needed for the scale + * @method getPadding + * @private + * @returns {Padding} the necessary padding + */ + getPadding: function() { + var me = this; + return { + left: me.paddingLeft || 0, + top: me.paddingTop || 0, + right: me.paddingRight || 0, + bottom: me.paddingBottom || 0 + }; + }, + + /** + * Returns the scale tick objects ({label, major}) + * @since 2.7 + */ + getTicks: function() { + return this._ticks; + }, + + /** + * @private + */ + _getLabels: function() { + var data = this.chart.data; + return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || []; + }, + + // These methods are ordered by lifecyle. Utilities then follow. + // Any function defined here is inherited by all scale types. + // Any function can be extended by the scale type + + /** + * Provided for backward compatibility, not available anymore + * @function Chart.Scale.mergeTicksOptions + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ + mergeTicksOptions: function() { + // noop + }, + + beforeUpdate: function() { + helpers$1.callback(this.options.beforeUpdate, [this]); + }, + + /** + * @param {number} maxWidth - the max width in pixels + * @param {number} maxHeight - the max height in pixels + * @param {object} margins - the space between the edge of the other scales and edge of the chart + * This space comes from two sources: + * - padding - space that's required to show the labels at the edges of the scale + * - thickness of scales or legends in another orientation + */ + update: function(maxWidth, maxHeight, margins) { + var me = this; + var tickOpts = me.options.ticks; + var sampleSize = tickOpts.sampleSize; + var i, ilen, labels, ticks, samplingEnabled; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = helpers$1.extend({ + left: 0, + right: 0, + top: 0, + bottom: 0 + }, margins); + + me._ticks = null; + me.ticks = null; + me._labelSizes = null; + me._maxLabelLines = 0; + me.longestLabelWidth = 0; + me.longestTextCache = me.longestTextCache || {}; + me._gridLineItems = null; + me._labelItems = null; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + + // Data min/max + me.beforeDataLimits(); + me.determineDataLimits(); + me.afterDataLimits(); + + // Ticks - `this.ticks` is now DEPRECATED! + // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member + // and must not be accessed directly from outside this class. `this.ticks` being + // around for long time and not marked as private, we can't change its structure + // without unexpected breaking changes. If you need to access the scale ticks, + // use scale.getTicks() instead. + + me.beforeBuildTicks(); + + // New implementations should return an array of objects but for BACKWARD COMPAT, + // we still support no return (`this.ticks` internally set by calling this method). + ticks = me.buildTicks() || []; + + // Allow modification of ticks in callback. + ticks = me.afterBuildTicks(ticks) || ticks; + + // Ensure ticks contains ticks in new tick format + if ((!ticks || !ticks.length) && me.ticks) { + ticks = []; + for (i = 0, ilen = me.ticks.length; i < ilen; ++i) { + ticks.push({ + value: me.ticks[i], + major: false + }); + } + } + + me._ticks = ticks; + + // Compute tick rotation and fit using a sampled subset of labels + // We generally don't need to compute the size of every single label for determining scale size + samplingEnabled = sampleSize < ticks.length; + labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks); + + // _configure is called twice, once here, once from core.controller.updateLayout. + // Here we haven't been positioned yet, but dimensions are correct. + // Variables set in _configure are needed for calculateTickRotation, and + // it's ok that coordinates are not correct there, only dimensions matter. + me._configure(); + + // Tick Rotation + me.beforeCalculateTickRotation(); + me.calculateTickRotation(); + me.afterCalculateTickRotation(); + + me.beforeFit(); + me.fit(); + me.afterFit(); + + // Auto-skip + me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks; + + if (samplingEnabled) { + // Generate labels using all non-skipped ticks + labels = me._convertTicksToLabels(me._ticksToDraw); + } + + me.ticks = labels; // BACKWARD COMPATIBILITY + + // IMPORTANT: after this point, we consider that `this.ticks` will NEVER change! + + me.afterUpdate(); + + // TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused + // make maxWidth and maxHeight private + return me.minSize; + }, + + /** + * @private + */ + _configure: function() { + var me = this; + var reversePixels = me.options.ticks.reverse; + var startPixel, endPixel; + + if (me.isHorizontal()) { + startPixel = me.left; + endPixel = me.right; + } else { + startPixel = me.top; + endPixel = me.bottom; + // by default vertical scales are from bottom to top, so pixels are reversed + reversePixels = !reversePixels; + } + me._startPixel = startPixel; + me._endPixel = endPixel; + me._reversePixels = reversePixels; + me._length = endPixel - startPixel; + }, + + afterUpdate: function() { + helpers$1.callback(this.options.afterUpdate, [this]); + }, + + // + + beforeSetDimensions: function() { + helpers$1.callback(this.options.beforeSetDimensions, [this]); + }, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + }, + afterSetDimensions: function() { + helpers$1.callback(this.options.afterSetDimensions, [this]); + }, + + // Data limits + beforeDataLimits: function() { + helpers$1.callback(this.options.beforeDataLimits, [this]); + }, + determineDataLimits: helpers$1.noop, + afterDataLimits: function() { + helpers$1.callback(this.options.afterDataLimits, [this]); + }, + + // + beforeBuildTicks: function() { + helpers$1.callback(this.options.beforeBuildTicks, [this]); + }, + buildTicks: helpers$1.noop, + afterBuildTicks: function(ticks) { + var me = this; + // ticks is empty for old axis implementations here + if (isArray(ticks) && ticks.length) { + return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); + } + // Support old implementations (that modified `this.ticks` directly in buildTicks) + me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; + return ticks; + }, + + beforeTickToLabelConversion: function() { + helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); + }, + convertTicksToLabels: function() { + var me = this; + // Convert ticks to strings + var tickOpts = me.options.ticks; + me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); + }, + afterTickToLabelConversion: function() { + helpers$1.callback(this.options.afterTickToLabelConversion, [this]); + }, + + // + + beforeCalculateTickRotation: function() { + helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); + }, + calculateTickRotation: function() { + var me = this; + var options = me.options; + var tickOpts = options.ticks; + var numTicks = me.getTicks().length; + var minRotation = tickOpts.minRotation || 0; + var maxRotation = tickOpts.maxRotation; + var labelRotation = minRotation; + var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal; + + if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) { + me.labelRotation = minRotation; + return; + } + + labelSizes = me._getLabelSizes(); + maxLabelWidth = labelSizes.widest.width; + maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset; + + // Estimate the width of each grid based on the canvas width, the maximum + // label width and the number of tick intervals + maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth); + tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1); + + // Allow 3 pixels x2 padding either side for label readability + if (maxLabelWidth + 6 > tickWidth) { + tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1)); + maxHeight = me.maxHeight - getTickMarkLength(options.gridLines) + - tickOpts.padding - getScaleLabelHeight(options.scaleLabel); + maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight); + labelRotation = helpers$1.toDegrees(Math.min( + Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)), + Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal) + )); + labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation)); + } + + me.labelRotation = labelRotation; + }, + afterCalculateTickRotation: function() { + helpers$1.callback(this.options.afterCalculateTickRotation, [this]); + }, + + // + + beforeFit: function() { + helpers$1.callback(this.options.beforeFit, [this]); + }, + fit: function() { + var me = this; + // Reset + var minSize = me.minSize = { + width: 0, + height: 0 + }; + + var chart = me.chart; + var opts = me.options; + var tickOpts = opts.ticks; + var scaleLabelOpts = opts.scaleLabel; + var gridLineOpts = opts.gridLines; + var display = me._isVisible(); + var isBottom = opts.position === 'bottom'; + var isHorizontal = me.isHorizontal(); + + // Width + if (isHorizontal) { + minSize.width = me.maxWidth; + } else if (display) { + minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); + } + + // height + if (!isHorizontal) { + minSize.height = me.maxHeight; // fill all the height + } else if (display) { + minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); + } + + // Don't bother fitting the ticks if we are not showing the labels + if (tickOpts.display && display) { + var tickFonts = parseTickFontOptions(tickOpts); + var labelSizes = me._getLabelSizes(); + var firstLabelSize = labelSizes.first; + var lastLabelSize = labelSizes.last; + var widestLabelSize = labelSizes.widest; + var highestLabelSize = labelSizes.highest; + var lineSpace = tickFonts.minor.lineHeight * 0.4; + var tickPadding = tickOpts.padding; + + if (isHorizontal) { + // A horizontal axis is more constrained by the height. + var isRotated = me.labelRotation !== 0; + var angleRadians = helpers$1.toRadians(me.labelRotation); + var cosRotation = Math.cos(angleRadians); + var sinRotation = Math.sin(angleRadians); + + var labelHeight = sinRotation * widestLabelSize.width + + cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0)) + + (isRotated ? 0 : lineSpace); // padding + + minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); + + var offsetLeft = me.getPixelForTick(0) - me.left; + var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1); + var paddingLeft, paddingRight; + + // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned + // which means that the right padding is dominated by the font height + if (isRotated) { + paddingLeft = isBottom ? + cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset : + sinRotation * (firstLabelSize.height - firstLabelSize.offset); + paddingRight = isBottom ? + sinRotation * (lastLabelSize.height - lastLabelSize.offset) : + cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset; + } else { + paddingLeft = firstLabelSize.width / 2; + paddingRight = lastLabelSize.width / 2; + } + + // Adjust padding taking into account changes in offsets + // and add 3 px to move away from canvas edges + me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3; + me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3; + } else { + // A vertical axis is more constrained by the width. Labels are the + // dominant factor here, so get that length first and account for padding + var labelWidth = tickOpts.mirror ? 0 : + // use lineSpace for consistency with horizontal axis + // tickPadding is not implemented for horizontal + widestLabelSize.width + tickPadding + lineSpace; + + minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth); + + me.paddingTop = firstLabelSize.height / 2; + me.paddingBottom = lastLabelSize.height / 2; + } + } + + me.handleMargins(); + + if (isHorizontal) { + me.width = me._length = chart.width - me.margins.left - me.margins.right; + me.height = minSize.height; + } else { + me.width = minSize.width; + me.height = me._length = chart.height - me.margins.top - me.margins.bottom; + } + }, + + /** + * Handle margins and padding interactions + * @private + */ + handleMargins: function() { + var me = this; + if (me.margins) { + me.margins.left = Math.max(me.paddingLeft, me.margins.left); + me.margins.top = Math.max(me.paddingTop, me.margins.top); + me.margins.right = Math.max(me.paddingRight, me.margins.right); + me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom); + } + }, + + afterFit: function() { + helpers$1.callback(this.options.afterFit, [this]); + }, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + isFullWidth: function() { + return this.options.fullWidth; + }, + + // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not + getRightValue: function(rawValue) { + // Null and undefined values first + if (isNullOrUndef(rawValue)) { + return NaN; + } + // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values + if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { + return NaN; + } + + // If it is in fact an object, dive in one more level + if (rawValue) { + if (this.isHorizontal()) { + if (rawValue.x !== undefined) { + return this.getRightValue(rawValue.x); + } + } else if (rawValue.y !== undefined) { + return this.getRightValue(rawValue.y); + } + } + + // Value is good, return it + return rawValue; + }, + + _convertTicksToLabels: function(ticks) { + var me = this; + var labels, i, ilen; + + me.ticks = ticks.map(function(tick) { + return tick.value; + }); + + me.beforeTickToLabelConversion(); + + // New implementations should return the formatted tick labels but for BACKWARD + // COMPAT, we still support no return (`this.ticks` internally changed by calling + // this method and supposed to contain only string values). + labels = me.convertTicksToLabels(ticks) || me.ticks; + + me.afterTickToLabelConversion(); + + // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + ticks[i].label = labels[i]; + } + + return labels; + }, + + /** + * @private + */ + _getLabelSizes: function() { + var me = this; + var labelSizes = me._labelSizes; + + if (!labelSizes) { + me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache); + me.longestLabelWidth = labelSizes.widest.width; + } + + return labelSizes; + }, + + /** + * @private + */ + _parseValue: function(value) { + var start, end, min, max; + + if (isArray(value)) { + start = +this.getRightValue(value[0]); + end = +this.getRightValue(value[1]); + min = Math.min(start, end); + max = Math.max(start, end); + } else { + value = +this.getRightValue(value); + start = undefined; + end = value; + min = value; + max = value; + } + + return { + min: min, + max: max, + start: start, + end: end + }; + }, + + /** + * @private + */ + _getScaleLabel: function(rawValue) { + var v = this._parseValue(rawValue); + if (v.start !== undefined) { + return '[' + v.start + ', ' + v.end + ']'; + } + + return +this.getRightValue(rawValue); + }, + + /** + * Used to get the value to display in the tooltip for the data at the given index + * @param index + * @param datasetIndex + */ + getLabelForIndex: helpers$1.noop, + + /** + * Returns the location of the given data point. Value can either be an index or a numerical value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param value + * @param index + * @param datasetIndex + */ + getPixelForValue: helpers$1.noop, + + /** + * Used to get the data value from a given pixel. This is the inverse of getPixelForValue + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param pixel + */ + getValueForPixel: helpers$1.noop, + + /** + * Returns the location of the tick at the given index + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForTick: function(index) { + var me = this; + var offset = me.options.offset; + var numTicks = me._ticks.length; + var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1); + + return index < 0 || index > numTicks - 1 + ? null + : me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0)); + }, + + /** + * Utility for getting the pixel location of a percentage of scale + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForDecimal: function(decimal) { + var me = this; + + if (me._reversePixels) { + decimal = 1 - decimal; + } + + return me._startPixel + decimal * me._length; + }, + + getDecimalForPixel: function(pixel) { + var decimal = (pixel - this._startPixel) / this._length; + return this._reversePixels ? 1 - decimal : decimal; + }, + + /** + * Returns the pixel for the minimum chart value + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getBasePixel: function() { + return this.getPixelForValue(this.getBaseValue()); + }, + + getBaseValue: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0; + }, + + /** + * Returns a subset of ticks to be plotted to avoid overlapping labels. + * @private + */ + _autoSkip: function(ticks) { + var me = this; + var tickOpts = me.options.ticks; + var axisLength = me._length; + var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1; + var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; + var numMajorIndices = majorIndices.length; + var first = majorIndices[0]; + var last = majorIndices[numMajorIndices - 1]; + var i, ilen, spacing, avgMajorSpacing; + + // If there are too many major ticks to display them all + if (numMajorIndices > ticksLimit) { + skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit); + return nonSkipped(ticks); + } + + spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit); + + if (numMajorIndices > 0) { + for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) { + skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]); + } + avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null; + skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); + skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); + return nonSkipped(ticks); + } + skip(ticks, spacing); + return nonSkipped(ticks); + }, + + /** + * @private + */ + _tickSize: function() { + var me = this; + var optionTicks = me.options.ticks; + + // Calculate space needed by label in axis direction. + var rot = helpers$1.toRadians(me.labelRotation); + var cos = Math.abs(Math.cos(rot)); + var sin = Math.abs(Math.sin(rot)); + + var labelSizes = me._getLabelSizes(); + var padding = optionTicks.autoSkipPadding || 0; + var w = labelSizes ? labelSizes.widest.width + padding : 0; + var h = labelSizes ? labelSizes.highest.height + padding : 0; + + // Calculate space needed for 1 tick in axis direction. + return me.isHorizontal() + ? h * cos > w * sin ? w / cos : h / sin + : h * sin < w * cos ? h / cos : w / sin; + }, + + /** + * @private + */ + _isVisible: function() { + var me = this; + var chart = me.chart; + var display = me.options.display; + var i, ilen, meta; + + if (display !== 'auto') { + return !!display; + } + + // When 'auto', the scale is visible if at least one associated dataset is visible. + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + if (meta.xAxisID === me.id || meta.yAxisID === me.id) { + return true; + } + } + } + + return false; + }, + + /** + * @private + */ + _computeGridLineItems: function(chartArea) { + var me = this; + var chart = me.chart; + var options = me.options; + var gridLines = options.gridLines; + var position = options.position; + var offsetGridLines = gridLines.offsetGridLines; + var isHorizontal = me.isHorizontal(); + var ticks = me._ticksToDraw; + var ticksLength = ticks.length + (offsetGridLines ? 1 : 0); + + var tl = getTickMarkLength(gridLines); + var items = []; + var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; + var axisHalfWidth = axisWidth / 2; + var alignPixel = helpers$1._alignPixel; + var alignBorderValue = function(pixel) { + return alignPixel(chart, pixel, axisWidth); + }; + var borderValue, i, tick, lineValue, alignedLineValue; + var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset; + + if (position === 'top') { + borderValue = alignBorderValue(me.bottom); + ty1 = me.bottom - tl; + ty2 = borderValue - axisHalfWidth; + y1 = alignBorderValue(chartArea.top) + axisHalfWidth; + y2 = chartArea.bottom; + } else if (position === 'bottom') { + borderValue = alignBorderValue(me.top); + y1 = chartArea.top; + y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; + ty1 = borderValue + axisHalfWidth; + ty2 = me.top + tl; + } else if (position === 'left') { + borderValue = alignBorderValue(me.right); + tx1 = me.right - tl; + tx2 = borderValue - axisHalfWidth; + x1 = alignBorderValue(chartArea.left) + axisHalfWidth; + x2 = chartArea.right; + } else { + borderValue = alignBorderValue(me.left); + x1 = chartArea.left; + x2 = alignBorderValue(chartArea.right) - axisHalfWidth; + tx1 = borderValue + axisHalfWidth; + tx2 = me.left + tl; + } + + for (i = 0; i < ticksLength; ++i) { + tick = ticks[i] || {}; + + // autoskipper skipped this tick (#4635) + if (isNullOrUndef(tick.label) && i < ticks.length) { + continue; + } + + if (i === me.zeroLineIndex && options.offset === offsetGridLines) { + // Draw the first index specially + lineWidth = gridLines.zeroLineWidth; + lineColor = gridLines.zeroLineColor; + borderDash = gridLines.zeroLineBorderDash || []; + borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; + } else { + lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1); + lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)'); + borderDash = gridLines.borderDash || []; + borderDashOffset = gridLines.borderDashOffset || 0.0; + } + + lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines); + + // Skip if the pixel is out of the range + if (lineValue === undefined) { + continue; + } + + alignedLineValue = alignPixel(chart, lineValue, lineWidth); + + if (isHorizontal) { + tx1 = tx2 = x1 = x2 = alignedLineValue; + } else { + ty1 = ty2 = y1 = y2 = alignedLineValue; + } + + items.push({ + tx1: tx1, + ty1: ty1, + tx2: tx2, + ty2: ty2, + x1: x1, + y1: y1, + x2: x2, + y2: y2, + width: lineWidth, + color: lineColor, + borderDash: borderDash, + borderDashOffset: borderDashOffset, + }); + } + + items.ticksLength = ticksLength; + items.borderValue = borderValue; + + return items; + }, + + /** + * @private + */ + _computeLabelItems: function() { + var me = this; + var options = me.options; + var optionTicks = options.ticks; + var position = options.position; + var isMirrored = optionTicks.mirror; + var isHorizontal = me.isHorizontal(); + var ticks = me._ticksToDraw; + var fonts = parseTickFontOptions(optionTicks); + var tickPadding = optionTicks.padding; + var tl = getTickMarkLength(options.gridLines); + var rotation = -helpers$1.toRadians(me.labelRotation); + var items = []; + var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; + + if (position === 'top') { + y = me.bottom - tl - tickPadding; + textAlign = !rotation ? 'center' : 'left'; + } else if (position === 'bottom') { + y = me.top + tl + tickPadding; + textAlign = !rotation ? 'center' : 'right'; + } else if (position === 'left') { + x = me.right - (isMirrored ? 0 : tl) - tickPadding; + textAlign = isMirrored ? 'left' : 'right'; + } else { + x = me.left + (isMirrored ? 0 : tl) + tickPadding; + textAlign = isMirrored ? 'right' : 'left'; + } + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + tick = ticks[i]; + label = tick.label; + + // autoskipper skipped this tick (#4635) + if (isNullOrUndef(label)) { + continue; + } + + pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset; + font = tick.major ? fonts.major : fonts.minor; + lineHeight = font.lineHeight; + lineCount = isArray(label) ? label.length : 1; + + if (isHorizontal) { + x = pixel; + textOffset = position === 'top' + ? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight + : (!rotation ? 0.5 : 0) * lineHeight; + } else { + y = pixel; + textOffset = (1 - lineCount) * lineHeight / 2; + } + + items.push({ + x: x, + y: y, + rotation: rotation, + label: label, + font: font, + textOffset: textOffset, + textAlign: textAlign + }); + } + + return items; + }, + + /** + * @private + */ + _drawGrid: function(chartArea) { + var me = this; + var gridLines = me.options.gridLines; + + if (!gridLines.display) { + return; + } + + var ctx = me.ctx; + var chart = me.chart; + var alignPixel = helpers$1._alignPixel; + var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; + var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea)); + var width, color, i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + width = item.width; + color = item.color; + + if (width && color) { + ctx.save(); + ctx.lineWidth = width; + ctx.strokeStyle = color; + if (ctx.setLineDash) { + ctx.setLineDash(item.borderDash); + ctx.lineDashOffset = item.borderDashOffset; + } + + ctx.beginPath(); + + if (gridLines.drawTicks) { + ctx.moveTo(item.tx1, item.ty1); + ctx.lineTo(item.tx2, item.ty2); + } + + if (gridLines.drawOnChartArea) { + ctx.moveTo(item.x1, item.y1); + ctx.lineTo(item.x2, item.y2); + } + + ctx.stroke(); + ctx.restore(); + } + } + + if (axisWidth) { + // Draw the line at the edge of the axis + var firstLineWidth = axisWidth; + var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1); + var borderValue = items.borderValue; + var x1, x2, y1, y2; + + if (me.isHorizontal()) { + x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; + x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; + y1 = y2 = borderValue; + } else { + y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; + y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; + x1 = x2 = borderValue; + } + + ctx.lineWidth = axisWidth; + ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } + }, + + /** + * @private + */ + _drawLabels: function() { + var me = this; + var optionTicks = me.options.ticks; + + if (!optionTicks.display) { + return; + } + + var ctx = me.ctx; + var items = me._labelItems || (me._labelItems = me._computeLabelItems()); + var i, j, ilen, jlen, item, tickFont, label, y; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + tickFont = item.font; + + // Make sure we draw text in the correct color and font + ctx.save(); + ctx.translate(item.x, item.y); + ctx.rotate(item.rotation); + ctx.font = tickFont.string; + ctx.fillStyle = tickFont.color; + ctx.textBaseline = 'middle'; + ctx.textAlign = item.textAlign; + + label = item.label; + y = item.textOffset; + if (isArray(label)) { + for (j = 0, jlen = label.length; j < jlen; ++j) { + // We just make sure the multiline element is a string here.. + ctx.fillText('' + label[j], 0, y); + y += tickFont.lineHeight; + } + } else { + ctx.fillText(label, 0, y); + } + ctx.restore(); + } + }, + + /** + * @private + */ + _drawTitle: function() { + var me = this; + var ctx = me.ctx; + var options = me.options; + var scaleLabel = options.scaleLabel; + + if (!scaleLabel.display) { + return; + } + + var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor); + var scaleLabelFont = helpers$1.options._parseFont(scaleLabel); + var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); + var halfLineHeight = scaleLabelFont.lineHeight / 2; + var position = options.position; + var rotation = 0; + var scaleLabelX, scaleLabelY; + + if (me.isHorizontal()) { + scaleLabelX = me.left + me.width / 2; // midpoint of the width + scaleLabelY = position === 'bottom' + ? me.bottom - halfLineHeight - scaleLabelPadding.bottom + : me.top + halfLineHeight + scaleLabelPadding.top; + } else { + var isLeft = position === 'left'; + scaleLabelX = isLeft + ? me.left + halfLineHeight + scaleLabelPadding.top + : me.right - halfLineHeight - scaleLabelPadding.top; + scaleLabelY = me.top + me.height / 2; + rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; + } + + ctx.save(); + ctx.translate(scaleLabelX, scaleLabelY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = scaleLabelFontColor; // render in correct colour + ctx.font = scaleLabelFont.string; + ctx.fillText(scaleLabel.labelString, 0, 0); + ctx.restore(); + }, + + draw: function(chartArea) { + var me = this; + + if (!me._isVisible()) { + return; + } + + me._drawGrid(chartArea); + me._drawTitle(); + me._drawLabels(); + }, + + /** + * @private + */ + _layers: function() { + var me = this; + var opts = me.options; + var tz = opts.ticks && opts.ticks.z || 0; + var gz = opts.gridLines && opts.gridLines.z || 0; + + if (!me._isVisible() || tz === gz || me.draw !== me._draw) { + // backward compatibility: draw has been overridden by custom scale + return [{ + z: tz, + draw: function() { + me.draw.apply(me, arguments); + } + }]; + } + + return [{ + z: gz, + draw: function() { + me._drawGrid.apply(me, arguments); + me._drawTitle.apply(me, arguments); + } + }, { + z: tz, + draw: function() { + me._drawLabels.apply(me, arguments); + } + }]; + }, + + /** + * @private + */ + _getMatchingVisibleMetas: function(type) { + var me = this; + var isHorizontal = me.isHorizontal(); + return me.chart._getSortedVisibleDatasetMetas() + .filter(function(meta) { + return (!type || meta.type === type) + && (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id); + }); + } +}); + +Scale.prototype._draw = Scale.prototype.draw; + +var core_scale = Scale; + +var isNullOrUndef$1 = helpers$1.isNullOrUndef; + +var defaultConfig = { + position: 'bottom' +}; + +var scale_category = core_scale.extend({ + determineDataLimits: function() { + var me = this; + var labels = me._getLabels(); + var ticksOpts = me.options.ticks; + var min = ticksOpts.min; + var max = ticksOpts.max; + var minIndex = 0; + var maxIndex = labels.length - 1; + var findIndex; + + if (min !== undefined) { + // user specified min value + findIndex = labels.indexOf(min); + if (findIndex >= 0) { + minIndex = findIndex; + } + } + + if (max !== undefined) { + // user specified max value + findIndex = labels.indexOf(max); + if (findIndex >= 0) { + maxIndex = findIndex; + } + } + + me.minIndex = minIndex; + me.maxIndex = maxIndex; + me.min = labels[minIndex]; + me.max = labels[maxIndex]; + }, + + buildTicks: function() { + var me = this; + var labels = me._getLabels(); + var minIndex = me.minIndex; + var maxIndex = me.maxIndex; + + // If we are viewing some subset of labels, slice the original array + me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var chart = me.chart; + + if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { + return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); + } + + return me._getLabels()[index]; + }, + + _configure: function() { + var me = this; + var offset = me.options.offset; + var ticks = me.ticks; + + core_scale.prototype._configure.call(me); + + if (!me.isHorizontal()) { + // For backward compatibility, vertical category scale reverse is inverted. + me._reversePixels = !me._reversePixels; + } + + if (!ticks) { + return; + } + + me._startValue = me.minIndex - (offset ? 0.5 : 0); + me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1); + }, + + // Used to get data value locations. Value can either be an index or a numerical value + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var valueCategory, labels, idx; + + if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) { + value = me.chart.data.datasets[datasetIndex].data[index]; + } + + // If value is a data object, then index is the index in the data array, + // not the index of the scale. We need to change that. + if (!isNullOrUndef$1(value)) { + valueCategory = me.isHorizontal() ? value.x : value.y; + } + if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { + labels = me._getLabels(); + value = helpers$1.valueOrDefault(valueCategory, value); + idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + if (isNaN(index)) { + index = value; + } + } + return me.getPixelForDecimal((index - me._startValue) / me._valueRange); + }, + + getPixelForTick: function(index) { + var ticks = this.ticks; + return index < 0 || index > ticks.length - 1 + ? null + : this.getPixelForValue(ticks[index], index + this.minIndex); + }, + + getValueForPixel: function(pixel) { + var me = this; + var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange); + return Math.min(Math.max(value, 0), me.ticks.length - 1); + }, + + getBasePixel: function() { + return this.bottom; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults = defaultConfig; +scale_category._defaults = _defaults; + +var noop = helpers$1.noop; +var isNullOrUndef$2 = helpers$1.isNullOrUndef; + +/** + * Generate a set of linear ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks(generationOptions, dataRange) { + var ticks = []; + // To get a "nice" value for the tick spacing, we will use the appropriately named + // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks + // for details. + + var MIN_SPACING = 1e-14; + var stepSize = generationOptions.stepSize; + var unit = stepSize || 1; + var maxNumSpaces = generationOptions.maxTicks - 1; + var min = generationOptions.min; + var max = generationOptions.max; + var precision = generationOptions.precision; + var rmin = dataRange.min; + var rmax = dataRange.max; + var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; + var factor, niceMin, niceMax, numSpaces; + + // Beyond MIN_SPACING floating point numbers being to lose precision + // such that we can't do the math necessary to generate ticks + if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) { + return [rmin, rmax]; + } + + numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); + if (numSpaces > maxNumSpaces) { + // If the calculated num of spaces exceeds maxNumSpaces, recalculate it + spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; + } + + if (stepSize || isNullOrUndef$2(precision)) { + // If a precision is not specified, calculate factor based on spacing + factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); + } else { + // If the user specified a precision, round to that number of decimal places + factor = Math.pow(10, precision); + spacing = Math.ceil(spacing * factor) / factor; + } + + niceMin = Math.floor(rmin / spacing) * spacing; + niceMax = Math.ceil(rmax / spacing) * spacing; + + // If min, max and stepSize is set and they make an evenly spaced scale use it. + if (stepSize) { + // If very close to our whole number, use it. + if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { + niceMin = min; + } + if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { + niceMax = max; + } + } + + numSpaces = (niceMax - niceMin) / spacing; + // If very close to our rounded value, use it. + if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { + numSpaces = Math.round(numSpaces); + } else { + numSpaces = Math.ceil(numSpaces); + } + + niceMin = Math.round(niceMin * factor) / factor; + niceMax = Math.round(niceMax * factor) / factor; + ticks.push(isNullOrUndef$2(min) ? niceMin : min); + for (var j = 1; j < numSpaces; ++j) { + ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); + } + ticks.push(isNullOrUndef$2(max) ? niceMax : max); + + return ticks; +} + +var scale_linearbase = core_scale.extend({ + getRightValue: function(value) { + if (typeof value === 'string') { + return +value; + } + return core_scale.prototype.getRightValue.call(this, value); + }, + + handleTickRangeOptions: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, + // do nothing since that would make the chart weird. If the user really wants a weird chart + // axis, they can manually override it + if (tickOpts.beginAtZero) { + var minSign = helpers$1.sign(me.min); + var maxSign = helpers$1.sign(me.max); + + if (minSign < 0 && maxSign < 0) { + // move the top up to 0 + me.max = 0; + } else if (minSign > 0 && maxSign > 0) { + // move the bottom down to 0 + me.min = 0; + } + } + + var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; + var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; + + if (tickOpts.min !== undefined) { + me.min = tickOpts.min; + } else if (tickOpts.suggestedMin !== undefined) { + if (me.min === null) { + me.min = tickOpts.suggestedMin; + } else { + me.min = Math.min(me.min, tickOpts.suggestedMin); + } + } + + if (tickOpts.max !== undefined) { + me.max = tickOpts.max; + } else if (tickOpts.suggestedMax !== undefined) { + if (me.max === null) { + me.max = tickOpts.suggestedMax; + } else { + me.max = Math.max(me.max, tickOpts.suggestedMax); + } + } + + if (setMin !== setMax) { + // We set the min or the max but not both. + // So ensure that our range is good + // Inverted or 0 length range can happen when + // ticks.min is set, and no datasets are visible + if (me.min >= me.max) { + if (setMin) { + me.max = me.min + 1; + } else { + me.min = me.max - 1; + } + } + } + + if (me.min === me.max) { + me.max++; + + if (!tickOpts.beginAtZero) { + me.min--; + } + } + }, + + getTickLimit: function() { + var me = this; + var tickOpts = me.options.ticks; + var stepSize = tickOpts.stepSize; + var maxTicksLimit = tickOpts.maxTicksLimit; + var maxTicks; + + if (stepSize) { + maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; + } else { + maxTicks = me._computeTickLimit(); + maxTicksLimit = maxTicksLimit || 11; + } + + if (maxTicksLimit) { + maxTicks = Math.min(maxTicksLimit, maxTicks); + } + + return maxTicks; + }, + + _computeTickLimit: function() { + return Number.POSITIVE_INFINITY; + }, + + handleDirectionalChanges: noop, + + buildTicks: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph. Make sure we always have at least 2 ticks + var maxTicks = me.getTickLimit(); + maxTicks = Math.max(2, maxTicks); + + var numericGeneratorOptions = { + maxTicks: maxTicks, + min: tickOpts.min, + max: tickOpts.max, + precision: tickOpts.precision, + stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) + }; + var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); + + me.handleDirectionalChanges(); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + ticks.reverse(); + + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + }, + + convertTicksToLabels: function() { + var me = this; + me.ticksAsNumbers = me.ticks.slice(); + me.zeroLineIndex = me.ticks.indexOf(0); + + core_scale.prototype.convertTicksToLabels.call(me); + }, + + _configure: function() { + var me = this; + var ticks = me.getTicks(); + var start = me.min; + var end = me.max; + var offset; + + core_scale.prototype._configure.call(me); + + if (me.options.offset && ticks.length) { + offset = (end - start) / Math.max(ticks.length - 1, 1) / 2; + start -= offset; + end += offset; + } + me._startValue = start; + me._endValue = end; + me._valueRange = end - start; + } +}); + +var defaultConfig$1 = { + position: 'left', + ticks: { + callback: core_ticks.formatters.linear + } +}; + +var DEFAULT_MIN = 0; +var DEFAULT_MAX = 1; + +function getOrCreateStack(stacks, stacked, meta) { + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + stacked === undefined && meta.stack === undefined ? meta.index : '', + meta.stack + ].join('.'); + + if (stacks[key] === undefined) { + stacks[key] = { + pos: [], + neg: [] + }; + } + + return stacks[key]; +} + +function stackData(scale, stacks, meta, data) { + var opts = scale.options; + var stacked = opts.stacked; + var stack = getOrCreateStack(stacks, stacked, meta); + var pos = stack.pos; + var neg = stack.neg; + var ilen = data.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = scale._parseValue(data[i]); + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { + continue; + } + + pos[i] = pos[i] || 0; + neg[i] = neg[i] || 0; + + if (opts.relativePoints) { + pos[i] = 100; + } else if (value.min < 0 || value.max < 0) { + neg[i] += value.min; + } else { + pos[i] += value.max; + } + } +} + +function updateMinMax(scale, meta, data) { + var ilen = data.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = scale._parseValue(data[i]); + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { + continue; + } + + scale.min = Math.min(scale.min, value.min); + scale.max = Math.max(scale.max, value.max); + } +} + +var scale_linear = scale_linearbase.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var datasets = chart.data.datasets; + var metasets = me._getMatchingVisibleMetas(); + var hasStacks = opts.stacked; + var stacks = {}; + var ilen = metasets.length; + var i, meta, data, values; + + me.min = Number.POSITIVE_INFINITY; + me.max = Number.NEGATIVE_INFINITY; + + if (hasStacks === undefined) { + for (i = 0; !hasStacks && i < ilen; ++i) { + meta = metasets[i]; + hasStacks = meta.stack !== undefined; + } + } + + for (i = 0; i < ilen; ++i) { + meta = metasets[i]; + data = datasets[meta.index].data; + if (hasStacks) { + stackData(me, stacks, meta, data); + } else { + updateMinMax(me, meta, data); + } + } + + helpers$1.each(stacks, function(stackValues) { + values = stackValues.pos.concat(stackValues.neg); + me.min = Math.min(me.min, helpers$1.min(values)); + me.max = Math.max(me.max, helpers$1.max(values)); + }); + + me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; + me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + var me = this; + var tickFont; + + if (me.isHorizontal()) { + return Math.ceil(me.width / 40); + } + tickFont = helpers$1.options._parseFont(me.options.ticks); + return Math.ceil(me.height / tickFont.lineHeight); + }, + + // Called after the ticks are built. We need + handleDirectionalChanges: function() { + if (!this.isHorizontal()) { + // We are in a vertical orientation. The top value is the highest. So reverse the array + this.ticks.reverse(); + } + }, + + getLabelForIndex: function(index, datasetIndex) { + return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); + }, + + // Utils + getPixelForValue: function(value) { + var me = this; + return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange); + }, + + getValueForPixel: function(pixel) { + return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange; + }, + + getPixelForTick: function(index) { + var ticks = this.ticksAsNumbers; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index]); + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$1 = defaultConfig$1; +scale_linear._defaults = _defaults$1; + +var valueOrDefault$b = helpers$1.valueOrDefault; +var log10 = helpers$1.math.log10; + +/** + * Generate a set of logarithmic ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks$1(generationOptions, dataRange) { + var ticks = []; + + var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); + + var endExp = Math.floor(log10(dataRange.max)); + var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + var exp, significand; + + if (tickVal === 0) { + exp = Math.floor(log10(dataRange.minNotZero)); + significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); + + ticks.push(tickVal); + tickVal = significand * Math.pow(10, exp); + } else { + exp = Math.floor(log10(tickVal)); + significand = Math.floor(tickVal / Math.pow(10, exp)); + } + var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + + do { + ticks.push(tickVal); + + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + precision = exp >= 0 ? 1 : precision; + } + + tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + + var lastTick = valueOrDefault$b(generationOptions.max, tickVal); + ticks.push(lastTick); + + return ticks; +} + +var defaultConfig$2 = { + position: 'left', + + // label settings + ticks: { + callback: core_ticks.formatters.logarithmic + } +}; + +// TODO(v3): change this to positiveOrDefault +function nonNegativeOrDefault(value, defaultValue) { + return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; +} + +var scale_logarithmic = core_scale.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var datasets = chart.data.datasets; + var isHorizontal = me.isHorizontal(); + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + var datasetIndex, meta, value, data, i, ilen; + + // Calculate Range + me.min = Number.POSITIVE_INFINITY; + me.max = Number.NEGATIVE_INFINITY; + me.minNotZero = Number.POSITIVE_INFINITY; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + break; + } + } + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = []; + } + + data = datasets[datasetIndex].data; + for (i = 0, ilen = data.length; i < ilen; i++) { + var values = valuesPerStack[key]; + value = me._parseValue(data[i]); + // invalid, hidden and negative values are ignored + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { + continue; + } + values[i] = values[i] || 0; + values[i] += value.max; + } + } + } + + helpers$1.each(valuesPerStack, function(valuesForType) { + if (valuesForType.length > 0) { + var minVal = helpers$1.min(valuesForType); + var maxVal = helpers$1.max(valuesForType); + me.min = Math.min(me.min, minVal); + me.max = Math.max(me.max, maxVal); + } + }); + + } else { + for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { + meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + data = datasets[datasetIndex].data; + for (i = 0, ilen = data.length; i < ilen; i++) { + value = me._parseValue(data[i]); + // invalid, hidden and negative values are ignored + if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { + continue; + } + + me.min = Math.min(value.min, me.min); + me.max = Math.max(value.max, me.max); + + if (value.min !== 0) { + me.minNotZero = Math.min(value.min, me.minNotZero); + } + } + } + } + } + + me.min = helpers$1.isFinite(me.min) ? me.min : null; + me.max = helpers$1.isFinite(me.max) ? me.max : null; + me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null; + + // Common base implementation to handle ticks.min, ticks.max + this.handleTickRangeOptions(); + }, + + handleTickRangeOptions: function() { + var me = this; + var tickOpts = me.options.ticks; + var DEFAULT_MIN = 1; + var DEFAULT_MAX = 10; + + me.min = nonNegativeOrDefault(tickOpts.min, me.min); + me.max = nonNegativeOrDefault(tickOpts.max, me.max); + + if (me.min === me.max) { + if (me.min !== 0 && me.min !== null) { + me.min = Math.pow(10, Math.floor(log10(me.min)) - 1); + me.max = Math.pow(10, Math.floor(log10(me.max)) + 1); + } else { + me.min = DEFAULT_MIN; + me.max = DEFAULT_MAX; + } + } + if (me.min === null) { + me.min = Math.pow(10, Math.floor(log10(me.max)) - 1); + } + if (me.max === null) { + me.max = me.min !== 0 + ? Math.pow(10, Math.floor(log10(me.min)) + 1) + : DEFAULT_MAX; + } + if (me.minNotZero === null) { + if (me.min > 0) { + me.minNotZero = me.min; + } else if (me.max < 1) { + me.minNotZero = Math.pow(10, Math.floor(log10(me.max))); + } else { + me.minNotZero = DEFAULT_MIN; + } + } + }, + + buildTicks: function() { + var me = this; + var tickOpts = me.options.ticks; + var reverse = !me.isHorizontal(); + + var generationOptions = { + min: nonNegativeOrDefault(tickOpts.min), + max: nonNegativeOrDefault(tickOpts.max) + }; + var ticks = me.ticks = generateTicks$1(generationOptions, me); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + reverse = !reverse; + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + if (reverse) { + ticks.reverse(); + } + }, + + convertTicksToLabels: function() { + this.tickValues = this.ticks.slice(); + + core_scale.prototype.convertTicksToLabels.call(this); + }, + + // Get the correct tooltip label + getLabelForIndex: function(index, datasetIndex) { + return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); + }, + + getPixelForTick: function(index) { + var ticks = this.tickValues; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index]); + }, + + /** + * Returns the value of the first tick. + * @param {number} value - The minimum not zero value. + * @return {number} The first tick value. + * @private + */ + _getFirstTickValue: function(value) { + var exp = Math.floor(log10(value)); + var significand = Math.floor(value / Math.pow(10, exp)); + + return significand * Math.pow(10, exp); + }, + + _configure: function() { + var me = this; + var start = me.min; + var offset = 0; + + core_scale.prototype._configure.call(me); + + if (start === 0) { + start = me._getFirstTickValue(me.minNotZero); + offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length; + } + + me._startValue = log10(start); + me._valueOffset = offset; + me._valueRange = (log10(me.max) - log10(start)) / (1 - offset); + }, + + getPixelForValue: function(value) { + var me = this; + var decimal = 0; + + value = +me.getRightValue(value); + + if (value > me.min && value > 0) { + decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset; + } + return me.getPixelForDecimal(decimal); + }, + + getValueForPixel: function(pixel) { + var me = this; + var decimal = me.getDecimalForPixel(pixel); + return decimal === 0 && me.min === 0 + ? 0 + : Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange); + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$2 = defaultConfig$2; +scale_logarithmic._defaults = _defaults$2; + +var valueOrDefault$c = helpers$1.valueOrDefault; +var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; +var resolve$4 = helpers$1.options.resolve; + +var defaultConfig$3 = { + display: true, + + // Boolean - Whether to animate scaling the chart from the centre + animate: true, + position: 'chartArea', + + angleLines: { + display: true, + color: 'rgba(0,0,0,0.1)', + lineWidth: 1, + borderDash: [], + borderDashOffset: 0.0 + }, + + gridLines: { + circular: false + }, + + // label settings + ticks: { + // Boolean - Show a backdrop to the scale label + showLabelBackdrop: true, + + // String - The colour of the label backdrop + backdropColor: 'rgba(255,255,255,0.75)', + + // Number - The backdrop padding above & below the label in pixels + backdropPaddingY: 2, + + // Number - The backdrop padding to the side of the label in pixels + backdropPaddingX: 2, + + callback: core_ticks.formatters.linear + }, + + pointLabels: { + // Boolean - if true, show point labels + display: true, + + // Number - Point label font size in pixels + fontSize: 10, + + // Function - Used to convert point labels + callback: function(label) { + return label; + } + } +}; + +function getTickBackdropHeight(opts) { + var tickOpts = opts.ticks; + + if (tickOpts.display && opts.display) { + return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; + } + return 0; +} + +function measureLabelSize(ctx, lineHeight, label) { + if (helpers$1.isArray(label)) { + return { + w: helpers$1.longestText(ctx, ctx.font, label), + h: label.length * lineHeight + }; + } + + return { + w: ctx.measureText(label).width, + h: lineHeight + }; +} + +function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size, + end: pos + }; + } + + return { + start: pos, + end: pos + size + }; +} + +/** + * Helper function to fit a radial linear scale with point labels + */ +function fitWithPointLabels(scale) { + + // Right, this is really confusing and there is a lot of maths going on here + // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 + // + // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif + // + // Solution: + // + // We assume the radius of the polygon is half the size of the canvas at first + // at each index we check if the text overlaps. + // + // Where it does, we store that angle and that index. + // + // After finding the largest index and angle we calculate how much we need to remove + // from the shape radius to move the point inwards by that x. + // + // We average the left and right distances to get the maximum shape radius that can fit in the box + // along with labels. + // + // Once we have that, we can find the centre point for the chart, by taking the x text protrusion + // on each side, removing that from the size, halving it and adding the left x protrusion width. + // + // This will mean we have a shape fitted to the canvas, as large as it can be with the labels + // and position it in the most space efficient manner + // + // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif + + var plFont = helpers$1.options._parseFont(scale.options.pointLabels); + + // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. + // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points + var furthestLimits = { + l: 0, + r: scale.width, + t: 0, + b: scale.height - scale.paddingTop + }; + var furthestAngles = {}; + var i, textSize, pointPosition; + + scale.ctx.font = plFont.string; + scale._pointLabelSizes = []; + + var valueCount = scale.chart.data.labels.length; + for (i = 0; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); + textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]); + scale._pointLabelSizes[i] = textSize; + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians) % 360; + var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + + if (hLimits.start < furthestLimits.l) { + furthestLimits.l = hLimits.start; + furthestAngles.l = angleRadians; + } + + if (hLimits.end > furthestLimits.r) { + furthestLimits.r = hLimits.end; + furthestAngles.r = angleRadians; + } + + if (vLimits.start < furthestLimits.t) { + furthestLimits.t = vLimits.start; + furthestAngles.t = angleRadians; + } + + if (vLimits.end > furthestLimits.b) { + furthestLimits.b = vLimits.end; + furthestAngles.b = angleRadians; + } + } + + scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); +} + +function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + + return 'right'; +} + +function fillText(ctx, text, position, lineHeight) { + var y = position.y + lineHeight / 2; + var i, ilen; + + if (helpers$1.isArray(text)) { + for (i = 0, ilen = text.length; i < ilen; ++i) { + ctx.fillText(text[i], position.x, y); + y += lineHeight; + } + } else { + ctx.fillText(text, position.x, y); + } +} + +function adjustPointPositionForLabelHeight(angle, textSize, position) { + if (angle === 90 || angle === 270) { + position.y -= (textSize.h / 2); + } else if (angle > 270 || angle < 90) { + position.y -= textSize.h; + } +} + +function drawPointLabels(scale) { + var ctx = scale.ctx; + var opts = scale.options; + var pointLabelOpts = opts.pointLabels; + var tickBackdropHeight = getTickBackdropHeight(opts); + var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); + var plFont = helpers$1.options._parseFont(pointLabelOpts); + + ctx.save(); + + ctx.font = plFont.string; + ctx.textBaseline = 'middle'; + + for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) { + // Extra pixels out for some label spacing + var extra = (i === 0 ? tickBackdropHeight / 2 : 0); + var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); + + // Keep this in loop since we may support array properties here + var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); + ctx.fillStyle = pointLabelFontColor; + + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians); + ctx.textAlign = getTextAlignForAngle(angle); + adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); + fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight); + } + ctx.restore(); +} + +function drawRadiusLine(scale, gridLineOpts, radius, index) { + var ctx = scale.ctx; + var circular = gridLineOpts.circular; + var valueCount = scale.chart.data.labels.length; + var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); + var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); + var pointPosition; + + if ((!circular && !valueCount) || !lineColor || !lineWidth) { + return; + } + + ctx.save(); + ctx.strokeStyle = lineColor; + ctx.lineWidth = lineWidth; + if (ctx.setLineDash) { + ctx.setLineDash(gridLineOpts.borderDash || []); + ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; + } + + ctx.beginPath(); + if (circular) { + // Draw circular arcs between the points + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); + } else { + // Draw straight lines connecting each index + pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + + for (var i = 1; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + } + ctx.closePath(); + ctx.stroke(); + ctx.restore(); +} + +function numberOrZero(param) { + return helpers$1.isNumber(param) ? param : 0; +} + +var scale_radialLinear = scale_linearbase.extend({ + setDimensions: function() { + var me = this; + + // Set the unconstrained dimension before label rotation + me.width = me.maxWidth; + me.height = me.maxHeight; + me.paddingTop = getTickBackdropHeight(me.options) / 2; + me.xCenter = Math.floor(me.width / 2); + me.yCenter = Math.floor((me.height - me.paddingTop) / 2); + me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var min = Number.POSITIVE_INFINITY; + var max = Number.NEGATIVE_INFINITY; + + helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + min = Math.min(value, min); + max = Math.max(value, max); + }); + } + }); + + me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); + me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); + }, + + convertTicksToLabels: function() { + var me = this; + + scale_linearbase.prototype.convertTicksToLabels.call(me); + + // Point labels + me.pointLabels = me.chart.data.labels.map(function() { + var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me); + return label || label === 0 ? label : ''; + }); + }, + + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + + fit: function() { + var me = this; + var opts = me.options; + + if (opts.display && opts.pointLabels.display) { + fitWithPointLabels(me); + } else { + me.setCenterPoint(0, 0, 0, 0); + } + }, + + /** + * Set radius reductions and determine new radius and center point + * @private + */ + setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { + var me = this; + var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); + var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); + var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); + var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); + + radiusReductionLeft = numberOrZero(radiusReductionLeft); + radiusReductionRight = numberOrZero(radiusReductionRight); + radiusReductionTop = numberOrZero(radiusReductionTop); + radiusReductionBottom = numberOrZero(radiusReductionBottom); + + me.drawingArea = Math.min( + Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), + Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); + me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); + }, + + setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { + var me = this; + var maxRight = me.width - rightMovement - me.drawingArea; + var maxLeft = leftMovement + me.drawingArea; + var maxTop = topMovement + me.drawingArea; + var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; + + me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); + me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); + }, + + getIndexAngle: function(index) { + var chart = this.chart; + var angleMultiplier = 360 / chart.data.labels.length; + var options = chart.options || {}; + var startAngle = options.startAngle || 0; + + // Start from the top instead of right, so remove a quarter of the circle + var angle = (index * angleMultiplier + startAngle) % 360; + + return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360; + }, + + getDistanceFromCenterForValue: function(value) { + var me = this; + + if (helpers$1.isNullOrUndef(value)) { + return NaN; + } + + // Take into account half font size + the yPadding of the top value + var scalingFactor = me.drawingArea / (me.max - me.min); + if (me.options.ticks.reverse) { + return (me.max - value) * scalingFactor; + } + return (value - me.min) * scalingFactor; + }, + + getPointPosition: function(index, distanceFromCenter) { + var me = this; + var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); + return { + x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, + y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter + }; + }, + + getPointPositionForValue: function(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + }, + + getBasePosition: function(index) { + var me = this; + var min = me.min; + var max = me.max; + + return me.getPointPositionForValue(index || 0, + me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0); + }, + + /** + * @private + */ + _drawGrid: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + var gridLineOpts = opts.gridLines; + var angleLineOpts = opts.angleLines; + var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth); + var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color); + var i, offset, position; + + if (opts.pointLabels.display) { + drawPointLabels(me); + } + + if (gridLineOpts.display) { + helpers$1.each(me.ticks, function(label, index) { + if (index !== 0) { + offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + drawRadiusLine(me, gridLineOpts, offset, index); + } + }); + } + + if (angleLineOpts.display && lineWidth && lineColor) { + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = lineColor; + if (ctx.setLineDash) { + ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); + ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); + } + + for (i = me.chart.data.labels.length - 1; i >= 0; i--) { + offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max); + position = me.getPointPosition(i, offset); + ctx.beginPath(); + ctx.moveTo(me.xCenter, me.yCenter); + ctx.lineTo(position.x, position.y); + ctx.stroke(); + } + + ctx.restore(); + } + }, + + /** + * @private + */ + _drawLabels: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + var tickOpts = opts.ticks; + + if (!tickOpts.display) { + return; + } + + var startAngle = me.getIndexAngle(0); + var tickFont = helpers$1.options._parseFont(tickOpts); + var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor); + var offset, width; + + ctx.save(); + ctx.font = tickFont.string; + ctx.translate(me.xCenter, me.yCenter); + ctx.rotate(startAngle); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + helpers$1.each(me.ticks, function(label, index) { + if (index === 0 && !tickOpts.reverse) { + return; + } + + offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + + if (tickOpts.showLabelBackdrop) { + width = ctx.measureText(label).width; + ctx.fillStyle = tickOpts.backdropColor; + + ctx.fillRect( + -width / 2 - tickOpts.backdropPaddingX, + -offset - tickFont.size / 2 - tickOpts.backdropPaddingY, + width + tickOpts.backdropPaddingX * 2, + tickFont.size + tickOpts.backdropPaddingY * 2 + ); + } + + ctx.fillStyle = tickFontColor; + ctx.fillText(label, 0, -offset); + }); + + ctx.restore(); + }, + + /** + * @private + */ + _drawTitle: helpers$1.noop +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$3 = defaultConfig$3; +scale_radialLinear._defaults = _defaults$3; + +var deprecated$1 = helpers$1._deprecated; +var resolve$5 = helpers$1.options.resolve; +var valueOrDefault$d = helpers$1.valueOrDefault; + +// Integer constants are from the ES6 spec. +var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; +var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + +var INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: 1000 + }, + second: { + common: true, + size: 1000, + steps: 60 + }, + minute: { + common: true, + size: 60000, + steps: 60 + }, + hour: { + common: true, + size: 3600000, + steps: 24 + }, + day: { + common: true, + size: 86400000, + steps: 30 + }, + week: { + common: false, + size: 604800000, + steps: 4 + }, + month: { + common: true, + size: 2.628e9, + steps: 12 + }, + quarter: { + common: false, + size: 7.884e9, + steps: 4 + }, + year: { + common: true, + size: 3.154e10 + } +}; + +var UNITS = Object.keys(INTERVALS); + +function sorter(a, b) { + return a - b; +} + +function arrayUnique(items) { + var hash = {}; + var out = []; + var i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + if (!hash[item]) { + hash[item] = true; + out.push(item); + } + } + + return out; +} + +function getMin(options) { + return helpers$1.valueOrDefault(options.time.min, options.ticks.min); +} + +function getMax(options) { + return helpers$1.valueOrDefault(options.time.max, options.ticks.max); +} + +/** + * Returns an array of {time, pos} objects used to interpolate a specific `time` or position + * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is + * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other + * extremity (left + width or top + height). Note that it would be more optimized to directly + * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need + * to create the lookup table. The table ALWAYS contains at least two items: min and max. + * + * @param {number[]} timestamps - timestamps sorted from lowest to highest. + * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min + * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. + * If 'series', timestamps will be positioned at the same distance from each other. In this + * case, only timestamps that break the time linearity are registered, meaning that in the + * best case, all timestamps are linear, the table contains only min and max. + */ +function buildLookupTable(timestamps, min, max, distribution) { + if (distribution === 'linear' || !timestamps.length) { + return [ + {time: min, pos: 0}, + {time: max, pos: 1} + ]; + } + + var table = []; + var items = [min]; + var i, ilen, prev, curr, next; + + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr > min && curr < max) { + items.push(curr); + } + } + + items.push(max); + + for (i = 0, ilen = items.length; i < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + + // only add points that breaks the scale linearity + if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { + table.push({time: curr, pos: i / (ilen - 1)}); + } + } + + return table; +} + +// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ +function lookup(table, key, value) { + var lo = 0; + var hi = table.length - 1; + var mid, i0, i1; + + while (lo >= 0 && lo <= hi) { + mid = (lo + hi) >> 1; + i0 = table[mid - 1] || null; + i1 = table[mid]; + + if (!i0) { + // given value is outside table (before first item) + return {lo: null, hi: i1}; + } else if (i1[key] < value) { + lo = mid + 1; + } else if (i0[key] > value) { + hi = mid - 1; + } else { + return {lo: i0, hi: i1}; + } + } + + // given value is outside table (after last item) + return {lo: i1, hi: null}; +} + +/** + * Linearly interpolates the given source `value` using the table items `skey` values and + * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') + * returns the position for a timestamp equal to 42. If value is out of bounds, values at + * index [0, 1] or [n - 1, n] are used for the interpolation. + */ +function interpolate$1(table, skey, sval, tkey) { + var range = lookup(table, skey, sval); + + // Note: the lookup table ALWAYS contains at least 2 items (min and max) + var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; + var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; + + var span = next[skey] - prev[skey]; + var ratio = span ? (sval - prev[skey]) / span : 0; + var offset = (next[tkey] - prev[tkey]) * ratio; + + return prev[tkey] + offset; +} + +function toTimestamp(scale, input) { + var adapter = scale._adapter; + var options = scale.options.time; + var parser = options.parser; + var format = parser || options.format; + var value = input; + + if (typeof parser === 'function') { + value = parser(value); + } + + // Only parse if its not a timestamp already + if (!helpers$1.isFinite(value)) { + value = typeof format === 'string' + ? adapter.parse(value, format) + : adapter.parse(value); + } + + if (value !== null) { + return +value; + } + + // Labels are in an incompatible format and no `parser` has been provided. + // The user might still use the deprecated `format` option for parsing. + if (!parser && typeof format === 'function') { + value = format(input); + + // `format` could return something else than a timestamp, if so, parse it + if (!helpers$1.isFinite(value)) { + value = adapter.parse(value); + } + } + + return value; +} + +function parse(scale, input) { + if (helpers$1.isNullOrUndef(input)) { + return null; + } + + var options = scale.options.time; + var value = toTimestamp(scale, scale.getRightValue(input)); + if (value === null) { + return value; + } + + if (options.round) { + value = +scale._adapter.startOf(value, options.round); + } + + return value; +} + +/** + * Figures out what unit results in an appropriate number of auto-generated ticks + */ +function determineUnitForAutoTicks(minUnit, min, max, capacity) { + var ilen = UNITS.length; + var i, interval, factor; + + for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + interval = INTERVALS[UNITS[i]]; + factor = interval.steps ? interval.steps : MAX_INTEGER; + + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } + } + + return UNITS[ilen - 1]; +} + +/** + * Figures out what unit to format a set of ticks with + */ +function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { + var i, unit; + + for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { + unit = UNITS[i]; + if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { + return unit; + } + } + + return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; +} + +function determineMajorUnit(unit) { + for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; + } + } +} + +/** + * Generates a maximum of `capacity` timestamps between min and max, rounded to the + * `minor` unit using the given scale time `options`. + * Important: this method can return ticks outside the min and max range, it's the + * responsibility of the calling code to clamp values if needed. + */ +function generate(scale, min, max, capacity) { + var adapter = scale._adapter; + var options = scale.options; + var timeOpts = options.time; + var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); + var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]); + var weekday = minor === 'week' ? timeOpts.isoWeekday : false; + var first = min; + var ticks = []; + var time; + + // For 'week' unit, handle the first day of week option + if (weekday) { + first = +adapter.startOf(first, 'isoWeek', weekday); + } + + // Align first ticks on unit + first = +adapter.startOf(first, weekday ? 'day' : minor); + + // Prevent browser from freezing in case user options request millions of milliseconds + if (adapter.diff(max, min, minor) > 100000 * stepSize) { + throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor; + } + + for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) { + ticks.push(time); + } + + if (time === max || options.bounds === 'ticks') { + ticks.push(time); + } + + return ticks; +} + +/** + * Returns the start and end offsets from edges in the form of {start, end} + * where each value is a relative width to the scale and ranges between 0 and 1. + * They add extra margins on the both sides by scaling down the original scale. + * Offsets are added when the `offset` option is true. + */ +function computeOffsets(table, ticks, min, max, options) { + var start = 0; + var end = 0; + var first, last; + + if (options.offset && ticks.length) { + first = interpolate$1(table, 'time', ticks[0], 'pos'); + if (ticks.length === 1) { + start = 1 - first; + } else { + start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; + } + last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); + if (ticks.length === 1) { + end = last; + } else { + end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; + } + } + + return {start: start, end: end, factor: 1 / (start + 1 + end)}; +} + +function setMajorTicks(scale, ticks, map, majorUnit) { + var adapter = scale._adapter; + var first = +adapter.startOf(ticks[0].value, majorUnit); + var last = ticks[ticks.length - 1].value; + var major, index; + + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index = map[major]; + if (index >= 0) { + ticks[index].major = true; + } + } + return ticks; +} + +function ticksFromTimestamps(scale, values, majorUnit) { + var ticks = []; + var map = {}; + var ilen = values.length; + var i, value; + + for (i = 0; i < ilen; ++i) { + value = values[i]; + map[value] = i; + + ticks.push({ + value: value, + major: false + }); + } + + // We set the major ticks separately from the above loop because calling startOf for every tick + // is expensive when there is a large number of ticks + return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); +} + +var defaultConfig$4 = { + position: 'bottom', + + /** + * Data distribution along the scale: + * - 'linear': data are spread according to their time (distances can vary), + * - 'series': data are spread at the same distance from each other. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + distribution: 'linear', + + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @see https://github.com/chartjs/Chart.js/pull/4556 + * @since 2.7.0 + */ + bounds: 'data', + + adapters: {}, + time: { + parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment + unit: false, // false == automatic or override with week, month, year, etc. + round: false, // none, or override with week, month, year, etc. + displayFormat: false, // DEPRECATED + isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + autoSkip: false, + + /** + * Ticks generation input values: + * - 'auto': generates "optimal" ticks based on scale size and time options. + * - 'data': generates ticks from data (including labels from data {t|x|y} objects). + * - 'labels': generates ticks from user given `data.labels` values ONLY. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + source: 'auto', + + major: { + enabled: false + } + } +}; + +var scale_time = core_scale.extend({ + initialize: function() { + this.mergeTicksOptions(); + core_scale.prototype.initialize.call(this); + }, + + update: function() { + var me = this; + var options = me.options; + var time = options.time || (options.time = {}); + var adapter = me._adapter = new core_adapters._date(options.adapters.date); + + // DEPRECATIONS: output a message only one time per update + deprecated$1('time scale', time.format, 'time.format', 'time.parser'); + deprecated$1('time scale', time.min, 'time.min', 'ticks.min'); + deprecated$1('time scale', time.max, 'time.max', 'ticks.max'); + + // Backward compatibility: before introducing adapter, `displayFormats` was + // supposed to contain *all* unit/string pairs but this can't be resolved + // when loading the scale (adapters are loaded afterward), so let's populate + // missing formats on update + helpers$1.mergeIf(time.displayFormats, adapter.formats()); + + return core_scale.prototype.update.apply(me, arguments); + }, + + /** + * Allows data to be referenced via 't' attribute + */ + getRightValue: function(rawValue) { + if (rawValue && rawValue.t !== undefined) { + rawValue = rawValue.t; + } + return core_scale.prototype.getRightValue.call(this, rawValue); + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var adapter = me._adapter; + var options = me.options; + var unit = options.time.unit || 'day'; + var min = MAX_INTEGER; + var max = MIN_INTEGER; + var timestamps = []; + var datasets = []; + var labels = []; + var i, j, ilen, jlen, data, timestamp, labelsAdded; + var dataLabels = me._getLabels(); + + for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { + labels.push(parse(me, dataLabels[i])); + } + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + data = chart.data.datasets[i].data; + + // Let's consider that all data have the same format. + if (helpers$1.isObject(data[0])) { + datasets[i] = []; + + for (j = 0, jlen = data.length; j < jlen; ++j) { + timestamp = parse(me, data[j]); + timestamps.push(timestamp); + datasets[i][j] = timestamp; + } + } else { + datasets[i] = labels.slice(0); + if (!labelsAdded) { + timestamps = timestamps.concat(labels); + labelsAdded = true; + } + } + } else { + datasets[i] = []; + } + } + + if (labels.length) { + min = Math.min(min, labels[0]); + max = Math.max(max, labels[labels.length - 1]); + } + + if (timestamps.length) { + timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter); + min = Math.min(min, timestamps[0]); + max = Math.max(max, timestamps[timestamps.length - 1]); + } + + min = parse(me, getMin(options)) || min; + max = parse(me, getMax(options)) || max; + + // In case there is no valid min/max, set limits based on unit time option + min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; + max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; + + // Make sure that max is strictly higher than min (required by the lookup table) + me.min = Math.min(min, max); + me.max = Math.max(min + 1, max); + + // PRIVATE + me._table = []; + me._timestamps = { + data: timestamps, + datasets: datasets, + labels: labels + }; + }, + + buildTicks: function() { + var me = this; + var min = me.min; + var max = me.max; + var options = me.options; + var tickOpts = options.ticks; + var timeOpts = options.time; + var timestamps = me._timestamps; + var ticks = []; + var capacity = me.getLabelCapacity(min); + var source = tickOpts.source; + var distribution = options.distribution; + var i, ilen, timestamp; + + if (source === 'data' || (source === 'auto' && distribution === 'series')) { + timestamps = timestamps.data; + } else if (source === 'labels') { + timestamps = timestamps.labels; + } else { + timestamps = generate(me, min, max, capacity); + } + + if (options.bounds === 'ticks' && timestamps.length) { + min = timestamps[0]; + max = timestamps[timestamps.length - 1]; + } + + // Enforce limits with user min/max options + min = parse(me, getMin(options)) || min; + max = parse(me, getMax(options)) || max; + + // Remove ticks outside the min/max range + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + timestamp = timestamps[i]; + if (timestamp >= min && timestamp <= max) { + ticks.push(timestamp); + } + } + + me.min = min; + me.max = max; + + // PRIVATE + // determineUnitForFormatting relies on the number of ticks so we don't use it when + // autoSkip is enabled because we don't yet know what the final number of ticks will be + me._unit = timeOpts.unit || (tickOpts.autoSkip + ? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity) + : determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max)); + me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined + : determineMajorUnit(me._unit); + me._table = buildLookupTable(me._timestamps.data, min, max, distribution); + me._offsets = computeOffsets(me._table, ticks, min, max, options); + + if (tickOpts.reverse) { + ticks.reverse(); + } + + return ticksFromTimestamps(me, ticks, me._majorUnit); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var adapter = me._adapter; + var data = me.chart.data; + var timeOpts = me.options.time; + var label = data.labels && index < data.labels.length ? data.labels[index] : ''; + var value = data.datasets[datasetIndex].data[index]; + + if (helpers$1.isObject(value)) { + label = me.getRightValue(value); + } + if (timeOpts.tooltipFormat) { + return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); + } + if (typeof label === 'string') { + return label; + } + return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); + }, + + /** + * Function to format an individual tick mark + * @private + */ + tickFormatFunction: function(time, index, ticks, format) { + var me = this; + var adapter = me._adapter; + var options = me.options; + var formats = options.time.displayFormats; + var minorFormat = formats[me._unit]; + var majorUnit = me._majorUnit; + var majorFormat = formats[majorUnit]; + var tick = ticks[index]; + var tickOpts = options.ticks; + var major = majorUnit && majorFormat && tick && tick.major; + var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); + var nestedTickOpts = major ? tickOpts.major : tickOpts.minor; + var formatter = resolve$5([ + nestedTickOpts.callback, + nestedTickOpts.userCallback, + tickOpts.callback, + tickOpts.userCallback + ]); + + return formatter ? formatter(label, index, ticks) : label; + }, + + convertTicksToLabels: function(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); + } + + return labels; + }, + + /** + * @private + */ + getPixelForOffset: function(time) { + var me = this; + var offsets = me._offsets; + var pos = interpolate$1(me._table, 'time', time, 'pos'); + return me.getPixelForDecimal((offsets.start + pos) * offsets.factor); + }, + + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var time = null; + + if (index !== undefined && datasetIndex !== undefined) { + time = me._timestamps.datasets[datasetIndex][index]; + } + + if (time === null) { + time = parse(me, value); + } + + if (time !== null) { + return me.getPixelForOffset(time); + } + }, + + getPixelForTick: function(index) { + var ticks = this.getTicks(); + return index >= 0 && index < ticks.length ? + this.getPixelForOffset(ticks[index].value) : + null; + }, + + getValueForPixel: function(pixel) { + var me = this; + var offsets = me._offsets; + var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + var time = interpolate$1(me._table, 'pos', pos, 'time'); + + // DEPRECATION, we should return time directly + return me._adapter._create(time); + }, + + /** + * @private + */ + _getLabelSize: function(label) { + var me = this; + var ticksOpts = me.options.ticks; + var tickLabelWidth = me.ctx.measureText(label).width; + var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); + var cosRotation = Math.cos(angle); + var sinRotation = Math.sin(angle); + var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize); + + return { + w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation), + h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation) + }; + }, + + /** + * Crude approximation of what the label width might be + * @private + */ + getLabelWidth: function(label) { + return this._getLabelSize(label).w; + }, + + /** + * @private + */ + getLabelCapacity: function(exampleTime) { + var me = this; + var timeOpts = me.options.time; + var displayFormats = timeOpts.displayFormats; + + // pick the longest format (milliseconds) for guestimation + var format = displayFormats[timeOpts.unit] || displayFormats.millisecond; + var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format); + var size = me._getLabelSize(exampleLabel); + var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h); + + if (me.options.offset) { + capacity--; + } + + return capacity > 0 ? capacity : 1; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$4 = defaultConfig$4; +scale_time._defaults = _defaults$4; + +var scales = { + category: scale_category, + linear: scale_linear, + logarithmic: scale_logarithmic, + radialLinear: scale_radialLinear, + time: scale_time +}; + +var FORMATS = { + datetime: 'MMM D, YYYY, h:mm:ss a', + millisecond: 'h:mm:ss.SSS a', + second: 'h:mm:ss a', + minute: 'h:mm a', + hour: 'hA', + day: 'MMM D', + week: 'll', + month: 'MMM YYYY', + quarter: '[Q]Q - YYYY', + year: 'YYYY' +}; + +core_adapters._date.override(typeof moment === 'function' ? { + _id: 'moment', // DEBUG ONLY + + formats: function() { + return FORMATS; + }, + + parse: function(value, format) { + if (typeof value === 'string' && typeof format === 'string') { + value = moment(value, format); + } else if (!(value instanceof moment)) { + value = moment(value); + } + return value.isValid() ? value.valueOf() : null; + }, + + format: function(time, format) { + return moment(time).format(format); + }, + + add: function(time, amount, unit) { + return moment(time).add(amount, unit).valueOf(); + }, + + diff: function(max, min, unit) { + return moment(max).diff(moment(min), unit); + }, + + startOf: function(time, unit, weekday) { + time = moment(time); + if (unit === 'isoWeek') { + return time.isoWeekday(weekday).valueOf(); + } + return time.startOf(unit).valueOf(); + }, + + endOf: function(time, unit) { + return moment(time).endOf(unit).valueOf(); + }, + + // DEPRECATIONS + + /** + * Provided for backward compatibility with scale.getValueForPixel(). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(time) { + return moment(time); + }, +} : {}); + +core_defaults._set('global', { + plugins: { + filler: { + propagate: true + } + } +}); + +var mappers = { + dataset: function(source) { + var index = source.fill; + var chart = source.chart; + var meta = chart.getDatasetMeta(index); + var visible = meta && chart.isDatasetVisible(index); + var points = (visible && meta.dataset._children) || []; + var length = points.length || 0; + + return !length ? null : function(point, i) { + return (i < length && points[i]._view) || null; + }; + }, + + boundary: function(source) { + var boundary = source.boundary; + var x = boundary ? boundary.x : null; + var y = boundary ? boundary.y : null; + + if (helpers$1.isArray(boundary)) { + return function(point, i) { + return boundary[i]; + }; + } + + return function(point) { + return { + x: x === null ? point.x : x, + y: y === null ? point.y : y, + }; + }; + } +}; + +// @todo if (fill[0] === '#') +function decodeFill(el, index, count) { + var model = el._model || {}; + var fill = model.fill; + var target; + + if (fill === undefined) { + fill = !!model.backgroundColor; + } + + if (fill === false || fill === null) { + return false; + } + + if (fill === true) { + return 'origin'; + } + + target = parseFloat(fill, 10); + if (isFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + + if (target === index || target < 0 || target >= count) { + return false; + } + + return target; + } + + switch (fill) { + // compatibility + case 'bottom': + return 'start'; + case 'top': + return 'end'; + case 'zero': + return 'origin'; + // supported boundaries + case 'origin': + case 'start': + case 'end': + return fill; + // invalid fill values + default: + return false; + } +} + +function computeLinearBoundary(source) { + var model = source.el._model || {}; + var scale = source.el._scale || {}; + var fill = source.fill; + var target = null; + var horizontal; + + if (isFinite(fill)) { + return null; + } + + // Backward compatibility: until v3, we still need to support boundary values set on + // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and + // controllers might still use it (e.g. the Smith chart). + + if (fill === 'start') { + target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; + } else if (fill === 'end') { + target = model.scaleTop === undefined ? scale.top : model.scaleTop; + } else if (model.scaleZero !== undefined) { + target = model.scaleZero; + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + + if (target !== undefined && target !== null) { + if (target.x !== undefined && target.y !== undefined) { + return target; + } + + if (helpers$1.isFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + } + + return null; +} + +function computeCircularBoundary(source) { + var scale = source.el._scale; + var options = scale.options; + var length = scale.chart.data.labels.length; + var fill = source.fill; + var target = []; + var start, end, center, i, point; + + if (!length) { + return null; + } + + start = options.ticks.reverse ? scale.max : scale.min; + end = options.ticks.reverse ? scale.min : scale.max; + center = scale.getPointPositionForValue(0, start); + for (i = 0; i < length; ++i) { + point = fill === 'start' || fill === 'end' + ? scale.getPointPositionForValue(i, fill === 'start' ? start : end) + : scale.getBasePosition(i); + if (options.gridLines.circular) { + point.cx = center.x; + point.cy = center.y; + point.angle = scale.getIndexAngle(i) - Math.PI / 2; + } + target.push(point); + } + return target; +} + +function computeBoundary(source) { + var scale = source.el._scale || {}; + + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); +} + +function resolveTarget(sources, index, propagate) { + var source = sources[index]; + var fill = source.fill; + var visited = [index]; + var target; + + if (!propagate) { + return fill; + } + + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isFinite(fill)) { + return fill; + } + + target = sources[fill]; + if (!target) { + return false; + } + + if (target.visible) { + return fill; + } + + visited.push(fill); + fill = target.fill; + } + + return false; +} + +function createMapper(source) { + var fill = source.fill; + var type = 'dataset'; + + if (fill === false) { + return null; + } + + if (!isFinite(fill)) { + type = 'boundary'; + } + + return mappers[type](source); +} + +function isDrawable(point) { + return point && !point.skip; +} + +function drawArea(ctx, curve0, curve1, len0, len1) { + var i, cx, cy, r; + + if (!len0 || !len1) { + return; + } + + // building first area curve (normal) + ctx.moveTo(curve0[0].x, curve0[0].y); + for (i = 1; i < len0; ++i) { + helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); + } + + if (curve1[0].angle !== undefined) { + cx = curve1[0].cx; + cy = curve1[0].cy; + r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2)); + for (i = len1 - 1; i > 0; --i) { + ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true); + } + return; + } + + // joining the two area curves + ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); + + // building opposite area curve (reverse) + for (i = len1 - 1; i > 0; --i) { + helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); + } +} + +function doFill(ctx, points, mapper, view, color, loop) { + var count = points.length; + var span = view.spanGaps; + var curve0 = []; + var curve1 = []; + var len0 = 0; + var len1 = 0; + var i, ilen, index, p0, p1, d0, d1, loopOffset; + + ctx.beginPath(); + + for (i = 0, ilen = count; i < ilen; ++i) { + index = i % count; + p0 = points[index]._view; + p1 = mapper(p0, index, view); + d0 = isDrawable(p0); + d1 = isDrawable(p1); + + if (loop && loopOffset === undefined && d0) { + loopOffset = i + 1; + ilen = count + loopOffset; + } + + if (d0 && d1) { + len0 = curve0.push(p0); + len1 = curve1.push(p1); + } else if (len0 && len1) { + if (!span) { + drawArea(ctx, curve0, curve1, len0, len1); + len0 = len1 = 0; + curve0 = []; + curve1 = []; + } else { + if (d0) { + curve0.push(p0); + } + if (d1) { + curve1.push(p1); + } + } + } + } + + drawArea(ctx, curve0, curve1, len0, len1); + + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); +} + +var plugin_filler = { + id: 'filler', + + afterDatasetsUpdate: function(chart, options) { + var count = (chart.data.datasets || []).length; + var propagate = options.propagate; + var sources = []; + var meta, i, el, source; + + for (i = 0; i < count; ++i) { + meta = chart.getDatasetMeta(i); + el = meta.dataset; + source = null; + + if (el && el._model && el instanceof elements.Line) { + source = { + visible: chart.isDatasetVisible(i), + fill: decodeFill(el, i, count), + chart: chart, + el: el + }; + } + + meta.$filler = source; + sources.push(source); + } + + for (i = 0; i < count; ++i) { + source = sources[i]; + if (!source) { + continue; + } + + source.fill = resolveTarget(sources, i, propagate); + source.boundary = computeBoundary(source); + source.mapper = createMapper(source); + } + }, + + beforeDatasetsDraw: function(chart) { + var metasets = chart._getSortedVisibleDatasetMetas(); + var ctx = chart.ctx; + var meta, i, el, view, points, mapper, color; + + for (i = metasets.length - 1; i >= 0; --i) { + meta = metasets[i].$filler; + + if (!meta || !meta.visible) { + continue; + } + + el = meta.el; + view = el._view; + points = el._children || []; + mapper = meta.mapper; + color = view.backgroundColor || core_defaults.global.defaultColor; + + if (mapper && color && points.length) { + helpers$1.canvas.clipArea(ctx, chart.chartArea); + doFill(ctx, points, mapper, view, color, el._loop); + helpers$1.canvas.unclipArea(ctx); + } + } + } +}; + +var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter; +var noop$1 = helpers$1.noop; +var valueOrDefault$e = helpers$1.valueOrDefault; + +core_defaults._set('global', { + legend: { + display: true, + position: 'top', + align: 'center', + fullWidth: true, + reverse: false, + weight: 1000, + + // a callback that will handle + onClick: function(e, legendItem) { + var index = legendItem.datasetIndex; + var ci = this.chart; + var meta = ci.getDatasetMeta(index); + + // See controller.isDatasetVisible comment + meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; + + // We hid a dataset ... rerender the chart + ci.update(); + }, + + onHover: null, + onLeave: null, + + labels: { + boxWidth: 40, + padding: 10, + // Generates labels shown in the legend + // Valid properties to return: + // text : text to display + // fillStyle : fill of coloured box + // strokeStyle: stroke of coloured box + // hidden : if this legend item refers to a hidden item + // lineCap : cap style for line + // lineDash + // lineDashOffset : + // lineJoin : + // lineWidth : + generateLabels: function(chart) { + var datasets = chart.data.datasets; + var options = chart.options.legend || {}; + var usePointStyle = options.labels && options.labels.usePointStyle; + + return chart._getSortedDatasetMetas().map(function(meta) { + var style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + + return { + text: datasets[meta.index].label, + fillStyle: style.backgroundColor, + hidden: !chart.isDatasetVisible(meta.index), + lineCap: style.borderCapStyle, + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: style.borderWidth, + strokeStyle: style.borderColor, + pointStyle: style.pointStyle, + rotation: style.rotation, + + // Below is extra data used for toggling the datasets + datasetIndex: meta.index + }; + }, this); + } + } + }, + + legendCallback: function(chart) { + var list = document.createElement('ul'); + var datasets = chart.data.datasets; + var i, ilen, listItem, listItemSpan; + + list.setAttribute('class', chart.id + '-legend'); + + for (i = 0, ilen = datasets.length; i < ilen; i++) { + listItem = list.appendChild(document.createElement('li')); + listItemSpan = listItem.appendChild(document.createElement('span')); + listItemSpan.style.backgroundColor = datasets[i].backgroundColor; + if (datasets[i].label) { + listItem.appendChild(document.createTextNode(datasets[i].label)); + } + } + + return list.outerHTML; + } +}); + +/** + * Helper function to get the box width based on the usePointStyle option + * @param {object} labelopts - the label options on the legend + * @param {number} fontSize - the label font size + * @return {number} width of the color box area + */ +function getBoxWidth(labelOpts, fontSize) { + return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? + fontSize : + labelOpts.boxWidth; +} + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Legend = core_element.extend({ + + initialize: function(config) { + var me = this; + helpers$1.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + + /** + * @private + */ + me._hoveredItem = null; + + // Are we in doughnut mode which has a different data type + me.doughnutMode = false; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + // Any function defined here is inherited by all legend types. + // Any function can be extended by the legend type + + beforeUpdate: noop$1, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + }, + afterUpdate: noop$1, + + // + + beforeSetDimensions: noop$1, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$1, + + // + + beforeBuildLabels: noop$1, + buildLabels: function() { + var me = this; + var labelOpts = me.options.labels || {}; + var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; + + if (labelOpts.filter) { + legendItems = legendItems.filter(function(item) { + return labelOpts.filter(item, me.chart.data); + }); + } + + if (me.options.reverse) { + legendItems.reverse(); + } + + me.legendItems = legendItems; + }, + afterBuildLabels: noop$1, + + // + + beforeFit: noop$1, + fit: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var display = opts.display; + + var ctx = me.ctx; + + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + + // Reset hit boxes + var hitboxes = me.legendHitBoxes = []; + + var minSize = me.minSize; + var isHorizontal = me.isHorizontal(); + + if (isHorizontal) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = display ? 10 : 0; + } else { + minSize.width = display ? 10 : 0; + minSize.height = me.maxHeight; // fill all the height + } + + // Increase sizes here + if (!display) { + me.width = minSize.width = me.height = minSize.height = 0; + return; + } + ctx.font = labelFont.string; + + if (isHorizontal) { + // Labels + + // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one + var lineWidths = me.lineWidths = [0]; + var totalHeight = 0; + + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { + totalHeight += fontSize + labelOpts.padding; + lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; + } + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: width, + height: fontSize + }; + + lineWidths[lineWidths.length - 1] += width + labelOpts.padding; + }); + + minSize.height += totalHeight; + + } else { + var vPadding = labelOpts.padding; + var columnWidths = me.columnWidths = []; + var columnHeights = me.columnHeights = []; + var totalWidth = labelOpts.padding; + var currentColWidth = 0; + var currentColHeight = 0; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + // If too tall, go to new column + if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) { + totalWidth += currentColWidth + labelOpts.padding; + columnWidths.push(currentColWidth); // previous column width + columnHeights.push(currentColHeight); + currentColWidth = 0; + currentColHeight = 0; + } + + // Get max width + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += fontSize + vPadding; + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: itemWidth, + height: fontSize + }; + }); + + totalWidth += currentColWidth; + columnWidths.push(currentColWidth); + columnHeights.push(currentColHeight); + minSize.width += totalWidth; + } + + me.width = minSize.width; + me.height = minSize.height; + }, + afterFit: noop$1, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + + // Actually draw the legend on the canvas + draw: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; + var lineDefault = globalDefaults.elements.line; + var legendHeight = me.height; + var columnHeights = me.columnHeights; + var legendWidth = me.width; + var lineWidths = me.lineWidths; + + if (!opts.display) { + return; + } + + var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width); + var ctx = me.ctx; + var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor); + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + var cursor; + + // Canvas setup + ctx.textAlign = rtlHelper.textAlign('left'); + ctx.textBaseline = 'middle'; + ctx.lineWidth = 0.5; + ctx.strokeStyle = fontColor; // for strikethrough effect + ctx.fillStyle = fontColor; // render in correct colour + ctx.font = labelFont.string; + + var boxWidth = getBoxWidth(labelOpts, fontSize); + var hitboxes = me.legendHitBoxes; + + // current position + var drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0) { + return; + } + + // Set the ctx for the box + ctx.save(); + + var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth); + ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor); + ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle); + ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset); + ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor); + + if (ctx.setLineDash) { + // IE 9 and 10 do not support line dash + ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash)); + } + + if (labelOpts && labelOpts.usePointStyle) { + // Recalculate x and y for drawPoint() because its expecting + // x and y to be center of figure (instead of top left) + var radius = boxWidth * Math.SQRT2 / 2; + var centerX = rtlHelper.xPlus(x, boxWidth / 2); + var centerY = y + fontSize / 2; + + // Draw pointStyle as legend symbol + helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); + } else { + // Draw box as legend symbol + ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + if (lineWidth !== 0) { + ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + } + } + + ctx.restore(); + }; + + var fillText = function(x, y, legendItem, textWidth) { + var halfFontSize = fontSize / 2; + var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); + var yMiddle = y + halfFontSize; + + ctx.fillText(legendItem.text, xLeft, yMiddle); + + if (legendItem.hidden) { + // Strikethrough the text if hidden + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.moveTo(xLeft, yMiddle); + ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle); + ctx.stroke(); + } + }; + + var alignmentOffset = function(dimension, blockSize) { + switch (opts.align) { + case 'start': + return labelOpts.padding; + case 'end': + return dimension - blockSize; + default: // center + return (dimension - blockSize + labelOpts.padding) / 2; + } + }; + + // Horizontal + var isHorizontal = me.isHorizontal(); + if (isHorizontal) { + cursor = { + x: me.left + alignmentOffset(legendWidth, lineWidths[0]), + y: me.top + labelOpts.padding, + line: 0 + }; + } else { + cursor = { + x: me.left + labelOpts.padding, + y: me.top + alignmentOffset(legendHeight, columnHeights[0]), + line: 0 + }; + } + + helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection); + + var itemHeight = fontSize + labelOpts.padding; + helpers$1.each(me.legendItems, function(legendItem, i) { + var textWidth = ctx.measureText(legendItem.text).width; + var width = boxWidth + (fontSize / 2) + textWidth; + var x = cursor.x; + var y = cursor.y; + + rtlHelper.setWidth(me.minSize.width); + + // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) + // instead of me.right and me.bottom because me.width and me.height + // may have been changed since me.minSize was calculated + if (isHorizontal) { + if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { + y = cursor.y += itemHeight; + cursor.line++; + x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]); + } + } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { + x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; + cursor.line++; + y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]); + } + + var realX = rtlHelper.x(x); + + drawLegendBox(realX, y, legendItem); + + hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width); + hitboxes[i].top = y; + + // Fill the actual label + fillText(realX, y, legendItem, textWidth); + + if (isHorizontal) { + cursor.x += width + labelOpts.padding; + } else { + cursor.y += itemHeight; + } + }); + + helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection); + }, + + /** + * @private + */ + _getLegendItemAt: function(x, y) { + var me = this; + var i, hitBox, lh; + + if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { + // See if we are touching one of the dataset boxes + lh = me.legendHitBoxes; + for (i = 0; i < lh.length; ++i) { + hitBox = lh[i]; + + if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { + // Touching an element + return me.legendItems[i]; + } + } + } + + return null; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + */ + handleEvent: function(e) { + var me = this; + var opts = me.options; + var type = e.type === 'mouseup' ? 'click' : e.type; + var hoveredItem; + + if (type === 'mousemove') { + if (!opts.onHover && !opts.onLeave) { + return; + } + } else if (type === 'click') { + if (!opts.onClick) { + return; + } + } else { + return; + } + + // Chart event already has relative position in it + hoveredItem = me._getLegendItemAt(e.x, e.y); + + if (type === 'click') { + if (hoveredItem && opts.onClick) { + // use e.native for backwards compatibility + opts.onClick.call(me, e.native, hoveredItem); + } + } else { + if (opts.onLeave && hoveredItem !== me._hoveredItem) { + if (me._hoveredItem) { + opts.onLeave.call(me, e.native, me._hoveredItem); + } + me._hoveredItem = hoveredItem; + } + + if (opts.onHover && hoveredItem) { + // use e.native for backwards compatibility + opts.onHover.call(me, e.native, hoveredItem); + } + } + } +}); + +function createNewLegendAndAttach(chart, legendOpts) { + var legend = new Legend({ + ctx: chart.ctx, + options: legendOpts, + chart: chart + }); + + core_layouts.configure(chart, legend, legendOpts); + core_layouts.addBox(chart, legend); + chart.legend = legend; +} + +var plugin_legend = { + id: 'legend', + + /** + * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making + * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Legend, + + beforeInit: function(chart) { + var legendOpts = chart.options.legend; + + if (legendOpts) { + createNewLegendAndAttach(chart, legendOpts); + } + }, + + beforeUpdate: function(chart) { + var legendOpts = chart.options.legend; + var legend = chart.legend; + + if (legendOpts) { + helpers$1.mergeIf(legendOpts, core_defaults.global.legend); + + if (legend) { + core_layouts.configure(chart, legend, legendOpts); + legend.options = legendOpts; + } else { + createNewLegendAndAttach(chart, legendOpts); + } + } else if (legend) { + core_layouts.removeBox(chart, legend); + delete chart.legend; + } + }, + + afterEvent: function(chart, e) { + var legend = chart.legend; + if (legend) { + legend.handleEvent(e); + } + } +}; + +var noop$2 = helpers$1.noop; + +core_defaults._set('global', { + title: { + display: false, + fontStyle: 'bold', + fullWidth: true, + padding: 10, + position: 'top', + text: '', + weight: 2000 // by default greater than legend (1000) to be above + } +}); + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Title = core_element.extend({ + initialize: function(config) { + var me = this; + helpers$1.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + + beforeUpdate: noop$2, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: noop$2, + + // + + beforeSetDimensions: noop$2, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$2, + + // + + beforeBuildLabels: noop$2, + buildLabels: noop$2, + afterBuildLabels: noop$2, + + // + + beforeFit: noop$2, + fit: function() { + var me = this; + var opts = me.options; + var minSize = me.minSize = {}; + var isHorizontal = me.isHorizontal(); + var lineCount, textSize; + + if (!opts.display) { + me.width = minSize.width = me.height = minSize.height = 0; + return; + } + + lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; + textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2; + + me.width = minSize.width = isHorizontal ? me.maxWidth : textSize; + me.height = minSize.height = isHorizontal ? textSize : me.maxHeight; + }, + afterFit: noop$2, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + + // Actually draw the title block on the canvas + draw: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + + if (!opts.display) { + return; + } + + var fontOpts = helpers$1.options._parseFont(opts); + var lineHeight = fontOpts.lineHeight; + var offset = lineHeight / 2 + opts.padding; + var rotation = 0; + var top = me.top; + var left = me.left; + var bottom = me.bottom; + var right = me.right; + var maxWidth, titleX, titleY; + + ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour + ctx.font = fontOpts.string; + + // Horizontal + if (me.isHorizontal()) { + titleX = left + ((right - left) / 2); // midpoint of the width + titleY = top + offset; + maxWidth = right - left; + } else { + titleX = opts.position === 'left' ? left + offset : right - offset; + titleY = top + ((bottom - top) / 2); + maxWidth = bottom - top; + rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); + } + + ctx.save(); + ctx.translate(titleX, titleY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + var text = opts.text; + if (helpers$1.isArray(text)) { + var y = 0; + for (var i = 0; i < text.length; ++i) { + ctx.fillText(text[i], 0, y, maxWidth); + y += lineHeight; + } + } else { + ctx.fillText(text, 0, 0, maxWidth); + } + + ctx.restore(); + } +}); + +function createNewTitleBlockAndAttach(chart, titleOpts) { + var title = new Title({ + ctx: chart.ctx, + options: titleOpts, + chart: chart + }); + + core_layouts.configure(chart, title, titleOpts); + core_layouts.addBox(chart, title); + chart.titleBlock = title; +} + +var plugin_title = { + id: 'title', + + /** + * Backward compatibility: since 2.1.5, the title is registered as a plugin, making + * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Title, + + beforeInit: function(chart) { + var titleOpts = chart.options.title; + + if (titleOpts) { + createNewTitleBlockAndAttach(chart, titleOpts); + } + }, + + beforeUpdate: function(chart) { + var titleOpts = chart.options.title; + var titleBlock = chart.titleBlock; + + if (titleOpts) { + helpers$1.mergeIf(titleOpts, core_defaults.global.title); + + if (titleBlock) { + core_layouts.configure(chart, titleBlock, titleOpts); + titleBlock.options = titleOpts; + } else { + createNewTitleBlockAndAttach(chart, titleOpts); + } + } else if (titleBlock) { + core_layouts.removeBox(chart, titleBlock); + delete chart.titleBlock; + } + } +}; + +var plugins = {}; +var filler = plugin_filler; +var legend = plugin_legend; +var title = plugin_title; +plugins.filler = filler; +plugins.legend = legend; +plugins.title = title; + +/** + * @namespace Chart + */ + + +core_controller.helpers = helpers$1; + +// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! +core_helpers(); + +core_controller._adapters = core_adapters; +core_controller.Animation = core_animation; +core_controller.animationService = core_animations; +core_controller.controllers = controllers; +core_controller.DatasetController = core_datasetController; +core_controller.defaults = core_defaults; +core_controller.Element = core_element; +core_controller.elements = elements; +core_controller.Interaction = core_interaction; +core_controller.layouts = core_layouts; +core_controller.platform = platform; +core_controller.plugins = core_plugins; +core_controller.Scale = core_scale; +core_controller.scaleService = core_scaleService; +core_controller.Ticks = core_ticks; +core_controller.Tooltip = core_tooltip; + +// Register built-in scales + +core_controller.helpers.each(scales, function(scale, type) { + core_controller.scaleService.registerScaleType(type, scale, scale._defaults); +}); + +// Load to register built-in adapters (as side effects) + + +// Loading built-in plugins + +for (var k in plugins) { + if (plugins.hasOwnProperty(k)) { + core_controller.plugins.register(plugins[k]); + } +} + +core_controller.platform.initialize(); + +var src = core_controller; +if (typeof window !== 'undefined') { + window.Chart = core_controller; +} + +// DEPRECATIONS + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Chart + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +core_controller.Chart = core_controller; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Legend + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Legend = plugins.legend._element; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Title + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Title = plugins.title._element; + +/** + * Provided for backward compatibility, use Chart.plugins instead + * @namespace Chart.pluginService + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.pluginService = core_controller.plugins; + +/** + * Provided for backward compatibility, inheriting from Chart.PlugingBase has no + * effect, instead simply create/register plugins via plain JavaScript objects. + * @interface Chart.PluginBase + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ +core_controller.PluginBase = core_controller.Element.extend({}); + +/** + * Provided for backward compatibility, use Chart.helpers.canvas instead. + * @namespace Chart.canvasHelpers + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +core_controller.canvasHelpers = core_controller.helpers.canvas; + +/** + * Provided for backward compatibility, use Chart.layouts instead. + * @namespace Chart.layoutService + * @deprecated since version 2.7.3 + * @todo remove at version 3 + * @private + */ +core_controller.layoutService = core_controller.layouts; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.LinearScaleBase + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +core_controller.LinearScaleBase = scale_linearbase; + +/** + * Provided for backward compatibility, instead we should create a new Chart + * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ +core_controller.helpers.each( + [ + 'Bar', + 'Bubble', + 'Doughnut', + 'Line', + 'PolarArea', + 'Radar', + 'Scatter' + ], + function(klass) { + core_controller[klass] = function(ctx, cfg) { + return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { + type: klass.charAt(0).toLowerCase() + klass.slice(1) + })); + }; + } +); + +return src; + +}))); diff --git a/lib/web/chartjs/Chart.min.css b/lib/web/chartjs/Chart.min.css new file mode 100644 index 0000000000000..9dc5ac2e5faca --- /dev/null +++ b/lib/web/chartjs/Chart.min.css @@ -0,0 +1 @@ +@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} \ No newline at end of file diff --git a/lib/web/chartjs/Chart.min.js b/lib/web/chartjs/Chart.min.js new file mode 100644 index 0000000000000..7c16b0d1287d0 --- /dev/null +++ b/lib/web/chartjs/Chart.min.js @@ -0,0 +1,7 @@ +/*! + * Chart.js v2.9.3 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],(function(t){return e(function(){try{return t("moment")}catch(t){}}())})):(t=t||self).Chart=e(t.moment)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},n=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var n={};for(var i in e)e.hasOwnProperty(i)&&(n[e[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,s=1/0;for(var l in e)if(e.hasOwnProperty(l)){var u=e[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return e[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a<i;a++)t[e[a]]={distance:-1,parent:null};return t}(),i=[t];for(e[t].distance=0;i.length;)for(var a=i.pop(),r=Object.keys(n[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,i.unshift(l))}return e}function a(t,e){return function(n){return e(t(n))}}function r(t,e){for(var i=[e[t].parent,t],r=n[e[t].parent][t],o=e[t].parent;e[o].parent;)i.unshift(e[o].parent),r=a(n[e[o].parent][o],r),o=e[o].parent;return r.conversion=i,r}var o={};Object.keys(n).forEach((function(t){o[t]={},Object.defineProperty(o[t],"channels",{value:n[t].channels}),Object.defineProperty(o[t],"labels",{value:n[t].labels});var e=function(t){for(var e=i(t),n={},a=Object.keys(e),o=a.length,s=0;s<o;s++){var l=a[s];null!==e[l].parent&&(n[l]=r(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];o[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),o[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var s=o,l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:h,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=h(t);return e&&e.slice(0,3)},getHwb:c,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=h(t))return e[3];if(e=c(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=l[i[1]]))return}for(r=0;r<e.length;r++)e[r]=m(e[r],0,255);return n=n||0==n?m(n,0,1):1,e[3]=n,e}}function h(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function c(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function f(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function g(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function m(t,e,n){return Math.min(Math.max(e,t),n)}function v(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var b={};for(var x in l)b[l[x]]=x;var y=function(t){return t instanceof y?t:this instanceof y?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=u.getRgba(t))?this.setValues("rgb",e):(e=u.getHsla(t))?this.setValues("hsl",e):(e=u.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new y(t);var e};y.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return u.hexString(this.values.rgb)},rgbString:function(){return u.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return u.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return u.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return u.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return u.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return u.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return u.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},y.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,l=1;if(this.valid=!0,"alpha"===t)l=e;else if(e.length)a[t]=e.slice(0,t.length),l=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];l=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];l=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===l?a.alpha:l)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=s[t][d](a[t]));return!0},y.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},y.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=y);var _,k=y,w={noop:function(){},uid:(_=0,function(){return _++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return w.valueOrDefault(w.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(w.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(w.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!w.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(w.isArray(t))return t.map(w.clone);if(w.isObject(t)){for(var e={},n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=w.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){var a=e[t],r=n[t];w.isObject(a)&&w.isObject(r)?w.merge(a,r,i):e[t]=w.clone(r)},_mergerIf:function(t,e,n){var i=e[t],a=n[t];w.isObject(i)&&w.isObject(a)?w.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=w.clone(a))},merge:function(t,e,n){var i,a,r,o,s,l=w.isArray(e)?e:[e],u=l.length;if(!w.isObject(t))return t;for(i=(n=n||{}).merger||w._merger,a=0;a<u;++a)if(e=l[a],w.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return w.merge(t,e,{merger:w._mergerIf})},extend:Object.assign||function(t){return w.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=w.inherits,t&&w.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},M=w;w.callCallback=w.callback,w.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},w.getValueOrDefault=w.valueOrDefault,w.getValueAtIndexOrDefault=w.valueAtIndexOrDefault;var S={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-S.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*S.easeInBounce(2*t):.5*S.easeOutBounce(2*t-1)+.5}},C={effects:S};M.easingEffects=S;var P=Math.PI,A=P/180,D=2*P,T=P/2,I=P/4,F=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-P,-T),t.arc(u,l,o,-T,0),t.arc(u,d,o,0,T),t.arc(s,d,o,T,P)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-T,T),t.arc(s,l,o,T,P+T)):l<d?(t.arc(s,l,o,-P,0),t.arc(s,d,o,0,P)):t.arc(s,l,o,-P,P),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*A;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,D),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=F,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=F,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+I)*u,l=Math.sin(h+I)*u,t.arc(i-s,a-l,d,h-P,h-T),t.arc(i+l,a-s,d,h-T,h),t.arc(i+s,a+l,d,h,h+T),t.arc(i-l,a+s,d,h+T,h+P),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=I;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=I;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=I,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},O=L;M.clear=L.clear,M.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var R={_set:function(t,e){return M.merge(this[t]||(this[t]={}),e)}};R._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var z=R,N=M.valueOrDefault;var B={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return M.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=z.global,n=N(t.fontSize,e.defaultFontSize),i={family:N(t.fontFamily,e.defaultFontFamily),lineHeight:M.options.toLineHeight(N(t.lineHeight,e.defaultLineHeight),n),size:n,style:N(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||M.isNullOrUndef(t.size)||M.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&M.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},E={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},W=E;M.log10=E.log10;var V=M,H=C,j=O,q=B,U=W,Y={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};V.easing=H,V.canvas=j,V.options=q,V.math=U,V.rtl=Y;var G=function(t){V.extend(this,t),this.initialize.apply(this,arguments)};V.extend(G.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=V.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=k(l)).valid&&(c=k(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(V.isFinite(l)&&V.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=V.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return V.isNumber(this._model.x)&&V.isNumber(this._model.y)}}),G.extend=V.inherits;var X=G,K=X.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),Z=K;Object.defineProperty(K.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(K.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),z._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:V.noop,onComplete:V.noop}});var $={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=V.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=V.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),V.callback(t.render,[e,t],e),V.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(V.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},J=V.options.resolve,Q=["push","pop","shift","splice","unshift"];function tt(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(Q.forEach((function(e){delete t[e]})),delete t._chartjs)}}var et=function(t,e){this.initialize(t,e)};V.extend(et.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&tt(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&tt(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),Q.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return V.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=V.merge({},[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&V._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:V.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=J([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},p={cacheable:!i};if(i=i||{},V.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=J([i[l],d[l],h[l]],g,e,p);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=J([i[l],d[c[l]],d[l],h[l]],g,e,p);return p.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){V.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=V.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=J([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=J([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=J([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),et.extend=V.inherits;var nt=et,it=2*Math.PI;function at(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function rt(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+it,at(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=it,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+it,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+it),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&at(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}z._set("global",{elements:{arc:{backgroundColor:z.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var ot=X.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=V.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=it;for(;a>s;)a-=it;for(;a<o;)a+=it;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/it)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+it,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%it}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&rt(e,n,a),e.restore()}}),st=V.valueOrDefault,lt=z.global.defaultColor;z._set("global",{elements:{line:{tension:.4,backgroundColor:lt,borderWidth:3,borderColor:lt,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var ut=X.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=z.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=V.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=st(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=st(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?V.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):V.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),dt=V.valueOrDefault,ht=z.global.defaultColor;function ct(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}z._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ht,borderColor:ht,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var ft=X.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ct,inXRange:ct,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=z.global,u=l.defaultColor;e.skip||(void 0===t||V.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=dt(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,V.canvas.drawPoint(n,i,r,o,s,a))}}),gt=z.global.defaultColor;function pt(t){return t&&void 0!==t.width}function mt(t){var e,n,i,a,r;return pt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function vt(t,e,n){return t===e?n:t===n?e:t}function bt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=vt(e,"left","right")):t.base<t.y&&(e=vt(e,"bottom","top")),n[e]=!0,n):n}(t);return V.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&mt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}z._set("global",{elements:{rectangle:{backgroundColor:gt,borderColor:gt,borderSkipped:"bottom",borderWidth:0}}});var yt=X.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=mt(t),n=e.right-e.left,i=e.bottom-e.top,a=bt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),_t={},kt=ot,wt=ut,Mt=ft,St=yt;_t.Arc=kt,_t.Line=wt,_t.Point=Mt,_t.Rectangle=St;var Ct=V._deprecated,Pt=V.valueOrDefault;function At(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=V.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return V.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}z._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),z._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Dt=nt.extend({dataElementType:_t.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;nt.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},V.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),p=h._parseValue(f[t].data[e]),m=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,x=void 0===p.start?0:p.max>=0&&p.min>=0?p.min:p.max,y=void 0===p.start?p.end:p.max>=0&&p.min>=0?p.max-p.min:p.min-p.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(p.min<0&&r<0||p.max>=0&&r>0)&&(x+=r));return o=h.getPixelForValue(x),l=(s=h.getPixelForValue(x+y))-o,void 0!==m&&Math.abs(l)<m&&(l=m,s=y>=0&&!c||y<0&&c?o-m:o+m),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):At(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(Pt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(V.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}V.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=V.extend({},nt.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Pt(n.barPercentage,e.barPercentage),e.barThickness=Pt(n.barThickness,e.barThickness),e.categoryPercentage=Pt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Pt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Pt(i.minBarLength,e.minBarLength),e}}),Tt=V.valueOrDefault,It=V.options.resolve;z._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var Ft=nt.extend({dataElementType:_t.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;V.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Tt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Tt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Tt(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=nt.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=V.extend({},s)),s.radius=It([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=V.valueOrDefault,Ot=Math.PI,Rt=2*Ot,zt=Ot/2;z._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-zt,circumference:Rt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return V.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Nt=nt.extend({dataElementType:_t.Arc,linkScales:V.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,p=l.cutoutPercentage/100||0,m=l.circumference,v=r._getRingWeight(r.index);if(m<Rt){var b=l.rotation%Rt,x=(b+=b>=Ot?-Rt:b<-Ot?Rt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=Rt,S=b<=zt&&x>=zt||x>=Rt+zt,C=b<=-zt&&x>=-zt||x>=Ot+zt,P=b===-Ot||x>=Ot?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,h=-(D+P)/2,c=-(T+A)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*p,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/Rt),g=n&&s.animateScale?0:i.innerRadius,p=n&&s.animateScale?0:i.outerRadius,m=t._options||{};V.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:m.backgroundColor,borderColor:m.borderColor,borderWidth:m.borderWidth,borderAlign:m.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:p,innerRadius:g,label:V.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return V.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Rt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});z._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),z._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var Bt=Dt.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Et=V.valueOrDefault,Wt=V.options.resolve,Vt=V.canvas._isPointInArea;function Ht(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function jt(t,e,n){var i=n/2,a=Ht(t,i),r=Ht(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function qt(t){var e,n,i,a;return V.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}z._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Ut=nt.extend({datasetElementType:_t.Line,dataElementType:_t.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Et(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Et(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=nt.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Et(n.spanGaps,a.spanGaps),o.tension=Et(n.lineTension,r.tension),o.steppedLine=Wt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=qt(Et(n.clip,jt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)V.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=V.splineCurve(V.previousItem(l,t)._model,n,V.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Vt(n,s)&&(t>0&&Vt(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Vt(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,V.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),V.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Et(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Et(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Et(n.hoverBorderWidth,n.borderWidth),e.radius=Et(n.hoverRadius,n.radius)}}),Yt=V.options.resolve;z._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Gt=nt.extend({dataElementType:_t.Arc,linkScales:V.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],p=g+(t.hidden?0:i._angles[e]),m=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};V.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?m:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:p,label:V.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return V.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor,a=V.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return Yt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});z._set("pie",V.clone(z.doughnut)),z._set("pie",{cutoutPercentage:0});var Xt=Nt,Kt=V.valueOrDefault;z._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var Zt=nt.extend({datasetElementType:_t.Line,dataElementType:_t.Point,linkScales:V.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Kt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=nt.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Kt(e.spanGaps,n.spanGaps),i.tension=Kt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=V.splineCurve(V.previousItem(o,t,!0)._model,n,V.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Kt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Kt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Kt(n.hoverBorderWidth,n.borderWidth),e.radius=Kt(n.hoverRadius,n.radius)}});z._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),z._set("global",{datasets:{scatter:{showLine:!1}}});var $t={bar:Dt,bubble:Ft,doughnut:Nt,horizontalBar:Bt,line:Ut,polarArea:Gt,pie:Xt,radar:Zt,scatter:Ut};function Jt(t,e){return t.native?{x:t.x,y:t.y}:V.getRelativePosition(t,e)}function Qt(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function te(t,e){var n=[];return Qt(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ee(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return Qt(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ne(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ie(t,e,n){var i=Jt(e,t);n.axis=n.axis||"x";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var ae={modes:{single:function(t,e){var n=Jt(e,t),i=[];return Qt(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ie,index:ie,dataset:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||"xy";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ie(t,e,{intersect:!1})},point:function(t,e){return te(t,Jt(e,t))},nearest:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||"xy";var a=ne(n.axis);return ee(t,i,n.intersect,a)},x:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},re=V.extend;function oe(t,e){return V.where(t,(function(t){return t.pos===e}))}function se(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function le(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function ue(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-le(o,t,"left","right"),a=e.outerHeight-le(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function de(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function he(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,de(r.horizontal,e)),ue(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&he(u,e,n)||l}function ce(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}z._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var fe,ge={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=V.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=se(oe(e,"left"),!0),i=se(oe(e,"right")),a=se(oe(e,"top"),!0),r=se(oe(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:oe(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=re({maxPadding:re({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),he(l,h,d),he(u,h,d)&&he(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),ce(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,ce(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},V.each(s.chartArea,(function(e){var n=e.box;re(n,t.chartArea),n.update(h.w,h.h)}))}}},pe=(fe=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&fe.default||fe,me="$chartjs",ve="chartjs-size-monitor",be="chartjs-render-monitor",xe="chartjs-render-animation",ye=["animationstart","webkitAnimationStart"],_e={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=V.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var we=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Me(t,e,n){t.addEventListener(e,n,we)}function Se(t,e,n){t.removeEventListener(e,n,we)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Ae(t,e,n){var i,a,r,o,s=t[me]||(t[me]={}),l=s.resizer=function(t){var e=Pe(ve),n=Pe(ve+"-expand"),i=Pe(ve+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Me(n,"scroll",a.bind(n,"expand")),Me(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Ce("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,V.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[me]||(t[me]={}),i=n.renderProxy=function(t){t.animationName===xe&&e()};V.each(ye,(function(e){Me(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(be)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function De(t){var e=t[me]||{},n=e.resizer;delete e.resizer,function(t){var e=t[me]||{},n=e.renderProxy;n&&(V.each(ye,(function(e){Se(t,e,n)})),delete e.renderProxy),t.classList.remove(be)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Te={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[me]||(t[me]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[me]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=ke(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=ke(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[me]){var n=e[me].initial;["height","width"].forEach((function(t){var i=n[t];V.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),V.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[me]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[me]||(n[me]={});Me(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=_e[t.type]||t.type,i=V.getRelativePosition(t,e);return Ce(n,e,i.x,i.y,t)}(e,t))})}else Ae(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[me]||{}).proxies||{})[t.id+"_"+e];a&&Se(i,e,a)}else De(i)}};V.addEvent=Me,V.removeEvent=Se;var Ie=Te._enabled?Te:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Fe=V.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Ie);z._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=V.clone(z.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Oe={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=V.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?V.merge({},[z.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=V.extend(this.defaults[t],e))},addScalesToLayout:function(t){V.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,ge.addBox(t,e)}))}},Re=V.valueOrDefault,ze=V.rtl.getRtlAdapter;z._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:V.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:V.noop,beforeBody:V.noop,beforeLabel:V.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),V.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:V.noop,afterBody:V.noop,beforeFooter:V.noop,footer:V.noop,afterFooter:V.noop}}});var Ne={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=V.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function Be(t,e){return e&&(V.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ee(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function We(t){var e=z.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Re(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Re(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Re(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Re(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Re(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Re(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Re(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Re(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Re(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function Ve(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function He(t){return Be([],Ee(t))}var je=X.extend({initialize:function(){this._model=We(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=Be(o,Ee(i)),o=Be(o,Ee(a)),o=Be(o,Ee(r))},getBeforeBody:function(){return He(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return V.each(t,(function(t){var r={before:[],lines:[],after:[]};Be(r.before,Ee(i.beforeLabel.call(n,t,e))),Be(r.lines,i.label.call(n,t,e)),Be(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return He(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=Be(r,Ee(n)),r=Be(r,Ee(i)),r=Be(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=We(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Ne[c.position].call(h,p,h._eventPosition);var w=[];for(e=0,n=p.length;e<n;++e)w.push((i=p[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(w=w.filter((function(t){return c.filter(t,m)}))),c.itemSort&&(w=w.sort((function(t,e){return c.itemSort(t,e,m)}))),V.each(w,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),k.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(w,m),g.beforeBody=h.getBeforeBody(w,m),g.body=h.getBody(w,m),g.afterBody=h.getAfterBody(w,m),g.footer=h.getFooter(w,m),g.x=y.x,g.y=y.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=k,g.dataPoints=w,x=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=V.fontString(u,e._titleFontStyle,e._titleFontFamily),V.each(e.title,f),n.font=V.fontString(d,e._bodyFontStyle,e._bodyFontFamily),V.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,V.each(r,(function(t){V.each(t.before,f),V.each(t.lines,f),V.each(t.after,f)})),c=0,n.font=V.fontString(h,e._footerFontStyle,e._footerFontFamily),V.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,x,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+p)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+m)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=ze(e.rtl,e.x,e.width);for(t.x=Ve(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=V.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,p=e.displayColors,m=0,v=p?Ve(e,"left"):0,b=ze(e.rtl,e.x,e.width),x=function(e){n.fillText(e,b.x(t.x+m),t.y+h/2),t.y+=h+c},y=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=V.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=Ve(e,y),n.fillStyle=e.bodyFontColor,V.each(e.beforeBody,x),m=p&&"right"!==y?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,V.each(i.before,x),l=0,d=(o=i.lines).length;l<d;++l){if(p){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}x(o[l])}V.each(i.after,x)}m=0,V.each(e.afterBody,x),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=ze(e.rtl,e.x,e.width);for(t.x=Ve(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=V.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,V.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),V.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!V.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),qe=Ne,Ue=je;Ue.positioners=qe;var Ye=V.valueOrDefault;function Ge(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=Ye(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?V.merge(e[t][a],[Oe.getScaleDefaults(r),o]):V.merge(e[t][a],o)}else V._merger(t,e,n,i)}})}function Xe(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ge(a,r):"scale"===t?e[t]=V.merge(a,[Oe.getScaleDefaults(r.type),r]):V._merger(t,e,n,i)}})}function Ke(t){var e=t.options;V.each(t.scales,(function(e){ge.removeBox(t,e)})),e=Xe(z.global,z[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ze(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(V.findIndex(t,a)>=0);return i}function $e(t){return"top"===t||"bottom"===t}function Je(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}z._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var Qe=function(t,e){return this.construct(t,e),this};V.extend(Qe.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Xe(z.global,z[t.type],t.options||{}),t}(e);var i=Fe.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=V.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,Qe.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),V.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return V.canvas.clear(this),this},stop:function(){return $.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(V.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:V.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",V.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;V.each(e.xAxes,(function(t,n){t.id||(t.id=Ze(e.xAxes,"x-axis-",n))})),V.each(e.yAxes,(function(t,n){t.id||(t.id=Ze(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),V.each(i,(function(e){var i=e.options,r=i.id,o=Ye(i.type,e.dtype);$e(i.position)!==$e(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Oe.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),V.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Oe.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=$t[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;V.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Ke(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&V.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Je("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(ge.update(this,this.width,this.height),t._layers=[],V.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=Ye(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),V.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new Z({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=V.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});$.addAnimation(e,o,i,a)}else e.draw(),r(new Z({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),V.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Je("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return ae.modes.single(this,t)},getElementsAtEvent:function(t){return ae.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ae.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=ae.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return ae.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),V.canvas.clear(n),Fe.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete Qe.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ue({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};V.each(t.options.events,(function(i){Fe.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Fe.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,V.each(e,(function(e,n){Fe.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),V.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!V.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),Qe.instances={};var tn=Qe;Qe.Controller=Qe,Qe.types={},V.configMerge=Xe,V.scaleMerge=Ge;function en(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function nn(t){this.options=t||{}}V.extend(nn.prototype,{formats:en,parse:en,format:en,add:en,diff:en,startOf:en,endOf:en,_create:function(t){return t}}),nn.override=function(t){V.extend(nn.prototype,t)};var an={_date:nn},rn={formatters:{values:function(t){return V.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=V.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=V.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(V.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},on=V.isArray,sn=V.isNullOrUndef,ln=V.valueOrDefault,un=V.valueAtIndexOrDefault;function dn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function hn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,p,m,v=n.length,b=[],x=[],y=[];for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,sn(s)||on(s)){if(on(s))for(r=0,o=s.length;r<o;++r)g=s[r],sn(g)||on(g)||(c=V.measureText(t,d.data,d.gc,c,g),f+=h)}else c=V.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),x.push(f),y.push(h/2)}function _(t){return{width:b[t]||0,height:x[t]||0,offset:y[t]||0}}return function(t,e){V.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),p=b.indexOf(Math.max.apply(null,b)),m=x.indexOf(Math.max.apply(null,x)),{first:_(0),last:_(v-1),widest:_(p),highest:_(m)}}function cn(t){return t.drawTicks?t.tickMarkLength:0}function fn(t){var e,n;return t.display?(e=V.options._parseFont(t),n=V.options.toPadding(t.padding),e.lineHeight+n.height):0}function gn(t,e){return V.extend(V.options._parseFont({fontFamily:ln(e.fontFamily,t.fontFamily),fontSize:ln(e.fontSize,t.fontSize),fontStyle:ln(e.fontStyle,t.fontStyle),lineHeight:ln(e.lineHeight,t.lineHeight)}),{color:V.options.resolve([e.fontColor,t.fontColor,z.global.defaultFontColor])})}function pn(t){var e=gn(t,t.minor);return{minor:e,major:t.major.enabled?gn(t,t.major):e}}function mn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function vn(t,e,n,i){var a,r,o,s,l=ln(n,0),u=Math.min(ln(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}z._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:rn.formatters.values,minor:{},major:{}}});var bn=X.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){V.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=V.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){V.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){V.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){V.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){V.callback(this.options.beforeDataLimits,[this])},determineDataLimits:V.noop,afterDataLimits:function(){V.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){V.callback(this.options.beforeBuildTicks,[this])},buildTicks:V.noop,afterBuildTicks:function(t){var e=this;return on(t)&&t.length?V.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=V.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){V.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){V.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){V.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-cn(l.gridLines)-u.padding-fn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=V.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){V.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){V.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=cn(o)+fn(r)),u?s&&(e.height=cn(o)+fn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,p=h.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=V.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=l?y*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):y*f.width+_*f.offset):(w=c.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){V.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(sn(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=hn(t.ctx,pn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return on(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:V.noop,getPixelForValue:V.noop,getValueForPixel:V.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),mn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=V.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)vn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,vn(t,i,V.isNullOrUndef(a)?0:d-a,d),vn(t,i,h,V.isNullOrUndef(a)?t.length:h+a),mn(t)}return vn(t,i),mn(t)},_tickSize:function(){var t=this.options.ticks,e=V.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,p,m,v,b=this,x=b.chart,y=b.options,_=y.gridLines,k=y.position,w=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,C=S.length+(w?1:0),P=cn(_),A=[],D=_.drawBorder?un(_.lineWidth,0,0):0,T=D/2,I=V._alignPixel,F=function(t){return I(x,t,D)};for("top"===k?(e=F(b.bottom),s=b.bottom-P,u=e-T,h=F(t.top)+T,f=t.bottom):"bottom"===k?(e=F(b.top),h=t.top,f=F(t.bottom)-T,s=e+T,u=b.top+P):"left"===k?(e=F(b.right),o=b.right-P,l=e-T,d=F(t.left)+T,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-T,o=e+T,l=b.left+P),n=0;n<C;++n)i=S[n]||{},sn(i.label)&&n<S.length||(n===b.zeroLineIndex&&y.offset===w?(g=_.zeroLineWidth,p=_.zeroLineColor,m=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=un(_.lineWidth,n,1),p=un(_.color,n,"rgba(0,0,0,0.1)"),m=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=dn(b,i._index||n,w))&&(r=I(x,a,g),M?o=l=d=c=r:s=u=h=f=r,A.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:p,borderDash:m,borderDashOffset:v})));return A.ticksLength=C,A.borderValue=e,A},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,p=f.position,m=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,x=pn(g),y=g.padding,_=cn(f.gridLines),k=-V.toRadians(c.labelRotation),w=[];for("top"===p?(r=c.bottom-_-y,o=k?"left":"center"):"bottom"===p?(r=c.top+_+y,o=k?"right":"center"):"left"===p?(a=c.right-(m?0:_)-y,o=m?"left":"right"):(a=c.left+(m?0:_)+y,o=m?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,sn(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?x.major:x.minor).lineHeight,d=on(i)?i.length:1,v?(a=s,h="top"===p?((k?1:.5)-d)*u:(k?0:.5)*u):(r=s,h=(1-d)*u/2),w.push({x:a,y:r,rotation:k,label:i,font:l,textOffset:h,textAlign:o}));return w},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=V._alignPixel,h=n.drawBorder?un(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,p,m,v=h,b=un(n.lineWidth,c.ticksLength-1,1),x=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,p=m=x):(p=d(u,e.top,v)-v/2,m=d(u,e.bottom,b)+b/2,f=g=x),l.lineWidth=h,l.strokeStyle=un(n.color,0),l.beginPath(),l.moveTo(f,p),l.lineTo(g,m),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,on(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=ln(i.fontColor,z.global.defaultFontColor),s=V.options._parseFont(i),l=V.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});bn.prototype._draw=bn.prototype.draw;var xn=bn,yn=V.isNullOrUndef,_n=xn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return yn(e)||yn(n)||(t=o.chart.data.datasets[n].data[e]),yn(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=V.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};_n._defaults=kn;var wn=V.noop,Mn=V.isNullOrUndef;var Sn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=V.sign(t.min),i=V.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:wn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:V.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=V.niceNum((g-f)/u/l)*l;if(p<1e-14&&Mn(d)&&Mn(h))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=V.niceNum(r*p/u/l)*l),s||Mn(c)?n=Math.pow(10,V._decimalPlaces(p)):(n=Math.pow(10,c),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!Mn(d)&&V.almostWhole(d/p,p/1e3)&&(i=d),!Mn(h)&&V.almostWhole(h/p,p/1e3)&&(a=h)),r=(a-i)/p,r=V.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Mn(d)?i:d);for(var m=1;m<r;++m)o.push(Math.round((i+m*p)*n)/n);return o.push(Mn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=V.max(a),t.min=V.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),xn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;xn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Cn={position:"left",ticks:{callback:rn.formatters.linear}};function Pn(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function An(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var Dn=Sn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?Pn(a,u,e,n):An(a,e,n);V.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,V.min(i)),a.max=Math.max(a.max,V.max(i))})),a.min=V.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=V.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=V.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),Tn=Cn;Dn._defaults=Tn;var In=V.valueOrDefault,Fn=V.math.log10;var Ln={position:"left",ticks:{callback:rn.formatters.logarithmic}};function On(t,e){return V.isFinite(t)&&t>=0?t:e}var Rn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var p=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(p[a]=p[a]||0,p[a]+=n.max)}}V.each(f,(function(t){if(t.length>0){var e=V.min(t),n=V.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=V.isFinite(o.min)?o.min:null,o.max=V.isFinite(o.max)?o.max:null,o.minNotZero=V.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=On(e.min,t.min),t.max=On(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(Fn(t.min))-1),t.max=Math.pow(10,Math.floor(Fn(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(Fn(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(Fn(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(Fn(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:On(e.min),max:On(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=In(t.min,Math.pow(10,Math.floor(Fn(e.min)))),o=Math.floor(Fn(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(Fn(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(Fn(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=In(t.max,r);return a.push(u),a}(i,t);t.max=V.max(a),t.min=V.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),xn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(Fn(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=In(t.options.ticks.fontSize,z.global.defaultFontSize)/t._length),t._startValue=Fn(e),t._valueOffset=n,t._valueRange=(Fn(t.max)-Fn(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(Fn(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),zn=Ln;Rn._defaults=zn;var Nn=V.valueOrDefault,Bn=V.valueAtIndexOrDefault,En=V.options.resolve,Wn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:rn.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Vn(t){var e=t.ticks;return e.display&&t.display?Nn(e.fontSize,z.global.defaultFontSize)+2*e.backdropPaddingY:0}function Hn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function qn(t,e,n,i){var a,r,o=n.y+i/2;if(V.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Un(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function Yn(t){return V.isNumber(t)?t:0}var Gn=Sn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Vn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;V.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);V.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Vn(this.options))},convertTicksToLabels:function(){var t=this;Sn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=V.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=V.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=V.isArray(u)?{w:V.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=V.toDegrees(h)%360,f=Hn(c,i.x,n.w,0,180),g=Hn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Yn(a),r=Yn(r),o=Yn(o),s=Yn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(V.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Nn(s.lineWidth,o.lineWidth),u=Nn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Vn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=V.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=Bn(i.fontColor,s,z.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=V.toDegrees(h);e.textAlign=jn(c),Un(c,t._pointLabelSizes[s],u),qn(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&V.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=Bn(e.color,i-1),u=Bn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(En([s.borderDash,o.borderDash,[]])),a.lineDashOffset=En([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=V.options._parseFont(n),s=Nn(n.fontColor,z.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",V.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:V.noop}),Xn=Wn;Gn._defaults=Xn;var Kn=V._deprecated,Zn=V.options.resolve,$n=V.valueOrDefault,Jn=Number.MIN_SAFE_INTEGER||-9007199254740991,Qn=Number.MAX_SAFE_INTEGER||9007199254740991,ti={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ei=Object.keys(ti);function ni(t,e){return t-e}function ii(t){return V.valueOrDefault(t.time.min,t.ticks.min)}function ai(t){return V.valueOrDefault(t.time.max,t.ticks.max)}function ri(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function oi(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),V.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),V.isFinite(o)||(o=n.parse(o))),o)}function si(t,e){if(V.isNullOrUndef(e))return null;var n=t.options.time,i=oi(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function li(t,e,n,i){var a,r,o,s=ei.length;for(a=ei.indexOf(t);a<s-1;++a)if(o=(r=ti[ei[a]]).steps?r.steps:Qn,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ei[a];return ei[s-1]}function ui(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var di=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new an._date(e.adapters.date);return Kn("time scale",n.format,"time.format","time.parser"),Kn("time scale",n.min,"time.min","ticks.min"),Kn("time scale",n.max,"time.max","ticks.max"),V.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=Qn,f=Jn,g=[],p=[],m=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)m.push(si(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,V.isObject(a[0]))for(p[t]=[],e=0,i=a.length;e<i;++e)r=si(s,a[e]),g.push(r),p[t][e]=r;else p[t]=m.slice(0),o||(g=g.concat(m),o=!0);else p[t]=[];m.length&&(c=Math.min(c,m[0]),f=Math.max(f,m[m.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ni):g.sort(ni),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=si(s,ii(d))||c,f=si(s,ai(d))||f,c=c===Qn?+u.startOf(Date.now(),h):c,f=f===Jn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:p,labels:m}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||li(s.minUnit,e,n,i),u=Zn([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=si(i,ii(o))||a,r=si(i,ai(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?li(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ei.length-1;r>=ei.indexOf(n);r--)if(o=ei[r],ti[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ei[n?ei.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ei.indexOf(t)+1,n=ei.length;e<n;++e)if(ti[ei[e]].common)return ei[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=ri(t,"time",e[0],"pos"),s=1===e.length?1-r:(ri(t,"time",e[1],"pos")-r)/2,o=ri(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-ri(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),ui(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return V.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(oi(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(oi(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,p=Zn([g.callback,g.userCallback,h.callback,h.userCallback]);return p?p(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=ri(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=si(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=ri(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=V.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=$n(e.fontSize,z.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,ui(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),hi={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};di._defaults=hi;var ci={category:_n,linear:Dn,logarithmic:Rn,radialLinear:Gn,time:di},fi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};an._date.override("function"==typeof t?{_id:"moment",formats:function(){return fi},parse:function(e,n){return"string"==typeof e&&"string"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),"isoWeek"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),z._set("global",{plugins:{filler:{propagate:!0}}});var gi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return V.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function pi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function mi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(V.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function vi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function bi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),gi[n](t))}function xi(t){return t&&!t.skip}function yi(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)V.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)V.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function _i(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=xi(u),c=xi(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=m.push(u),x=v.push(d)):b&&x&&(p?(h&&m.push(u),c&&v.push(d)):(yi(t,m,v,b,x),b=x=0,m=[],v=[]));yi(t,m,v,b,x),t.closePath(),t.fillStyle=a,t.fill()}var ki={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof _t.Line&&(r={visible:t.isDatasetVisible(i),fill:pi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=vi(l,i,s),r.boundary=mi(r),r.mapper=bi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||z.global.defaultColor,o&&s&&r.length&&(V.canvas.clipArea(u,t.chartArea),_i(u,r,o,a,s,i._loop),V.canvas.unclipArea(u)))}},wi=V.rtl.getRtlAdapter,Mi=V.noop,Si=V.valueOrDefault;function Ci(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}z._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Pi=X.extend({initialize:function(t){V.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Mi,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Mi,beforeSetDimensions:Mi,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Mi,beforeBuildLabels:Mi,buildLabels:function(){var t=this,e=t.options.labels||{},n=V.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Mi,beforeFit:Mi,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=V.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",V.each(t.legendItems,(function(t,e){var i=Ci(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;V.each(t.legendItems,(function(t,e){var i=Ci(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),l.width+=p}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Mi,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=z.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=wi(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Si(n.fontColor,i.defaultFontColor),g=V.options._parseFont(n),p=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var m=Ci(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},V.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;V.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;h.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,s[d.line]));var w=h.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){c.save();var o=Si(i.lineWidth,r.borderWidth);if(c.fillStyle=Si(i.fillStyle,a),c.lineCap=Si(i.lineCap,r.borderCapStyle),c.lineDashOffset=Si(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Si(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Si(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Si(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=m*Math.SQRT2/2,l=h.xPlus(t,m/2),u=e+p/2;V.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,m),e,m,p),0!==o&&c.strokeRect(h.leftForLtr(t,m),e,m,p);c.restore()}}(w,k,e),v[i].left=h.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=h.xPlus(t,m+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),V.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Ai(t,e){var n=new Pi({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.legend=n}var Di={id:"legend",_element:Pi,beforeInit:function(t){var e=t.options.legend;e&&Ai(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(V.mergeIf(e,z.global.legend),n?(ge.configure(t,n,e),n.options=e):Ai(t,e)):n&&(ge.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ti=V.noop;z._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=X.extend({initialize:function(t){V.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ti,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ti,beforeSetDimensions:Ti,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ti,beforeBuildLabels:Ti,buildLabels:Ti,afterBuildLabels:Ti,beforeFit:Ti,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(V.isArray(n.text)?n.text.length:1)*V.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ti,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=V.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=V.valueOrDefault(n.fontColor,z.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(V.isArray(g))for(var p=0,m=0;m<g.length;++m)e.fillText(g[m],0,p,i),p+=s;else e.fillText(g,0,0,i);e.restore()}}});function Fi(t,e){var n=new Ii({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.titleBlock=n}var Li={},Oi=ki,Ri=Di,zi={id:"title",_element:Ii,beforeInit:function(t){var e=t.options.title;e&&Fi(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(V.mergeIf(e,z.global.title),n?(ge.configure(t,n,e),n.options=e):Fi(t,e)):n&&(ge.removeBox(t,n),delete t.titleBlock)}};for(var Ni in Li.filler=Oi,Li.legend=Ri,Li.title=zi,tn.helpers=V,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=V._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}V.where=function(t,e){if(V.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return V.each(t,(function(t){e(t)&&n.push(t)})),n},V.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},V.findNextWhere=function(t,e,n){V.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},V.findPreviousWhere=function(t,e,n){V.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},V.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},V.almostEquals=function(t,e,n){return Math.abs(t-e)<n},V.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},V.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},V.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},V.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},V.toRadians=function(t){return t*(Math.PI/180)},V.toDegrees=function(t){return t*(180/Math.PI)},V._decimalPlaces=function(t){if(V.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},V.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},V.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},V.aliasPixel=function(t){return t%2==0?0:.5},V._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},V.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},V.EPSILON=Number.EPSILON||1e-14,V.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(V.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},V.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},V.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},V.niceNum=function(t,e){var n=Math.floor(V.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},V.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},V.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(V.getStyle(r,"padding-left")),u=parseFloat(V.getStyle(r,"padding-top")),d=parseFloat(V.getStyle(r,"padding-right")),h=parseFloat(V.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},V.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},V.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},V._calculatePadding=function(t,e,n){return(e=V.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},V._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},V.getMaximumWidth=function(t){var e=V._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-V._calculatePadding(e,"padding-left",n)-V._calculatePadding(e,"padding-right",n),a=V.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},V.getMaximumHeight=function(t){var e=V._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-V._calculatePadding(e,"padding-top",n)-V._calculatePadding(e,"padding-bottom",n),a=V.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},V.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},V.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},V.fontString=function(t,e,n){return e+" "+t+"px "+n},V.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==V.isArray(u))h=V.measureText(t,a,r,h,u);else if(V.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||V.isArray(d)||(h=V.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},V.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},V.numberOfLabelLines=function(t){var e=1;return V.each(t,(function(t){V.isArray(t)&&t.length>e&&(e=t.length)})),e},V.color=k?function(t){return t instanceof CanvasGradient&&(t=z.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},V.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:V.color(t).saturate(.5).darken(.1).rgbString()}}(),tn._adapters=an,tn.Animation=Z,tn.animationService=$,tn.controllers=$t,tn.DatasetController=nt,tn.defaults=z,tn.Element=X,tn.elements=_t,tn.Interaction=ae,tn.layouts=ge,tn.platform=Fe,tn.plugins=Le,tn.Scale=xn,tn.scaleService=Oe,tn.Ticks=rn,tn.Tooltip=Ue,tn.helpers.each(ci,(function(t,e){tn.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Ni)&&tn.plugins.register(Li[Ni]);tn.platform.initialize();var Bi=tn;return"undefined"!=typeof window&&(window.Chart=tn),tn.Chart=tn,tn.Legend=Li.legend._element,tn.Title=Li.title._element,tn.pluginService=tn.plugins,tn.PluginBase=tn.Element.extend({}),tn.canvasHelpers=tn.helpers.canvas,tn.layoutService=tn.layouts,tn.LinearScaleBase=Sn,tn.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){tn[t]=function(e,n){return new tn(e,tn.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Bi})); From 5c94848bccf5625dce16376be325252c40a6a598 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 24 Feb 2020 10:26:03 +0100 Subject: [PATCH 196/734] Refresh file list after a directory has been removed --- lib/web/mage/adminhtml/browser.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 7780ac524fa49..d6502af9ab74b 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -417,6 +417,8 @@ define([ showLoader: true }).done($.proxy(function () { self.tree.jstree('refresh', self.activeNode.id); + self.reload(); + $(window).trigger('fileDeleted.mediabrowser'); }, this)); }, From e001493a7877e5f00a53ae1d34a98039b8e0313e Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 14:49:16 +0100 Subject: [PATCH 197/734] #26986 Fix invalid value returned for "records found" section in Admin Panel --- .../ResourceModel/Product/Collection.php | 84 +++++++++++-------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index afbe279045a38..9fa664cfda0cc 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1013,9 +1013,9 @@ public function getMaxAttributeValue($attribute) $tableAlias = $attributeCode . '_max_value'; $fieldAlias = 'max_' . $attributeCode; $condition = 'e.entity_id = ' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->join( [$tableAlias => $attribute->getBackend()->getTable()], @@ -1048,9 +1048,9 @@ public function getAttributeValueCountByRange($attribute, $range) $tableAlias = $attributeCode . '_range_count_value'; $condition = 'e.entity_id = ' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->reset(\Magento\Framework\DB\Select::GROUP); $select->join( @@ -1088,9 +1088,9 @@ public function getAttributeValueCount($attribute) $select->reset(\Magento\Framework\DB\Select::GROUP); $condition = 'e.entity_id=' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->join( [$tableAlias => $attribute->getBackend()->getTable()], @@ -1180,9 +1180,27 @@ protected function _getSelectCountSql(?Select $select = null, $resetLeftJoins = if ($resetLeftJoins) { $countSelect->resetJoinLeft(); } + + $this->removeEntityIdentifierFromGroupBy($countSelect); + return $countSelect; } + /** + * Using `entity_id` for `GROUP BY` causes COUNT() return {n} rows of value = 1 instead of 1 row of value {n} + * + * @param Select $select + * @throws \Zend_Db_Select_Exception + */ + private function removeEntityIdentifierFromGroupBy(Select $select): void + { + $groupBy = array_filter($select->getPart(Select::GROUP), function ($field) { + return false === strpos($field, $this->getIdFieldName()); + }); + + $select->setPart(Select::GROUP, $groupBy); + } + /** * Prepare statistics data * @@ -1766,28 +1784,28 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) protected function _prepareProductLimitationFilters() { if (isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) + $this->_productLimitationFilters['visibility'] + ) && !isset( + $this->_productLimitationFilters['store_id'] + ) ) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } if (isset( - $this->_productLimitationFilters['category_id'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) + $this->_productLimitationFilters['category_id'] + ) && !isset( + $this->_productLimitationFilters['store_id'] + ) ) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } if (isset( - $this->_productLimitationFilters['store_id'] - ) && isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['category_id'] - ) + $this->_productLimitationFilters['store_id'] + ) && isset( + $this->_productLimitationFilters['visibility'] + ) && !isset( + $this->_productLimitationFilters['category_id'] + ) ) { $this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore( $this->_productLimitationFilters['store_id'] @@ -1820,12 +1838,12 @@ protected function _productLimitationJoinWebsite() 'int' ); } elseif (isset( - $filters['store_id'] - ) && (!isset( - $filters['visibility'] - ) && !isset( - $filters['category_id'] - )) && !$this->isEnabledFlat() + $filters['store_id'] + ) && (!isset( + $filters['visibility'] + ) && !isset( + $filters['category_id'] + )) && !$this->isEnabledFlat() ) { $joinWebsite = true; $websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId(); @@ -1901,9 +1919,9 @@ protected function _productLimitationJoinPrice() /** * Join Product Price Table with left-join possibility * - * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice() * @param bool $joinLeft * @return $this + * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice() */ protected function _productLimitationPrice($joinLeft = false) { @@ -2334,8 +2352,8 @@ public function addPriceDataFieldFilter($comparisonFormat, $fields) * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - * @since 101.0.1 * @throws \Magento\Framework\Exception\LocalizedException + * @since 101.0.1 */ public function addMediaGalleryData() { @@ -2360,7 +2378,7 @@ public function addMediaGalleryData() 'entity.' . $linkField . ' IN (?)', array_map( function ($item) use ($linkField) { - return (int) $item->getOrigData($linkField); + return (int)$item->getOrigData($linkField); }, $items ) @@ -2411,9 +2429,9 @@ private function getGalleryReadHandler() /** * Retrieve Media gallery resource. * + * @return \Magento\Catalog\Model\ResourceModel\Product\Gallery * @deprecated 101.0.1 * - * @return \Magento\Catalog\Model\ResourceModel\Product\Gallery */ private function getMediaGalleryResource() { From ecb3a8d393854ab1ab0543f541fd64d5c1941015 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 15:53:34 +0100 Subject: [PATCH 198/734] #26986 Fix Static checks for Collection file --- .../ResourceModel/Product/Collection.php | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 9fa664cfda0cc..e22b4b30b43f2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -12,6 +12,7 @@ use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; +use Magento\Catalog\Model\ResourceModel\Category; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; @@ -23,7 +24,6 @@ use Magento\Framework\Indexer\DimensionFactory; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\Store; -use Magento\Catalog\Model\ResourceModel\Category; /** * Product collection @@ -1013,9 +1013,9 @@ public function getMaxAttributeValue($attribute) $tableAlias = $attributeCode . '_max_value'; $fieldAlias = 'max_' . $attributeCode; $condition = 'e.entity_id = ' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->join( [$tableAlias => $attribute->getBackend()->getTable()], @@ -1048,9 +1048,9 @@ public function getAttributeValueCountByRange($attribute, $range) $tableAlias = $attributeCode . '_range_count_value'; $condition = 'e.entity_id = ' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->reset(\Magento\Framework\DB\Select::GROUP); $select->join( @@ -1088,9 +1088,9 @@ public function getAttributeValueCount($attribute) $select->reset(\Magento\Framework\DB\Select::GROUP); $condition = 'e.entity_id=' . $tableAlias . '.entity_id AND ' . $this->_getConditionSql( - $tableAlias . '.attribute_id', - $attribute->getId() - ); + $tableAlias . '.attribute_id', + $attribute->getId() + ); $select->join( [$tableAlias => $attribute->getBackend()->getTable()], @@ -1783,30 +1783,19 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) */ protected function _prepareProductLimitationFilters() { - if (isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) - ) { + if (isset($this->_productLimitationFilters['visibility']) + && !isset($this->_productLimitationFilters['store_id'])) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } - if (isset( - $this->_productLimitationFilters['category_id'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) - ) { + + if (isset($this->_productLimitationFilters['category_id']) + && !isset($this->_productLimitationFilters['store_id'])) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } - if (isset( - $this->_productLimitationFilters['store_id'] - ) && isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['category_id'] - ) - ) { + + if (isset($this->_productLimitationFilters['store_id']) + && isset($this->_productLimitationFilters['visibility']) + && !isset($this->_productLimitationFilters['category_id'])) { $this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore( $this->_productLimitationFilters['store_id'] )->getRootCategoryId(); @@ -1837,14 +1826,8 @@ protected function _productLimitationJoinWebsite() $filters['website_ids'], 'int' ); - } elseif (isset( - $filters['store_id'] - ) && (!isset( - $filters['visibility'] - ) && !isset( - $filters['category_id'] - )) && !$this->isEnabledFlat() - ) { + } elseif (isset($filters['store_id']) && !$this->isEnabledFlat() + && (!isset($filters['visibility']) && !isset($filters['category_id']))) { $joinWebsite = true; $websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId(); $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int'); @@ -2431,7 +2414,6 @@ private function getGalleryReadHandler() * * @return \Magento\Catalog\Model\ResourceModel\Product\Gallery * @deprecated 101.0.1 - * */ private function getMediaGalleryResource() { From 015c5fcc53b96efa460c05ec4047006fbf68e753 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 15:57:59 +0100 Subject: [PATCH 199/734] #26986 Fix failing Functional Test (assert empty page) --- .../AssertStorefrontNoProductsFoundActionGroup.xml | 8 ++++++++ .../Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml | 2 +- ...inProductCategoryIndexerInUpdateOnScheduleModeTest.xml | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml new file mode 100644 index 0000000000000..422387313390d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontNoProductsFoundActionGroup"> + <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index 7e6e79cd08c26..1e8ba0a3f944b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -157,7 +157,7 @@ <!-- # Category should open successfully # <product1> should be absent on the page --> <see userInput="$$createAnchoredCategory1.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategory1Name"/> - <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/> <dontSee userInput="$$simpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/> <!-- Log in to the backend: Admin user is logged in--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index 2283a0e4d6158..cafacfd135df2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -89,7 +89,7 @@ <!-- The category is still empty --> <see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategoryA1Name"/> - <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/> <dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1"/> <!-- 4. Run cron to reindex --> @@ -128,7 +128,7 @@ <!-- Category A is empty now --> <see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeOnPageCategoryAName"/> - <see userInput="We can't find products matching the selection." stepKey="seeOnPageEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeOnPageEmptyNotice"/> <dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1OnPage"/> <!-- Case: change product status --> From 18e43a8df8581586b7ba3110e3feebcd6e1910d7 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 16:05:17 +0100 Subject: [PATCH 200/734] #26986 Fix failing Unit Tests --- .../Catalog/Model/ResourceModel/Product/Collection.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index e22b4b30b43f2..78cdac8e8a8a0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1194,7 +1194,13 @@ protected function _getSelectCountSql(?Select $select = null, $resetLeftJoins = */ private function removeEntityIdentifierFromGroupBy(Select $select): void { - $groupBy = array_filter($select->getPart(Select::GROUP), function ($field) { + $originalGroupBy = $select->getPart(Select::GROUP); + + if (!is_array($originalGroupBy)) { + return; + } + + $groupBy = array_filter($originalGroupBy, function ($field) { return false === strpos($field, $this->getIdFieldName()); }); From 37731349799b541f05e6799bb8f66da23de0d2f7 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Mon, 24 Feb 2020 15:34:18 +0200 Subject: [PATCH 201/734] Mftf test, changed properties visibility --- .../Grid/Column/Renderer/ScheduleStatus.php | 31 +++++---- .../Data/AdminIndexManagementGridData.xml | 13 ++++ .../Section/AdminIndexManagementSection.xml | 2 + ...inSystemIndexManagementGridChangesTest.xml | 63 +++++++++++++++++++ 4 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 7737077609b51..4d90d9c178c12 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -3,11 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +use Magento\Backend\Block\Context; use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; +use Magento\Framework\DataObject; use Magento\Framework\Escaper; use Magento\Framework\Mview\View; +use Magento\Framework\Mview\ViewInterface; use Magento\Framework\Phrase; /** @@ -16,23 +21,23 @@ class ScheduleStatus extends AbstractRenderer { /** - * @var \Magento\Framework\Escaper + * @var Escaper */ - protected $escaper; + private $escaper; /** - * @var \Magento\Framework\Mview\ViewInterface + * @var ViewInterface */ - protected $viewModel; + private $viewModel; - /** - * @param \Magento\Backend\Block\Context $context - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Framework\Mview\ViewInterface $viewModel - * @param array $data - */ + /** + * @param Context $context + * @param Escaper $escaper + * @param ViewInterface $viewModel + * @param array $data + */ public function __construct( - \Magento\Backend\Block\Context $context, + Context $context, Escaper $escaper, View $viewModel, array $data = [] @@ -45,10 +50,10 @@ public function __construct( /** * Render indexer status * - * @param \Magento\Framework\DataObject $row + * @param DataObject $row * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { try { if (!$row->getIsScheduled()) { diff --git a/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml b/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml new file mode 100644 index 0000000000000..74494ee6b469c --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml @@ -0,0 +1,13 @@ +<?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="AdminIndexManagementGridData"> + <data key="rowProductPrice">Product Price</data> + </entity> +</entities> diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml index 020cd9654e36b..825358e74f2af 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -17,5 +17,7 @@ <element name="indexerStatus" type="text" selector="//tr[descendant::td[contains(., '{{status}}')]]//*[contains(@class, 'col-indexer_status')]/span" parameterized="true"/> <element name="successMessage" type="text" selector="//*[@data-ui-id='messages-message-success']" timeout="120"/> <element name="selectMassAction" type="select" selector="#gridIndexer_massaction-mass-select"/> + <element name="columnScheduleStatus" type="text" selector="//th[contains(@class, 'col-indexer_schedule_status')]"/> + <element name="indexerScheduleStatus" type="text" selector="//tr[contains(.,'{{var1}}')]//td[contains(@class,'col-indexer_schedule_status')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml new file mode 100644 index 0000000000000..75b040a623451 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml @@ -0,0 +1,63 @@ +<?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="AdminSystemIndexManagementGridChangesTest"> + <annotations> + <features value="Indexer"/> + <stories value="Menu Navigation"/> + <title value="Admin system index management grid change test"/> + <description value="Verify changes in 'Schedule column' on system index management"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Open Index Management Page and Select Index mode "Update by Schedule" --> + <magentoCLI command="indexer:set-mode" arguments="schedule" stepKey="setIndexerModeSchedule"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/></before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setIndexerModeRealTime"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPageFirst"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemToolsIndexManagement.dataUiId}}"/> + </actionGroup> + <grabTextFrom selector="{{AdminIndexManagementSection.indexerScheduleStatus(AdminIndexManagementGridData.rowProductPrice)}}" stepKey="gradScheduleStatusBeforeChange"/> + + <!-- Verify 'Schedule status' column is present --> + <seeElement selector="{{AdminIndexManagementSection.columnScheduleStatus}}" stepKey="seeScheduleStatusColumn"/> + + <!--Adding Special price to product--> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminProductEditPage"/> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPageSecond"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemToolsIndexManagement.dataUiId}}"/> + </actionGroup> + <grabTextFrom selector="{{AdminIndexManagementSection.indexerScheduleStatus(AdminIndexManagementGridData.rowProductPrice)}}" stepKey="gradScheduleStatusAfterChange"/> + + <!-- Verify 'Schedule Status' column changes for 'Product Price' --> + <assertNotEquals stepKey="assertChange"> + <expectedResult type="string">$gradScheduleStatusBeforeChange</expectedResult> + <actualResult type="string">$gradScheduleStatusAfterChange</actualResult> + </assertNotEquals> + </test> +</tests> From 9d0ae13e97e6403766fe7eba7cde9946095866e5 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 16:13:34 +0100 Subject: [PATCH 202/734] #26986 Fix failing Functional Test: Change of behaviour when loading non-existing page (eg. `999`) --- ...hUsingElasticSearch6WithNotAvailablePageTest.xml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml index b4eb436fc1b2a..40e32c93c7ed6 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml @@ -30,17 +30,6 @@ <argument name="phrase" value="AAA"/> <argument name="pageNumber" value="999"/> </actionGroup> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="scrollToBottomToolbarPager"/> - <grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfCurrentPage"/> - <assertEquals stepKey="assertCurrentPageIsLastPageOfCatalogSearchResult"> - <expectedResult type="variable">grabNumberOfLastPage</expectedResult> - <actualResult type="variable">grabNumberOfCurrentPage</actualResult> - </assertEquals> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertProductOnLastCatalogSearchPage"> - <argument name="product" value="$createSecondProduct$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnLastCatalogSearchPage"> - <argument name="productName" value="$createFirstProduct.name$"/> - </actionGroup> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> </test> </tests> From dfa5fd3e6067eb7e4edbe062be34cf134b5e608a Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 24 Feb 2020 17:58:33 +0200 Subject: [PATCH 203/734] MC-31729: Non-cacheable block added to default handle makes every page non-cacheable --- .../Model/Layout/DepersonalizePlugin.php | 34 +- .../Model/Layout/DepersonalizePluginTest.php | 71 ++- .../Model/Layout/DepersonalizePlugin.php | 30 +- .../Model/Layout/DepersonalizePluginTest.php | 80 +-- .../Model/Layout/DepersonalizePlugin.php | 77 +-- .../Model/Layout/DepersonalizePluginTest.php | 141 +++-- .../Model/Layout/DepersonalizePlugin.php | 38 +- .../PageCache/Model/Layout/LayoutPlugin.php | 30 +- .../Model/Layout/DepersonalizePluginTest.php | 81 ++- .../Unit/Model/Layout/LayoutPluginTest.php | 112 ++-- .../Model/Layout/DepersonalizePlugin.php | 35 +- .../Model/Layout/DepersonalizePluginTest.php | 115 ++-- .../Tax/Model/Layout/DepersonalizePlugin.php | 45 +- .../Model/Layout/DepersonalizePluginTest.php | 166 +++++ .../Model/Layout/DepersonalizePluginTest.php | 100 +++ ...acheable_block_with_declared_reference.xml | 22 + ...acheable_block_with_missing_refference.xml | 22 + .../Model/Layout/DepersonalizePluginTest.php | 100 +++ .../Magento/Framework/View/Layout.php | 19 +- .../Framework/View/Test/Unit/LayoutTest.php | 587 +++++++++++------- 20 files changed, 1267 insertions(+), 638 deletions(-) create mode 100644 app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php diff --git a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php index 50ef65520c87d..d99dc41bcba12 100644 --- a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php @@ -1,55 +1,55 @@ <?php /** - * Depersonalize catalog session data - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Layout; +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * Catalog session - * - * @var \Magento\Catalog\Model\Session + * @var CatalogSession */ - protected $catalogSession; + private $catalogSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Catalog\Model\Session $catalogSession + * @param CatalogSession $catalogSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Catalog\Model\Session $catalogSession + CatalogSession $catalogSession ) { - $this->catalogSession = $catalogSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->catalogSession = $catalogSession; } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->catalogSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php index d644443e461a2..cac1d8eb244d9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,59 +3,82 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Model\Layout; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Model\Layout\DepersonalizePlugin; +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PhpUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +/** + * Unit tests for \Magento\Catalog\Model\Layout\DepersonalizePlugin class. + */ +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Catalog\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Catalog\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CatalogSession|MockObject */ - protected $catalogSessionMock; + private $catalogSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $resultLayout; + private $layoutMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->catalogSessionMock = $this->createPartialMock(\Magento\Catalog\Model\Session::class, ['clearStorage']); - $this->resultLayout = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = (new ObjectManager($this))->getObject( - \Magento\Catalog\Model\Layout\DepersonalizePlugin::class, - ['catalogSession' => $this->catalogSessionMock, 'depersonalizeChecker' => $this->depersonalizeCheckerMock] + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->catalogSessionMock = $this->createPartialMock(CatalogSession::class, ['clearStorage']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'catalogSession' => $this->catalogSessionMock, + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + ] ); } - public function testAfterGenerateXml() + /** + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { $this->catalogSessionMock->expects($this->once())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $this->resultLayout); - $this->assertEquals($this->resultLayout, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { $this->catalogSessionMock->expects($this->never())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $this->resultLayout); - $this->assertEquals($this->resultLayout, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php index de36bf658c59d..4857c14251765 100644 --- a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php @@ -3,50 +3,54 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Model\Layout; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Checkout\Model\Session + * @var CheckoutSession */ - protected $checkoutSession; + private $checkoutSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Checkout\Model\Session $checkoutSession + * @param CheckoutSession $checkoutSession * @codeCoverageIgnore */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Checkout\Model\Session $checkoutSession + CheckoutSession $checkoutSession ) { - $this->checkoutSession = $checkoutSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->checkoutSession = $checkoutSession; } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->checkoutSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 350f9954208fa..29b47de21b8f8 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,84 +3,90 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Checkout\Test\Unit\Model\Layout; +use Magento\Checkout\Model\Layout\DepersonalizePlugin; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Checkout\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Customer\Model\Layout\DepersonalizePluginTest + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Checkout\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CheckoutSession|MockObject */ - protected $checkoutSessionMock; + private $checkoutSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->checkoutSessionMock = $this->createPartialMock( - \Magento\Framework\Session\Generic::class, - ['clearStorage', 'setData', 'getData'] - ); - $this->checkoutSessionMock = $this->createPartialMock(\Magento\Checkout\Model\Session::class, ['clearStorage']); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); - $this->cacheConfigMock = $this->createMock(\Magento\PageCache\Model\Config::class); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->checkoutSessionMock = $this->createPartialMock(CheckoutSession::class, ['clearStorage']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); - $this->plugin = new \Magento\Checkout\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->checkoutSessionMock + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'checkoutSession' => $this->checkoutSessionMock, + ] ); } /** - * Test method afterGenerateXml + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void */ - public function testAfterGenerateXml() + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->checkoutSessionMock ->expects($this->once()) ->method('clearStorage') - ->will($this->returnValue($expectedResult)); + ->willReturnSelf(); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->checkoutSessionMock ->expects($this->never()) ->method('clearStorage') - ->will($this->returnValue($expectedResult)); + ->willReturnSelf(); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php index 2bb9934933839..4dcf1a2ca9ba2 100644 --- a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php @@ -1,108 +1,113 @@ <?php /** - * Depersonalize customer session data - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Model\Layout; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Customer\Model\Visitor; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Framework\Session\SessionManagerInterface + * @var SessionManagerInterface */ - protected $session; + private $session; /** - * @var \Magento\Customer\Model\Session + * @var CustomerSession */ - protected $customerSession; + private $customerSession; /** - * @var \Magento\Customer\Model\CustomerFactory + * @var CustomerFactory */ - protected $customerFactory; + private $customerFactory; /** - * @var \Magento\Customer\Model\Visitor + * @var Visitor */ - protected $visitor; + private $visitor; /** * @var int */ - protected $customerGroupId; + private $customerGroupId; /** * @var string */ - protected $formKey; + private $formKey; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Framework\Session\SessionManagerInterface $session - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\Visitor $visitor + * @param SessionManagerInterface $session + * @param CustomerSession $customerSession + * @param CustomerFactory $customerFactory + * @param Visitor $visitor */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Customer\Model\Session $customerSession, - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\Visitor $visitor + SessionManagerInterface $session, + CustomerSession $customerSession, + CustomerFactory $customerFactory, + Visitor $visitor ) { + $this->depersonalizeChecker = $depersonalizeChecker; $this->session = $session; $this->customerSession = $customerSession; $this->customerFactory = $customerFactory; $this->visitor = $visitor; - $this->depersonalizeChecker = $depersonalizeChecker; } /** - * Before generate Xml + * Retrieves sensitive customer data. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @return array + * @param LayoutInterface $subject + * @return void */ - public function beforeGenerateXml(\Magento\Framework\View\LayoutInterface $subject) + public function beforeGenerateXml(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->customerGroupId = $this->customerSession->getCustomerGroupId(); - $this->formKey = $this->session->getData(\Magento\Framework\Data\Form\FormKey::FORM_KEY); + $this->formKey = $this->session->getData(FormKey::FORM_KEY); } - return []; } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->visitor->setSkipRequestLogging(true); $this->visitor->unsetData(); $this->session->clearStorage(); $this->customerSession->clearStorage(); - $this->session->setData(\Magento\Framework\Data\Form\FormKey::FORM_KEY, $this->formKey); + $this->session->setData(FormKey::FORM_KEY, $this->formKey); $this->customerSession->setCustomerGroupId($this->customerGroupId); $this->customerSession->setCustomer($this->customerFactory->create()->setGroupId($this->customerGroupId)); } - return $result; } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 15337d8968305..245b39c79f6a7 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,143 +3,177 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Test\Unit\Model\Layout; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\Layout\DepersonalizePlugin; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Customer\Model\Visitor as VisitorModel; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Session\Generic as GenericSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Customer\Model\Layout\DepersonalizePlugin class. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Customer\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\Session\Generic|\PHPUnit_Framework_MockObject_MockObject + * @var GenericSession|MockObject */ - protected $sessionMock; + private $sessionMock; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerSession|MockObject */ - protected $customerSessionMock; + private $customerSessionMock; /** - * @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerFactory|MockObject */ - protected $customerFactoryMock; + private $customerFactoryMock; /** - * @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject + * @var Customer|MockObject */ - protected $customerMock; + private $customerMock; /** - * @var \Magento\Customer\Model\Visitor|\PHPUnit_Framework_MockObject_MockObject + * @var VisitorModel|MockObject */ - protected $visitorMock; + private $visitorMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); $this->sessionMock = $this->createPartialMock( - \Magento\Framework\Session\Generic::class, + GenericSession::class, ['clearStorage', 'setData', 'getData'] ); $this->customerSessionMock = $this->createPartialMock( - \Magento\Customer\Model\Session::class, + CustomerSession::class, ['getCustomerGroupId', 'setCustomerGroupId', 'clearStorage', 'setCustomer'] ); - $this->customerFactoryMock = $this->createPartialMock( - \Magento\Customer\Model\CustomerFactory::class, - ['create'] - ); + $this->customerFactoryMock = $this->createPartialMock(CustomerFactory::class, ['create']); $this->customerMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['setGroupId', '__wakeup'] ); - $this->visitorMock = $this->createMock(\Magento\Customer\Model\Visitor::class); + $this->visitorMock = $this->createMock(VisitorModel::class); $this->customerFactoryMock->expects($this->any()) ->method('create') - ->will($this->returnValue($this->customerMock)); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = new \Magento\Customer\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->sessionMock, - $this->customerSessionMock, - $this->customerFactoryMock, - $this->visitorMock + ->willReturn($this->customerMock); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'session' => $this->sessionMock, + 'customerSession' => $this->customerSessionMock, + 'customerFactory' => $this->customerFactoryMock, + 'visitor' => $this->visitorMock, + ] ); } - public function testBeforeGenerateXml() + /** + * Tests beforeGenerateXml method when depersonalization is needed. + * + * @return void + */ + public function testBeforeGenerateXml(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->customerSessionMock->expects($this->once())->method('getCustomerGroupId'); $this->sessionMock ->expects($this->once()) ->method('getData') - ->with($this->equalTo(\Magento\Framework\Data\Form\FormKey::FORM_KEY)); - $output = $this->plugin->beforeGenerateXml($this->layoutMock); - $this->assertEquals([], $output); + ->with($this->equalTo(FormKey::FORM_KEY)); + $this->plugin->beforeGenerateXml($this->layoutMock); } - public function testBeforeGenerateXmlNoDepersonalize() + /** + * Tests beforeGenerateXml method when depersonalization is not needed. + * + * @return void + */ + public function testBeforeGenerateXmlNoDepersonalize(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->customerSessionMock->expects($this->never())->method('getCustomerGroupId'); $this->sessionMock ->expects($this->never()) ->method('getData'); - $output = $this->plugin->beforeGenerateXml($this->layoutMock); - $this->assertEquals([], $output); + $this->plugin->beforeGenerateXml($this->layoutMock); } - public function testAfterGenerateXml() + /** + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->visitorMock->expects($this->once())->method('setSkipRequestLogging')->with($this->equalTo(true)); $this->visitorMock->expects($this->once())->method('unsetData'); $this->sessionMock->expects($this->once())->method('clearStorage'); $this->customerSessionMock->expects($this->once())->method('clearStorage'); $this->customerSessionMock->expects($this->once())->method('setCustomerGroupId')->with($this->equalTo(null)); - $this->customerMock->expects($this->once())->method('setGroupId')->with($this->equalTo(null))->willReturnSelf(); + $this->customerMock + ->expects($this->once()) + ->method('setGroupId') + ->with($this->equalTo(null)) + ->willReturnSelf(); $this->sessionMock ->expects($this->once()) ->method('setData') ->with( - $this->equalTo(\Magento\Framework\Data\Form\FormKey::FORM_KEY), + $this->equalTo(FormKey::FORM_KEY), $this->equalTo(null) ); $this->customerSessionMock ->expects($this->once()) ->method('setCustomer') ->with($this->equalTo($this->customerMock)); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertSame($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->visitorMock->expects($this->never())->method('setSkipRequestLogging'); $this->visitorMock->expects($this->never())->method('unsetData'); @@ -149,7 +183,6 @@ public function testAfterGenerateXmlNoDepersonalize() $this->customerMock->expects($this->never())->method('setGroupId'); $this->sessionMock->expects($this->never())->method('setData'); $this->customerSessionMock->expects($this->never())->method('setCustomer'); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertSame($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php index 52b304d994bb3..3218afabe0511 100644 --- a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php @@ -3,39 +3,46 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Model\Layout; +use Magento\Framework\Event\Manager as EventManager; +use Magento\Framework\Message\Session as MessageSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Framework\Event\Manager + * @var EventManager */ - protected $eventManager; + private $eventManager; /** - * @var \Magento\Framework\Message\Session + * @var MessageSession */ - protected $messageSession; + private $messageSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Framework\Event\Manager $eventManager - * @param \Magento\Framework\Message\Session $messageSession + * @param EventManager $eventManager + * @param MessageSession $messageSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Framework\Event\Manager $eventManager, - \Magento\Framework\Message\Session $messageSession + EventManager $eventManager, + MessageSession $messageSession ) { $this->depersonalizeChecker = $depersonalizeChecker; $this->eventManager = $eventManager; @@ -43,19 +50,18 @@ public function __construct( } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->eventManager->dispatch('depersonalize_clear_session'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction session_write_close(); $this->messageSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index b4d9cb2d5e076..c30efd6ae78ae 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -3,31 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\PageCache\Model\Layout; use Magento\Framework\App\MaintenanceMode; use Magento\Framework\App\ResponseInterface; use Magento\Framework\DataObject\IdentityInterface; -use Magento\Framework\View\Layout; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\Config; /** - * Class LayoutPlugin - * - * Plugin for Magento\Framework\View\Layout + * Appends cacheable pages response headers. */ class LayoutPlugin { /** * @var Config */ - protected $config; + private $config; /** * @var ResponseInterface */ - protected $response; + private $response; /** * @var MaintenanceMode @@ -35,8 +34,6 @@ class LayoutPlugin private $maintenanceMode; /** - * Constructor - * * @param ResponseInterface $response * @param Config $config * @param MaintenanceMode $maintenanceMode @@ -52,30 +49,28 @@ public function __construct( } /** - * Set appropriate Cache-Control headers + * Sets appropriate Cache-Control headers. * * We have to set public headers in order to tell Varnish and Builtin app that page should be cached * - * @param Layout $subject - * @param mixed $result - * @return mixed + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(Layout $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($subject->isCacheable() && !$this->maintenanceMode->isOn() && $this->config->isEnabled()) { $this->response->setPublicHeaders($this->config->getTtl()); } - return $result; } /** - * Retrieve all identities from blocks for further cache invalidation + * Retrieves all identities from blocks for further cache invalidation. * - * @param Layout $subject + * @param LayoutInterface $subject * @param mixed $result * @return mixed */ - public function afterGetOutput(Layout $subject, $result) + public function afterGetOutput(LayoutInterface $subject, $result) { if ($subject->isCacheable() && $this->config->isEnabled()) { $tags = [[]]; @@ -92,6 +87,7 @@ public function afterGetOutput(Layout $subject, $result) $tags = array_unique(array_merge(...$tags)); $this->response->setHeader('X-Magento-Tags', implode(',', $tags)); } + return $result; } } diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php index c9638a3d5b8f6..baed50e989e1c 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,81 +3,98 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\PageCache\Test\Unit\Model\Layout; +use Magento\Framework\Event\Manager; +use Magento\Framework\Message\Session; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\PageCache\Model\Layout\DepersonalizePlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\PageCache\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\PageCache\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\Event\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \Magento\Framework\Message\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $messageSessionMock; + private $messageSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\Manager::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->eventManagerMock = $this->createMock(Manager::class); $this->messageSessionMock = $this->createPartialMock( - \Magento\Framework\Message\Session::class, + Session::class, ['clearStorage'] ); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - $this->plugin = new \Magento\PageCache\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->eventManagerMock, - $this->messageSessionMock + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'eventManager' => $this->eventManagerMock, + 'messageSession' => $this->messageSessionMock, + ] ); } - public function testAfterGenerateXml() + /** + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->eventManagerMock->expects($this->once()) ->method('dispatch') ->with($this->equalTo('depersonalize_clear_session')); $this->messageSessionMock->expects($this->once())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); - $this->eventManagerMock->expects($this->never()) - ->method('dispatch'); + $this->eventManagerMock->expects($this->never())->method('dispatch'); $this->messageSessionMock->expects($this->never())->method('clearStorage'); - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php index e2bc7f237ab0a..f3d5c449c654e 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php @@ -3,54 +3,69 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Test\Unit\Model\Layout; -class LayoutPluginTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\MaintenanceMode; +use Magento\Framework\App\Response\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Model\Layout\LayoutPlugin; +use Magento\PageCache\Test\Unit\Block\Controller\StubBlock; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit tests for \Magento\PageCache\Model\Layout\LayoutPlugin class. + */ +class LayoutPluginTest extends TestCase { /** - * @var \Magento\PageCache\Model\Layout\LayoutPlugin + * @var LayoutPlugin */ - protected $model; + private $model; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ - protected $responseMock; + private $responseMock; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface|MockObject */ - protected $configMock; + private $configMock; /** - * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit\Framework\MockObject\MockObject + * @var MaintenanceMode|MockObject */ private $maintenanceModeMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->layoutMock = $this->getMockForAbstractClass( - \Magento\Framework\View\Layout::class, - [], - '', - false, - true, - true, - ['isCacheable', 'getAllBlocks'] - ); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->configMock = $this->createMock(\Magento\PageCache\Model\Config::class); - $this->maintenanceModeMock = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); - - $this->model = new \Magento\PageCache\Model\Layout\LayoutPlugin( - $this->responseMock, - $this->configMock, - $this->maintenanceModeMock + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->responseMock = $this->createMock(Http::class); + $this->configMock = $this->createMock(Config::class); + $this->maintenanceModeMock = $this->createMock(MaintenanceMode::class); + + $this->model = (new ObjectManagerHelper($this))->getObject( + LayoutPlugin::class, + [ + 'response' => $this->responseMock, + 'config' => $this->configMock, + 'maintenanceMode' => $this->maintenanceModeMock, + ] ); } @@ -58,33 +73,31 @@ protected function setUp() * @param $cacheState * @param $layoutIsCacheable * @param $maintenanceModeIsEnabled - * - * @dataProvider afterGenerateXmlDataProvider + * @return void + * @dataProvider afterGenerateElementsDataProvider */ - public function testAfterGenerateXml($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled) + public function testAfterGenerateElements($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled): void { $maxAge = 180; - $result = 'test'; - $this->layoutMock->expects($this->once())->method('isCacheable')->will($this->returnValue($layoutIsCacheable)); - $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); - $this->maintenanceModeMock->expects($this->any())->method('isOn') - ->will($this->returnValue($maintenanceModeIsEnabled)); + $this->layoutMock->expects($this->once())->method('isCacheable')->willReturn($layoutIsCacheable); + $this->configMock->expects($this->any())->method('isEnabled')->willReturn($cacheState); + $this->maintenanceModeMock->expects($this->any())->method('isOn')->willReturn($maintenanceModeIsEnabled); if ($layoutIsCacheable && $cacheState && !$maintenanceModeIsEnabled) { - $this->configMock->expects($this->once())->method('getTtl')->will($this->returnValue($maxAge)); + $this->configMock->expects($this->once())->method('getTtl')->willReturn($maxAge); $this->responseMock->expects($this->once())->method('setPublicHeaders')->with($maxAge); } else { $this->responseMock->expects($this->never())->method('setPublicHeaders'); } - $output = $this->model->afterGenerateXml($this->layoutMock, $result); - $this->assertSame($result, $output); + + $this->assertEmpty($this->model->afterGenerateElements($this->layoutMock)); } /** * @return array */ - public function afterGenerateXmlDataProvider() + public function afterGenerateElementsDataProvider(): array { return [ 'Full_cache state is true, Layout is cache-able' => [true, true, false], @@ -101,14 +114,15 @@ public function afterGenerateXmlDataProvider() * @param $expectedTags * @param $configCacheType * @param $ttl + * @return void * @dataProvider afterGetOutputDataProvider */ - public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTags, $configCacheType, $ttl) + public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTags, $configCacheType, $ttl): void { $html = 'html'; $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); $blockStub = $this->createPartialMock( - \Magento\PageCache\Test\Unit\Block\Controller\StubBlock::class, + StubBlock::class, ['getIdentities'] ); $blockStub->setTtl($ttl); @@ -130,42 +144,42 @@ public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTag /** * @return array */ - public function afterGetOutputDataProvider() + public function afterGetOutputDataProvider(): array { $tags = 'identity1,identity2'; return [ 'Cacheable layout, Full_cache state is true' => [true, true, $tags, null, 0], 'Non-cacheable layout' => [true, false, null, null, 0], - 'Cacheable layout with Varnish' => [true, true, $tags, \Magento\PageCache\Model\Config::VARNISH, 0], + 'Cacheable layout with Varnish' => [true, true, $tags, Config::VARNISH, 0], 'Cacheable layout with Varnish, Full_cache state is false' => [ false, true, $tags, - \Magento\PageCache\Model\Config::VARNISH, + Config::VARNISH, 0, ], 'Cacheable layout with Varnish and esi' => [ true, true, null, - \Magento\PageCache\Model\Config::VARNISH, + Config::VARNISH, 100, ], - 'Cacheable layout with Builtin' => [true, true, $tags, \Magento\PageCache\Model\Config::BUILT_IN, 0], + 'Cacheable layout with Builtin' => [true, true, $tags, Config::BUILT_IN, 0], 'Cacheable layout with Builtin, Full_cache state is false' => [ false, true, $tags, - \Magento\PageCache\Model\Config::BUILT_IN, + Config::BUILT_IN, 0, ], 'Cacheable layout with Builtin and esi' => [ true, false, $tags, - \Magento\PageCache\Model\Config::BUILT_IN, + Config::BUILT_IN, 100, - ] + ], ]; } } diff --git a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php index 3bb15a2625dbb..5546cf3ef8fba 100644 --- a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php @@ -3,54 +3,51 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Persistent\Model\Layout; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Persistent\Model\Session as PersistentSession; /** - * Class DepersonalizePlugin + * Depersonalize customer data. */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Persistent\Model\Session + * @var PersistentSession */ - protected $persistentSession; + private $persistentSession; /** - * Constructor - * * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Persistent\Model\Session $persistentSession + * @param PersistentSession $persistentSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Persistent\Model\Session $persistentSession + PersistentSession $persistentSession ) { - $this->persistentSession = $persistentSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->persistentSession = $persistentSession; } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml( - \Magento\Framework\View\LayoutInterface $subject, - \Magento\Framework\View\LayoutInterface $result - ) { + public function afterGenerateElements(LayoutInterface $subject) + { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->persistentSession->setCustomerId(null); } - - return $result; } } diff --git a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 9731811ea8a97..633e39fbb63b9 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,117 +3,84 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Persistent\Test\Unit\Model\Layout; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Persistent\Model\Layout\DepersonalizePlugin; +use Magento\Persistent\Model\Session as PersistentSession; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Persistent\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Persistent\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var PersistentSession|MockObject */ - protected $persistentSessionMock; + private $persistentSessionMock; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var DepersonalizePlugin */ - protected $objectManager; + private $plugin; /** - * @var \Magento\Persistent\Model\Layout\DepersonalizePlugin + * @var DepersonalizeChecker|MockObject */ - protected $plugin; + private $depersonalizeCheckerMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $depersonalizeCheckerMock; + private $layoutMock; /** - * Set up - * - * @return void + * @inheritdoc */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->persistentSessionMock = $this->createPartialMock( - \Magento\Persistent\Model\Session::class, - ['setCustomerId'] - ); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['setCustomerId']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $this->moduleManagerMock = $this->createPartialMock(\Magento\Framework\Module\Manager::class, ['isEnabled']); - $this->cacheConfigMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['isEnabled']); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = $this->objectManager->getObject( - \Magento\Persistent\Model\Layout\DepersonalizePlugin::class, + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, [ - 'persistentSession' => $this->persistentSessionMock, 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'persistentSession' => $this->persistentSessionMock, ] ); } - public function testAfterGenerateXml() + /** + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ - $subjectMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - ['isCacheable'] - ); - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ - $resultMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - [] - ); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->persistentSessionMock->expects($this->once())->method('setCustomerId')->with(null); - $this->assertEquals($resultMock, $this->plugin->afterGenerateXml($subjectMock, $resultMock)); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ - $subjectMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - ['isCacheable'] - ); - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ - $resultMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - [] - ); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->persistentSessionMock->expects($this->never())->method('setCustomerId'); - $this->assertEquals($resultMock, $this->plugin->afterGenerateXml($subjectMock, $resultMock)); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php index ad17406c34b75..c19580b873d77 100644 --- a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php @@ -1,85 +1,86 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Tax\Model\Layout; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Customer\Model\Session + * @var CustomerSession */ - protected $customerSession; + private $customerSession; /** * @var array */ - protected $defaultTaxShippingAddress; + private $defaultTaxShippingAddress; /** * @var array */ - protected $defaultTaxBillingAddress; + private $defaultTaxBillingAddress; /** * @var int */ - protected $customerTaxClassId; + private $customerTaxClassId; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Customer\Model\Session $customerSession + * @param CustomerSession $customerSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Customer\Model\Session $customerSession + CustomerSession $customerSession ) { - $this->customerSession = $customerSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->customerSession = $customerSession; } /** - * Before generate Xml + * Resolves sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @return array + * @param LayoutInterface $subject + * @return void */ - public function beforeGenerateXml(\Magento\Framework\View\LayoutInterface $subject) + public function beforeGenerateXml(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->defaultTaxBillingAddress = $this->customerSession->getDefaultTaxBillingAddress(); $this->defaultTaxShippingAddress = $this->customerSession->getDefaultTaxShippingAddress(); $this->customerTaxClassId = $this->customerSession->getCustomerTaxClassId(); } - return []; } /** - * After generate Xml + * Changes sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->customerSession->setDefaultTaxBillingAddress($this->defaultTaxBillingAddress); $this->customerSession->setDefaultTaxShippingAddress($this->defaultTaxShippingAddress); $this->customerSession->setCustomerTaxClassId($this->customerTaxClassId); } - return $result; } } diff --git a/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..46a11731006d0 --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Tax\Test\Unit\Model\Layout; + +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Tax\Model\Layout\DepersonalizePlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit tests for \Magento\Tax\Model\Layout\DepersonalizePlugin class. + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var CustomerSession|MockObject + */ + private $customerSessionMock; + + /** + * @var DepersonalizePlugin + */ + private $plugin; + + /** + * @var DepersonalizeChecker|MockObject + */ + private $depersonalizeCheckerMock; + + /** + * @var LayoutInterface|MockObject + */ + private $layoutMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->customerSessionMock = $this->createPartialMock( + CustomerSession::class, + [ + 'getDefaultTaxBillingAddress', + 'getDefaultTaxShippingAddress', + 'getCustomerTaxClassId', + 'setDefaultTaxBillingAddress', + 'setDefaultTaxShippingAddress', + 'setCustomerTaxClassId', + ] + ); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'customerSession' => $this->customerSessionMock, + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + ] + ); + } + + /** + * Tests beforeGenerateXml method when depersonalization is needed. + * + * @return void + */ + public function testBeforeGenerateXml(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); + $this->customerSessionMock->expects($this->once())->method('getDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->once())->method('getDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->once())->method('getCustomerTaxClassId'); + $this->plugin->beforeGenerateXml($this->layoutMock); + } + + /** + * Tests beforeGenerateXml method when depersonalization is not needed. + * + * @return void + */ + public function testBeforeGenerateXmlNoDepersonalize(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); + $this->customerSessionMock->expects($this->never())->method('getDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->never())->method('getDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->never())->method('getCustomerTaxClassId'); + $this->plugin->beforeGenerateXml($this->layoutMock); + } + + /** + * Tests afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); + $this->customerSessionMock->expects($this->once())->method('setDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->once())->method('setDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->once())->method('setCustomerTaxClassId'); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } + + /** + * Tests afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); + $this->customerSessionMock->expects($this->never())->method('setDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->never())->method('setDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->never())->method('setCustomerTaxClassId'); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } + + /** + * Tests beforeGenerateXml and afterGenerateElements methods. + * + * @return void + */ + public function testBeforeAndAfter(): void + { + $defaultTaxBillingAddress = []; + $defaultTaxShippingAddress = []; + $customerTaxClassId = 1; + + $this->depersonalizeCheckerMock->expects($this->exactly(2)) + ->method('checkIfDepersonalize') + ->willReturn(true); + + $this->customerSessionMock->expects($this->once()) + ->method('getDefaultTaxBillingAddress') + ->willReturn($defaultTaxBillingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('getDefaultTaxShippingAddress') + ->willReturn($defaultTaxShippingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('getCustomerTaxClassId') + ->willReturn($customerTaxClassId); + + $this->plugin->beforeGenerateXml($this->layoutMock); + + $this->customerSessionMock->expects($this->once()) + ->method('setDefaultTaxBillingAddress') + ->with($defaultTaxBillingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('setDefaultTaxShippingAddress') + ->with($defaultTaxShippingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('setCustomerTaxClassId') + ->with($customerTaxClassId); + + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..43b5422dbb90a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Layout; + +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\App\Cache\Type\Layout as LayoutCache; +use Magento\Framework\View\Layout; +use Magento\Framework\View\LayoutFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Integration tests for \Magento\Catalog\Model\Layout\DepersonalizePlugin class. + * + * @magentoAppArea frontend + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var CatalogSession + */ + private $catalogSession; + + /** + * @var Layout + */ + private $layout; + + /** + * @var LayoutCache + */ + private $cache; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->catalogSession = Bootstrap::getObjectManager()->get(CatalogSession::class); + $this->layout = Bootstrap::getObjectManager()->get(LayoutFactory::class)->create(); + $this->cache = Bootstrap::getObjectManager()->get(LayoutCache::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->catalogSession->clearStorage(); + } + + /** + * @magentoCache full_page enabled + * @dataProvider afterGenerateElementsDataProvider + * + * @param string $layout + * @param array $expectedResult + * @return void + */ + public function testAfterGenerateElements(string $layout, array $expectedResult): void + { + $this->cache->clean(); + $this->assertTrue($this->layout->loadFile($layout)); + $this->catalogSession->setData(['some_data' => 1]); + $this->layout->generateElements(); + $this->assertEquals($expectedResult, $this->catalogSession->getData()); + } + + /** + * @return array + */ + public function afterGenerateElementsDataProvider(): array + { + return [ + 'cacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/cacheable.xml', + 'expectedResult' => [], + ], + 'nonCacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable.xml', + 'expectedResult' => ['some_data' => 1], + ], + 'nonCacheableBlockWithoutReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml', + 'expectedResult' => [], + ], + 'nonCacheableBlockWithExistedReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml', + 'expectedResult' => ['some_data' => 1], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml new file mode 100644 index 0000000000000..b99452e3345ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd"> + <container name="cacheable_container1-1" label="test1" htmlTag="div" htmlId="cacheable_container11" + htmlClass="class11"> + <block class="Magento\Framework\View\Element\Text" name="block-1-in-cacheable-container1"> + <action method="setText"> + <argument name="text" xsi:type="string">Test11</argument> + </action> + </block> + </container> + + <referenceContainer name="cacheable_container1-1"> + <block name="non_cacheable_block" cacheable="false"/> + </referenceContainer> +</layout> diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml new file mode 100644 index 0000000000000..2dc6ac25a71bf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd"> + <container name="cacheable_container1-1" label="test1" htmlTag="div" htmlId="cacheable_container11" + htmlClass="class11"> + <block class="Magento\Framework\View\Element\Text" name="block-1-in-cacheable-container1"> + <action method="setText"> + <argument name="text" xsi:type="string">Test11</argument> + </action> + </block> + </container> + + <referenceContainer name="cacheable_container1-1-2"> + <block name="non_cacheable_block" cacheable="false"/> + </referenceContainer> +</layout> diff --git a/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php b/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..96cd5c009d6c3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PageCache\Model\Layout; + +use Magento\Framework\App\Cache\Type\Layout as LayoutCache; +use Magento\Framework\Message\Session; +use Magento\Framework\View\Layout; +use Magento\Framework\View\LayoutFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Integration tests for \Magento\PageCache\Model\Layout\DepersonalizePlugin class. + * + * @magentoAppArea frontend + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var Session + */ + private $messageSession; + + /** + * @var Layout + */ + private $layout; + + /** + * @var LayoutCache + */ + private $cache; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->messageSession = Bootstrap::getObjectManager()->get(Session::class); + $this->layout = Bootstrap::getObjectManager()->get(LayoutFactory::class)->create(); + $this->cache = Bootstrap::getObjectManager()->get(LayoutCache::class); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + $this->messageSession->clearStorage(); + } + + /** + * @magentoCache full_page enabled + * @dataProvider afterGenerateElementsDataProvider + * + * @param string $layout + * @param array $expectedResult + * @return void + */ + public function testAfterGenerateElements(string $layout, array $expectedResult): void + { + $this->cache->clean(); + $this->assertTrue($this->layout->loadFile($layout)); + $this->messageSession->setData(['some_data' => 1]); + $this->layout->generateElements(); + $this->assertEquals($expectedResult, $this->messageSession->getData()); + } + + /** + * @return array + */ + public function afterGenerateElementsDataProvider(): array + { + return [ + 'cacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/cacheable.xml', + 'expectedResult' => [], + ], + 'nonCacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable.xml', + 'expectedResult' => ['some_data' => 1], + ], + 'nonCacheableBlockWithoutReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml', + 'expectedResult' => [], + ], + 'nonCacheableBlockWithExistedReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml', + 'expectedResult' => ['some_data' => 1], + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index a622006f32a1e..c6058887ee66e 100644 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\View; use Magento\Framework\App\ObjectManager; @@ -261,7 +263,7 @@ protected function build() /** * Public build. * - * Will be eliminated in MAGETWO-28359 + * @todo Will be eliminated in MAGETWO-28359 * * @return void */ @@ -1101,15 +1103,24 @@ protected function _prepareMessageGroup($messageGroups) } /** - * Check is exists non-cacheable layout elements + * Checks existed non-cacheable layout elements. * * @return bool */ public function isCacheable() { $this->build(); - $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]')); - return $this->cacheable && $cacheableXml; + $elements = $this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'); + $cacheable = $this->cacheable; + foreach ($elements as $element) { + $blockName = $element->getBlockName(); + if ($blockName !== false && $this->structure->hasElement($blockName)) { + $cacheable = false; + break; + } + } + + return $cacheable; } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index a11d132b193a0..84dd31121a270 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -3,172 +3,193 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\View\Test\Unit; +use Magento\Framework\App\State; +use Magento\Framework\Cache\FrontendInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Event\ManagerInterface as EventManager; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Design\Theme\ResolverInterface; +use Magento\Framework\View\Design\ThemeInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Layout; +use Magento\Framework\View\Layout\Data\Structure as LayoutStructure; +use Magento\Framework\View\Layout\Element; +use Magento\Framework\View\Layout\Generator\Block; +use Magento\Framework\View\Layout\Generator\Container; +use Magento\Framework\View\Layout\Generator\Context; +use Magento\Framework\View\Layout\Generator\ContextFactory; +use Magento\Framework\View\Layout\GeneratorPool; +use Magento\Framework\View\Layout\ProcessorFactory; +use Magento\Framework\View\Layout\Reader\Context as LayoutReaderContext; +use Magento\Framework\View\Layout\Reader\ContextFactory as LayoutReaderContextFactory; +use Magento\Framework\View\Layout\ReaderPool; +use Magento\Framework\View\Layout\ScheduledStructure; +use Magento\Framework\View\Model\Layout\Merge; +use Magento\Framework\View\Page\Config\Structure; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** + * Unit tests for \Magento\Framework\View\Layout class. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LayoutTest extends \PHPUnit\Framework\TestCase +class LayoutTest extends TestCase { /** - * @var \Magento\Framework\View\Layout + * @var Layout */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LayoutStructure|MockObject */ - protected $structureMock; + private $structureMock; /** - * @var \Magento\Framework\View\Layout\ProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ProcessorFactory|MockObject */ - protected $processorFactoryMock; + private $processorFactoryMock; /** - * @var \Magento\Framework\View\Design\Theme\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResolverInterface|MockObject */ - protected $themeResolverMock; + private $themeResolverMock; /** - * @var \Magento\Framework\View\Model\Layout\Merge|\PHPUnit_Framework_MockObject_MockObject + * @var Merge|MockObject */ - protected $processorMock; + private $processorMock; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var EventManager|MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \Magento\Framework\View\Layout\Generator\Block|\PHPUnit_Framework_MockObject_MockObject + * @var Block|MockObject */ - protected $generatorBlockMock; + private $generatorBlockMock; /** - * @var \Magento\Framework\View\Layout\Generator\Container|\PHPUnit_Framework_MockObject_MockObject + * @var Container|MockObject */ - protected $generatorContainerMock; + private $generatorContainerMock; /** - * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject + * @var FrontendInterface|MockObject */ - protected $cacheMock; + private $cacheMock; /** - * @var \Magento\Framework\View\Layout\ReaderPool|\PHPUnit_Framework_MockObject_MockObject + * @var ReaderPool|MockObject */ - protected $readerPoolMock; + private $readerPoolMock; /** - * @var \Magento\Framework\View\Layout\GeneratorPool|\PHPUnit_Framework_MockObject_MockObject + * @var GeneratorPool|MockObject */ - protected $generatorPoolMock; + private $generatorPoolMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ - protected $messageManagerMock; + private $messageManagerMock; /** - * @var \Magento\Framework\View\Layout\Reader\ContextFactory|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutReaderContextFactory|MockObject */ - protected $readerContextFactoryMock; + private $readerContextFactoryMock; /** - * @var \Magento\Framework\View\Layout\Reader\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Layout\Reader\Context|MockObject */ - protected $readerContextMock; + private $readerContextMock; /** - * @var \Magento\Framework\View\Page\Config\Structure|\PHPUnit_Framework_MockObject_MockObject + * @var Structure|MockObject */ private $pageConfigStructure; /** - * @var \Magento\Framework\View\Layout\ScheduledStructure|\PHPUnit_Framework_MockObject_MockObject + * @var ScheduledStructure|MockObject */ private $layoutScheduledSructure; /** - * @var \Magento\Framework\View\Layout\Generator\ContextFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ContextFactory|MockObject */ - protected $generatorContextFactoryMock; + private $generatorContextFactoryMock; /** - * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ - protected $appStateMock; + private $appStateMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ - protected $loggerMock; + private $loggerMock; /** - * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SerializerInterface|MockObject */ private $serializer; /** + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $this->structureMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Data\Structure::class) + $this->structureMock = $this->getMockBuilder(LayoutStructure::class) ->disableOriginalConstructor() ->getMock(); - $this->processorFactoryMock = $this->createPartialMock( - \Magento\Framework\View\Layout\ProcessorFactory::class, - ['create'] - ); - $this->themeResolverMock = $this->getMockForAbstractClass( - \Magento\Framework\View\Design\Theme\ResolverInterface::class - ); - $this->processorMock = $this->createMock(\Magento\Framework\View\Model\Layout\Merge::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->generatorBlockMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Block::class) - ->disableOriginalConstructor()->getMock(); - $this->generatorContainerMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Container::class) - ->disableOriginalConstructor()->getMock(); - $this->cacheMock = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class) + $this->processorFactoryMock = $this->createPartialMock(ProcessorFactory::class, ['create']); + $this->themeResolverMock = $this->getMockForAbstractClass(ResolverInterface::class); + $this->processorMock = $this->createMock(Merge::class); + $this->eventManagerMock = $this->getMockForAbstractClass(EventManager::class); + $this->generatorBlockMock = $this->getMockBuilder(Block::class) ->disableOriginalConstructor() ->getMock(); - $this->readerPoolMock = $this->getMockBuilder(\Magento\Framework\View\Layout\ReaderPool::class) + $this->generatorContainerMock = $this->getMockBuilder(Container::class) ->disableOriginalConstructor() ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + $this->cacheMock = $this->getMockForAbstractClass(FrontendInterface::class); + $this->readerPoolMock = $this->getMockBuilder(ReaderPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->messageManagerMock = $this->getMockForAbstractClass(ManagerInterface::class); + $this->generatorPoolMock = $this->getMockBuilder(GeneratorPool::class) ->disableOriginalConstructor() ->getMock(); - - $this->generatorPoolMock = $this->getMockBuilder(\Magento\Framework\View\Layout\GeneratorPool::class) - ->disableOriginalConstructor()->getMock(); $this->generatorPoolMock->expects($this->any()) ->method('getGenerator') - ->will( - $this->returnValueMap( - [ - [\Magento\Framework\View\Layout\Generator\Block::TYPE, $this->generatorBlockMock], - [\Magento\Framework\View\Layout\Generator\Container::TYPE, $this->generatorContainerMock], - ] - ) + ->willReturnMap( + [ + [Block::TYPE, $this->generatorBlockMock], + [Container::TYPE, $this->generatorContainerMock], + ] ); - - $this->readerContextFactoryMock = $this->getMockBuilder( - \Magento\Framework\View\Layout\Reader\ContextFactory::class - )->disableOriginalConstructor()->getMock(); - - $this->pageConfigStructure = $this->getMockBuilder(\Magento\Framework\View\Page\Config\Structure::class) + $this->readerContextFactoryMock = $this->getMockBuilder(LayoutReaderContextFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pageConfigStructure = $this->getMockBuilder(Structure::class) ->setMethods(['__toArray', 'populateWithArray']) ->getMock(); - $this->layoutScheduledSructure = $this->getMockBuilder(\Magento\Framework\View\Layout\ScheduledStructure::class) + $this->layoutScheduledSructure = $this->getMockBuilder(ScheduledStructure::class) ->setMethods(['__toArray', 'populateWithArray']) ->getMock(); - $this->readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) + $this->readerContextMock = $this->getMockBuilder(LayoutReaderContext::class) ->setMethods(['getPageConfigStructure', 'getScheduledStructure']) ->disableOriginalConstructor() ->getMock(); @@ -176,125 +197,135 @@ protected function setUp() ->willReturn($this->pageConfigStructure); $this->readerContextMock->expects($this->any())->method('getScheduledStructure') ->willReturn($this->layoutScheduledSructure); - - $this->generatorContextFactoryMock = $this->getMockBuilder( - \Magento\Framework\View\Layout\Generator\ContextFactory::class - ) + $this->generatorContextFactoryMock = $this->getMockBuilder(ContextFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->appStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $this->appStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) - ->getMock(); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\SerializerInterface::class); + $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + $this->serializer = $this->getMockForAbstractClass(SerializerInterface::class); $this->serializer->expects($this->any())->method('serialize') ->willReturnCallback( function ($value) { return json_encode($value); } ); - $this->serializer->expects( - $this->any() - )->method('unserialize') + $this->serializer->expects($this->any())->method('unserialize') ->willReturnCallback( function ($value) { return json_decode($value, true); } ); - $this->model = new \Magento\Framework\View\Layout( - $this->processorFactoryMock, - $this->eventManagerMock, - $this->structureMock, - $this->messageManagerMock, - $this->themeResolverMock, - $this->readerPoolMock, - $this->generatorPoolMock, - $this->cacheMock, - $this->readerContextFactoryMock, - $this->generatorContextFactoryMock, - $this->appStateMock, - $this->loggerMock, - true, - $this->serializer + $this->model = (new ObjectManagerHelper($this))->getObject( + Layout::class, + [ + 'processorFactory' => $this->processorFactoryMock, + 'eventManager' => $this->eventManagerMock, + 'structure' => $this->structureMock, + 'messageManager' => $this->messageManagerMock, + 'themeResolver' => $this->themeResolverMock, + 'readerPool' => $this->readerPoolMock, + 'generatorPool' => $this->generatorPoolMock, + 'cache' => $this->cacheMock, + 'readerContextFactory' => $this->readerContextFactoryMock, + 'generatorContextFactory' => $this->generatorContextFactoryMock, + 'appState' => $this->appStateMock, + 'logger' => $this->loggerMock, + 'cacheable' => true, + 'serializer' => $this->serializer, + ] ); } - public function testCreateBlockSuccess() + /** + * @return void + */ + public function testCreateBlockSuccess(): void { - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('createStructuralElement') ->with( 'blockname', - \Magento\Framework\View\Layout\Element::TYPE_BLOCK, - \Magento\Framework\View\Element\AbstractBlock::class - )->willReturn('blockname'); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + Element::TYPE_BLOCK, + AbstractBlock::class + ) + ->willReturn('blockname'); + $this->generatorBlockMock->expects($this->once())->method('createBlock')->willReturn($blockMock); - $this->model->createBlock(\Magento\Framework\View\Element\AbstractBlock::class, 'blockname', []); + $this->model->createBlock(AbstractBlock::class, 'blockname', []); $this->assertInstanceOf( - \Magento\Framework\View\Element\AbstractBlock::class, + AbstractBlock::class, $this->model->getBlock('blockname') ); $this->assertFalse($this->model->getBlock('not_exist')); } - public function testGetUpdate() + /** + * @return void + */ + public function testGetUpdate(): void { - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') - ->will($this->returnValue($themeMock)); + ->willReturn($themeMock); $this->processorFactoryMock->expects($this->once()) ->method('create') ->with(['theme' => $themeMock]) - ->will($this->returnValue($this->processorMock)); + ->willReturn($this->processorMock); $this->assertEquals($this->processorMock, $this->model->getUpdate()); $this->assertEquals($this->processorMock, $this->model->getUpdate()); } - public function testGenerateXml() + /** + * @return void + */ + public function testGenerateXml(): void { - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') - ->will($this->returnValue($themeMock)); + ->willReturn($themeMock); $this->processorFactoryMock->expects($this->once()) ->method('create') ->with(['theme' => $themeMock]) - ->will($this->returnValue($this->processorMock)); + ->willReturn($this->processorMock); $xmlString = '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . '<some_update>123</some_update></layout>'; - $xml = simplexml_load_string($xmlString, \Magento\Framework\View\Layout\Element::class); + $xml = simplexml_load_string($xmlString, Element::class); $this->processorMock->expects($this->once()) ->method('asSimplexml') - ->will($this->returnValue($xml)); + ->willReturn($xml); $this->structureMock->expects($this->once()) ->method('importElements') ->with($this->equalTo([])) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->generateXml()); $this->assertSame('<some_update>123</some_update>', $this->model->getNode('some_update')->asXML()); } - public function testGetChildBlock() + /** + * @return void + */ + public function testGetChildBlock(): void { $customBlockName = 'custom_block'; $customBlockParentName = 'custom_block_parent'; $customBlockAlias = 'custom_block_alias'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template::class) + $blockMock = $this->getMockBuilder(Template::class) ->disableOriginalConstructor() ->getMock(); @@ -311,11 +342,11 @@ public function testGetChildBlock() $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($customBlockName, 'type') - ->willReturn(\Magento\Framework\View\Layout\Element::TYPE_BLOCK); + ->willReturn(Element::TYPE_BLOCK); $this->model->setBlock($customBlockName, $blockMock); $this->assertInstanceOf( - \Magento\Framework\View\Element\AbstractBlock::class, + AbstractBlock::class, $this->model->getChildBlock($customBlockParentName, $customBlockAlias) ); } @@ -329,7 +360,10 @@ public function testGetChildNonExistBlock() $this->assertFalse($this->model->getChildBlock('non_exist_parent', 'non_exist_alias')); } - public function testSetChild() + /** + * @return void + */ + public function testSetChild(): void { $elementName = 'child'; $parentName = 'parent'; @@ -337,104 +371,131 @@ public function testSetChild() $this->structureMock->expects($this->once()) ->method('setAsChild') ->with($this->equalTo($elementName), $this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->setChild($parentName, $elementName, $alias)); } - public function testUnsetChild() + /** + * @return void + */ + public function testUnsetChild(): void { $parentName = 'parent'; $alias = 'some_alias'; $this->structureMock->expects($this->once()) ->method('unsetChild') ->with($this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->unsetChild($parentName, $alias)); } - public function testGetChildNames() + /** + * @return void + */ + public function testGetChildNames(): void { $parentName = 'parent'; $childrenArray = ['key1' => 'value1', 'key2' => 'value2']; $this->structureMock->expects($this->once()) ->method('getChildren') ->with($this->equalTo($parentName)) - ->will($this->returnValue($childrenArray)); + ->willReturn($childrenArray); $this->assertSame(['key1', 'key2'], $this->model->getChildNames($parentName)); } - public function testGetChildBlocks() + /** + * @return void + */ + public function testGetChildBlocks(): void { $parentName = 'parent'; $childrenArray = ['block_name' => 'value1']; $this->structureMock->expects($this->once()) ->method('getChildren') ->with($this->equalTo($parentName)) - ->will($this->returnValue($childrenArray)); + ->willReturn($childrenArray); - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('createStructuralElement') ->with( 'block_name', - \Magento\Framework\View\Layout\Element::TYPE_BLOCK, - \Magento\Framework\View\Element\AbstractBlock::class - )->willReturn('block_name'); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + Element::TYPE_BLOCK, + AbstractBlock::class + ) + ->willReturn('block_name'); + $this->generatorBlockMock->expects($this->once()) + ->method('createBlock') + ->willReturn($blockMock); $this->assertSame( $blockMock, - $this->model->createBlock(\Magento\Framework\View\Element\AbstractBlock::class, 'block_name', []) + $this->model->createBlock(AbstractBlock::class, 'block_name', []) ); $this->assertSame(['value1' => $blockMock], $this->model->getChildBlocks($parentName)); } - public function testGetChildName() + /** + * @return void + */ + public function testGetChildName(): void { $parentName = 'parent'; $alias = 'some_alias'; $this->structureMock->expects($this->once()) ->method('getChildId') ->with($this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnValue('1')); + ->willReturn('1'); $this->assertSame('1', $this->model->getChildName($parentName, $alias)); } - public function testAddToParentGroup() + /** + * @return void + */ + public function testAddToParentGroup(): void { $blockName = 'block_name'; $parentGroup = 'parent_group'; $this->structureMock->expects($this->once()) ->method('addToParentGroup') ->with($this->equalTo($blockName), $this->equalTo($parentGroup)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->structureMock, $this->model->addToParentGroup($blockName, $parentGroup)); } - public function testGetGroupChildNames() + /** + * @return void + */ + public function testGetGroupChildNames(): void { $blockName = 'block_name'; $groupName = 'group_name'; $this->structureMock->expects($this->once()) ->method('getGroupChildNames') ->with($this->equalTo($blockName), $this->equalTo($groupName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->structureMock, $this->model->getGroupChildNames($blockName, $groupName)); } - public function testHasElement() + /** + * @return void + */ + public function testHasElement(): void { $elementName = 'name'; $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($elementName)) - ->will($this->returnValue(true)); + ->willReturn(true); $this->assertTrue($this->model->hasElement($elementName)); } - public function testGetElementProperty() + /** + * @return void + */ + public function testGetElementProperty(): void { $elementName = 'name'; $elementAttr = 'attribute'; @@ -442,7 +503,7 @@ public function testGetElementProperty() $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($elementName), $this->equalTo($elementAttr)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->model->getElementProperty($elementName, $elementAttr)); } @@ -450,20 +511,21 @@ public function testGetElementProperty() * @param bool $hasElement * @param string $attribute * @param bool $result + * @return void * @dataProvider isContainerDataProvider */ - public function testIsContainer($hasElement, $attribute, $result) + public function testIsContainer($hasElement, $attribute, $result): void { $elementName = 'element_name'; $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($elementName)) - ->will($this->returnValue($hasElement)); + ->willReturn($hasElement); if ($hasElement) { $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($elementName), $this->equalTo('type')) - ->will($this->returnValue($attribute)); + ->willReturn($attribute); } $this->assertSame($result, $this->model->isContainer($elementName)); } @@ -471,7 +533,7 @@ public function testIsContainer($hasElement, $attribute, $result) /** * @return array */ - public function isContainerDataProvider() + public function isContainerDataProvider(): array { return [ [false, '', false], @@ -485,25 +547,26 @@ public function isContainerDataProvider() * @param bool $parentName * @param array $containerConfig * @param bool $result + * @return void * @dataProvider isManipulationAllowedDataProvider */ - public function testIsManipulationAllowed($parentName, $containerConfig, $result) + public function testIsManipulationAllowed($parentName, $containerConfig, $result): void { $elementName = 'element_name'; $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($elementName)) - ->will($this->returnValue($parentName)); + ->willReturn($parentName); if ($parentName) { $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($parentName)) - ->will($this->returnValue($containerConfig['has_element'])); + ->willReturn($containerConfig['has_element']); if ($containerConfig['has_element']) { $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($parentName), $this->equalTo('type')) - ->will($this->returnValue($containerConfig['attribute'])); + ->willReturn($containerConfig['attribute']); } } @@ -513,7 +576,7 @@ public function testIsManipulationAllowed($parentName, $containerConfig, $result /** * @return array */ - public function isManipulationAllowedDataProvider() + public function isManipulationAllowedDataProvider(): array { return [ ['parent', ['has_element' => true, 'attribute' => 'container'], true], @@ -526,11 +589,12 @@ public function isManipulationAllowedDataProvider() * @covers \Magento\Framework\View\Layout::setBlock * @covers \Magento\Framework\View\Layout::getAllBlocks * @covers \Magento\Framework\View\Layout::unsetElement + * @return void */ - public function testSetGetBlock() + public function testSetGetBlock(): void { $blockName = 'some_name'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->assertSame($this->model, $this->model->setBlock($blockName, $blockMock)); @@ -538,40 +602,49 @@ public function testSetGetBlock() $this->structureMock->expects($this->once()) ->method('unsetElement') ->with($this->equalTo($blockName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->unsetElement($blockName)); $this->assertSame([], $this->model->getAllBlocks()); } - public function testRenameElement() + /** + * @return void + */ + public function testRenameElement(): void { $oldName = 'old_name'; $newName = 'new_name'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('renameElement') ->with($this->equalTo($oldName), $this->equalTo($newName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->setBlock($oldName, $blockMock)); $this->assertSame($this->model, $this->model->renameElement($oldName, $newName)); $this->assertSame([$newName => $blockMock], $this->model->getAllBlocks()); } - public function testGetParentName() + /** + * @return void + */ + public function testGetParentName(): void { $childName = 'child_name'; $parentId = 'parent_id'; $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($childName)) - ->will($this->returnValue($parentId)); + ->willReturn($parentId); $this->assertSame($parentId, $this->model->getParentName($childName)); } - public function testGetElementAlias() + /** + * @return void + */ + public function testGetElementAlias(): void { $name = 'child_name'; $parentId = 'parent_id'; @@ -579,21 +652,27 @@ public function testGetElementAlias() $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($name)) - ->will($this->returnValue($parentId)); + ->willReturn($parentId); $this->structureMock->expects($this->once()) ->method('getChildAlias') ->with($this->equalTo($parentId), $this->equalTo($name)) - ->will($this->returnValue($alias)); + ->willReturn($alias); $this->assertSame($alias, $this->model->getElementAlias($name)); } - public function testAddRemoveOutputElement() + /** + * @return void + */ + public function testAddRemoveOutputElement(): void { $this->assertSame($this->model, $this->model->addOutputElement('name')); $this->assertSame($this->model, $this->model->removeOutputElement('name')); } - public function testIsPrivate() + /** + * @return void + */ + public function testIsPrivate(): void { $this->assertFalse($this->model->isPrivate()); $this->assertSame($this->model, $this->model->setIsPrivate(true)); @@ -603,18 +682,19 @@ public function testIsPrivate() /** * @param array $type * @param array $blockInstance + * @return void * @dataProvider getBlockSingletonDataProvider */ - public function testGetBlockSingleton($type, $blockInstance, $isAbstract) + public function testGetBlockSingleton($type, $blockInstance, $isAbstract): void { $blockMock = $this->createMock($blockInstance); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + $this->generatorBlockMock->expects($this->once())->method('createBlock')->willReturn($blockMock); if ($isAbstract) { $blockMock->expects($this->any()) ->method('setLayout') ->with($this->equalTo($this->model)) - ->will($this->returnSelf()); + ->willReturnSelf(); } $this->assertInstanceOf($blockInstance, $this->model->getBlockSingleton($type)); // singleton test @@ -624,11 +704,12 @@ public function testGetBlockSingleton($type, $blockInstance, $isAbstract) /** * @return array */ - public function getBlockSingletonDataProvider() + public function getBlockSingletonDataProvider(): array { return [ [ - 'some_type', \Magento\Framework\View\Element\Template::class, + 'some_type', + Template::class, true, ], ]; @@ -638,9 +719,10 @@ public function getBlockSingletonDataProvider() * @param array $rendererData * @param array $getData * @param bool $result + * @return void * @dataProvider getRendererOptionsDataProvider */ - public function testAddGetRendererOptions($rendererData, $getData, $result) + public function testAddGetRendererOptions($rendererData, $getData, $result): void { $this->assertSame( $this->model, @@ -662,7 +744,7 @@ public function testAddGetRendererOptions($rendererData, $getData, $result) /** * @return array */ - public function getRendererOptionsDataProvider() + public function getRendererOptionsDataProvider(): array { $rendererData = [ 'namespace' => 'namespace_value', @@ -718,47 +800,94 @@ public function getRendererOptionsDataProvider() /** * @param string $xmlString - * @param bool $result + * @param string $blockName + * @param bool $hasElement + * @param bool $cacheable + * @return void * @dataProvider isCacheableDataProvider */ - public function testIsCacheable($xmlString, $result) + public function testIsCacheable(string $xmlString, string $blockName, bool $hasElement, bool $cacheable): void { - $xml = simplexml_load_string($xmlString, \Magento\Framework\View\Layout\Element::class); - $this->assertSame($this->model, $this->model->setXml($xml)); - $this->assertSame($result, $this->model->isCacheable()); + $this->structureMock->method('hasElement')->with($blockName)->willReturn($hasElement); + + $this->assertTrue($this->model->loadString($xmlString)); + $this->assertSame($cacheable, $this->model->isCacheable()); } /** * @return array */ - public function isCacheableDataProvider() + public function isCacheableDataProvider(): array { return [ - [ - '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' - . '<block></block></layout>', - true, + 'blockWithoutName' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<block></block></layout>', + 'blockName' => '', + 'hasElement' => true, + 'cacheable' => true, ], - [ - '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' - . '<block cacheable="false"></block></layout>', - false + 'notCacheableBlockWithoutName' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<block cacheable="false"></block></layout>', + 'blockName' => '', + 'hasElement' => true, + 'cacheable' => true, + ], + 'notCacheableBlockWithMissingBlockReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceBlock name="not_existing_block">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceBlock></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => false, + 'cacheable' => true, + ], + 'notCacheableBlockWithMissingContainerReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceContainer name="not_existing_container">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceContainer></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => false, + 'cacheable' => true, + ], + 'notCacheableBlockWithExistingBlockReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceBlock name="existing_block">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceBlock></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => true, + 'cacheable' => false, + ], + 'notCacheableBlockWithExistingContainerReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceContainer name="existing_container">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceContainer></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => true, + 'cacheable' => false, ], ]; } - public function testGenerateElementsWithoutCache() + /** + * @return void + */ + public function testGenerateElementsWithoutCache(): void { $this->readerContextFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->readerContextMock); $layoutCacheId = 'layout_cache_id'; $handles = ['default', 'another']; - /** @var \Magento\Framework\View\Layout\Element $xml */ - $xml = simplexml_load_string('<layout/>', \Magento\Framework\View\Layout\Element::class); + /** @var Element $xml */ + $xml = simplexml_load_string('<layout/>', Element::class); $this->model->setXml($xml); - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') ->willReturn($themeMock); @@ -791,20 +920,22 @@ public function testGenerateElementsWithoutCache() 'field_3_1' => '1244', 'field_3_2' => null, 'field_3_3' => false, - ] + ], ]; - $this->pageConfigStructure->expects($this->any())->method('__toArray') + $this->pageConfigStructure->expects($this->any()) + ->method('__toArray') ->willReturn($pageConfigStructureData); $layoutScheduledStructureData = [ 'field_1' => 1283, - 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + 'field_2' => 'text_qwertyuiop[]asdfghjkl;', ]; - $this->layoutScheduledSructure->expects($this->any())->method('__toArray') + $this->layoutScheduledSructure->expects($this->any()) + ->method('__toArray') ->willReturn($layoutScheduledStructureData); $data = [ 'pageConfigStructure' => $pageConfigStructureData, - 'scheduledStructure' => $layoutScheduledStructureData + 'scheduledStructure' => $layoutScheduledStructureData, ]; $this->cacheMock->expects($this->once()) @@ -812,7 +943,7 @@ public function testGenerateElementsWithoutCache() ->with(json_encode($data), 'structure_' . $layoutCacheId, $handles) ->willReturn(true); - $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) + $generatorContextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); $this->generatorContextFactoryMock->expects($this->once()) @@ -827,9 +958,9 @@ public function testGenerateElementsWithoutCache() $elements = [ 'name_1' => ['type' => '', 'parent' => null], - 'name_2' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => null], + 'name_2' => ['type' => Element::TYPE_CONTAINER, 'parent' => null], 'name_3' => ['type' => '', 'parent' => 'parent'], - 'name_4' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => 'parent'], + 'name_4' => ['type' => Element::TYPE_CONTAINER, 'parent' => 'parent'], ]; $this->structureMock->expects($this->once()) @@ -839,17 +970,20 @@ public function testGenerateElementsWithoutCache() $this->model->generateElements(); } - public function testGenerateElementsWithCache() + /** + * @return void + */ + public function testGenerateElementsWithCache(): void { $layoutCacheId = 'layout_cache_id'; - /** @var \Magento\Framework\View\Layout\Element $xml */ - $xml = simplexml_load_string('<layout/>', \Magento\Framework\View\Layout\Element::class); + /** @var Element $xml */ + $xml = simplexml_load_string('<layout/>', Element::class); $this->model->setXml($xml); $this->readerContextFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->readerContextMock); - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') ->willReturn($themeMock); @@ -871,18 +1005,20 @@ public function testGenerateElementsWithCache() 'field_3_3' => false, ] ]; - $this->pageConfigStructure->expects($this->once())->method('populateWithArray') + $this->pageConfigStructure->expects($this->once()) + ->method('populateWithArray') ->with($pageConfigStructureData); $layoutScheduledStructureData = [ 'field_1' => 1283, - 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + 'field_2' => 'text_qwertyuiop[]asdfghjkl;', ]; - $this->layoutScheduledSructure->expects($this->once())->method('populateWithArray') + $this->layoutScheduledSructure->expects($this->once()) + ->method('populateWithArray') ->with($layoutScheduledStructureData); $data = [ 'pageConfigStructure' => $pageConfigStructureData, - 'scheduledStructure' => $layoutScheduledStructureData + 'scheduledStructure' => $layoutScheduledStructureData, ]; $this->cacheMock->expects($this->once()) @@ -895,7 +1031,7 @@ public function testGenerateElementsWithCache() $this->cacheMock->expects($this->never()) ->method('save'); - $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) + $generatorContextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); $this->generatorContextFactoryMock->expects($this->once()) @@ -910,9 +1046,9 @@ public function testGenerateElementsWithCache() $elements = [ 'name_1' => ['type' => '', 'parent' => null], - 'name_2' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => null], + 'name_2' => ['type' => Element::TYPE_CONTAINER, 'parent' => null], 'name_3' => ['type' => '', 'parent' => 'parent'], - 'name_4' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => 'parent'], + 'name_4' => ['type' => Element::TYPE_CONTAINER, 'parent' => 'parent'], ]; $this->structureMock->expects($this->once()) @@ -922,17 +1058,21 @@ public function testGenerateElementsWithCache() $this->model->generateElements(); } - public function testGetXml() + /** + * @return void + */ + public function testGetXml(): void { $xml = '<layout/>'; - $this->assertSame($xml, \Magento\Framework\View\Layout::LAYOUT_NODE); + $this->assertSame($xml, Layout::LAYOUT_NODE); } /** * @param mixed $displayValue + * @return void * @dataProvider renderElementDisplayDataProvider */ - public function testRenderElementDisplay($displayValue) + public function testRenderElementDisplay($displayValue): void { $name = 'test_container'; $child = 'child_block'; @@ -945,14 +1085,14 @@ public function testRenderElementDisplay($displayValue) [ [$name, 'display', $displayValue], [$child, 'display', $displayValue], - [$child, 'type', \Magento\Framework\View\Layout\Element::TYPE_BLOCK] + [$child, 'type', Element::TYPE_BLOCK], ] ); $this->structureMock->expects($this->atLeastOnce())->method('hasElement') ->willReturnMap( [ - [$child, true] + [$child, true], ] ); @@ -961,10 +1101,10 @@ public function testRenderElementDisplay($displayValue) ->with($name) ->willReturn($children); - $block = $this->createMock(\Magento\Framework\View\Element\AbstractBlock::class); + $block = $this->createMock(AbstractBlock::class); $block->expects($this->once())->method('toHtml')->willReturn($blockHtml); - $renderingOutput = new \Magento\Framework\DataObject(); + $renderingOutput = new DataObject(); $renderingOutput->setData('output', $blockHtml); $this->eventManagerMock->expects($this->at(0)) @@ -990,7 +1130,6 @@ public function testRenderElementDisplay($displayValue) */ public function testRenderElementDoNotDisplay($displayValue) { - $displayValue = 'false'; $name = 'test_container'; $blockHtml = ''; @@ -1004,19 +1143,19 @@ public function testRenderElementDoNotDisplay($displayValue) /** * @return array */ - public function renderElementDoNotDisplayDataProvider() + public function renderElementDoNotDisplayDataProvider(): array { return [ ['false'], ['0'], - [0] + [0], ]; } /** * @return array */ - public function renderElementDisplayDataProvider() + public function renderElementDisplayDataProvider(): array { return [ [true], @@ -1024,7 +1163,7 @@ public function renderElementDisplayDataProvider() [1], ['true'], [false], - [null] + [null], ]; } } From 2439e17df3ec42440f25efad2e2b2a7d3be0de5e Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 24 Feb 2020 23:22:11 +0100 Subject: [PATCH 204/734] #26986 Add missing PageSize to the ElasticSearch results. --- .../ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php index ce88fc290e23c..4519bfbe18530 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php @@ -80,6 +80,7 @@ public function resolve(): SearchCriteria $searchCriteria->setRequestName($this->searchRequestName); $searchCriteria->setSortOrders($this->orders); $searchCriteria->setCurrentPage($this->currentPage - 1); + $searchCriteria->setPageSize($this->size); return $searchCriteria; } From 14ea3358e08c26403d1a322f270daa55a4d2e033 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 00:52:08 +0100 Subject: [PATCH 205/734] Add missing Copyright --- .../AssertStorefrontNoProductsFoundActionGroup.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml index 422387313390d..f0a4faeb68e4b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml @@ -1,5 +1,10 @@ <?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="AssertStorefrontNoProductsFoundActionGroup"> From 0fe9cf207bc902350d5ebfc4b5a665679db6edc0 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 01:06:30 +0100 Subject: [PATCH 206/734] #26986 Fix failing Functional Test: Do not display repeated products --- ...frontProductQuickSearchUsingElasticSearch6Test.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml index e763df7dd3227..09206c58734fc 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -30,6 +30,8 @@ <field key="name">Product Simple AAA</field> </createData> <magentoCLI command="config:set {{CustomGridPerPageValuesConfigData.path}} {{CustomGridPerPageValuesConfigData.value}}" stepKey="setCustomGridPerPageValues"/> + <magentoCLI stepKey="flushConfigCache" command="cache:flush config"/> + <magentoCron groups="index" stepKey="runCronIndex"/> </before> <after> @@ -57,11 +59,8 @@ </actionGroup> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> - <argument name="product" value="$createFirstProduct$"/> - </actionGroup> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> - <argument name="product" value="$createSecondProduct$"/> - </actionGroup> + + <seeInCurrentUrl stepKey="assertStillOnSecondPage" url="?p=2"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> </test> </tests> From dab0cda47e62d8b519fcac798f9221066615d7ec Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 03:13:32 +0100 Subject: [PATCH 207/734] Fix failing test on ElasticSearch Suite --- .../StorefrontProductQuickSearchUsingElasticSearch6Test.xml | 3 +-- ...tQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml index 09206c58734fc..5f615c9d64f56 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -59,8 +59,7 @@ </actionGroup> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - - <seeInCurrentUrl stepKey="assertStillOnSecondPage" url="?p=2"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> </test> </tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml index 40e32c93c7ed6..90d736078b97c 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml @@ -26,10 +26,9 @@ <remove keyForRemoval="assertFirstProductDisplayedOnCatalogSearchPage"/> <remove keyForRemoval="assertSecondProductDisplayedOnCatalogSearchPage"/> <grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfLastPage"/> - <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage"> + <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage" before="assertEmptyResultsPage"> <argument name="phrase" value="AAA"/> <argument name="pageNumber" value="999"/> </actionGroup> - <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> </test> </tests> From 0f9452b30291f34b2b4aa8c26c07512c1aa70deb Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 03:31:30 +0100 Subject: [PATCH 208/734] Add missing filtering of Grid by e-mail address --- .../Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml index ebbd9ce469587..87c612db08698 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml @@ -15,9 +15,13 @@ <arguments> <argument name="customerEmail"/> </arguments> - + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomersPage"/> <conditionalClick selector="{{AdminCustomerFiltersSection.clearAll}}" dependentSelector="{{AdminCustomerFiltersSection.clearAll}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForFiltersClear"/> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilters"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{customerEmail}}" stepKey="fillEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="clickApplyFilters"/> <click stepKey="chooseCustomer" selector="{{AdminCustomerGridMainActionsSection.customerCheckbox(customerEmail)}}"/> <click stepKey="openActions" selector="{{AdminCustomerGridMainActionsSection.actions}}"/> <waitForPageLoad stepKey="waitActions"/> From eabd0fd5d3f155dac0127a268f4f1bd2f70b611d Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Tue, 25 Feb 2020 13:35:46 +0200 Subject: [PATCH 209/734] magento/magento2#26742: graphql mutation setShippingMethodsOnCart get wrong data in available_shipping_methods. --- .../AvailableShippingMethods.php | 3 ++- .../Customer/SetShippingMethodsOnCartTest.php | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php index eebed5aab6cc9..430019c7b4690 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php @@ -53,7 +53,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" values should be specified')); } - $address = $value['model']; + $address = clone $value['model']; + $address->setLimitCarrier(null); // Allow shipping rates by setting country id for new addresses if (!$address->getCountryId() && $address->getCountryCode()) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index 293bfdaf502d9..860785dd31dd2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -39,6 +39,7 @@ protected function setUp() } /** + * @magentoConfigFixture default_store carriers/freeshipping/active 1 * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php @@ -64,7 +65,17 @@ public function testSetShippingMethodOnCartWithSimpleProduct() self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['shipping_addresses']); $shippingAddress = current($response['setShippingMethodsOnCart']['cart']['shipping_addresses']); + $availableShippingMethods = $shippingAddress['available_shipping_methods']; + self::assertArrayHasKey('selected_shipping_method', $shippingAddress); + self::assertArrayHasKey('available_shipping_methods', $shippingAddress); + + self::assertCount(2, $availableShippingMethods); + self::assertEquals('freeshipping', $availableShippingMethods[0]['carrier_code']); + self::assertEquals($carrierCode, $availableShippingMethods[1]['carrier_code']); + + self::assertEquals($availableShippingMethods[0]['amount']['value'], 0); + self::assertEquals($availableShippingMethods[1]['amount']['value'], 10); self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']); self::assertEquals('flatrate', $shippingAddress['selected_shipping_method']['carrier_code']); @@ -146,7 +157,7 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ $query = <<<QUERY mutation { setShippingMethodsOnCart(input: { - {$input} + {$input} }) { cart { shipping_addresses { @@ -237,7 +248,7 @@ public function testSetMultipleShippingMethods() $query = <<<QUERY mutation { setShippingMethodsOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", shipping_methods: [ { carrier_code: "flatrate" @@ -329,9 +340,9 @@ private function getQuery( ): string { return <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$shippingCarrierCode" method_code: "$shippingMethodCode" @@ -349,6 +360,12 @@ private function getQuery( currency } } + available_shipping_methods { + amount{ + value + } + carrier_code + } } } } From c403c52ac09373dded105ec0c5d08772730df7b6 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 12:46:09 +0100 Subject: [PATCH 210/734] Fix failing Functional Tests --- .../StorefrontProductQuickSearchUsingElasticSearch6Test.xml | 1 + ...uickSearchUsingElasticSearch6WithNotAvailablePageTest.xml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml index 5f615c9d64f56..0905ac0e3a70f 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -60,6 +60,7 @@ <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInCurrentUrl stepKey="assertStillOnSecondPage" url="?p=2"/> <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> </test> </tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml index 90d736078b97c..1dceb21a7c95c 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml @@ -25,8 +25,9 @@ <remove keyForRemoval="selectDisplayedProductInGridPerPage"/> <remove keyForRemoval="assertFirstProductDisplayedOnCatalogSearchPage"/> <remove keyForRemoval="assertSecondProductDisplayedOnCatalogSearchPage"/> - <grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfLastPage"/> - <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage" before="assertEmptyResultsPage"> + <remove keyForRemoval="selectDisplayedProductInGridPerPage"/> + <remove keyForRemoval="assertStillOnSecondPage"/> + <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage" before="waitForPageLoad"> <argument name="phrase" value="AAA"/> <argument name="pageNumber" value="999"/> </actionGroup> From d7cdd7bf6f397119c83814d412ed02f8702cb7fb Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Tue, 25 Feb 2020 14:06:44 +0100 Subject: [PATCH 211/734] I have no idea why it should work --- .../Fulltext/Collection/SearchCriteriaResolver.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php index 4519bfbe18530..64ce76c729334 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php @@ -80,7 +80,9 @@ public function resolve(): SearchCriteria $searchCriteria->setRequestName($this->searchRequestName); $searchCriteria->setSortOrders($this->orders); $searchCriteria->setCurrentPage($this->currentPage - 1); - $searchCriteria->setPageSize($this->size); + if ($this->size) { + $searchCriteria->setPageSize($this->size); + } return $searchCriteria; } From 0f92be1c21bdab12be3f588611b32754792aeccc Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Tue, 25 Feb 2020 15:32:42 +0000 Subject: [PATCH 212/734] Fixed URL Rewrite generation and removal on product/website change. Moved scope of ProductToWebsiteChangeObserver to base instead of adminhtml as this functionality has been moved to a consumer. With the observer set to adminhtml, this observer was not being called properly on changing the websites for a particular product in the consumer. Corrected ProductProcessUrlRewriteSavingObserver to correctly add and remove rewrite URLs when products are added and removed from websites using the Product Edit Admin Page. Corrected ProductToWebsiteChangeObserver to correctly add and remove rewrite URLs when products are added and removed using the Products Grid Mass Action function. Extended unit tests to cover both Observers. Fixed MFTF test for Product URL Rewrite generation. --- .../Magento/Catalog/Model/Product/Action.php | 10 +- ...ProductProcessUrlRewriteSavingObserver.php | 44 ++- .../ProductToWebsiteChangeObserver.php | 41 +-- ...uctProcessUrlRewriteSavingObserverTest.php | 146 +++++++--- .../ProductToWebsiteChangeObserverTest.php | 271 ++++++++++++++++++ .../etc/adminhtml/events.xml | 12 - .../Magento/CatalogUrlRewrite/etc/events.xml | 3 + .../Model/ProductUrlRewriteTest.php | 3 + 8 files changed, 465 insertions(+), 65 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php delete mode 100644 app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml diff --git a/app/code/Magento/Catalog/Model/Product/Action.php b/app/code/Magento/Catalog/Model/Product/Action.php index 3863cf2457247..aba82e458a693 100644 --- a/app/code/Magento/Catalog/Model/Product/Action.php +++ b/app/code/Magento/Catalog/Model/Product/Action.php @@ -169,6 +169,14 @@ public function updateWebsites($productIds, $websiteIds, $type) $categoryIndexer->reindexList(array_unique($productIds)); } - $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); + //Dispatch event to update Rewrite URLs for new/removed websites + $this->_eventManager->dispatch( + 'catalog_product_to_website_change', + [ + 'products' => $productIds, + 'website_ids' => $websiteIds, + 'action_type' => $type + ] + ); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 6eda8dd0b61ee..ded4bfbaa04ed 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -11,9 +11,17 @@ use Magento\Framework\App\ObjectManager; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Store\Api\StoreWebsiteRelationInterface; /** * Class ProductProcessUrlRewriteSavingObserver + * + * Observer to update the Rewrite URLs for a product. + * This observer is triggered on the save function when making changes + * to the products website on the Product Edit page. */ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { @@ -32,20 +40,38 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $productUrlPathGenerator; + /** + * @var StoreManagerInterface $storeManager + */ + private $storeManager; + + /** + * @var StoreWebsiteRelationInterface + */ + private $storeWebsiteRelation; + /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist * @param ProductUrlPathGenerator|null $productUrlPathGenerator + * @param StoreManagerInterface|null $storeManager + * @param StoreWebsiteRelationInterface|null $storeWebsiteRelation */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $productUrlPathGenerator = null + ProductUrlPathGenerator $productUrlPathGenerator = null, + StoreManagerInterface $storeManager = null, + StoreWebsiteRelationInterface $storeWebsiteRelation = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() ->get(ProductUrlPathGenerator::class); + $this->storeManager = $storeManager ?: ObjectManager::getInstance() + ->get(StoreManagerInterface::class); + $this->storeWebsiteRelation = $storeWebsiteRelation ?: ObjectManager::getInstance() + ->get(StoreWebsiteRelationInterface::class); } /** @@ -65,10 +91,24 @@ public function execute(\Magento\Framework\Event\Observer $observer) || $product->getIsChangedWebsites() || $product->dataHasChangedFor('visibility') ) { - if ($product->isVisibleInSiteVisibility()) { + if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { $product->unsUrlPath(); $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + + //Remove any rewrite URLs for websites the product is not in + foreach ($this->storeManager->getWebsites() as $website) { + $websiteId = $website->getWebsiteId(); + if (!in_array($websiteId, $product->getWebsiteIds())) { + foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($websiteId) as $storeId) { + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId + ]); + } + } + } } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php index 44b47faf3d4b8..45cec45a3c75d 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php @@ -17,7 +17,11 @@ use Magento\Framework\App\ObjectManager; /** - * Observer to assign the products to website + * Class ProductToWebsiteChangeObserver + * + * Observer to update the Rewrite URLs for a product. + * This observer is triggered by the product_action_attribute.website.update + * consumer in response to Mass Action changes in the Admin Product Grid. */ class ProductToWebsiteChangeObserver implements ObserverInterface { @@ -36,11 +40,6 @@ class ProductToWebsiteChangeObserver implements ObserverInterface */ protected $productRepository; - /** - * @var RequestInterface - */ - protected $request; - /** * @var StoreWebsiteRelationInterface */ @@ -52,6 +51,8 @@ class ProductToWebsiteChangeObserver implements ObserverInterface * @param ProductRepositoryInterface $productRepository * @param RequestInterface $request * @param StoreWebsiteRelationInterface $storeWebsiteRelation + * + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, @@ -63,7 +64,6 @@ public function __construct( $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; $this->productRepository = $productRepository; - $this->request = $request; $this->storeWebsiteRelation = $storeWebsiteRelation ?: ObjectManager::getInstance()->get(StoreWebsiteRelationInterface::class); } @@ -77,24 +77,29 @@ public function __construct( public function execute(\Magento\Framework\Event\Observer $observer) { foreach ($observer->getEvent()->getProducts() as $productId) { + /* @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->getById( $productId, false, - $this->request->getParam('store_id', Store::DEFAULT_STORE_ID) + Store::DEFAULT_STORE_ID, + true ); - if (!empty($this->productUrlRewriteGenerator->generate($product))) { - if ($this->request->getParam('remove_website_ids')) { - foreach ($this->request->getParam('remove_website_ids') as $webId) { - foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeId) { - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::STORE_ID => $storeId - ]); - } + // Remove the URLs from websites this product no longer belongs to + if ($observer->getEvent()->getActionType() == "remove" && $observer->getEvent()->getWebsiteIds()) { + foreach ($observer->getEvent()->getWebsiteIds() as $webId) { + foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeId) { + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId + ]); } } + } + + // Refresh all existing URLs for the product + if (!empty($this->productUrlRewriteGenerator->generate($product))) { if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 39317b42af989..f68eaa2a02b9f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -8,38 +8,51 @@ use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Store\Api\StoreWebsiteRelationInterface; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Website; +use Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver; +use Magento\Catalog\Model\Product\Visibility; /** * Class ProductProcessUrlRewriteSavingObserverTest * + * Tests the ProductProcessUrlRewriteSavingObserver to ensure the + * replace method (refresh existing URLs) and deleteByData (remove + * old URLs) are called the correct number of times. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\UrlRewrite\Model\UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject + * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $urlPersist; /** - * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject + * @var Event|\PHPUnit_Framework_MockObject_MockObject */ protected $event; /** - * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject + * @var Observer|\PHPUnit_Framework_MockObject_MockObject */ protected $observer; /** - * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + * @var Product|\PHPUnit_Framework_MockObject_MockObject */ protected $product; /** - * @var \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject + * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject */ protected $productUrlRewriteGenerator; @@ -49,42 +62,87 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test protected $objectManager; /** - * @var \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver + * @var ProductProcessUrlRewriteSavingObserver */ protected $model; + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var Website|\PHPUnit_Framework_MockObject_MockObject + */ + protected $website1; + + /** + * @var Website|\PHPUnit_Framework_MockObject_MockObject + */ + protected $website2; + + /** + * @var StoreWebsiteRelationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeWebsiteRelation; + /** * Set up + * Website_ID = 1 -> Store_ID = 1 + * Website_ID = 2 -> Store_ID = 2 & 5 */ protected function setUp() { - $this->urlPersist = $this->createMock(\Magento\UrlRewrite\Model\UrlPersistInterface::class); - $this->product = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ + $this->urlPersist = $this->createMock(UrlPersistInterface::class); + $this->product = $this->createPartialMock( + Product::class, + [ 'getId', 'dataHasChangedFor', - 'isVisibleInSiteVisibility', + 'getVisibility', 'getIsChangedWebsites', 'getIsChangedCategories', - 'getStoreId' - ]); + 'getStoreId', + 'getWebsiteIds' + ] + ); $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); - $this->event = $this->createPartialMock(\Magento\Framework\Event::class, ['getProduct']); + $this->event = $this->createPartialMock(Event::class, ['getProduct']); $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); - $this->observer = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); $this->observer->expects($this->any())->method('getEvent')->willReturn($this->event); $this->productUrlRewriteGenerator = $this->createPartialMock( - \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::class, + ProductUrlRewriteGenerator::class, ['generate'] ); $this->productUrlRewriteGenerator->expects($this->any()) ->method('generate') ->will($this->returnValue([3 => 'rewrite'])); $this->objectManager = new ObjectManager($this); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); + $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); + $this->storeManager->expects($this->any()) + ->method('getWebsites') + ->will($this->returnValue([$this->website1, $this->website2])); + + $this->storeWebsiteRelation = $this->createPartialMock( + StoreWebsiteRelationInterface::class, + ['getStoreByWebsiteId'] + ); + $this->storeWebsiteRelation->expects($this->any()) + ->method('getStoreByWebsiteId') + ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); + $this->model = $this->objectManager->getObject( - \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver::class, + ProductProcessUrlRewriteSavingObserver::class, [ 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'urlPersist' => $this->urlPersist + 'urlPersist' => $this->urlPersist, + 'storeManager' => $this->storeManager, + 'storeWebsiteRelation' => $this->storeWebsiteRelation ] ); } @@ -102,49 +160,61 @@ public function urlKeyDataProvider() 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => true, + 'visibilityResult' => Visibility::VISIBILITY_BOTH, 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 2, + 'productInWebsites' => [1] ], - 'no chnages' => [ + 'no changes' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => true, - 'expectedReplaceCount' => 0 + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 0, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2] ], 'visibility changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => true, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => true, - 'expectedReplaceCount' => 1 + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2] ], 'websites changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => true, 'isChangedCategories' => false, - 'visibilityResult' => true, - 'expectedReplaceCount' => 1 + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2] ], 'categories changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => true, - 'visibilityResult' => true, - 'expectedReplaceCount' => 1 + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2] ], 'url changed invisible' => [ 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => false, - 'expectedReplaceCount' => 0 + 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, + 'expectedReplaceCount' => 0, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2] ], ]; } @@ -156,6 +226,8 @@ public function urlKeyDataProvider() * @param bool $isChangedCategories * @param bool $visibilityResult * @param int $expectedReplaceCount + * @param int $expectedDeleteCount + * @param int $productInWebsites * * @dataProvider urlKeyDataProvider */ @@ -165,9 +237,16 @@ public function testExecuteUrlKey( $isChangedWebsites, $isChangedCategories, $visibilityResult, - $expectedReplaceCount + $expectedReplaceCount, + $expectedDeleteCount, + $productInWebsites ) { - $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(12)); + $this->product->expects($this->any())->method('getStoreId')->will( + $this->returnValue(Store::DEFAULT_STORE_ID) + ); + $this->product->expects($this->any())->method('getWebsiteIds')->will( + $this->returnValue($productInWebsites) + ); $this->product->expects($this->any()) ->method('dataHasChangedFor') @@ -187,13 +266,16 @@ public function testExecuteUrlKey( ->will($this->returnValue($isChangedCategories)); $this->product->expects($this->any()) - ->method('isVisibleInSiteVisibility') + ->method('getVisibility') ->will($this->returnValue($visibilityResult)); $this->urlPersist->expects($this->exactly($expectedReplaceCount)) ->method('replace') ->with([3 => 'rewrite']); + $this->urlPersist->expects($this->exactly($expectedDeleteCount)) + ->method('deleteByData'); + $this->model->execute($this->observer); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php new file mode 100644 index 0000000000000..04bf14974b6e7 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php @@ -0,0 +1,271 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; + +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Website; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver; +use Magento\Store\Api\StoreWebsiteRelationInterface; + +/** + * Class ProductToWebsiteChangeObserverTest + * + * Tests the ProductToWebsiteChangeObserver to ensure the + * replace method (refresh existing URLs) and deleteByData (remove + * old URLs) are called the correct number of times. + * + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductToWebsiteChangeObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $urlPersist; + + /** + * @var Event|\PHPUnit_Framework_MockObject_MockObject + */ + protected $event; + + /** + * @var Observer|\PHPUnit_Framework_MockObject_MockObject + */ + protected $observer; + + /** + * @var Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $product; + + /** + * @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepository; + + /** + * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productUrlRewriteGenerator; + + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var ProductToWebsiteChangeObserver + */ + protected $model; + + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var Website|\PHPUnit_Framework_MockObject_MockObject + */ + protected $website1; + + /** + * @var Website|\PHPUnit_Framework_MockObject_MockObject + */ + protected $website2; + + /** + * @var StoreWebsiteRelationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeWebsiteRelation; + + /** + * Set up + * Website_ID = 1 -> Store_ID = 1 + * Website_ID = 2 -> Store_ID = 2 & 5 + */ + protected function setUp() + { + $this->urlPersist = $this->createMock(UrlPersistInterface::class); + $this->productUrlRewriteGenerator = $this->createPartialMock( + ProductUrlRewriteGenerator::class, + ['generate'] + ); + + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); + $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); + $this->storeManager->expects($this->any()) + ->method('getWebsites') + ->will($this->returnValue([$this->website1, $this->website2])); + + $this->storeWebsiteRelation = $this->createPartialMock( + StoreWebsiteRelationInterface::class, + ['getStoreByWebsiteId'] + ); + $this->storeWebsiteRelation->expects($this->any()) + ->method('getStoreByWebsiteId') + ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); + + $this->product = $this->createPartialMock( + Product::class, + ['getId', 'getVisibility', 'getStoreId', 'getWebsiteIds'] + ); + $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); + $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class); + $this->productRepository->expects($this->any())->method('getById')->willReturn($this->product); + $this->event = $this->createPartialMock( + Event::class, + ['getProducts', 'getActionType', 'getWebsiteIds'] + ); + $this->event->expects($this->any())->method('getProducts')->willReturn([$this->product]); + $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); + $this->observer->expects($this->any())->method('getEvent')->willReturn($this->event); + + $this->objectManager = new ObjectManager($this); + $this->model = $this->objectManager->getObject( + ProductToWebsiteChangeObserver::class, + [ + 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, + 'urlPersist' => $this->urlPersist, + 'productRepository' => $this->productRepository, + 'storeWebsiteRelation' => $this->storeWebsiteRelation + ] + ); + } + + /** + * Data provider + * + * @return array + */ + public function urlKeyDataProvider() + { + return [ + 'in_all_no_changes' => [ + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2], + 'actionType' => null, + 'websitesChanged' => [], + 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] + ], + 'add_to_website_from_empty' => [ + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2], + 'actionType' => 'add', + 'websitesChanged' => [1, 2], + 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] + ], + 'add_to_website_existing' => [ + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2], + 'actionType' => 'add', + 'websitesChanged' => [2], + 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] + ], + 'remove_single' => [ + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 1, + 'expectedDeleteCount' => 2, + 'productInWebsites' => [1], + 'actionType' => 'remove', + 'websitesChanged' => [2], + 'rewrites' => [1 => 'url'] + ], + 'remove_all' => [ + 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'expectedReplaceCount' => 0, + 'expectedDeleteCount' => 3, + 'productInWebsites' => [], + 'actionType' => 'remove', + 'websitesChanged' => [1, 2], + 'rewrites' => [] + ], + 'not_visible_add' => [ + 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, + 'expectedReplaceCount' => 0, + 'expectedDeleteCount' => 0, + 'productInWebsites' => [1, 2], + 'actionType' => 'add', + 'websitesChanged' => [1, 2], + 'rewrites' => [] + ], + 'not_visible_remove' => [ + 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, + 'expectedReplaceCount' => 0, + 'expectedDeleteCount' => 3, + 'productInWebsites' => [], + 'actionType' => 'remove', + 'websitesChanged' => [1, 2], + 'rewrites' => [] + ], + ]; + } + + /** + * @param bool $visibilityResult + * @param int $expectedReplaceCount + * @param int $expectedDeleteCount + * @param int $productInWebsites + * @param string $actionType + * @param array $websitesChanged + * @param array $rewrites + * + * @dataProvider urlKeyDataProvider + */ + public function testExecuteUrlKey( + $visibilityResult, + $expectedReplaceCount, + $expectedDeleteCount, + $productInWebsites, + $actionType, + $websitesChanged, + $rewrites + ) { + $this->product->expects($this->any())->method('getStoreId')->will( + $this->returnValue(Store::DEFAULT_STORE_ID) + ); + $this->product->expects($this->any())->method('getWebsiteIds')->will( + $this->returnValue($productInWebsites) + ); + $this->event->expects($this->any())->method('getActionType')->willReturn($actionType); + $this->event->expects($this->any())->method('getWebsiteIds')->willReturn($websitesChanged); + + $this->productUrlRewriteGenerator->expects($this->any()) + ->method('generate') + ->will($this->returnValue($rewrites)); + + $this->product->expects($this->any()) + ->method('getVisibility') + ->will($this->returnValue($visibilityResult)); + + $this->urlPersist->expects($this->exactly($expectedReplaceCount)) + ->method('replace') + ->with($rewrites); + + $this->urlPersist->expects($this->exactly($expectedDeleteCount)) + ->method('deleteByData'); + + $this->model->execute($this->observer); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml deleted file mode 100644 index 9c4a8aaf41231..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="catalog_product_to_website_change"> - <observer name="catalog_product_to_website_change" instance="Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver"/> - </event> -</config> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/events.xml index 728442acf7a44..a6e5a5265b6c8 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/events.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/events.xml @@ -36,4 +36,7 @@ <event name="catalog_category_move_after"> <observer name="process_url_rewrite_moving" instance="Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteMovingObserver"/> </event> + <event name="catalog_product_to_website_change"> + <observer name="catalog_product_to_website_change" instance="Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver"/> + </event> </config> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php index f8fe68c2e0a2d..b0ecd58745444 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -85,6 +85,7 @@ public function productDataProvider(): array 'sku' => 'test-product', 'name' => 'test product', 'price' => 150, + 'website_ids' => [1] ], 'expected_data' => [ [ @@ -104,6 +105,7 @@ public function productDataProvider(): array 'name' => 'test product', 'price' => 150, 'url_key' => 'test-product-url-key', + 'website_ids' => [1] ], 'expected_data' => [ [ @@ -123,6 +125,7 @@ public function productDataProvider(): array 'name' => 'test product', 'price' => 150, 'url_key' => 'test-product-url-key', + 'website_ids' => [1] ], 'expected_data' => [], ], From 77fee571409509a8f4e6c825a03de4eaf3ce3388 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Tue, 25 Feb 2020 22:12:29 +0100 Subject: [PATCH 213/734] MC-26683: Removed get errors of cart allowing to add product to cart --- .../QuoteGraphQl/Model/Cart/AddProductsToCart.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index 91c77a1a3ecc5..94fc525209997 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -54,16 +54,6 @@ public function execute(Quote $cart, array $cartItems): void $this->addProductToCart->execute($cart, $cartItemData); } - if ($cart->getData('has_error')) { - $e = new GraphQlInputException(__('Shopping cart errors')); - $errors = $cart->getErrors(); - foreach ($errors as $error) { - /** @var MessageInterface $error */ - $e->addError(new GraphQlInputException(__($error->getText()))); - } - throw $e; - } - $this->cartRepository->save($cart); } } From e8e164f63fc090341b334439e96b0049261c9cde Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Wed, 26 Feb 2020 14:12:21 +0100 Subject: [PATCH 214/734] #23191 Port delete processing from commit 769c48d239648fd700f5b18157f2a5ef8d726b77 after merge --- .../Model/Import/Product/LinkProcessor.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index ff5e4527a0ebf..fdb0ec3534f30 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -140,6 +140,7 @@ private function processLinkBunches( $productIds = []; $linkRows = []; $positionRows = []; + $linksToDelete = []; $bunch = array_filter($bunch, [$importEntity, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); foreach ($bunch as $rowData) { @@ -156,6 +157,12 @@ function ($linkName) use ($rowData) { ); foreach ($linkNameToId as $linkName => $linkId) { $linkSkus = explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + + //process empty value + if (!empty($linkSkus[0]) && $linkSkus[0] === $importEntity->getEmptyAttributeValueConstant()) { + $linksToDelete[$linkId][] = $productId; + continue; + } $linkPositions = ! empty($rowData[$linkName . 'position']) ? explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'position']) : []; @@ -203,9 +210,43 @@ function ($linkedSku) use ($sku, $importEntity) { } } } + + $this->deleteProductsLinks($importEntity, $resource, $linksToDelete); $this->saveLinksData($importEntity, $resource, $productIds, $linkRows, $positionRows); } + /** + * Delete links + * + * @param Product $importEntity + * @param Link $resource + * @param array $linksToDelete + * @return void + * @throws LocalizedException + */ + private function deleteProductsLinks( + Product $importEntity, + Link $resource, + array $linksToDelete + ) + { + if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $importEntity->getBehavior()) { + foreach ($linksToDelete as $linkTypeId => $productIds) { + if (!empty($productIds)) { + $whereLinkId = $importEntity->getConnection()->quoteInto('link_type_id', $linkTypeId); + $whereProductId = $importEntity->getConnection()->quoteInto( + 'product_id IN (?)', + array_unique($productIds) + ); + $importEntity->getConnection()->delete( + $resource->getMainTable(), + $whereLinkId . ' AND ' . $whereProductId + ); + } + } + } + } + /** * Check if product exists for specified SKU * From 37ad6a2dcfeff6542a6a23dc93d67744eab3008b Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Wed, 26 Feb 2020 18:25:50 +0100 Subject: [PATCH 215/734] #23191 Add unit test to check new ability to inject more custom link types + CSFix --- .../Model/Import/Product/LinkProcessor.php | 3 +- .../Import/Product/LinkProcessorTest.php | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index fdb0ec3534f30..4c4438e3e9d7d 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -228,8 +228,7 @@ private function deleteProductsLinks( Product $importEntity, Link $resource, array $linksToDelete - ) - { + ) { if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $importEntity->getBehavior()) { foreach ($linksToDelete as $linkTypeId => $productIds) { if (!empty($productIds)) { diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php new file mode 100644 index 0000000000000..0e053a339071f --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class CategoryProcessorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** @var ObjectManagerHelper */ + protected $objectManagerHelper; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\LinkProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $linkProcessor; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType + */ + protected $product; + + /** + * @var \Magento\ImportExport\Model\ResourceModel\Helper + */ + protected $resourceHelper; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Link + */ + protected $resource; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory + */ + protected $linkFactory; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class + */ + protected $skuProcessor; + + /** + * @var \Psr\Log\LoggerInterface + */ + protected $logger; + + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->resourceHelper = $this->createMock(\Magento\ImportExport\Model\ResourceModel\Helper::class); + + $this->resource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Link::class); + $this->resource->method('getMainTable')->willReturn('main_link_table'); + + $this->linkFactory = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product\LinkFactory::class, ['create']); + $this->linkFactory->method('create')->willReturn($this->resource); + + $this->skuProcessor = $this->createMock(\Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class, []); + $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); + } + + /** + * @dataProvider diConfigDataProvider + * @param $expectedCallCount + * @param $linkToNameId + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testSaveLinks($expectedCallCount, $linkToNameId) + { + $this->linkProcessor = + new \Magento\CatalogImportExport\Model\Import\Product\LinkProcessor( + $this->linkFactory, + $this->resourceHelper, + $this->skuProcessor, + $this->logger, + $linkToNameId + ); + + $importEntity = $this->createMock(\Magento\CatalogImportExport\Model\Import\Product::class); + $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $importEntity->method('getConnection')->willReturn($connection); + $select = $this->createMock(\Magento\Framework\DB\Select::class); + + // expect one call per linkToNameId + $connection->expects($this->exactly($expectedCallCount))->method('select')->willReturn($select); + + $select->method('from')->willReturn($select); + + $dataSourceModel = $this->createMock(\Magento\ImportExport\Model\ResourceModel\Import\Data::class); + + $this->linkProcessor->saveLinks($importEntity, $dataSourceModel, '_related_'); + } + + /** + * @return array + */ + public function diConfigDataProvider() + { + return [ + [3, [ + '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, + '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, + '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, + ]], + [4, [ + '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, + '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, + '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, + '_custom_link_' => 9, + ]], + ]; + } +} From c4a16c8a9a18f9a4cb27108fdd274ad3cedda9ea Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Wed, 26 Feb 2020 12:25:42 -0600 Subject: [PATCH 216/734] MQE-1993:Refactor MFTF tests/actionGroups using <executeInSelenium> Commented out steps/ skipped tests using <executeSelenium> & <peformOn> waiting for custom actions --- .../AdminFillIntegrationFormActionGroup.xml | 3 ++- ...nLockAdminUserWhenCreatingNewIntegrationTest.xml | 2 ++ .../AdminLockAdminUserWhenCreatingNewRoleTest.xml | 2 ++ .../AdminFillUserRoleFormActionGroup.xml | 3 ++- .../User/Test/Mftf/Test/AdminUpdateUserTest.xml | 2 ++ composer.json | 2 +- composer.lock | 13 +++++++------ 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml index 85e4efb495955..6d4e4ed39f6e2 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml @@ -20,6 +20,7 @@ <waitForPageLoad stepKey="waitForApiTab" /> <selectOption userInput="{{integration.resourceAccess}}" selector="{{AdminNewIntegrationFormSection.resourceAccess}}" stepKey="selectResourceAccess" /> - <performOn stepKey="checkNeededResources" selector="{{AdminNewIntegrationFormSection.resourceTree}}" function="function($I,$apiResources={{integration.resources}}){foreach($apiResources as $apiResource){$I->conditionalClick('//li[@data-id=\'' . $apiResource . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $apiResource . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + <!--TODO waiting for custom action functionality with MQE-1964 --> + <!--<performOn stepKey="checkNeededResources" selector="{{AdminNewIntegrationFormSection.resourceTree}}" function="function($I,$apiResources={{integration.resources}}){foreach($apiResources as $apiResource){$I->conditionalClick('//li[@data-id=\'' . $apiResource . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $apiResource . '\' and contains(@class, \'jstree-checked\')]',false);}}" />--> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml index 03681a1753288..c3945fe0199b6 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml @@ -18,6 +18,8 @@ <testCaseId value="MC-14382" /> <group value="security"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml index c7fb05a3b7358..bf13ab6b5c6aa 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml @@ -18,6 +18,8 @@ <testCaseId value="MC-14384" /> <group value="security"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml index 7b913382651ae..2facfbac04820 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml @@ -24,6 +24,7 @@ <waitForPageLoad stepKey="waitForRoleResourceTab" /> <selectOption userInput="{{role.resourceAccess}}" selector="{{AdminCreateRoleSection.resourceAccess}}" stepKey="selectResourceAccess" /> - <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resource}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + <!--TODO waiting for custom action functionality with MQE-1964 --> + <!--<performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resource}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" />--> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml index dfadee8ee6807..0cddf091f7a9d 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -18,6 +18,8 @@ <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> <group value="user"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> diff --git a/composer.json b/composer.json index 9c42a05a457c5..b5d0ab058319e 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "2.6.2", + "magento/magento2-functional-testing-framework": "dev-develop", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index 6ac7f36cd2e32..da8005f44cdb1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f06887dfc3e06489a251fbb5c18d30ca", + "content-hash": "d13e79b9e96092fc47dfd2b0ab21fb3b", "packages": [ { "name": "braintree/braintree_php", @@ -7381,16 +7381,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.6.2", + "version": "dev-develop", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "8b332582751a830b3a6eafe1b09ac3b403e9a20e" + "reference": "76693d9cb7c5f6c01ef231f184372eb784f3e555" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/8b332582751a830b3a6eafe1b09ac3b403e9a20e", - "reference": "8b332582751a830b3a6eafe1b09ac3b403e9a20e", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/76693d9cb7c5f6c01ef231f184372eb784f3e555", + "reference": "76693d9cb7c5f6c01ef231f184372eb784f3e555", "shasum": "" }, "require": { @@ -7462,7 +7462,7 @@ "magento", "testing" ], - "time": "2020-02-13T21:29:32+00:00" + "time": "2020-02-20T21:09:13+00:00" }, { "name": "mikey179/vfsstream", @@ -10525,6 +10525,7 @@ "minimum-stability": "stable", "stability-flags": { "magento/composer": 20, + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, From 11c3525adab3175b6a4245d8e68b7b91564cccb3 Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Wed, 26 Feb 2020 21:16:58 +0100 Subject: [PATCH 217/734] #23191 Fix class name of test --- .../Test/Unit/Model/Import/Product/LinkProcessorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php index 0e053a339071f..b402c9aa05922 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php @@ -8,7 +8,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -class CategoryProcessorTest extends \PHPUnit\Framework\TestCase +class LinkProcessorTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager From 0a79989737fd30ac4f39e0a24d75b5328d635f2c Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Wed, 26 Feb 2020 21:37:41 +0100 Subject: [PATCH 218/734] MC-26683: Updated description of the class --- app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index 94fc525209997..cfe78389fffe4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -8,12 +8,11 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\Message\MessageInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; /** - * Add products to cart + * Adding products to cart using GraphQL */ class AddProductsToCart { From 1140cb7a518b61142077928c6da8be1f3195ba38 Mon Sep 17 00:00:00 2001 From: Tjitse <Tjitse@vendic.nl> Date: Thu, 27 Feb 2020 08:56:56 +0100 Subject: [PATCH 219/734] Fixing code style issues --- lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php index fdb6dd5c816dc..646908f99693c 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php @@ -70,8 +70,7 @@ public function gmtDate($format = null, $input = null) } /** - * Converts input date into date with timezone offset - * Input date must be in GMT timezone + * Converts input date into date with timezone offset. Input date must be in GMT timezone. * * @param string $format * @param int|string $input date in GMT timezone @@ -114,8 +113,7 @@ public function gmtTimestamp($input = null) } /** - * Converts input date into timestamp with timezone offset - * Input date must be in GMT timezone + * Converts input date into timestamp with timezone offset. Input date must be in GMT timezone. * * @param int|string $input date in GMT timezone * @return int From 4f853e8e48adf01b07306f9e4482030cd088c541 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Thu, 27 Feb 2020 14:00:07 +0200 Subject: [PATCH 220/734] Fix static --- .../view/frontend/templates/product/layered/renderer.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml index fe6863a494f10..70eb51651f663 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml @@ -68,7 +68,8 @@ ) ?>" style="background: <?= $block->escapeHtmlAttr( $swatchData['swatches'][$option]['value'] - ) ?> no-repeat center; background-size: initial;"></div> + ) ?> no-repeat center; background-size: initial;"> + </div> <?php break; case '0': default: From 502d66e2ff322bf2f3884f146f378228977b956a Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Thu, 27 Feb 2020 14:02:13 +0100 Subject: [PATCH 221/734] #23191 Fix static tests We have to ignore the CyclomaticComplexity of 11 of \Magento\CatalogImportExport\Model\Import\Product\LinkProcessor::processLinkBunches It's since we added the if() to check for empty links. I do not find a easy way to reduce it, because the if contains a continue statement. --- .../Model/Import/Product/LinkProcessor.php | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index 4c4438e3e9d7d..ddbbf242ee67a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -15,7 +15,9 @@ use Psr\Log\LoggerInterface; /** - * Class LinkProcessor + * Processor for links between products + * + * Remark: Via DI it is possible to supply additional link types. */ class LinkProcessor { @@ -76,7 +78,7 @@ public function __construct( * @param Product $importEntity * @param Data $dataSourceModel * @param string $linkField - * @return $this + * @return void * @throws LocalizedException */ public function saveLinks( @@ -128,6 +130,7 @@ public function addNameToIds(array $nameToIds): void * * @return void * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processLinkBunches( Product $importEntity, @@ -163,6 +166,7 @@ function ($linkName) use ($rowData) { $linksToDelete[$linkId][] = $productId; continue; } + $linkPositions = ! empty($rowData[$linkName . 'position']) ? explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'position']) : []; @@ -180,7 +184,7 @@ function ($linkedSku) use ($sku, $importEntity) { } ); foreach ($linkSkus as $linkedKey => $linkedSku) { - $linkedId = $this->getProductLinkedId($linkedSku); + $linkedId = $this->getProductLinkedId($importEntity, $linkedSku); if ($linkedId == null) { // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" // link causing fatal errors. @@ -259,6 +263,19 @@ private function isSkuExist(Product $importEntity, string $sku): bool return isset($importEntity->getOldSku()[$sku]); } + /** + * Get existing SKU record + * + * @param Product $importEntity + * @param string $sku + * @return mixed + */ + private function getExistingSku(Product $importEntity, string $sku) + { + $sku = strtolower($sku); + return $importEntity->getOldSku()[$sku]; + } + /** * Fetches Product Links * @@ -289,15 +306,18 @@ private function fetchProductLinks(Product $importEntity, Link $resource, int $p /** * Gets the Id of the Sku * + * @param Product $importEntity * @param string $linkedSku - * * @return int|null */ - private function getProductLinkedId(string $linkedSku): ?int + private function getProductLinkedId(Product $importEntity, string $linkedSku): ?int { $linkedSku = trim($linkedSku); $newSku = $this->skuProcessor->getNewSku($linkedSku); - $linkedId = ! empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id']; + + $linkedId = ! empty($newSku) ? + $newSku['entity_id'] : + $this->getExistingSku($importEntity, $linkedSku)['entity_id']; return $linkedId; } From e05bb102f5b8b50e014415927a415337291ceabb Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Thu, 27 Feb 2020 14:13:58 +0100 Subject: [PATCH 222/734] #23191 Reduce CyclomaticComplexity of processLinkBunches --- .../Model/Import/Product/LinkProcessor.php | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php index ddbbf242ee67a..a45338c391a58 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -130,7 +130,6 @@ public function addNameToIds(array $nameToIds): void * * @return void * @throws LocalizedException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processLinkBunches( Product $importEntity, @@ -151,13 +150,8 @@ private function processLinkBunches( $productId = $this->skuProcessor->getNewSku($sku)[$linkField]; $productIds[] = $productId; $productLinkKeys = $this->fetchProductLinks($importEntity, $resource, $productId); - $linkNameToId = array_filter( - $this->linkNameToId, - function ($linkName) use ($rowData) { - return isset($rowData[$linkName . 'sku']); - }, - ARRAY_FILTER_USE_KEY - ); + $linkNameToId = $this->filterProvidedLinkTypes($rowData); + foreach ($linkNameToId as $linkName => $linkId) { $linkSkus = explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); @@ -171,18 +165,8 @@ function ($linkName) use ($rowData) { ? explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'position']) : []; - $linkSkus = array_filter( - $linkSkus, - function ($linkedSku) use ($sku, $importEntity) { - $linkedSku = trim($linkedSku); + $linkSkus = $this->filterValidLinks($importEntity, $sku, $linkSkus); - return ( - $this->skuProcessor->getNewSku($linkedSku) !== null - || $this->isSkuExist($importEntity, $linkedSku) - ) - && strcasecmp($linkedSku, $sku) !== 0; - } - ); foreach ($linkSkus as $linkedKey => $linkedSku) { $linkedId = $this->getProductLinkedId($importEntity, $linkedSku); if ($linkedId == null) { @@ -373,4 +357,45 @@ private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId): { return "{$productId}-{$linkedId}-{$linkTypeId}"; } + + /** + * Filter out link types which are not provided in the rowData + * + * @param array $rowData + * @return array + */ + private function filterProvidedLinkTypes(array $rowData) + { + return array_filter( + $this->linkNameToId, + function ($linkName) use ($rowData) { + return isset($rowData[$linkName . 'sku']); + }, + ARRAY_FILTER_USE_KEY + ); + } + + /** + * Filter out invalid links + * + * @param Product $importEntity + * @param string $sku + * @param array $linkSkus + * @return array + */ + private function filterValidLinks(Product $importEntity, string $sku, array $linkSkus) + { + return array_filter( + $linkSkus, + function ($linkedSku) use ($sku, $importEntity) { + $linkedSku = trim($linkedSku); + + return ( + $this->skuProcessor->getNewSku($linkedSku) !== null + || $this->isSkuExist($importEntity, $linkedSku) + ) + && strcasecmp($linkedSku, $sku) !== 0; + } + ); + } } From aaac9b3173fcc00c555d6c10f37cdcdd8a9091ea Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 27 Feb 2020 15:10:37 +0000 Subject: [PATCH 223/734] Fixed directory isExists method handling of relative paths with double-dots --- .../Framework/Filesystem/Directory/ReadTest.php | 14 ++++++++++---- .../Framework/Filesystem/Directory/Read.php | 14 ++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php index bc77eeb932c9a..e04063eabc36b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php @@ -73,17 +73,17 @@ public function testGetRelativePathOutside() $exceptions = 0; $dir = $this->getDirectoryInstance('foo'); try { - $dir->getRelativePath(__DIR__ .'/ReadTest.php'); + $dir->getRelativePath(__DIR__ . '/ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } try { - $dir->getRelativePath(__DIR__ .'//./..////Directory/ReadTest.php'); + $dir->getRelativePath(__DIR__ . '//./..////Directory/ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } try { - $dir->getRelativePath(__DIR__ .'\..\Directory\ReadTest.php'); + $dir->getRelativePath(__DIR__ . '\..\Directory\ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } @@ -222,7 +222,13 @@ public function testIsExist($dirPath, $path, $exists) */ public function existsProvider() { - return [['foo', 'bar', true], ['foo', 'bar/baz/', true], ['foo', 'bar/notexists', false]]; + return [ + ['foo', 'bar', true], + ['foo', 'bar/baz/', true], + ['foo', 'bar/notexists', false], + ['foo', 'foo/../bar/', true], + ['foo', 'foo/../notexists/', false] + ]; } public function testIsExistOutside() diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php index a3a4cec59953f..e23eadd57d866 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\ValidatorException; /** + * Filesystem directory instance for read operations * @api */ class Read implements ReadInterface @@ -40,8 +41,6 @@ class Read implements ReadInterface private $pathValidator; /** - * Constructor. Set properties. - * * @param \Magento\Framework\Filesystem\File\ReadFactory $fileFactory * @param \Magento\Framework\Filesystem\DriverInterface $driver * @param string $path @@ -60,6 +59,8 @@ public function __construct( } /** + * Validate the path is correct and within the directory + * * @param null|string $path * @param null|string $scheme * @param bool $absolutePath @@ -96,8 +97,7 @@ protected function setPath($path) } /** - * Retrieves absolute path - * E.g.: /var/www/application/file.txt + * Retrieves absolute path i.e. /var/www/application/file.txt * * @param string $path * @param string $scheme @@ -151,7 +151,7 @@ public function read($path = null) /** * Read recursively * - * @param null $path + * @param string|null $path * @throws ValidatorException * @return string[] */ @@ -207,7 +207,9 @@ public function isExist($path = null) { $this->validatePath($path); - return $this->driver->isExists($this->driver->getAbsolutePath($this->path, $path)); + return $this->driver->isExists( + $this->driver->getRealPathSafety($this->driver->getAbsolutePath($this->path, $path)) + ); } /** From d437cec4e0a794b54eb41f80b8c4499a29e3a2fe Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Thu, 27 Feb 2020 17:52:53 +0100 Subject: [PATCH 224/734] #23191 Fix static tests for B2B edition, which checks tests --- .../Unit/Model/Import/Product/LinkProcessorTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php index b402c9aa05922..a109b7029ff74 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php @@ -8,6 +8,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class LinkProcessorTest extends \PHPUnit\Framework\TestCase { /** @@ -63,10 +66,15 @@ protected function setUp() $this->resource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Link::class); $this->resource->method('getMainTable')->willReturn('main_link_table'); - $this->linkFactory = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product\LinkFactory::class, ['create']); + $this->linkFactory = $this->createPartialMock( + \Magento\Catalog\Model\ResourceModel\Product\LinkFactory::class, + ['create'] + ); $this->linkFactory->method('create')->willReturn($this->resource); - $this->skuProcessor = $this->createMock(\Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class, []); + $this->skuProcessor = $this->createMock( + \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class, + ); $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); } From 5ca49ae2b1a62cc87e8dd0c2989a05bd19ec74da Mon Sep 17 00:00:00 2001 From: Alexander MEnk <a.menk@imi.de> Date: Thu, 27 Feb 2020 20:16:53 +0100 Subject: [PATCH 225/734] #23191 Fix static tests: trailing comma in function call --- .../Test/Unit/Model/Import/Product/LinkProcessorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php index a109b7029ff74..284ef0b783ec8 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php @@ -73,7 +73,7 @@ protected function setUp() $this->linkFactory->method('create')->willReturn($this->resource); $this->skuProcessor = $this->createMock( - \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class, + \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class ); $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); } From 2506c0824c4db0819ce4e8519992f21cabdd2a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Tue, 26 Nov 2019 10:20:36 +0100 Subject: [PATCH 226/734] Fix #6310 - Changing products 'this item has weight' using 'Update Attributes' is not possible --- .../Edit/Action/Attribute/Tab/Attributes.php | 74 ++-- .../Adminhtml/Product/Helper/Form/Weight.php | 147 ++++++-- .../Product/Action/Attribute/Save.php | 7 +- .../Model/ResourceModel/Product/Action.php | 164 ++++++++- .../Product/Helper/Form/WeightTest.php | 56 +-- .../ResourceModel/Product/ActionTest.php | 338 ++++++++++++++++++ .../product/edit/action/attribute.phtml | 1 + .../web/js/product/weight-handler.js | 53 +++ .../web/css/source/_module-old.less | 16 +- .../web/css/source/_module-old.less | 21 +- 10 files changed, 752 insertions(+), 125 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php index 2df0ff0b6cd7c..1ebfa14200364 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php @@ -13,7 +13,18 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Tab\TabInterface; +use Magento\Catalog\Block\Adminhtml\Form; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Boolean; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Image; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Price; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight; +use Magento\Catalog\Helper\Product\Edit\Action\Attribute; +use Magento\Catalog\Model\ProductFactory; use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Registry; /** * Attributes tab block @@ -23,37 +34,38 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements - \Magento\Backend\Block\Widget\Tab\TabInterface +class Attributes extends Form implements TabInterface { /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $_productFactory; /** - * @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute + * @var Attribute */ protected $_attributeAction; - /** @var array */ + /** + * @var array + */ private $excludeFields; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Data\FormFactory $formFactory - * @param \Magento\Catalog\Model\ProductFactory $productFactory - * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction + * @param Context $context + * @param Registry $registry + * @param FormFactory $formFactory + * @param ProductFactory $productFactory + * @param Attribute $attributeAction * @param array $data * @param array|null $excludeFields */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Data\FormFactory $formFactory, - \Magento\Catalog\Model\ProductFactory $productFactory, - \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction, + Context $context, + Registry $registry, + FormFactory $formFactory, + ProductFactory $productFactory, + Attribute $attributeAction, array $data = [], array $excludeFields = null ) { @@ -72,7 +84,7 @@ public function __construct( */ protected function _prepareForm(): void { - $this->setFormExcludedFieldList($this->getExcludedFields()); + $this->setFormExcludedFieldList($this->excludeFields); $this->_eventManager->dispatch( 'adminhtml_catalog_product_form_prepare_excluded_field_list', ['object' => $this] @@ -110,10 +122,10 @@ public function getAttributes() protected function _getAdditionalElementTypes() { return [ - 'price' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Price::class, - 'weight' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight::class, - 'image' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Image::class, - 'boolean' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Boolean::class + 'price' => Price::class, + 'weight' => Weight::class, + 'image' => Image::class, + 'boolean' => Boolean::class ]; } @@ -129,7 +141,7 @@ protected function _getAdditionalElementHtml($element) $nameAttributeHtml = $element->getExtType() === 'multiple' ? 'name="' . $element->getId() . '_checkbox"' : ''; $elementId = $element->getId(); $dataAttribute = "data-disable='{$elementId}'"; - $dataCheckboxName = "toggle_" . "{$elementId}"; + $dataCheckboxName = "toggle_{$elementId}"; $checkboxLabel = __('Change'); // @codingStandardsIgnoreStart $html = <<<HTML @@ -140,14 +152,8 @@ protected function _getAdditionalElementHtml($element) </label> </span> HTML; - if ($elementId === 'weight') { - $html .= <<<HTML -<script>require(['Magento_Catalog/js/product/weight-handler'], function (weightHandle) { - weightHandle.hideWeightSwitcher(); -});</script> -HTML; - // @codingStandardsIgnoreEnd - } + + // @codingStandardsIgnoreEnd return $html; } @@ -190,14 +196,4 @@ public function isHidden() { return false; } - - /** - * Returns excluded fields - * - * @return array - */ - private function getExcludedFields(): array - { - return $this->excludeFields; - } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php index fec3daeeacd13..8b855e095dec7 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php @@ -3,48 +3,57 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Product form weight field helper - */ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Directory\Helper\Data; use Magento\Framework\Data\Form; use Magento\Catalog\Model\Product\Edit\WeightResolver; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Data\Form\Element\Radios; +use Magento\Framework\Data\Form\Element\Text; +use Magento\Framework\Escaper; +use Magento\Framework\Locale\Format; -class Weight extends \Magento\Framework\Data\Form\Element\Text +/** + * Product form weight field helper + */ +class Weight extends Text { /** * Weight switcher radio-button element * - * @var \Magento\Framework\Data\Form\Element\Checkbox + * @var Radios */ protected $weightSwitcher; /** - * @var \Magento\Framework\Locale\Format + * @var Format */ protected $localeFormat; /** - * @var \Magento\Directory\Helper\Data + * @var Data */ protected $directoryHelper; /** - * @param \Magento\Framework\Data\Form\Element\Factory $factoryElement - * @param \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Framework\Locale\Format $localeFormat - * @param \Magento\Directory\Helper\Data $directoryHelper + * @param Factory $factoryElement + * @param CollectionFactory $factoryCollection + * @param Escaper $escaper + * @param Format $localeFormat + * @param Data $directoryHelper * @param array $data */ public function __construct( - \Magento\Framework\Data\Form\Element\Factory $factoryElement, - \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection, - \Magento\Framework\Escaper $escaper, - \Magento\Framework\Locale\Format $localeFormat, - \Magento\Directory\Helper\Data $directoryHelper, + Factory $factoryElement, + CollectionFactory $factoryCollection, + Escaper $escaper, + Format $localeFormat, + Data $directoryHelper, array $data = [] ) { $this->directoryHelper = $directoryHelper; @@ -78,25 +87,45 @@ public function getElementHtml() if (!$this->getForm()->getDataObject()->getTypeInstance()->hasWeight()) { $this->weightSwitcher->setValue(WeightResolver::HAS_NO_WEIGHT); } + if ($this->getDisabled()) { $this->weightSwitcher->setDisabled($this->getDisabled()); } - return '<div class="admin__field-control weight-switcher">' . - '<div class="admin__control-switcher" data-role="weight-switcher">' . - $this->weightSwitcher->getLabelHtml() . - '<div class="admin__field-control-group">' . - $this->weightSwitcher->getElementHtml() . - '</div>' . - '</div>' . - '<div class="admin__control-addon">' . - parent::getElementHtml() . - '<label class="admin__addon-suffix" for="' . - $this->getHtmlId() . - '"><span>' . - $this->directoryHelper->getWeightUnit() . - '</span></label>' . - '</div>' . - '</div>'; + + $htmlId = $this->getHtmlId(); + $html = ''; + + if ($beforeElementHtml = $this->getBeforeElementHtml()) { + $html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>'; + } + + $html .= '<div class="admin__control-addon">'; + + if (is_array($this->getValue())) { + foreach ($this->getValue() as $value) { + $html .= $this->getHtmlForInputByValue($this->_escape($value)); + } + } else { + $html .= $this->getHtmlForInputByValue($this->getEscapedValue()); + } + + $html .= '<label class="admin__addon-suffix" for="' . + $this->getHtmlId() . + '"><span>' . + $this->directoryHelper->getWeightUnit() . + '</span></label></div>'; + + if ($afterElementJs = $this->getAfterElementJs()) { + $html .= $afterElementJs; + } + + if ($afterElementHtml = $this->getAfterElementHtml()) { + $html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>'; + } + + $html .= $this->getHtmlForWeightSwitcher(); + + return $html; } /** @@ -112,8 +141,7 @@ public function setForm($form) } /** - * @param null|int|string $index - * @return null|string + * @inheritDoc * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getEscapedValue($index = null) @@ -134,4 +162,53 @@ public function getEscapedValue($index = null) return $value; } + + /** + * Get input html by sting value. + * + * @param string|null $value + * + * @return string + */ + private function getHtmlForInputByValue($value) + { + return '<input id="' . $this->getHtmlId() . '" name="' . $this->getName() . '" ' . $this->_getUiId() + . ' value="' . $value . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>'; + } + + /** + * Get weight switcher html. + * + * @return string + */ + private function getHtmlForWeightSwitcher() + { + $html = '<div class="admin__control-addon">'; + $html .= '<div class="admin__field-control weight-switcher">' . + '<div class="admin__control-switcher" data-role="weight-switcher">' . + $this->weightSwitcher->getLabelHtml() . + '<div class="admin__field-control-group">' . + $this->weightSwitcher->getElementHtml() . + '</div>' . + '</div>'; + + $html .= '<label class="addafter">'; + $elementId = ProductAttributeInterface::CODE_HAS_WEIGHT; + $nameAttributeHtml = 'name="' . $elementId . '_checkbox"'; + $dataCheckboxName = "toggle_{$elementId}"; + $checkboxLabel = __('Change'); + $html .= <<<HTML +<span class="attribute-change-checkbox"> + <input type="checkbox" id="$dataCheckboxName" name="$dataCheckboxName" class="checkbox" $nameAttributeHtml + onclick="toogleFieldEditMode(this, 'weight-switcher1'); toogleFieldEditMode(this, 'weight-switcher0');" /> + <label class="label" for="$dataCheckboxName"> + {$checkboxLabel} + </label> +</span> +HTML; + + $html .= '</label></div></div>'; + + return $html; + } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 2d10d7148fb71..696401e5430d6 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Eav\Model\Config; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; @@ -14,7 +15,7 @@ use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** - * Class Save + * Class used for saving mass updated products attributes. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface @@ -146,6 +147,10 @@ private function sanitizeProductAttributes($attributesData) $dateFormat = $this->timezone->getDateFormat(\IntlDateFormatter::SHORT); foreach ($attributesData as $attributeCode => $value) { + if ($attributeCode === ProductAttributeInterface::CODE_HAS_WEIGHT) { + continue; + } + $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); if (!$attribute->getAttributeId()) { unset($attributesData[$attributeCode]); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php index d0a3af92126d3..71ab9413a0d09 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php @@ -3,42 +3,81 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Factory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\TypeTransitionManager; +use Magento\Catalog\Model\ResourceModel\AbstractResource; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\Entity\Context; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Store\Model\StoreManagerInterface; /** * Catalog Product Mass processing resource model * * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Action extends \Magento\Catalog\Model\ResourceModel\AbstractResource +class Action extends AbstractResource { /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ private $dateTime; /** - * @param \Magento\Eav\Model\Entity\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Catalog\Model\Factory $modelFactory - * @param \Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface $uniqueValidator - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + * @var ProductCollectionFactory + */ + private $productCollectionFactory; + + /** + * @var TypeTransitionManager + */ + private $typeTransitionManager; + + /** + * Entity type id values to save + * + * @var array + */ + private $typeIdValuesToSave = []; + + /** + * @param Context $context + * @param StoreManagerInterface $storeManager + * @param Factory $modelFactory + * @param UniqueValidationInterface $uniqueValidator + * @param DateTime $dateTime + * @param CollectionFactory $productCollectionFactory + * @param TypeTransitionManager $typeTransitionManager * @param array $data */ public function __construct( - \Magento\Eav\Model\Entity\Context $context, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Factory $modelFactory, - \Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface $uniqueValidator, - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, + Context $context, + StoreManagerInterface $storeManager, + Factory $modelFactory, + UniqueValidationInterface $uniqueValidator, + DateTime $dateTime, + ProductCollectionFactory $productCollectionFactory, + TypeTransitionManager $typeTransitionManager, $data = [] ) { parent::__construct($context, $storeManager, $modelFactory, $data, $uniqueValidator); $this->dateTime = $dateTime; + $this->productCollectionFactory = $productCollectionFactory; + $this->typeTransitionManager = $typeTransitionManager; } /** @@ -50,7 +89,7 @@ protected function _construct() { $resource = $this->_resource; $this->setType( - \Magento\Catalog\Model\Product::ENTITY + Product::ENTITY )->setConnection( $resource->getConnection('catalog') ); @@ -67,13 +106,18 @@ protected function _construct() */ public function updateAttributes($entityIds, $attrData, $storeId) { - $object = new \Magento\Framework\DataObject(); + $object = new DataObject(); $object->setStoreId($storeId); $attrData[ProductInterface::UPDATED_AT] = $this->dateTime->gmtDate(); $this->getConnection()->beginTransaction(); try { foreach ($attrData as $attrCode => $value) { + if ($attrCode === ProductAttributeInterface::CODE_HAS_WEIGHT) { + $this->updateHasWeightAttribute($entityIds, $value); + continue; + } + $attribute = $this->getAttribute($attrCode); if (!$attribute->getAttributeId()) { continue; @@ -105,7 +149,7 @@ public function updateAttributes($entityIds, $attrData, $storeId) /** * Insert or Update attribute data * - * @param \Magento\Catalog\Model\AbstractModel $object + * @param AbstractModel $object * @param AbstractAttribute $attribute * @param mixed $value * @return $this @@ -136,13 +180,13 @@ protected function _saveAttributeValue($object, $attribute, $value) } $data = $attribute->isStatic() - ? new \Magento\Framework\DataObject( + ? new DataObject( [ $this->getLinkField() => $entityId, $attribute->getAttributeCode() => $this->_prepareValueForSave($value, $attribute), ] ) - : new \Magento\Framework\DataObject( + : new DataObject( [ 'attribute_id' => $attribute->getAttributeId(), 'store_id' => $storeId, @@ -178,9 +222,10 @@ protected function _saveAttributeValue($object, $attribute, $value) } /** - * Resolve entity id + * Resolve entity id for current entity * * @param int $entityId + * * @return int */ protected function resolveEntityId($entityId) @@ -194,4 +239,87 @@ protected function resolveEntityId($entityId) ->where('entity_id = ?', $entityId); return $this->getConnection()->fetchOne($select); } + + /** + * Process product_has_weight attribute update + * + * @param array $entityIds + * @param string $value + */ + private function updateHasWeightAttribute($entityIds, $value): void + { + $productCollection = $this->productCollectionFactory->create(); + $productCollection->addIdFilter($entityIds); + // Type can be changed depending on weight only between simple and virtual products + $productCollection->addFieldToFilter( + Product::TYPE_ID, + [ + 'in' => [ + Type::TYPE_SIMPLE, + Type::TYPE_VIRTUAL + ] + ] + ); + $productCollection->addFieldToSelect(Product::TYPE_ID); + $i = 0; + + foreach ($productCollection->getItems() as $product) { + $product->setData(ProductAttributeInterface::CODE_HAS_WEIGHT, $value); + $oldTypeId = $product->getTypeId(); + $this->typeTransitionManager->processProduct($product); + + if ($oldTypeId !== $product->getTypeId()) { + $i++; + $this->saveTypeIdValue($product); + + // save collected data every 1000 rows + if ($i % 1000 === 0) { + $this->processTypeIdValues(); + } + } + } + + $this->processTypeIdValues(); + } + + /** + * Save type id value to be updated + * + * @param Product $product + * @return $this + */ + private function saveTypeIdValue($product): self + { + $typeId = $product->getTypeId(); + + if (!array_key_exists($typeId, $this->typeIdValuesToSave)) { + $this->typeIdValuesToSave[$typeId] = []; + } + + $this->typeIdValuesToSave[$typeId][] = $product->getId(); + + return $this; + } + + /** + * Update type id values + * + * @return $this + */ + private function processTypeIdValues(): self + { + $connection = $this->getConnection(); + $table = $this->getTable('catalog_product_entity'); + + foreach ($this->typeIdValuesToSave as $typeId => $entityIds) { + $connection->update( + $table, + ['type_id' => $typeId], + ['entity_id IN (?)' => $entityIds] + ); + } + $this->typeIdValuesToSave = []; + + return $this; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php index ffcb476465dc4..2b11d62961007 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php @@ -5,62 +5,72 @@ */ namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Helper\Form; -class WeightTest extends \PHPUnit\Framework\TestCase +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Data\Form\Element\Radios; +use Magento\Framework\Locale\Format; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class WeightTest extends TestCase { /** - * @var \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight + * @var Weight */ protected $_model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Radios|MockObject */ protected $weightSwitcher; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Factory|MockObject */ protected $factory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $collectionFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Format|MockObject */ protected $localeFormat; protected function setUp() { $this->weightSwitcher = $this->createPartialMock( - \Magento\Framework\Data\Form\Element\Radios::class, + Radios::class, ['setId', 'setName', 'setLabel', 'setForm'] ); - $this->weightSwitcher->expects($this->any())->method('setId')->will($this->returnSelf()); - $this->weightSwitcher->expects($this->any())->method('setName')->will($this->returnSelf()); - $this->weightSwitcher->expects($this->any())->method('setLabel')->will($this->returnSelf()); + $this->weightSwitcher->method('setId')->will($this->returnSelf()); + $this->weightSwitcher->method('setName')->will($this->returnSelf()); + $this->weightSwitcher->method('setLabel')->will($this->returnSelf()); - $this->factory = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class); + $this->factory = $this->createMock(Factory::class); $this->factory->expects( $this->once() )->method( 'create' )->with( $this->equalTo('radios') - )->will( - $this->returnValue($this->weightSwitcher) + )->willReturn( + $this->weightSwitcher ); - $this->localeFormat = $this->createMock(\Magento\Framework\Locale\Format::class); + $this->localeFormat = $this->createMock(Format::class); $this->collectionFactory = $this->createPartialMock( - \Magento\Framework\Data\Form\Element\CollectionFactory::class, + CollectionFactory::class, ['create'] ); - $this->_model = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( - \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight::class, + $this->_model = (new ObjectManager($this))->getObject( + Weight::class, [ 'factoryElement' => $this->factory, 'factoryCollection' => $this->collectionFactory, @@ -71,10 +81,8 @@ protected function setUp() public function testSetForm() { - $form = $this->createMock(\Magento\Framework\Data\Form::class); - $this->weightSwitcher->expects( - $this->any() - )->method( + $form = $this->createMock(Form::class); + $this->weightSwitcher->method( 'setForm' )->with( $this->equalTo($form) @@ -87,9 +95,7 @@ public function testSetForm() public function testGetEscapedValue() { - $this->localeFormat->expects( - $this->any() - )->method( + $this->localeFormat->method( 'getPriceFormat' )->willReturn([ 'precision' => 2, @@ -97,7 +103,7 @@ public function testGetEscapedValue() 'groupSymbol' => '.', ]); - $this->_model->setValue('30000.4'); + $this->_model->setValue(30000.4); $this->_model->setEntityAttribute(true); $return = $this->_model->getEscapedValue('30000.4'); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php new file mode 100644 index 0000000000000..0027fd9fecc11 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php @@ -0,0 +1,338 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Factory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Edit\WeightResolver; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\TypeTransitionManager; +use Magento\Catalog\Model\ResourceModel\Product\Action; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\Entity\Context; +use Magento\Eav\Model\Entity\Type as EntityType; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ActionTest extends TestCase +{ + private const ENTITY_IDS = [1, 2, 5, 10]; + + /** + * @var Action + */ + private $model; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var Factory|MockObject + */ + private $factoryMock; + + /** + * @var UniqueValidationInterface|MockObject + */ + private $uniqueValidatorMock; + + /** + * @var ProductCollectionFactory|MockObject + */ + private $productCollectionFactoryMock; + + /** + * @var TypeTransitionManager|MockObject + */ + private $typeTransitionManagerMock; + + /** + * @var DateTime|MockObject + */ + private $dateTimeMock; + + /** + * @var Config|MockObject + */ + private $eavConfigMock; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceMock; + + /** + * @var EntityType|MockObject + */ + private $entityTypeMock; + + /** + * @var AdapterInterface|MockObject + */ + private $connectionMock; + + /** + * @var ProductCollection|MockObject + */ + private $productCollectionMock; + + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->getMockForAbstractClass(); + $this->factoryMock = $this->getMockBuilder(Factory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->uniqueValidatorMock = $this->getMockBuilder(UniqueValidationInterface::class) + ->getMockForAbstractClass(); + $this->productCollectionFactoryMock = $this->getMockBuilder(ProductCollectionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->typeTransitionManagerMock = $this->createPartialMock( + TypeTransitionManager::class, + ['processProduct'] + ); + $this->dateTimeMock = $this->getMockBuilder(DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->eavConfigMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityTypeMock = $this->getMockBuilder(EntityType::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->contextMock->method('getEavConfig') + ->willReturn($this->eavConfigMock); + $this->contextMock->method('getResource') + ->willReturn($this->resourceMock); + $this->eavConfigMock->method('getEntityType') + ->willReturn($this->entityTypeMock); + $updatedAtAttributeMock = $this->getMockBuilder(AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->eavConfigMock->method('getAttribute') + ->willReturn($updatedAtAttributeMock); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Action::class, + [ + 'context' => $this->contextMock, + 'storeManager' => $this->storeManagerMock, + 'modelFactory' => $this->factoryMock, + 'uniqueValidator' => $this->uniqueValidatorMock, + 'dateTime' => $this->dateTimeMock, + 'productCollectionFactory' => $this->productCollectionFactoryMock, + 'typeTransitionManager' => $this->typeTransitionManagerMock, + 'data' => [] + ] + ); + } + + private function prepareAdapter() + { + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) + ->getMockForAbstractClass(); + $this->resourceMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceMock->method('getTableName') + ->willReturn('catalog_product_entity'); + } + + private function prepareProductCollection($items) + { + $this->productCollectionMock = $this->getMockBuilder(ProductCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productCollectionMock->method('addIdFilter') + ->with(static::ENTITY_IDS) + ->willReturnSelf(); + $this->productCollectionMock->method('addFieldToFilter') + ->willReturnSelf(); + $this->productCollectionMock->method('addFieldToSelect') + ->willReturnSelf(); + $this->productCollectionMock->method('getItems') + ->willReturn($items); + $this->productCollectionFactoryMock->method('create') + ->willReturn($this->productCollectionMock); + } + + /** + * @param int $hasWeight + * @param string $typeId + * @param Product[] $items + * @param int[] $entityIds + * @dataProvider updateProductHasWeightAttributesDataProvider + */ + public function testUpdateProductHasWeightAttributes($hasWeight, $typeId, $items, $entityIds) + { + $this->prepareAdapter(); + $this->prepareProductCollection($items); + $attrData = [ + ProductAttributeInterface::CODE_HAS_WEIGHT => $hasWeight + ]; + $storeId = 0; + + $this->connectionMock->expects($this->once()) + ->method('update') + ->with( + 'catalog_product_entity', + ['type_id' => $typeId], + ['entity_id IN (?)' => $entityIds] + ); + + $this->model->updateAttributes(static::ENTITY_IDS, $attrData, $storeId); + } + + /** + * Update Attributes data provider + * + * @return array + */ + public function updateProductHasWeightAttributesDataProvider() + { + return [ + [ + WeightResolver::HAS_WEIGHT, + Type::TYPE_SIMPLE, + $this->getProductsVirtualToSimple(), + static::ENTITY_IDS + ], + [ + WeightResolver::HAS_NO_WEIGHT, + Type::TYPE_VIRTUAL, + $this->getProductsSimpleToVirtual(), + static::ENTITY_IDS + ], + [ + WeightResolver::HAS_NO_WEIGHT, + Type::TYPE_VIRTUAL, + $this->getProductsMixedTypes(), + array_slice(static::ENTITY_IDS, 2, 2) + ] + ]; + } + + private function getProductsSimpleToVirtual() + { + $result = []; + + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + + $result[] = $productMock; + } + + return $result; + } + + private function getProductsVirtualToSimple() + { + $result = []; + + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + + $result[] = $productMock; + } + + return $result; + } + + private function getProductsMixedTypes() + { + $result = []; + + $i = 0; + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + + if ($i < 2) { + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + } else { + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + } + + $result[] = $productMock; + $i++; + } + + return $result; + } +} diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml index 056cf014f769a..1eb3f9a03d3f9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml @@ -17,6 +17,7 @@ <script type="text/x-magento-init"> { "#attributes-edit-form": { + "Magento_Catalog/js/product/weight-handler": {}, "Magento_Catalog/catalog/product/edit/attribute": { "validationUrl": "<?= $block->escapeJs($block->escapeUrl($block->getValidationUrl())) ?>" } diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js index 94300e31f74b5..1d7993fd98823 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js @@ -29,6 +29,14 @@ define([ return $('[data-role=weight-switcher]'); }, + /** + * Weight Change Toggle + * @returns {*|jQuery|HTMLElement} + */ + $weightChangeToggle: function () { + return $('#toggle_weight'); + }, + /** * Is locked * @returns {*} @@ -51,14 +59,39 @@ define([ this.$weight().removeClass('ignore-validate').prop('disabled', false); }, + /** + * Disabled Switcher + */ + disabledSwitcher: function () { + this.$weightSwitcher().find('input[type="radio"]').addClass('ignore-validate').prop('disabled', true); + }, + + /** + * Enabled Switcher + */ + enabledSwitcher: function () { + this.$weightSwitcher().find('input[type="radio"]').removeClass('ignore-validate').prop('disabled', false); + }, + /** * Switch Weight * @returns {*} */ switchWeight: function () { + if (this.hasWeightChangeToggle()) { + return; + } + return this.productHasWeightBySwitcher() ? this.enabled() : this.disabled(); }, + /** + * Toggle Switcher + */ + toggleSwitcher: function () { + this.isWeightChanging() ? this.enabledSwitcher() : this.disabledSwitcher(); + }, + /** * Hide weight switcher */ @@ -82,6 +115,14 @@ define([ return this.$weight.is(':visible'); }, + /** + * Has weight change toggle + * @returns {*} + */ + hasWeightChangeToggle: function () { + return this.$weightChangeToggle().is(':visible'); + }, + /** * Product has weight * @returns {Bool} @@ -90,6 +131,14 @@ define([ return $('input:checked', this.$weightSwitcher()).val() === '1'; }, + /** + * Product weight toggle is checked + * @returns {Bool} + */ + isWeightChanging: function () { + return this.$weightChangeToggle().is(':checked'); + }, + /** * Change * @param {String} data @@ -110,6 +159,10 @@ define([ if (this.hasWeightSwitcher()) { this.switchWeight(); } + + if (this.hasWeightChangeToggle()) { + this.toggleSwitcher(); + } }, /** diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less index 2074106e719db..027561423892f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less @@ -25,11 +25,13 @@ // Attributes .attributes-edit-form { .field { + .addon { + display: block; + position: relative; + } + &:not(.field-weight) { .addon { - display: block; - position: relative; - input[type="text"] { border-width: 1px; } @@ -46,6 +48,14 @@ } } } + + &.field-weight { + .addon { + .addafter { + border-width: 0; + } + } + } } .with-addon .textarea { diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less index 3756fe678a3c9..5708c5ef72518 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less @@ -340,11 +340,24 @@ } .weight-switcher { - margin-bottom: 18px; + .lib-vendor-prefix-flex-grow(1); + margin-top: 18px; - .addafter { - margin-top: 30px; - position: absolute; + .admin__control-switcher { + .label { + display: block; + float: none; + text-align: left; + width: auto; + } + + .admin__field-option { + position: relative; + } + + .admin__control-radio { + position: absolute; + } } .mage-error { From 54f93e22ae3d9387d5a8c259d6933efbcd5461e8 Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Fri, 28 Feb 2020 09:03:21 +0200 Subject: [PATCH 227/734] fixed 'wrong image gallery', 'wrong gallery behavior when query url' for swatches, 'product image updating' for dropdown type, added MFTF tests to cover all cases: #26473, #26856, #26858 --- .../AdminAssignImageBaseRoleActionGroup.xml | 25 ++ .../Catalog/Test/Mftf/Data/ProductData.xml | 8 + ...rontConfigurableOptionsThumbImagesTest.xml | 228 ++++++++++++++++++ ...ramsConfigurableOptionsThumbImagesTest.xml | 38 +++ .../view/frontend/web/js/configurable.js | 43 +++- ...minUpdateAttributeInputTypeActionGroup.xml | 20 ++ ...nfigurableSwatchOptionsThumbImagesTest.xml | 48 ++++ ...nfigurableSwatchOptionsThumbImagesTest.xml | 54 +++++ .../view/base/web/js/swatch-renderer.js | 54 +++-- lib/web/mage/gallery/gallery.js | 2 +- 10 files changed, 488 insertions(+), 32 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml new file mode 100644 index 0000000000000..aa5311d389d7a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssignImageBaseRoleActionGroup"> + <annotations> + <description>Requires the navigation to the Product Creation page. Checks the Base Role area for image.</description> + </annotations> + <arguments> + <argument name="image"/> + </arguments> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageFile(image.fileName)}}" visible="false" stepKey="expandImages"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="seeProductImageName"/> + <click selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="clickProductImage"/> + <waitForElementVisible selector="{{AdminProductImagesSection.altText}}" stepKey="seeAltTextSection"/> + <checkOption selector="{{AdminProductImagesSection.roleBase}}" stepKey="checkRoles"/> + <click selector="{{AdminSlideOutDialogSection.closeButton}}" stepKey="clickCloseButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index a44db8010a822..867f0fa9ccde5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -876,6 +876,14 @@ <data key="weight">5</data> <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> </entity> + <entity name="Magento2" type="image"> + <data key="title" unique="suffix">Magento2</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">magento2.jpg</data> + <data key="filename">magento2</data> + <data key="file_extension">jpg</data> + </entity> <entity name="Magento3" type="image"> <data key="title" unique="suffix">Magento3</data> <data key="price">1.00</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..134fd89c2c813 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml @@ -0,0 +1,228 @@ +<?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="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check thumbnail images and active image for Configurable Product"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each selected option for the configurable product"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + + <!-- Create Default Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the first option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the second option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create Configurable product --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a simple product and give it the attribute with the first option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!--Create a simple product and give it the attribute with the second option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + + <!-- Add the second simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- ConfigProduct --> + <!-- Go to Product Page (ConfigProduct) --> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToConfigProduct"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForConfigProduct"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add images for ConfigProduct --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addConfigProductMagento3"> + <argument name="image" value="Magento3"/> + </actionGroup> + + <actionGroup ref="AddProductImageActionGroup" stepKey="addConfigProductTestImageAdobe"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + <actionGroup ref="AdminAssignImageBaseRoleActionGroup" stepKey="assignTestImageAdobeBaseRole"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + + <!-- Save changes fot ConfigProduct --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigProductProduct"/> + + <!-- ChildProduct1 --> + <!-- Go to Product Page (ChildProduct1) --> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct1"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForChildProduct1"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add images for ChildProduct1 --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct1ProductImage"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct1Magento2"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AdminAssignImageRolesIfUnassignedActionGroup" stepKey="assignMagento2Role"> + <argument name="image" value="Magento2"/> + </actionGroup> + + <!-- Save changes fot ChildProduct1 --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveChildProduct1Product"/> + + <!-- ChildProduct2 --> + <!-- Go to Product Page (ChildProduct2) --> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct2"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForChildProduct2"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add image for ChildProduct2 --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct2TestImageNew"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + + <!-- Save changes fot ChildProduct2 --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveChildProduct2Product"/> + </before> + <after> + <!-- Delete Created Data --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Open ConfigProduct in Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + + <!-- Check fotorama thumbnail images (no selected options) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForNoOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeActiveTestImageAdobeForNoOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <!-- Select first option --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$getConfigAttributeOption1.label$$"/> + </actionGroup> + + <!-- Check fotorama thumbnail images (first option selected) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForFirstOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeTestImageAdobeForFirstOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeProductImageForFirstOption"> + <argument name="fileName" value="{{ProductImage.filename}}"/> + </actionGroup> + + <!-- Check active fotorama thumbnail image (first option selected) --> + <actionGroup ref="StorefrontAssertActiveProductImageActionGroup" stepKey="seeActiveMagento2ForFirstOption"> + <argument name="fileName" value="{{Magento2.filename}}"/> + </actionGroup> + + <!-- Select second option --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectSecondOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> + + <!-- Check fotorama thumbnail images (second option selected) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForSecondOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeTestImageAdobeForSecondOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <!-- Check active fotorama thumbnail image (second option selected) --> + <actionGroup ref="StorefrontAssertActiveProductImageActionGroup" stepKey="seeActiveTestImageNewForSecondOption"> + <argument name="fileName" value="{{TestImageNew.filename}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..941ae6fb84c56 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml @@ -0,0 +1,38 @@ +<?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="StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check thumbnail images and active image for Configurable Product with predefined + by query params options"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each option for the configurable product using product URL with params + to selected needed option."/> + <group value="catalog"/> + </annotations> + + <!-- Select first option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption1.value$$" + stepKey="selectFirstOptionValue"/> + <reloadPage stepKey="selectFirstOptionValueRefreshPage" after="selectFirstOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedFirstOptionToLoad" after="selectFirstOptionValueRefreshPage"/> + + <!-- Select second option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption2.value$$" + stepKey="selectSecondOptionValue"/> + <reloadPage stepKey="selectSecondOptionValueRefreshPage" after="selectSecondOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedSecondOptionToLoad" after="selectSecondOptionValueRefreshPage"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index 6f3af43bf5c7a..8fd1d761040d7 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -13,7 +13,8 @@ define([ 'priceUtils', 'priceBox', 'jquery-ui-modules/widget', - 'jquery/jquery.parsequery' + 'jquery/jquery.parsequery', + 'fotoramaVideoEvents' ], function ($, _, mageTemplate, $t, priceUtils) { 'use strict'; @@ -307,9 +308,13 @@ define([ _changeProductImage: function () { var images, initialImages = this.options.mediaGalleryInitial, - galleryObject = $(this.options.mediaGallerySelector).data('gallery'); + gallery = $(this.options.mediaGallerySelector).data('gallery'); + + if (_.isUndefined(gallery)) { + $(this.options.mediaGallerySelector).on('gallery:loaded', function () { + this._changeProductImage(); + }.bind(this)); - if (!galleryObject) { return; } @@ -325,17 +330,35 @@ define([ images = $.extend(true, [], images); images = this._setImageIndex(images); - galleryObject.updateData(images); - - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ - selectedOption: this.simpleProduct, - dataMergeStrategy: this.options.gallerySwitchStrategy - }); + gallery.updateData(images); + this._addFotoramaVideoEvents(false); } else { - galleryObject.updateData(initialImages); + gallery.updateData(initialImages); + this._addFotoramaVideoEvents(true); + } + }, + + /** + * Add video events + * + * @param {Boolean} isInitial + * @private + */ + _addFotoramaVideoEvents: function (isInitial) { + if (_.isUndefined($.mage.AddFotoramaVideoEvents)) { + return; + } + + if (isInitial) { $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); + + return; } + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ + selectedOption: this.simpleProduct, + dataMergeStrategy: this.options.gallerySwitchStrategy + }); }, /** diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml new file mode 100644 index 0000000000000..23264601cad94 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateAttributeInputTypeActionGroup"> + <annotations> + <description>Set value for the "Catalog Input Type for Store Owner" attribute option</description> + </annotations> + <arguments> + <argument name="value" type="string" defaultValue="swatch_visual"/> + </arguments> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{value}}" stepKey="setInputType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..8c90d88f51a10 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml @@ -0,0 +1,48 @@ +<?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="StorefrontConfigurableSwatchOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Check thumbnail images and active image for Configurable Product with swatch attribute"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each selected option for the configurable product"/> + <group value="swatches"/> + </annotations> + <before> + <!-- Go to created attribute (attribute page) --> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute" after="createConfigProductOption"> + <argument name="ProductAttribute" value="$$createConfigProductAttribute.default_frontend_label$$"/> + </actionGroup> + + <!-- Set 'swatch_visual' value for option "Catalog Input Type for Store Owner" --> + <actionGroup ref="AdminUpdateAttributeInputTypeActionGroup" stepKey="selectSwatchVisualInputType" after="navigateToSkuProductAttribute"/> + + <!-- Set 'yes' value for option "Update Product Preview Image" --> + <actionGroup ref="AdminUpdateProductPreviewImageActionGroup" stepKey="setUpdateProductPreviewImage" after="selectSwatchVisualInputType"/> + + <!-- Save Product Attribute --> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute" after="setUpdateProductPreviewImage"/> + </before> + + <!-- Select first option --> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageActionGroup" stepKey="selectFirstOptionValue"> + <argument name="optionName" value="$$getConfigAttributeOption1.label$$"/> + </actionGroup> + + <!-- Select second option --> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageActionGroup" stepKey="selectSecondOptionValue"> + <argument name="optionName" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..8e4c6c0217b3a --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml @@ -0,0 +1,54 @@ +<?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="StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Check thumbnail images and active image for Configurable Product with swatch attribute + with predefined by query params options"/> + <description value="Login as admin, create swatch attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each option for the configurable product using product URL with params + to selected needed option."/> + <group value="swatches"/> + </annotations> + <before> + <!-- Go to created attribute (attribute page) --> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute" after="createConfigProductOption"> + <argument name="ProductAttribute" value="$$createConfigProductAttribute.default_frontend_label$$"/> + </actionGroup> + + <!-- Set 'swatch_visual' value for option "Catalog Input Type for Store Owner" --> + <actionGroup ref="AdminUpdateAttributeInputTypeActionGroup" stepKey="selectSwatchVisualInputType" after="navigateToSkuProductAttribute"/> + + <!-- Set 'yes' value for option "Update Product Preview Image" --> + <actionGroup ref="AdminUpdateProductPreviewImageActionGroup" stepKey="setUpdateProductPreviewImage" after="selectSwatchVisualInputType"/> + + <!-- Save Product Attribute --> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute" after="setUpdateProductPreviewImage"/> + </before> + + <!-- Select first option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption1.value$$" + stepKey="selectFirstOptionValue"/> + <reloadPage stepKey="selectFirstOptionValueRefreshPage" after="selectFirstOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedFirstOptionToLoad" after="selectFirstOptionValueRefreshPage"/> + + <!-- Select second option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption2.value$$" + stepKey="selectSecondOptionValue"/> + <reloadPage stepKey="selectSecondOptionValueRefreshPage" after="selectSecondOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedSecondOptionToLoad" after="selectSecondOptionValueRefreshPage"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 8b5dfcd80deb4..3e100d2c39168 100644 --- a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -1267,6 +1267,14 @@ define([ isInitial; if (isInProductView) { + if (_.isUndefined(gallery)) { + context.find(this.options.mediaGallerySelector).on('gallery:loaded', function () { + this.updateBaseImage(images, context, isInProductView); + }.bind(this)); + + return; + } + imagesToUpdate = images.length ? this._setImageType($.extend(true, [], images)) : []; isInitial = _.isEqual(imagesToUpdate, initialImages); @@ -1276,32 +1284,36 @@ define([ imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (!_.isUndefined(gallery)) { - gallery.updateData(imagesToUpdate); - } else { - context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { - loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); - loadedGallery.updateData(imagesToUpdate); - }.bind(this)); - } - - if (isInitial) { - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); - } else { - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ - selectedOption: this.getProduct(), - dataMergeStrategy: this.options.gallerySwitchStrategy - }); - } - - if (gallery) { - gallery.first(); - } + gallery.updateData(imagesToUpdate); + this._addFotoramaVideoEvents(isInitial); } else if (justAnImage && justAnImage.img) { context.find('.product-image-photo').attr('src', justAnImage.img); } }, + /** + * Add video events + * + * @param {Boolean} isInitial + * @private + */ + _addFotoramaVideoEvents: function (isInitial) { + if (_.isUndefined($.mage.AddFotoramaVideoEvents)) { + return; + } + + if (isInitial) { + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); + + return; + } + + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ + selectedOption: this.getProduct(), + dataMergeStrategy: this.options.gallerySwitchStrategy + }); + }, + /** * Set correct indexes for image set. * diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index 65a14f77de257..02ba72f64127b 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -480,7 +480,7 @@ define([ settings.fotoramaApi.load(data); mainImageIndex = getMainImageIndex(data); - if (mainImageIndex) { + if (settings.fotoramaApi.activeIndex !== mainImageIndex) { settings.fotoramaApi.show({ index: mainImageIndex, time: 0 From 05f1a2b01bbc085b3b9db9db1217c317c1f1de42 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 28 Feb 2020 18:01:24 +0100 Subject: [PATCH 228/734] Redirect to first page after changing `product_list_limit` to value higher than current. --- .../frontend/web/js/product/list/toolbar.js | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 6589f7eb0ba48..41f670ecb082d 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -80,25 +80,49 @@ define([ ); }, - /** - * @param {String} paramName - * @param {*} paramValue - * @param {*} defaultValue - */ - changeUrl: function (paramName, paramValue, defaultValue) { + getUrlParams: function() { var decode = window.decodeURIComponent, urlPaths = this.options.url.split('?'), - baseUrl = urlPaths[0], urlParams = urlPaths[1] ? urlPaths[1].split('&') : [], - paramData = {}, - parameters, i, form, params, key, input, formKey; + params = {}, parameters, i; for (i = 0; i < urlParams.length; i++) { parameters = urlParams[i].split('='); - paramData[decode(parameters[0])] = parameters[1] !== undefined ? + params[decode(parameters[0])] = parameters[1] !== undefined ? decode(parameters[1].replace(/\+/g, '%20')) : ''; } + + return params; + }, + + getCurrentLimit: function() + { + var currentLimit = this.getUrlParams()[this.options.limit]; + + if (currentLimit === undefined) { + currentLimit = this.options.limitDefault; + } + + return currentLimit; + }, + + + /** + * @param {String} paramName + * @param {*} paramValue + * @param {*} defaultValue + */ + changeUrl: function (paramName, paramValue, defaultValue) { + var urlPaths = this.options.url.split('?'), + baseUrl = urlPaths[0], + paramData = this.getUrlParams(), + form, params, key, input, formKey; + + if (paramName === this.options.limit && paramValue > this.getCurrentLimit()) { + delete paramData['p']; + } + paramData[paramName] = paramValue; if (this.options.post) { @@ -130,6 +154,7 @@ define([ if (paramValue == defaultValue) { //eslint-disable-line eqeqeq delete paramData[paramName]; } + paramData = $.param(paramData); location.href = baseUrl + (paramData.length ? '?' + paramData : ''); } From 43b44fb2f727754e71a4406c71a6e0d610551c75 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 28 Feb 2020 11:21:27 -0600 Subject: [PATCH 229/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index da8005f44cdb1..d5fb4ec0ed891 100644 --- a/composer.lock +++ b/composer.lock @@ -7385,12 +7385,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "76693d9cb7c5f6c01ef231f184372eb784f3e555" + "reference": "f1820ff3cf8401f02137ddb1457b6a0d298e64d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/76693d9cb7c5f6c01ef231f184372eb784f3e555", - "reference": "76693d9cb7c5f6c01ef231f184372eb784f3e555", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/f1820ff3cf8401f02137ddb1457b6a0d298e64d5", + "reference": "f1820ff3cf8401f02137ddb1457b6a0d298e64d5", "shasum": "" }, "require": { @@ -7462,7 +7462,7 @@ "magento", "testing" ], - "time": "2020-02-20T21:09:13+00:00" + "time": "2020-02-27T19:58:02+00:00" }, { "name": "mikey179/vfsstream", From f25de7bffe498d35d05064814cb3671a91208c09 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 28 Feb 2020 19:13:34 +0100 Subject: [PATCH 230/734] Introduce redirect pattern during change of limit --- .../frontend/web/js/product/list/toolbar.js | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 41f670ecb082d..15a300014def7 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -23,6 +23,7 @@ define([ direction: 'product_list_dir', order: 'product_list_order', limit: 'product_list_limit', + page: 'p', modeDefault: 'grid', directionDefault: 'asc', orderDefault: 'position', @@ -80,7 +81,7 @@ define([ ); }, - getUrlParams: function() { + getUrlParams: function () { var decode = window.decodeURIComponent, urlPaths = this.options.url.split('?'), urlParams = urlPaths[1] ? urlPaths[1].split('&') : [], @@ -96,17 +97,13 @@ define([ return params; }, - getCurrentLimit: function() - { - var currentLimit = this.getUrlParams()[this.options.limit]; - - if (currentLimit === undefined) { - currentLimit = this.options.limitDefault; - } - - return currentLimit; + getCurrentLimit: function () { + return this.getUrlParams()[this.options.limit] || this.options.limitDefault; }, + getCurrentPage: function () { + return this.getUrlParams()[this.options.page] || 1; + }, /** * @param {String} paramName @@ -117,10 +114,17 @@ define([ var urlPaths = this.options.url.split('?'), baseUrl = urlPaths[0], paramData = this.getUrlParams(), - form, params, key, input, formKey; + currentPage = this.getCurrentPage(), + form, params, key, input, formKey, newPage; + + if (currentPage > 1 && paramName === this.options.limit) { + newPage = Math.floor(this.getCurrentLimit() * this.getCurrentPage() / paramValue); - if (paramName === this.options.limit && paramValue > this.getCurrentLimit()) { - delete paramData['p']; + if (newPage > 1) { + paramData[this.options.page] = newPage; + } else { + delete paramData[this.options.page]; + } } paramData[paramName] = paramValue; From 51a18f657bbd17155792147dd1436faa377482e6 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 28 Feb 2020 19:37:05 +0100 Subject: [PATCH 231/734] Fix invalid redirect pattern --- .../Catalog/view/frontend/web/js/product/list/toolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 15a300014def7..d039538582c57 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -118,7 +118,7 @@ define([ form, params, key, input, formKey, newPage; if (currentPage > 1 && paramName === this.options.limit) { - newPage = Math.floor(this.getCurrentLimit() * this.getCurrentPage() / paramValue); + newPage = Math.floor(this.getCurrentLimit() * (currentPage-1) / paramValue) + 1; if (newPage > 1) { paramData[this.options.page] = newPage; From 0bae5d5420efb58ac0be0abe317d01c3931e49c4 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 28 Feb 2020 20:40:32 +0100 Subject: [PATCH 232/734] Static Tests fix --- .../view/frontend/web/js/product/list/toolbar.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index d039538582c57..738a48b3fff8b 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -81,6 +81,9 @@ define([ ); }, + /** + * @private + */ getUrlParams: function () { var decode = window.decodeURIComponent, urlPaths = this.options.url.split('?'), @@ -97,10 +100,18 @@ define([ return params; }, + /** + * @returns {String} + * @private + */ getCurrentLimit: function () { return this.getUrlParams()[this.options.limit] || this.options.limitDefault; }, + /** + * @returns {String} + * @private + */ getCurrentPage: function () { return this.getUrlParams()[this.options.page] || 1; }, @@ -118,7 +129,7 @@ define([ form, params, key, input, formKey, newPage; if (currentPage > 1 && paramName === this.options.limit) { - newPage = Math.floor(this.getCurrentLimit() * (currentPage-1) / paramValue) + 1; + newPage = Math.floor(this.getCurrentLimit() * (currentPage - 1) / paramValue) + 1; if (newPage > 1) { paramData[this.options.page] = newPage; From f5aa61dfeec3a96be19948d54eaeb701fcfc7231 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 28 Feb 2020 22:18:50 +0100 Subject: [PATCH 233/734] One more static tests fix --- .../Catalog/view/frontend/web/js/product/list/toolbar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 738a48b3fff8b..dfc0b4291cd6e 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -88,7 +88,8 @@ define([ var decode = window.decodeURIComponent, urlPaths = this.options.url.split('?'), urlParams = urlPaths[1] ? urlPaths[1].split('&') : [], - params = {}, parameters, i; + params = {}, + parameters, i; for (i = 0; i < urlParams.length; i++) { parameters = urlParams[i].split('='); From d03d8e1e2a705e8677e680f01f0e04e899daa305 Mon Sep 17 00:00:00 2001 From: Priya Panchal <priya.panchal@krishtechnolabs.com> Date: Sat, 29 Feb 2020 13:24:39 +0530 Subject: [PATCH 234/734] Fix Report date doesn't matching in configuration setting --- .../Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index 4ac12501aa90f..1b44728870035 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -23,8 +23,10 @@ protected function _getElementHtml(AbstractElement $element) { $_months = []; for ($i = 1; $i <= 12; $i++) { - $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i, 1))->format('m'); + $month = $this->_localeDate->date(mktime(null, null, null, $i, 1))->format('m'); + $_months[$month] = $month; } + ksort($_months); $_days = []; for ($i = 1; $i <= 31; $i++) { $_days[$i] = $i < 10 ? '0' . $i : $i; From e8d3b78b81d7041d1952d4a8150a1970950a7147 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 29 Feb 2020 09:28:16 +0100 Subject: [PATCH 235/734] Fix Functional tests for pagination --- .../Test/Mftf/Data/CatalogStorefrontConfigData.xml | 8 ++++++++ ...rontProductQuickSearchUsingElasticSearch6Test.xml | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml index be04c297cec25..f3ccc00192a43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml @@ -69,4 +69,12 @@ <data key="path">catalog/frontend/grid_per_page_values</data> <data key="value">1,2</data> </entity> + <entity name="DefaultGridPerPageDefaultConfigData"> + <data key="path">catalog/frontend/grid_per_page</data> + <data key="value">12</data> + </entity> + <entity name="CustomGridPerPageDefaultConfigData"> + <data key="path">catalog/frontend/grid_per_page</data> + <data key="value">1</data> + </entity> </entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml index 0905ac0e3a70f..3cb06014234dc 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -30,6 +30,7 @@ <field key="name">Product Simple AAA</field> </createData> <magentoCLI command="config:set {{CustomGridPerPageValuesConfigData.path}} {{CustomGridPerPageValuesConfigData.value}}" stepKey="setCustomGridPerPageValues"/> + <magentoCLI command="config:set {{CustomGridPerPageDefaultConfigData.path}} {{CustomGridPerPageDefaultConfigData.value}}" stepKey="setCustomGridPerPageDefaults"/> <magentoCLI stepKey="flushConfigCache" command="cache:flush config"/> <magentoCron groups="index" stepKey="runCronIndex"/> </before> @@ -38,6 +39,7 @@ <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> <magentoCLI command="config:set {{DefaultGridPerPageValuesConfigData.path}} {{DefaultGridPerPageValuesConfigData.value}}" stepKey="setDefaultGridPerPageValues"/> + <magentoCLI command="config:set {{DefaultGridPerPageDefaultConfigData.path}} {{DefaultGridPerPageDefaultConfigData.value}}" stepKey="setDefaultGridPerPageDefaults"/> </after> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontHomePage"/> @@ -60,7 +62,13 @@ <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <seeInCurrentUrl stepKey="assertStillOnSecondPage" url="?p=2"/> - <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="assertEmptyResultsPage"/> + <dontSeeInCurrentUrl stepKey="assertRedirectedToFirstPage" url="?p=2"/> + + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> + <argument name="product" value="$createFirstProduct$"/> + </actionGroup> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> </test> </tests> From 2ec8bf5d59cc858979e83d3786b7cb7ee563ad1b Mon Sep 17 00:00:00 2001 From: Priya Panchal <priya.panchal@krishtechnolabs.com> Date: Sat, 29 Feb 2020 16:15:33 +0530 Subject: [PATCH 236/734] Added Code Optimization --- .../Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index 1b44728870035..a64cb14ed9862 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -15,6 +15,8 @@ class YtdStart extends \Magento\Config\Block\System\Config\Form\Field { /** + * Get Month and Day Element + * * @param AbstractElement $element * @return string * @SuppressWarnings(PHPMD.NPathComplexity) From 6cf0e98444aea007f76d0b0d13ed78d627bd4a71 Mon Sep 17 00:00:00 2001 From: aniketp <aniket.prajapati@magedelight.com> Date: Sat, 29 Feb 2020 17:38:48 +0530 Subject: [PATCH 237/734] setbilling address graphql save address in addressbook --- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 20a3677ef1feb..bfdd1717a01e3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -90,7 +90,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b ); } - $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); + $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput,$sameAsShipping); $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } @@ -109,16 +109,17 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b private function createBillingAddress( ContextInterface $context, ?int $customerAddressId, - ?array $addressInput + ?array $addressInput, + $sameAsShipping ): Address { if (null === $customerAddressId) { $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); $customerId = $context->getUserId(); // need to save address only for registered user and if save_in_address_book = true - if (0 !== $customerId + if ((0 !== $customerId && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] === true + && (bool)$addressInput['save_in_address_book'] && $sameAsShipping !== true) === true ) { $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); } From 20d9e7f294d6ad875ce0442e4c712f4c7aa7ed56 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Sun, 1 Mar 2020 09:00:00 +0700 Subject: [PATCH 238/734] Update after review --- .../Quote/Model/Cart/CartTotalRepository.php | 11 +--- .../Model/Cart/CartTotalRepositoryTest.php | 62 +++++++++++-------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php index d686ade787ff5..8f7e6504cb7d8 100644 --- a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php +++ b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php @@ -83,11 +83,7 @@ public function __construct( } /** - * Get cart total repository - * - * @param int $cartId - * @return QuoteTotalsInterface - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @inheritdoc */ public function get($cartId): QuoteTotalsInterface { @@ -109,10 +105,7 @@ public function get($cartId): QuoteTotalsInterface $addressTotalsData, QuoteTotalsInterface::class ); - $items = []; - foreach ($quote->getAllVisibleItems() as $index => $item) { - $items[$index] = $this->itemConverter->modelToDataObject($item); - } + $items = array_map([$this->itemConverter, 'modelToDataObject'], $quote->getAllVisibleItems()); $calculatedTotals = $this->totalsConverter->process($addressTotals); $quoteTotals->setTotalSegments($calculatedTotals); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php index 14c7f073b9b89..8c67ff4314b17 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php @@ -20,6 +20,8 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Quote\Model\Cart\TotalsConverter; +use Magento\Quote\Api\Data\TotalsInterfaceFactory; +use Magento\Quote\Api\Data\TotalsInterface as QuoteTotalsInterface; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\MockObject\MockObject; @@ -30,24 +32,12 @@ */ class CartTotalRepositoryTest extends TestCase { - /** - * @var int - */ private const STUB_CART_ID = 12; - /** - * @var int - */ private const STUB_ITEMS_QTY = 100; - /** - * @var string - */ private const STUB_CURRENCY_CODE = 'en_US'; - /** - * @var string - */ private const STUB_COUPON = 'coupon'; /** @@ -76,7 +66,7 @@ class CartTotalRepositoryTest extends TestCase private $quoteMock; /** - * @var \Magento\Quote\Api\Data\TotalsInterfaceFactory|MockObject + * @var TotalsInterfaceFactory|MockObject */ private $totalsFactoryMock; @@ -104,10 +94,14 @@ protected function setUp() { $this->objectManager = new ObjectManagerHelper($this); $this->totalsFactoryMock = $this->createPartialMock( - \Magento\Quote\Api\Data\TotalsInterfaceFactory::class, - ['create'] + TotalsInterfaceFactory::class, + [ + 'create' + ] ); - $this->quoteMock = $this->createPartialMock(Quote::class, [ + $this->quoteMock = $this->createPartialMock( + Quote::class, + [ 'isVirtual', 'getShippingAddress', 'getBillingAddress', @@ -116,19 +110,31 @@ protected function setUp() 'getQuoteCurrencyCode', 'getItemsQty', 'collectTotals' - ]); - $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); + ] + ); + $this->quoteRepositoryMock = $this->createMock( + CartRepositoryInterface::class + ); $this->addressMock = $this->createPartialMock( Address::class, - ['getData', 'getTotals'] + [ + 'getData', + 'getTotals' + ] + ); + $this->dataObjectHelperMock = $this->getMockBuilder( + DataObjectHelper::class + )->disableOriginalConstructor()->getMock(); + $this->converterMock = $this->createMock( + ItemConverter::class ); - $this->dataObjectHelperMock = $this->getMockBuilder(DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->converterMock = $this->createMock(ItemConverter::class); - $this->couponServiceMock = $this->createMock(CouponManagementInterface::class); - $this->totalsConverterMock = $this->createMock(TotalsConverter::class); + $this->couponServiceMock = $this->createMock( + CouponManagementInterface::class + ); + $this->totalsConverterMock = $this->createMock( + TotalsConverter::class + ); $this->model = new CartTotalRepository( $this->totalsFactoryMock, @@ -189,8 +195,10 @@ public function testGetCartTotal($isVirtual, $getAddressType): void ->method('getTotals') ->willReturn($addressTotals); - $totalsMock = $this->createMock(\Magento\Quote\Api\Data\TotalsInterface::class); - $this->totalsFactoryMock->expects($this->once())->method('create')->willReturn($totalsMock); + $totalsMock = $this->createMock(QuoteTotalsInterface::class); + $this->totalsFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($totalsMock); $this->dataObjectHelperMock->expects($this->once())->method('populateWithArray'); $this->converterMock->expects($this->once()) ->method('modelToDataObject') From c2a27c36e9884c74825a7a5c6d1205bb3b3c8082 Mon Sep 17 00:00:00 2001 From: Michele Fantetti <mfantetti@ittweb.net> Date: Sun, 1 Mar 2020 19:55:52 +0100 Subject: [PATCH 239/734] Add Italy States --- .../Setup/Patch/Data/AddDataForItaly.php | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php new file mode 100644 index 0000000000000..6da0792d0a3e9 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See PLPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Directory\Setup\DataInstallerFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add Italy States + */ +class AddDataForItaly implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForItaly() + ); + } + + /** + * Italy states data. + * + * @return array + */ + private function getDataForItaly() + { + return [ + ['IT', 'AG', 'Agrigento'], + ['IT', 'AL', 'Alessandria'], + ['IT', 'AN', 'Ancona'], + ['IT', 'AO', 'Aosta'], + ['IT', 'AQ', 'L\'Aquila'], + ['IT', 'AR', 'Arezzo'], + ['IT', 'AP', 'Ascoli-Piceno'], + ['IT', 'AT', 'Asti'], + ['IT', 'AV', 'Avellino'], + ['IT', 'BA', 'Bari'], + ['IT', 'BT', 'Barletta-Andria-Trani'], + ['IT', 'BL', 'Belluno'], + ['IT', 'BN', 'Benevento'], + ['IT', 'BG', 'Bergamo'], + ['IT', 'BI', 'Biella'], + ['IT', 'BO', 'Bologna'], + ['IT', 'BZ', 'Bolzano'], + ['IT', 'BS', 'Brescia'], + ['IT', 'BR', 'Brindisi'], + ['IT', 'CA', 'Cagliari'], + ['IT', 'CL', 'Caltanissetta'], + ['IT', 'CB', 'Campobasso'], + ['IT', 'CI', 'Carbonia Iglesias'], + ['IT', 'CE', 'Caserta'], + ['IT', 'CT', 'Catania'], + ['IT', 'CZ', 'Catanzaro'], + ['IT', 'CH', 'Chieti'], + ['IT', 'CO', 'Como'], + ['IT', 'CS', 'Cosenza'], + ['IT', 'CR', 'Cremona'], + ['IT', 'KR', 'Crotone'], + ['IT', 'CN', 'Cuneo'], + ['IT', 'EN', 'Enna'], + ['IT', 'FM', 'Fermo'], + ['IT', 'FE', 'Ferrara'], + ['IT', 'FI', 'Firenze'], + ['IT', 'FG', 'Foggia'], + ['IT', 'FC', 'Forli-Cesena'], + ['IT', 'FR', 'Frosinone'], + ['IT', 'GE', 'Genova'], + ['IT', 'GO', 'Gorizia'], + ['IT', 'GR', 'Grosseto'], + ['IT', 'IM', 'Imperia'], + ['IT', 'IS', 'Isernia'], + ['IT', 'SP', 'La-Spezia'], + ['IT', 'LT', 'Latina'], + ['IT', 'LE', 'Lecce'], + ['IT', 'LC', 'Lecco'], + ['IT', 'LI', 'Livorno'], + ['IT', 'LO', 'Lodi'], + ['IT', 'LU', 'Lucca'], + ['IT', 'MC', 'Macerata'], + ['IT', 'MN', 'Mantova'], + ['IT', 'MS', 'Massa-Carrara'], + ['IT', 'MT', 'Matera'], + ['IT', 'VS', 'Medio Campidano'], + ['IT', 'ME', 'Messina'], + ['IT', 'MI', 'Milano'], + ['IT', 'MO', 'Modena'], + ['IT', 'MB', 'Monza-Brianza'], + ['IT', 'NA', 'Napoli'], + ['IT', 'NO', 'Novara'], + ['IT', 'NU', 'Nuoro'], + ['IT', 'OG', 'Ogliastra'], + ['IT', 'OT', 'Olbia Tempio'], + ['IT', 'OR', 'Oristano'], + ['IT', 'PD', 'Padova'], + ['IT', 'PA', 'Palermo'], + ['IT', 'PR', 'Parma'], + ['IT', 'PV', 'Pavia'], + ['IT', 'PG', 'Perugia'], + ['IT', 'PU', 'Pesaro-Urbino'], + ['IT', 'PE', 'Pescara'], + ['IT', 'PC', 'Piacenza'], + ['IT', 'PI', 'Pisa'], + ['IT', 'PT', 'Pistoia'], + ['IT', 'PN', 'Pordenone'], + ['IT', 'PZ', 'Potenza'], + ['IT', 'PO', 'Prato'], + ['IT', 'RG', 'Ragusa'], + ['IT', 'RA', 'Ravenna'], + ['IT', 'RC', 'Reggio-Calabria'], + ['IT', 'RE', 'Reggio-Emilia'], + ['IT', 'RI', 'Rieti'], + ['IT', 'RN', 'Rimini'], + ['IT', 'RM', 'Roma'], + ['IT', 'RO', 'Rovigo'], + ['IT', 'SA', 'Salerno'], + ['IT', 'SS', 'Sassari'], + ['IT', 'SV', 'Savona'], + ['IT', 'SI', 'Siena'], + ['IT', 'SR', 'Siracusa'], + ['IT', 'SO', 'Sondrio'], + ['IT', 'TA', 'Taranto'], + ['IT', 'TE', 'Teramo'], + ['IT', 'TR', 'Terni'], + ['IT', 'TO', 'Torino'], + ['IT', 'TP', 'Trapani'], + ['IT', 'TN', 'Trento'], + ['IT', 'TV', 'Treviso'], + ['IT', 'TS', 'Trieste'], + ['IT', 'UD', 'Udine'], + ['IT', 'VA', 'Varese'], + ['IT', 'VE', 'Venezia'], + ['IT', 'VB', 'Verbania'], + ['IT', 'VC', 'Vercelli'], + ['IT', 'VR', 'Verona'], + ['IT', 'VV', 'Vibo-Valentia'], + ['IT', 'VI', 'Vicenza'], + ['IT', 'VT', 'Viterbo'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} From 972d382359bbd00286fa1a64ff62fed432db6e5b Mon Sep 17 00:00:00 2001 From: WaPoNe <michele.fantetti@gmail.com> Date: Sun, 1 Mar 2020 20:01:51 +0100 Subject: [PATCH 240/734] Add Italy States --- app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php index 6da0792d0a3e9..44288c9f2a276 100644 --- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See PLPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); From 18954f6d2368ac9224ef1fd8d8a894771d01b147 Mon Sep 17 00:00:00 2001 From: aniketp <aniket.prajapati@magedelight.com> Date: Mon, 2 Mar 2020 11:55:15 +0530 Subject: [PATCH 241/734] Add Whitespace before the argument --- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index bfdd1717a01e3..b7cddab9273ea 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -90,7 +90,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b ); } - $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput,$sameAsShipping); + $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput, $sameAsShipping); $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } @@ -101,6 +101,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b * @param ContextInterface $context * @param int|null $customerAddressId * @param array $addressInput + * @param bool $sameAsShipping * @return Address * @throws GraphQlAuthorizationException * @throws GraphQlInputException From 5da031ed4cae912235fe0ecc44eb4e1cbe94bffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Sat, 29 Feb 2020 01:35:12 +0100 Subject: [PATCH 242/734] Remove unnecessary chart.js files, move retrieving request params to controllers, apply review requirements --- .../Backend/Block/Dashboard/Totals.php | 3 +- .../Adminhtml/Dashboard/Chart/Amounts.php | 8 +- .../Adminhtml/Dashboard/Chart/Orders.php | 8 +- .../Magento/Backend/Helper/Dashboard/Data.php | 47 +- .../Magento/Backend/Model/Dashboard/Chart.php | 44 +- .../Backend/Model/Dashboard/Chart/Date.php | 23 +- .../Backend/Model/Dashboard/Period.php | 56 + .../Test/Unit/Helper/Dashboard/DataTest.php | 22 +- .../Test/Unit/Model/Dashboard/ChartTest.php | 43 +- .../Test/Unit/Model/Dashboard/PeriodTest.php | 49 + .../Backend/ViewModel/ChartDisabled.php | 7 +- .../Backend/ViewModel/ChartsPeriod.php | 36 +- .../layout/adminhtml_dashboard_index.xml | 3 + .../adminhtml/templates/dashboard/chart.phtml | 7 + .../view/adminhtml/web/js/dashboard/chart.js | 35 +- .../Magento/Ui/view/base/requirejs-config.js | 4 +- .../Block/Dashboard/Tab/OrdersTest.php | 94 - lib/web/chartjs/Chart.bundle.js | 20755 ---------------- lib/web/chartjs/Chart.bundle.min.js | 7 - lib/web/chartjs/Chart.css | 47 - lib/web/chartjs/Chart.js | 16151 ------------ lib/web/chartjs/Chart.min.css | 1 - 22 files changed, 246 insertions(+), 37204 deletions(-) create mode 100644 app/code/Magento/Backend/Model/Dashboard/Period.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/OrdersTest.php delete mode 100644 lib/web/chartjs/Chart.bundle.js delete mode 100644 lib/web/chartjs/Chart.bundle.min.js delete mode 100644 lib/web/chartjs/Chart.css delete mode 100644 lib/web/chartjs/Chart.js delete mode 100644 lib/web/chartjs/Chart.min.css diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index 3921148acd33a..7da109c2fb602 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -8,6 +8,7 @@ namespace Magento\Backend\Block\Dashboard; use Magento\Backend\Block\Template\Context; +use Magento\Backend\Model\Dashboard\Period; use Magento\Framework\Module\Manager; use Magento\Reports\Model\ResourceModel\Order\Collection; use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; @@ -61,7 +62,7 @@ protected function _prepareLayout() ) || $this->getRequest()->getParam( 'group' ); - $period = $this->getRequest()->getParam('period', '24h'); + $period = $this->getRequest()->getParam('period', Period::PERIOD_24_HOURS); /* @var $collection Collection */ $collection = $this->_collectionFactory->create()->addCreateAtPeriodFilter( diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php index f46ab3a3a7d26..a668296f5bf6f 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php @@ -54,7 +54,13 @@ public function __construct( public function execute(): Json { $data = [ - 'data' => $this->chart->getByPeriod($this->_request->getParam('period'), 'revenue'), + 'data' => $this->chart->getByPeriod( + $this->_request->getParam('period'), + 'revenue', + $this->_request->getParam('store'), + $this->_request->getParam('website'), + $this->_request->getParam('group') + ), 'label' => __('Revenue') ]; diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php index 11d3d875f1626..b9f7c86f17dc0 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php @@ -54,7 +54,13 @@ public function __construct( public function execute(): Json { $data = [ - 'data' => $this->chart->getByPeriod($this->_request->getParam('period'), 'quantity'), + 'data' => $this->chart->getByPeriod( + $this->_request->getParam('period'), + 'quantity', + $this->_request->getParam('store'), + $this->_request->getParam('website'), + $this->_request->getParam('group') + ), 'label' => __('Quantity') ]; diff --git a/app/code/Magento/Backend/Helper/Dashboard/Data.php b/app/code/Magento/Backend/Helper/Dashboard/Data.php index c06e7ea3ba38f..f691d2b7cd4b9 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Data.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Data.php @@ -5,8 +5,14 @@ */ namespace Magento\Backend\Helper\Dashboard; +use Magento\Backend\Model\Dashboard\Period; use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\Helper\AbstractHelper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Store\Model\StoreManagerInterface; /** * Data helper for dashboard @@ -14,10 +20,10 @@ * @api * @since 100.0.2 */ -class Data extends \Magento\Framework\App\Helper\AbstractHelper +class Data extends AbstractHelper { /** - * @var \Magento\Framework\Data\Collection\AbstractDb + * @var AbstractDb */ protected $_stores; @@ -27,25 +33,33 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper protected $_installDate; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ private $_storeManager; /** - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @var Period + */ + private $period; + + /** + * @param Context $context + * @param StoreManagerInterface $storeManager * @param DeploymentConfig $deploymentConfig + * @param Period|null $period + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException */ public function __construct( - \Magento\Framework\App\Helper\Context $context, - \Magento\Store\Model\StoreManagerInterface $storeManager, - DeploymentConfig $deploymentConfig + Context $context, + StoreManagerInterface $storeManager, + DeploymentConfig $deploymentConfig, + ?Period $period = null ) { - parent::__construct( - $context - ); + parent::__construct($context); $this->_installDate = $deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE); $this->_storeManager = $storeManager; + $this->period = $period ?? ObjectManager::getInstance()->get(Period::class); } /** @@ -74,17 +88,14 @@ public function countStores() /** * Prepare array with periods for dashboard graphs * + * @deprecated periods were moved to it's own class + * @see Period::getDatePeriods() + * * @return array */ public function getDatePeriods() { - return [ - '24h' => __('Last 24 Hours'), - '7d' => __('Last 7 Days'), - '1m' => __('Current Month'), - '1y' => __('YTD'), - '2y' => __('2YTD') - ]; + return $this->period->getDatePeriods(); } /** diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart.php b/app/code/Magento/Backend/Model/Dashboard/Chart.php index d986a7b8f7063..346c1153ac74e 100644 --- a/app/code/Magento/Backend/Model/Dashboard/Chart.php +++ b/app/code/Magento/Backend/Model/Dashboard/Chart.php @@ -7,21 +7,14 @@ namespace Magento\Backend\Model\Dashboard; -use Magento\Backend\Helper\Dashboard\Data as DataHelper; use Magento\Backend\Helper\Dashboard\Order as OrderHelper; use Magento\Backend\Model\Dashboard\Chart\Date; -use Magento\Framework\App\RequestInterface; /** * Dashboard chart data retriever */ class Chart { - /** - * @var RequestInterface - */ - private $request; - /** * @var Date */ @@ -33,47 +26,52 @@ class Chart private $orderHelper; /** - * @var DataHelper + * @var Period */ - private $dataHelper; + private $period; /** * Chart constructor. - * @param RequestInterface $request * @param Date $dateRetriever * @param OrderHelper $orderHelper - * @param DataHelper $dataHelper + * @param Period $period */ public function __construct( - RequestInterface $request, Date $dateRetriever, OrderHelper $orderHelper, - DataHelper $dataHelper + Period $period ) { - $this->request = $request; $this->dateRetriever = $dateRetriever; $this->orderHelper = $orderHelper; - $this->dataHelper = $dataHelper; + $this->period = $period; } /** - * Get chart data by period and chart type parameter + * Get chart data by period and chart type parameter, with possibility to pass scope parameters * * @param string $period * @param string $chartParam + * @param string|null $store + * @param string|null $website + * @param string|null $group * * @return array */ - public function getByPeriod($period, $chartParam): array - { - $this->orderHelper->setParam('store', $this->request->getParam('store')); - $this->orderHelper->setParam('website', $this->request->getParam('website')); - $this->orderHelper->setParam('group', $this->request->getParam('group')); + public function getByPeriod( + string $period, + string $chartParam, + string $store = null, + string $website = null, + string $group = null + ): array { + $this->orderHelper->setParam('store', $store); + $this->orderHelper->setParam('website', $website); + $this->orderHelper->setParam('group', $group); - $availablePeriods = array_keys($this->dataHelper->getDatePeriods()); + $availablePeriods = array_keys($this->period->getDatePeriods()); $this->orderHelper->setParam( 'period', - $period && in_array($period, $availablePeriods, false) ? $period : '24h' + $period && in_array($period, $availablePeriods, false) ? $period : Period::PERIOD_24_HOURS ); $dates = $this->dateRetriever->getByPeriod($period); diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php index c91c42f940228..7e3f853714a34 100644 --- a/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php +++ b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php @@ -8,6 +8,7 @@ namespace Magento\Backend\Model\Dashboard\Chart; use DateTimeZone; +use Magento\Backend\Model\Dashboard\Period; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; @@ -17,14 +18,14 @@ class Date { /** - * @var TimezoneInterface + * @var CollectionFactory */ - private $localeDate; + private $collectionFactory; /** - * @var CollectionFactory + * @var TimezoneInterface */ - private $collectionFactory; + private $localeDate; /** * Date constructor. @@ -35,8 +36,8 @@ public function __construct( CollectionFactory $collectionFactory, TimezoneInterface $localeDate ) { - $this->localeDate = $localeDate; $this->collectionFactory = $collectionFactory; + $this->localeDate = $localeDate; } /** @@ -46,7 +47,7 @@ public function __construct( * * @return array */ - public function getByPeriod($period): array + public function getByPeriod(string $period): array { [$dateStart, $dateEnd] = $this->collectionFactory->create()->getDateRange( $period, @@ -60,7 +61,7 @@ public function getByPeriod($period): array $dateStart->setTimezone(new DateTimeZone($timezoneLocal)); $dateEnd->setTimezone(new DateTimeZone($timezoneLocal)); - if ($period === '24h') { + if ($period === Period::PERIOD_24_HOURS) { $dateEnd->modify('-1 hour'); } else { $dateEnd->setTime(23, 59, 59); @@ -71,13 +72,13 @@ public function getByPeriod($period): array while ($dateStart <= $dateEnd) { switch ($period) { - case '7d': - case '1m': + case Period::PERIOD_7_DAYS: + case Period::PERIOD_1_MONTH: $d = $dateStart->format('Y-m-d'); $dateStart->modify('+1 day'); break; - case '1y': - case '2y': + case Period::PERIOD_1_YEAR: + case Period::PERIOD_2_YEARS: $d = $dateStart->format('Y-m'); $dateStart->modify('first day of next month'); break; diff --git a/app/code/Magento/Backend/Model/Dashboard/Period.php b/app/code/Magento/Backend/Model/Dashboard/Period.php new file mode 100644 index 0000000000000..28286129e8a68 --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Period.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Dashboard; + +/** + * Dashboard period info retriever + */ +class Period +{ + public const PERIOD_24_HOURS = '24h'; + public const PERIOD_7_DAYS = '7d'; + public const PERIOD_1_MONTH = '1m'; + public const PERIOD_1_YEAR = '1y'; + public const PERIOD_2_YEARS = '2y'; + + private const PERIOD_UNIT_HOUR = 'hour'; + private const PERIOD_UNIT_DAY = 'day'; + private const PERIOD_UNIT_MONTH = 'month'; + + /** + * Prepare array with periods for dashboard graphs + * + * @return array + */ + public function getDatePeriods(): array + { + return [ + static::PERIOD_24_HOURS => __('Last 24 Hours'), + static::PERIOD_7_DAYS => __('Last 7 Days'), + static::PERIOD_1_MONTH => __('Current Month'), + static::PERIOD_1_YEAR => __('YTD'), + static::PERIOD_2_YEARS => __('2YTD') + ]; + } + + /** + * Prepare array with periods mapping to chart units + * + * @return array + */ + public function getPeriodChartUnits(): array + { + return [ + static::PERIOD_24_HOURS => static::PERIOD_UNIT_HOUR, + static::PERIOD_7_DAYS => static::PERIOD_UNIT_DAY, + static::PERIOD_1_MONTH => static::PERIOD_UNIT_DAY, + static::PERIOD_1_YEAR => static::PERIOD_UNIT_MONTH, + static::PERIOD_2_YEARS => static::PERIOD_UNIT_MONTH + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php index 21c72cb6b4477..7943c5e7fdcb1 100644 --- a/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php +++ b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Backend\Test\Unit\Helper\Dashboard; @@ -31,7 +30,7 @@ class DataTest extends TestCase private const STUB_CHART_DATA_HASH = '52870842b23068a78220e01eb9d4404d'; /** - * @var \Magento\Backend\Helper\Dashboard\Data + * @var HelperData */ private $helper; @@ -54,7 +53,7 @@ protected function setUp() $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); $this->deploymentConfigMock->expects($this->once())->method('get') ->with(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE) - ->will($this->returnValue(self::STUB_PATH_INSTALL)); + ->willReturn(self::STUB_PATH_INSTALL); $objectManager = new ObjectManager($this); $this->helper = $objectManager->getObject( @@ -81,23 +80,6 @@ public function testGetStoresWhenStoreAttributeIsNull() $this->assertEquals($storeCollectionMock, $this->helper->getStores()); } - /** - * Test getDatePeriods() method - */ - public function testGetDatePeriods() - { - $this->assertEquals( - [ - '24h' => (string)__('Last 24 Hours'), - '7d' => (string)__('Last 7 Days'), - '1m' => (string)__('Current Month'), - '1y' => (string)__('YTD'), - '2y' => (string)__('2YTD') - ], - $this->helper->getDatePeriods() - ); - } - /** * Test getChartDataHash() method */ diff --git a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php index 5aedb953dab37..fadd9e170e0b0 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php @@ -3,13 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Test\Unit\Model\Dashboard; -use Magento\Backend\Helper\Dashboard\Data as DataHelper; use Magento\Backend\Helper\Dashboard\Order as OrderHelper; use Magento\Backend\Model\Dashboard\Chart; use Magento\Backend\Model\Dashboard\Chart\Date as DateRetriever; -use Magento\Framework\App\RequestInterface; +use Magento\Backend\Model\Dashboard\Period; use Magento\Framework\DataObject; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Reports\Model\ResourceModel\Order\Collection; @@ -28,21 +29,11 @@ class ChartTest extends TestCase */ private $objectManagerHelper; - /** - * @var RequestInterface|MockObject - */ - private $requestMock; - /** * @var DateRetriever|MockObject */ private $dateRetrieverMock; - /** - * @var DataHelper|MockObject - */ - private $dataHelperMock; - /** * @var OrderHelper|MockObject */ @@ -57,25 +48,10 @@ protected function setUp() { $this->objectManagerHelper = new ObjectManager($this); - $this->requestMock = $this->getMockForAbstractClass(RequestInterface::class); - $this->requestMock->method('getParam')->willReturn(null); - $this->dateRetrieverMock = $this->getMockBuilder(DateRetriever::class) ->disableOriginalConstructor() ->getMock(); - $this->dataHelperMock = $this->getMockBuilder(DataHelper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dataHelperMock->method('getDatePeriods') - ->willReturn([ - '24h' => __('Last 24 Hours'), - '7d' => __('Last 7 Days'), - '1m' => __('Current Month'), - '1y' => __('YTD'), - '2y' => __('2YTD') - ]); - $this->orderHelperMock = $this->getMockBuilder(OrderHelper::class) ->disableOriginalConstructor() ->getMock(); @@ -86,13 +62,14 @@ protected function setUp() $this->orderHelperMock->method('getCollection') ->willReturn($this->collectionMock); + $period = $this->objectManagerHelper->getObject(Period::class); + $this->model = $this->objectManagerHelper->getObject( Chart::class, [ - 'request' => $this->requestMock, 'dateRetriever' => $this->dateRetrieverMock, - 'dataHelper' => $this->dataHelperMock, - 'orderHelper' => $this->orderHelperMock + 'orderHelper' => $this->orderHelperMock, + 'period' => $period ] ); } @@ -156,7 +133,7 @@ public function getByPeriodDataProvider(): array { return [ [ - '7d', + Period::PERIOD_7_DAYS, 'revenue', [ [ @@ -178,7 +155,7 @@ public function getByPeriodDataProvider(): array ] ], [ - '1m', + Period::PERIOD_1_MONTH, 'quantity', [ [ @@ -200,7 +177,7 @@ public function getByPeriodDataProvider(): array ] ], [ - '1y', + Period::PERIOD_1_YEAR, 'quantity', [ [ diff --git a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php new file mode 100644 index 0000000000000..5c71afdde3109 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Model\Dashboard; + +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class PeriodTest extends TestCase +{ + /** + * @var Period + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Period::class, + [] + ); + } + + /** + * Test getDatePeriods() method + */ + public function testGetDatePeriods() + { + $this->assertEquals( + [ + Period::PERIOD_24_HOURS => (string)__('Last 24 Hours'), + Period::PERIOD_7_DAYS => (string)__('Last 7 Days'), + Period::PERIOD_1_MONTH => (string)__('Current Month'), + Period::PERIOD_1_YEAR => (string)__('YTD'), + Period::PERIOD_2_YEARS => (string)__('2YTD') + ], + $this->model->getDatePeriods() + ); + } +} diff --git a/app/code/Magento/Backend/ViewModel/ChartDisabled.php b/app/code/Magento/Backend/ViewModel/ChartDisabled.php index f40e757ee7b64..f71a2d26441ef 100644 --- a/app/code/Magento/Backend/ViewModel/ChartDisabled.php +++ b/app/code/Magento/Backend/ViewModel/ChartDisabled.php @@ -22,6 +22,11 @@ class ChartDisabled implements ArgumentInterface */ private const XML_PATH_ENABLE_CHARTS = 'admin/dashboard/enable_charts'; + /** + * Route to Stores -> Configuration section + */ + private const ROUTE_SYSTEM_CONFIG = 'adminhtml/system_config/edit'; + /** * @var UrlInterface */ @@ -52,7 +57,7 @@ public function __construct( public function getConfigUrl(): string { return $this->urlBuilder->getUrl( - 'adminhtml/system_config/edit', + static::ROUTE_SYSTEM_CONFIG, ['section' => 'admin', '_fragment' => 'admin_dashboard-link'] ); } diff --git a/app/code/Magento/Backend/ViewModel/ChartsPeriod.php b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php index c118cb7c02a5f..effce0dc60585 100644 --- a/app/code/Magento/Backend/ViewModel/ChartsPeriod.php +++ b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php @@ -7,7 +7,8 @@ namespace Magento\Backend\ViewModel; -use Magento\Backend\Helper\Dashboard\Data; +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\View\Element\Block\ArgumentInterface; /** @@ -16,16 +17,25 @@ class ChartsPeriod implements ArgumentInterface { /** - * @var Data + * @var Period */ - private $dataHelper; + private $period; /** - * @param Data $dataHelper + * @var Json */ - public function __construct(Data $dataHelper) - { - $this->dataHelper = $dataHelper; + private $serializer; + + /** + * @param Period $period + * @param Json $serializer + */ + public function __construct( + Period $period, + Json $serializer + ) { + $this->period = $period; + $this->serializer = $serializer; } /** @@ -35,6 +45,16 @@ public function __construct(Data $dataHelper) */ public function getDatePeriods(): array { - return $this->dataHelper->getDatePeriods(); + return $this->period->getDatePeriods(); + } + + /** + * Get json-encoded chart period units + * + * @return string + */ + public function getPeriodUnits(): string + { + return $this->serializer->serialize($this->period->getPeriodChartUnits()); } } diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml index 86909162abb7c..4a255134ab062 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -56,14 +56,17 @@ <argument name="label" xsi:type="string" translate="true">Orders</argument> <argument name="title" xsi:type="string" translate="true">Orders</argument> <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/orders</argument> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> </arguments> </block> <block class="Magento\Backend\Block\Widget\Tab" name="dashboard.chart.amounts" as="amounts" template="Magento_Backend::dashboard/chart.phtml"> <arguments> + <argument name="chart_precision" xsi:type="number">2</argument> <argument name="html_id" xsi:type="string">amounts</argument> <argument name="label" xsi:type="string" translate="true">Amounts</argument> <argument name="title" xsi:type="string" translate="true">Amounts</argument> <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/amounts</argument> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> </arguments> </block> </block> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml index f289246157c47..65c0d292ee187 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml @@ -4,13 +4,16 @@ * See COPYING.txt for license details. */ +use Magento\Backend\ViewModel\ChartsPeriod; use Magento\Framework\Escaper; use Magento\Framework\View\Element\Template; /** * @var Template $block * @var Escaper $escaper + * @var ChartsPeriod $viewModel */ +$viewModel = $block->getViewModel(); ?> <div class="dashboard-diagram"> <div class="dashboard-diagram-graph"> @@ -28,6 +31,10 @@ use Magento\Framework\View\Element\Template; '_current' => true ])) ?>", "periodSelect": "#dashboard_chart_period", + "periodUnits": <?= /** @noEscape */ $viewModel->getPeriodUnits() ?>, + <?php if ($precision = $block->getData('chart_precision')): ?> + "precision": <?= (int)$precision ?>, + <?php endif; ?> "type": "<?= $escaper->escapeJs($block->getData('html_id')) ?>" } } diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js index c618e5db5c84a..9177939dcfa9f 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js @@ -17,6 +17,8 @@ define([ options: { updateUrl: '', periodSelect: null, + periodUnits: [], + precision: 0, type: '' }, chart: null, @@ -72,40 +74,13 @@ define([ $(this.element).toggle(response.data.length > 0); $(this.element).next('.dashboard-diagram-nodata').toggle(response.data.length === 0); - this.chart.options.scales.xAxes[0].time.unit = this.getPeriodUnit(); + this.chart.options.scales.xAxes[0].time.unit = this.options.periodUnits[this.period] ? + this.options.periodUnits[this.period] : 'hour'; this.chart.data.datasets[0].data = response.data; this.chart.data.datasets[0].label = response.label; this.chart.update(); }, - /** - * @returns {String} time unit per currently set period - */ - getPeriodUnit: function () { - switch (this.period) { - case '7d': - case '1m': - return 'day'; - - case '1y': - case '2y': - return 'month'; - } - - return 'hour'; - }, - - /** - * @returns {Number} precision of numeric chart data - */ - getPrecision: function () { - if (this.options.type === 'amounts') { - return 2; - } - - return 0; - }, - /** * @returns {Object} chart object configuration */ @@ -137,7 +112,7 @@ define([ yAxes: [{ ticks: { beginAtZero: true, - precision: this.getPrecision() + precision: this.options.precision } }] } diff --git a/app/code/Magento/Ui/view/base/requirejs-config.js b/app/code/Magento/Ui/view/base/requirejs-config.js index 6685a51d6561a..5e76600673254 100644 --- a/app/code/Magento/Ui/view/base/requirejs-config.js +++ b/app/code/Magento/Ui/view/base/requirejs-config.js @@ -5,7 +5,7 @@ var config = { shim: { - 'chartjs/Chart': ['moment'], + 'chartjs/Chart.min': ['moment'], 'tiny_mce_4/tinymce.min': { exports: 'tinyMCE' } @@ -24,7 +24,7 @@ var config = { consoleLogger: 'Magento_Ui/js/lib/logger/console-logger', uiLayout: 'Magento_Ui/js/core/renderer/layout', buttonAdapter: 'Magento_Ui/js/form/button-adapter', - chartJs: 'chartjs/Chart', + chartJs: 'chartjs/Chart.min', tinymce4: 'tiny_mce_4/tinymce.min', wysiwygAdapter: 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter' } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/OrdersTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/OrdersTest.php deleted file mode 100644 index 1ac68b8d7ff57..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/OrdersTest.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Backend\Block\Dashboard\Tab; - -use Magento\Backend\Block\Dashboard\Graph; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\View\LayoutInterface; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\TestCase; - -/** - * Test for \Magento\Backend\Block\Dashboard\Tab\Orders class. - * - * @magentoAppArea adminhtml - */ -class OrdersTest extends TestCase -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var LayoutInterface - */ - private $layout; - - /** - * @var Graph - */ - private $graphBlock; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->layout = $this->objectManager->get(LayoutInterface::class); - $this->graphBlock = $this->layout->createBlock(Graph::class); - } - - /** - * @magentoDataFixture Magento/Sales/_files/order_list_with_invoice.php - * @dataProvider chartUrlDataProvider - * @param string $period - * @param string $expectedAxisRange - * @return void - */ - public function testGetChartUrl(string $period, string $expectedAxisRange): void - { - $this->graphBlock->getRequest()->setParams(['period' => $period]); - $ordersBlock = $this->layout->createBlock(Orders::class); - $decodedChartUrl = urldecode($ordersBlock->getChartUrl()); - $this->assertEquals($expectedAxisRange, $this->getUrlParamData($decodedChartUrl, 'chxr')); - } - - /** - * @return array - */ - public function chartUrlDataProvider(): array - { - return [ - 'Last 24 Hours' => ['24h', '1,0,2,1'], - 'Last 7 Days' => ['7d', '1,0,3,1'], - 'Current Month' => ['1m', '1,0,3,1'], - 'YTD' => ['1y', '1,0,4,1'], - '2YTD' => ['2y', '1,0,4,1'], - ]; - } - - /** - * @param string $chartUrl - * @param string $paramName - * @return string - */ - private function getUrlParamData(string $chartUrl, string $paramName): string - { - $chartUrlSegments = explode('&', $chartUrl); - foreach ($chartUrlSegments as $chartUrlSegment) { - [$paramKey, $paramValue] = explode('=', $chartUrlSegment); - if ($paramKey === $paramName) { - return $paramValue; - } - } - - return ''; - } -} diff --git a/lib/web/chartjs/Chart.bundle.js b/lib/web/chartjs/Chart.bundle.js deleted file mode 100644 index b6f4f388c7459..0000000000000 --- a/lib/web/chartjs/Chart.bundle.js +++ /dev/null @@ -1,20755 +0,0 @@ -/*! - * Chart.js v2.9.3 - * https://www.chartjs.org - * (c) 2019 Chart.js Contributors - * Released under the MIT License - */ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : -typeof define === 'function' && define.amd ? define(factory) : -(global = global || self, global.Chart = factory()); -}(this, (function () { 'use strict'; - -var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - -function commonjsRequire () { - throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); -} - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -function getCjsExportFromNamespace (n) { - return n && n['default'] || n; -} - -var colorName = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - -var conversions = createCommonjsModule(function (module) { -/* MIT license */ - - -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) - -var reverseKeywords = {}; -for (var key in colorName) { - if (colorName.hasOwnProperty(key)) { - reverseKeywords[colorName[key]] = key; - } -} - -var convert = module.exports = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} -}; - -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } - - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } - - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } - - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); - } -} - -convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; - - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } - - h = Math.min(h * 60, 360); - - if (h < 0) { - h += 360; - } - - l = (min + max) / 2; - - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); - } - - return [h, s * 100, l * 100]; -}; - -convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; - - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; - - if (diff === 0) { - h = s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); - - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - } - - return [ - h * 360, - s * 100, - v * 100 - ]; -}; - -convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); - - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); - - return [h, w * 100, b * 100]; -}; - -convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; - - return [c * 100, m * 100, y * 100, k * 100]; -}; - -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ -function comparativeDistance(x, y) { - return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) - ); -} - -convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } - - var currentClosestDistance = Infinity; - var currentClosestKeyword; - - for (var keyword in colorName) { - if (colorName.hasOwnProperty(keyword)) { - var value = colorName[keyword]; - - // Compute comparative distance - var distance = comparativeDistance(rgb, value); - - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } - } - } - - return currentClosestKeyword; -}; - -convert.keyword.rgb = function (keyword) { - return colorName[keyword]; -}; - -convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - - return [x * 100, y * 100, z * 100]; -}; - -convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; - - if (s === 0) { - val = l * 255; - return [val, val, val]; - } - - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } - - t1 = 2 * l - t2; - - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } - if (t3 > 1) { - t3--; - } - - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; - } - - rgb[i] = val * 255; - } - - return rgb; -}; - -convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; - - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); - - return [h, sv * 100, v * 100]; -}; - -convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; - - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); - v *= 255; - - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -}; - -convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; - - l = (2 - s) * v; - lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; - - return [h, sl * 100, l * 100]; -}; - -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } - - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; - - if ((i & 0x01) !== 0) { - f = 1 - f; - } - - n = wh + f * (v - wh); // linear interpolation - - var r; - var g; - var b; - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } - - return [r * 255, g * 255, b * 255]; -}; - -convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; - - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; - - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - - // assume sRGB - r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r * 12.92; - - g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g * 12.92; - - b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b * 12.92; - - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; - - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; - - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; - - x *= 95.047; - y *= 100; - z *= 108.883; - - return [x, y, z]; -}; - -convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; - - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; - - if (h < 0) { - h += 360; - } - - c = Math.sqrt(a * a + b * b); - - return [l, c, h]; -}; - -convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; - - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); - - return [l, a, b]; -}; - -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization - - value = Math.round(value / 50); - - if (value === 0) { - return 30; - } - - var ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); - - if (value === 2) { - ansi += 60; - } - - return ansi; -}; - -convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; - -convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - - // we use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; - } - - if (r > 248) { - return 231; - } - - return Math.round(((r - 8) / 247) * 24) + 232; - } - - var ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); - - return ansi; -}; - -convert.ansi16.rgb = function (args) { - var color = args % 10; - - // handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; - } - - color = color / 10.5 * 255; - - return [color, color, color]; - } - - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; - - return [r, g, b]; -}; - -convert.ansi256.rgb = function (args) { - // handle greyscale - if (args >= 232) { - var c = (args - 232) * 10 + 8; - return [c, c, c]; - } - - args -= 16; - - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; - - return [r, g, b]; -}; - -convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; - } - - var colorString = match[0]; - - if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { - return char + char; - }).join(''); - } - - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; - - return [r, g, b]; -}; - -convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; - - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } - - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma + 4; - } - - hue /= 6; - hue %= 1; - - return [hue * 360, chroma * 100, grayscale * 100]; -}; - -convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; - - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } - - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } - - return [hsl[0], c * 100, f * 100]; -}; - -convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; - - var c = s * v; - var f = 0; - - if (c < 1.0) { - f = (v - c) / (1 - c); - } - - return [hsv[0], c * 100, f * 100]; -}; - -convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } - - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; - - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; - } - - mg = (1.0 - c) * g; - - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; - -convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var v = c + g * (1.0 - c); - var f = 0; - - if (v > 0.0) { - f = c / v; - } - - return [hcg[0], f * 100, v * 100]; -}; - -convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; - - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); - } - - return [hcg[0], s * 100, l * 100]; -}; - -convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; -}; - -convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; - - if (c < 1) { - g = (v - c) / (1 - c); - } - - return [hwb[0], c * 100, g * 100]; -}; - -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; - -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; - -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; - -convert.gray.hsl = convert.gray.hsv = function (args) { - return [0, 0, args[0]]; -}; - -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; - -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; - -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; - -convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; -}; -}); -var conversions_1 = conversions.rgb; -var conversions_2 = conversions.hsl; -var conversions_3 = conversions.hsv; -var conversions_4 = conversions.hwb; -var conversions_5 = conversions.cmyk; -var conversions_6 = conversions.xyz; -var conversions_7 = conversions.lab; -var conversions_8 = conversions.lch; -var conversions_9 = conversions.hex; -var conversions_10 = conversions.keyword; -var conversions_11 = conversions.ansi16; -var conversions_12 = conversions.ansi256; -var conversions_13 = conversions.hcg; -var conversions_14 = conversions.apple; -var conversions_15 = conversions.gray; - -/* - this function routes a model to all other models. - - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). - - conversions that are not possible simply are not included. -*/ - -function buildGraph() { - var graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); - - for (var len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; - } - - return graph; -} - -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop - - graph[fromModel].distance = 0; - - while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); - - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; - - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } - - return graph; -} - -function link(from, to) { - return function (args) { - return to(from(args)); - }; -} - -function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; - - var cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; - } - - fn.conversion = path; - return fn; -} - -var route = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; - - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; - - if (node.parent === null) { - // no possible conversion, or this node is the source model. - continue; - } - - conversion[toModel] = wrapConversion(toModel, graph); - } - - return conversion; -}; - -var convert = {}; - -var models = Object.keys(conversions); - -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - return fn(args); - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - var result = fn(args); - - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } - - return result; - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -models.forEach(function (fromModel) { - convert[fromModel] = {}; - - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - - var routes = route(fromModel); - var routeModels = Object.keys(routes); - - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; - - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); - -var colorConvert = convert; - -var colorName$1 = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - -/* MIT license */ - - -var colorString = { - getRgba: getRgba, - getHsla: getHsla, - getRgb: getRgb, - getHsl: getHsl, - getHwb: getHwb, - getAlpha: getAlpha, - - hexString: hexString, - rgbString: rgbString, - rgbaString: rgbaString, - percentString: percentString, - percentaString: percentaString, - hslString: hslString, - hslaString: hslaString, - hwbString: hwbString, - keyword: keyword -}; - -function getRgba(string) { - if (!string) { - return; - } - var abbr = /^#([a-fA-F0-9]{3,4})$/i, - hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, - rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - keyword = /(\w+)/; - - var rgb = [0, 0, 0], - a = 1, - match = string.match(abbr), - hexAlpha = ""; - if (match) { - match = match[1]; - hexAlpha = match[3]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i] + match[i], 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(hex)) { - hexAlpha = match[2]; - match = match[1]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(rgba)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i + 1]); - } - a = parseFloat(match[4]); - } - else if (match = string.match(per)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); - } - a = parseFloat(match[4]); - } - else if (match = string.match(keyword)) { - if (match[1] == "transparent") { - return [0, 0, 0, 0]; - } - rgb = colorName$1[match[1]]; - if (!rgb) { - return; - } - } - - for (var i = 0; i < rgb.length; i++) { - rgb[i] = scale(rgb[i], 0, 255); - } - if (!a && a != 0) { - a = 1; - } - else { - a = scale(a, 0, 1); - } - rgb[3] = a; - return rgb; -} - -function getHsla(string) { - if (!string) { - return; - } - var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hsl); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - s = scale(parseFloat(match[2]), 0, 100), - l = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, s, l, a]; - } -} - -function getHwb(string) { - if (!string) { - return; - } - var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hwb); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - w = scale(parseFloat(match[2]), 0, 100), - b = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, w, b, a]; - } -} - -function getRgb(string) { - var rgba = getRgba(string); - return rgba && rgba.slice(0, 3); -} - -function getHsl(string) { - var hsla = getHsla(string); - return hsla && hsla.slice(0, 3); -} - -function getAlpha(string) { - var vals = getRgba(string); - if (vals) { - return vals[3]; - } - else if (vals = getHsla(string)) { - return vals[3]; - } - else if (vals = getHwb(string)) { - return vals[3]; - } -} - -// generators -function hexString(rgba, a) { - var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; - return "#" + hexDouble(rgba[0]) - + hexDouble(rgba[1]) - + hexDouble(rgba[2]) - + ( - (a >= 0 && a < 1) - ? hexDouble(Math.round(a * 255)) - : "" - ); -} - -function rgbString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return rgbaString(rgba, alpha); - } - return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; -} - -function rgbaString(rgba, alpha) { - if (alpha === undefined) { - alpha = (rgba[3] !== undefined ? rgba[3] : 1); - } - return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] - + ", " + alpha + ")"; -} - -function percentString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return percentaString(rgba, alpha); - } - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - - return "rgb(" + r + "%, " + g + "%, " + b + "%)"; -} - -function percentaString(rgba, alpha) { - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; -} - -function hslString(hsla, alpha) { - if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { - return hslaString(hsla, alpha); - } - return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; -} - -function hslaString(hsla, alpha) { - if (alpha === undefined) { - alpha = (hsla[3] !== undefined ? hsla[3] : 1); - } - return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " - + alpha + ")"; -} - -// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax -// (hwb have alpha optional & 1 is default value) -function hwbString(hwb, alpha) { - if (alpha === undefined) { - alpha = (hwb[3] !== undefined ? hwb[3] : 1); - } - return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" - + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; -} - -function keyword(rgb) { - return reverseNames[rgb.slice(0, 3)]; -} - -// helpers -function scale(num, min, max) { - return Math.min(Math.max(min, num), max); -} - -function hexDouble(num) { - var str = num.toString(16).toUpperCase(); - return (str.length < 2) ? "0" + str : str; -} - - -//create a list of reverse color names -var reverseNames = {}; -for (var name in colorName$1) { - reverseNames[colorName$1[name]] = name; -} - -/* MIT license */ - - - -var Color = function (obj) { - if (obj instanceof Color) { - return obj; - } - if (!(this instanceof Color)) { - return new Color(obj); - } - - this.valid = false; - this.values = { - rgb: [0, 0, 0], - hsl: [0, 0, 0], - hsv: [0, 0, 0], - hwb: [0, 0, 0], - cmyk: [0, 0, 0, 0], - alpha: 1 - }; - - // parse Color() argument - var vals; - if (typeof obj === 'string') { - vals = colorString.getRgba(obj); - if (vals) { - this.setValues('rgb', vals); - } else if (vals = colorString.getHsla(obj)) { - this.setValues('hsl', vals); - } else if (vals = colorString.getHwb(obj)) { - this.setValues('hwb', vals); - } - } else if (typeof obj === 'object') { - vals = obj; - if (vals.r !== undefined || vals.red !== undefined) { - this.setValues('rgb', vals); - } else if (vals.l !== undefined || vals.lightness !== undefined) { - this.setValues('hsl', vals); - } else if (vals.v !== undefined || vals.value !== undefined) { - this.setValues('hsv', vals); - } else if (vals.w !== undefined || vals.whiteness !== undefined) { - this.setValues('hwb', vals); - } else if (vals.c !== undefined || vals.cyan !== undefined) { - this.setValues('cmyk', vals); - } - } -}; - -Color.prototype = { - isValid: function () { - return this.valid; - }, - rgb: function () { - return this.setSpace('rgb', arguments); - }, - hsl: function () { - return this.setSpace('hsl', arguments); - }, - hsv: function () { - return this.setSpace('hsv', arguments); - }, - hwb: function () { - return this.setSpace('hwb', arguments); - }, - cmyk: function () { - return this.setSpace('cmyk', arguments); - }, - - rgbArray: function () { - return this.values.rgb; - }, - hslArray: function () { - return this.values.hsl; - }, - hsvArray: function () { - return this.values.hsv; - }, - hwbArray: function () { - var values = this.values; - if (values.alpha !== 1) { - return values.hwb.concat([values.alpha]); - } - return values.hwb; - }, - cmykArray: function () { - return this.values.cmyk; - }, - rgbaArray: function () { - var values = this.values; - return values.rgb.concat([values.alpha]); - }, - hslaArray: function () { - var values = this.values; - return values.hsl.concat([values.alpha]); - }, - alpha: function (val) { - if (val === undefined) { - return this.values.alpha; - } - this.setValues('alpha', val); - return this; - }, - - red: function (val) { - return this.setChannel('rgb', 0, val); - }, - green: function (val) { - return this.setChannel('rgb', 1, val); - }, - blue: function (val) { - return this.setChannel('rgb', 2, val); - }, - hue: function (val) { - if (val) { - val %= 360; - val = val < 0 ? 360 + val : val; - } - return this.setChannel('hsl', 0, val); - }, - saturation: function (val) { - return this.setChannel('hsl', 1, val); - }, - lightness: function (val) { - return this.setChannel('hsl', 2, val); - }, - saturationv: function (val) { - return this.setChannel('hsv', 1, val); - }, - whiteness: function (val) { - return this.setChannel('hwb', 1, val); - }, - blackness: function (val) { - return this.setChannel('hwb', 2, val); - }, - value: function (val) { - return this.setChannel('hsv', 2, val); - }, - cyan: function (val) { - return this.setChannel('cmyk', 0, val); - }, - magenta: function (val) { - return this.setChannel('cmyk', 1, val); - }, - yellow: function (val) { - return this.setChannel('cmyk', 2, val); - }, - black: function (val) { - return this.setChannel('cmyk', 3, val); - }, - - hexString: function () { - return colorString.hexString(this.values.rgb); - }, - rgbString: function () { - return colorString.rgbString(this.values.rgb, this.values.alpha); - }, - rgbaString: function () { - return colorString.rgbaString(this.values.rgb, this.values.alpha); - }, - percentString: function () { - return colorString.percentString(this.values.rgb, this.values.alpha); - }, - hslString: function () { - return colorString.hslString(this.values.hsl, this.values.alpha); - }, - hslaString: function () { - return colorString.hslaString(this.values.hsl, this.values.alpha); - }, - hwbString: function () { - return colorString.hwbString(this.values.hwb, this.values.alpha); - }, - keyword: function () { - return colorString.keyword(this.values.rgb, this.values.alpha); - }, - - rgbNumber: function () { - var rgb = this.values.rgb; - return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; - }, - - luminosity: function () { - // http://www.w3.org/TR/WCAG20/#relativeluminancedef - var rgb = this.values.rgb; - var lum = []; - for (var i = 0; i < rgb.length; i++) { - var chan = rgb[i] / 255; - lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); - } - return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; - }, - - contrast: function (color2) { - // http://www.w3.org/TR/WCAG20/#contrast-ratiodef - var lum1 = this.luminosity(); - var lum2 = color2.luminosity(); - if (lum1 > lum2) { - return (lum1 + 0.05) / (lum2 + 0.05); - } - return (lum2 + 0.05) / (lum1 + 0.05); - }, - - level: function (color2) { - var contrastRatio = this.contrast(color2); - if (contrastRatio >= 7.1) { - return 'AAA'; - } - - return (contrastRatio >= 4.5) ? 'AA' : ''; - }, - - dark: function () { - // YIQ equation from http://24ways.org/2010/calculating-color-contrast - var rgb = this.values.rgb; - var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; - return yiq < 128; - }, - - light: function () { - return !this.dark(); - }, - - negate: function () { - var rgb = []; - for (var i = 0; i < 3; i++) { - rgb[i] = 255 - this.values.rgb[i]; - } - this.setValues('rgb', rgb); - return this; - }, - - lighten: function (ratio) { - var hsl = this.values.hsl; - hsl[2] += hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - darken: function (ratio) { - var hsl = this.values.hsl; - hsl[2] -= hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - saturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] += hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - desaturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] -= hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - whiten: function (ratio) { - var hwb = this.values.hwb; - hwb[1] += hwb[1] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - blacken: function (ratio) { - var hwb = this.values.hwb; - hwb[2] += hwb[2] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - greyscale: function () { - var rgb = this.values.rgb; - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; - this.setValues('rgb', [val, val, val]); - return this; - }, - - clearer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha - (alpha * ratio)); - return this; - }, - - opaquer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha + (alpha * ratio)); - return this; - }, - - rotate: function (degrees) { - var hsl = this.values.hsl; - var hue = (hsl[0] + degrees) % 360; - hsl[0] = hue < 0 ? 360 + hue : hue; - this.setValues('hsl', hsl); - return this; - }, - - /** - * Ported from sass implementation in C - * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 - */ - mix: function (mixinColor, weight) { - var color1 = this; - var color2 = mixinColor; - var p = weight === undefined ? 0.5 : weight; - - var w = 2 * p - 1; - var a = color1.alpha() - color2.alpha(); - - var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - return this - .rgb( - w1 * color1.red() + w2 * color2.red(), - w1 * color1.green() + w2 * color2.green(), - w1 * color1.blue() + w2 * color2.blue() - ) - .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); - }, - - toJSON: function () { - return this.rgb(); - }, - - clone: function () { - // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, - // making the final build way to big to embed in Chart.js. So let's do it manually, - // assuming that values to clone are 1 dimension arrays containing only numbers, - // except 'alpha' which is a number. - var result = new Color(); - var source = this.values; - var target = result.values; - var value, type; - - for (var prop in source) { - if (source.hasOwnProperty(prop)) { - value = source[prop]; - type = ({}).toString.call(value); - if (type === '[object Array]') { - target[prop] = value.slice(0); - } else if (type === '[object Number]') { - target[prop] = value; - } else { - console.error('unexpected color value:', value); - } - } - } - - return result; - } -}; - -Color.prototype.spaces = { - rgb: ['red', 'green', 'blue'], - hsl: ['hue', 'saturation', 'lightness'], - hsv: ['hue', 'saturation', 'value'], - hwb: ['hue', 'whiteness', 'blackness'], - cmyk: ['cyan', 'magenta', 'yellow', 'black'] -}; - -Color.prototype.maxes = { - rgb: [255, 255, 255], - hsl: [360, 100, 100], - hsv: [360, 100, 100], - hwb: [360, 100, 100], - cmyk: [100, 100, 100, 100] -}; - -Color.prototype.getValues = function (space) { - var values = this.values; - var vals = {}; - - for (var i = 0; i < space.length; i++) { - vals[space.charAt(i)] = values[space][i]; - } - - if (values.alpha !== 1) { - vals.a = values.alpha; - } - - // {r: 255, g: 255, b: 255, a: 0.4} - return vals; -}; - -Color.prototype.setValues = function (space, vals) { - var values = this.values; - var spaces = this.spaces; - var maxes = this.maxes; - var alpha = 1; - var i; - - this.valid = true; - - if (space === 'alpha') { - alpha = vals; - } else if (vals.length) { - // [10, 10, 10] - values[space] = vals.slice(0, space.length); - alpha = vals[space.length]; - } else if (vals[space.charAt(0)] !== undefined) { - // {r: 10, g: 10, b: 10} - for (i = 0; i < space.length; i++) { - values[space][i] = vals[space.charAt(i)]; - } - - alpha = vals.a; - } else if (vals[spaces[space][0]] !== undefined) { - // {red: 10, green: 10, blue: 10} - var chans = spaces[space]; - - for (i = 0; i < space.length; i++) { - values[space][i] = vals[chans[i]]; - } - - alpha = vals.alpha; - } - - values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); - - if (space === 'alpha') { - return false; - } - - var capped; - - // cap values of the space prior converting all values - for (i = 0; i < space.length; i++) { - capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); - values[space][i] = Math.round(capped); - } - - // convert to all the other color spaces - for (var sname in spaces) { - if (sname !== space) { - values[sname] = colorConvert[space][sname](values[space]); - } - } - - return true; -}; - -Color.prototype.setSpace = function (space, args) { - var vals = args[0]; - - if (vals === undefined) { - // color.rgb() - return this.getValues(space); - } - - // color.rgb(10, 10, 10) - if (typeof vals === 'number') { - vals = Array.prototype.slice.call(args); - } - - this.setValues(space, vals); - return this; -}; - -Color.prototype.setChannel = function (space, index, val) { - var svalues = this.values[space]; - if (val === undefined) { - // color.red() - return svalues[index]; - } else if (val === svalues[index]) { - // color.red(color.red()) - return this; - } - - // color.red(100) - svalues[index] = val; - this.setValues(space, svalues); - - return this; -}; - -if (typeof window !== 'undefined') { - window.Color = Color; -} - -var chartjsColor = Color; - -/** - * @namespace Chart.helpers - */ -var helpers = { - /** - * An empty function that can be used, for example, for optional callback. - */ - noop: function() {}, - - /** - * Returns a unique id, sequentially generated from a global variable. - * @returns {number} - * @function - */ - uid: (function() { - var id = 0; - return function() { - return id++; - }; - }()), - - /** - * Returns true if `value` is neither null nor undefined, else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isNullOrUndef: function(value) { - return value === null || typeof value === 'undefined'; - }, - - /** - * Returns true if `value` is an array (including typed arrays), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @function - */ - isArray: function(value) { - if (Array.isArray && Array.isArray(value)) { - return true; - } - var type = Object.prototype.toString.call(value); - if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { - return true; - } - return false; - }, - - /** - * Returns true if `value` is an object (excluding null), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isObject: function(value) { - return value !== null && Object.prototype.toString.call(value) === '[object Object]'; - }, - - /** - * Returns true if `value` is a finite number, else returns false - * @param {*} value - The value to test. - * @returns {boolean} - */ - isFinite: function(value) { - return (typeof value === 'number' || value instanceof Number) && isFinite(value); - }, - - /** - * Returns `value` if defined, else returns `defaultValue`. - * @param {*} value - The value to return if defined. - * @param {*} defaultValue - The value to return if `value` is undefined. - * @returns {*} - */ - valueOrDefault: function(value, defaultValue) { - return typeof value === 'undefined' ? defaultValue : value; - }, - - /** - * Returns value at the given `index` in array if defined, else returns `defaultValue`. - * @param {Array} value - The array to lookup for value at `index`. - * @param {number} index - The index in `value` to lookup for value. - * @param {*} defaultValue - The value to return if `value[index]` is undefined. - * @returns {*} - */ - valueAtIndexOrDefault: function(value, index, defaultValue) { - return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); - }, - - /** - * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the - * value returned by `fn`. If `fn` is not a function, this method returns undefined. - * @param {function} fn - The function to call. - * @param {Array|undefined|null} args - The arguments with which `fn` should be called. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @returns {*} - */ - callback: function(fn, args, thisArg) { - if (fn && typeof fn.call === 'function') { - return fn.apply(thisArg, args); - } - }, - - /** - * Note(SB) for performance sake, this method should only be used when loopable type - * is unknown or in none intensive code (not called often and small loopable). Else - * it's preferable to use a regular for() loop and save extra function calls. - * @param {object|Array} loopable - The object or array to be iterated. - * @param {function} fn - The function to call for each item. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @param {boolean} [reverse] - If true, iterates backward on the loopable. - */ - each: function(loopable, fn, thisArg, reverse) { - var i, len, keys; - if (helpers.isArray(loopable)) { - len = loopable.length; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - fn.call(thisArg, loopable[i], i); - } - } else { - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[i], i); - } - } - } else if (helpers.isObject(loopable)) { - keys = Object.keys(loopable); - len = keys.length; - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[keys[i]], keys[i]); - } - } - }, - - /** - * Returns true if the `a0` and `a1` arrays have the same content, else returns false. - * @see https://stackoverflow.com/a/14853974 - * @param {Array} a0 - The array to compare - * @param {Array} a1 - The array to compare - * @returns {boolean} - */ - arrayEquals: function(a0, a1) { - var i, ilen, v0, v1; - - if (!a0 || !a1 || a0.length !== a1.length) { - return false; - } - - for (i = 0, ilen = a0.length; i < ilen; ++i) { - v0 = a0[i]; - v1 = a1[i]; - - if (v0 instanceof Array && v1 instanceof Array) { - if (!helpers.arrayEquals(v0, v1)) { - return false; - } - } else if (v0 !== v1) { - // NOTE: two different object instances will never be equal: {x:20} != {x:20} - return false; - } - } - - return true; - }, - - /** - * Returns a deep copy of `source` without keeping references on objects and arrays. - * @param {*} source - The value to clone. - * @returns {*} - */ - clone: function(source) { - if (helpers.isArray(source)) { - return source.map(helpers.clone); - } - - if (helpers.isObject(source)) { - var target = {}; - var keys = Object.keys(source); - var klen = keys.length; - var k = 0; - - for (; k < klen; ++k) { - target[keys[k]] = helpers.clone(source[keys[k]]); - } - - return target; - } - - return source; - }, - - /** - * The default merger when Chart.helpers.merge is called without merger option. - * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. - * @private - */ - _merger: function(key, target, source, options) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.merge(tval, sval, options); - } else { - target[key] = helpers.clone(sval); - } - }, - - /** - * Merges source[key] in target[key] only if target[key] is undefined. - * @private - */ - _mergerIf: function(key, target, source) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.mergeIf(tval, sval); - } else if (!target.hasOwnProperty(key)) { - target[key] = helpers.clone(sval); - } - }, - - /** - * Recursively deep copies `source` properties into `target` with the given `options`. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @param {object} [options] - Merging options: - * @param {function} [options.merger] - The merge method (key, target, source, options) - * @returns {object} The `target` object. - */ - merge: function(target, source, options) { - var sources = helpers.isArray(source) ? source : [source]; - var ilen = sources.length; - var merge, i, keys, klen, k; - - if (!helpers.isObject(target)) { - return target; - } - - options = options || {}; - merge = options.merger || helpers._merger; - - for (i = 0; i < ilen; ++i) { - source = sources[i]; - if (!helpers.isObject(source)) { - continue; - } - - keys = Object.keys(source); - for (k = 0, klen = keys.length; k < klen; ++k) { - merge(keys[k], target, source, options); - } - } - - return target; - }, - - /** - * Recursively deep copies `source` properties into `target` *only* if not defined in target. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @returns {object} The `target` object. - */ - mergeIf: function(target, source) { - return helpers.merge(target, source, {merger: helpers._mergerIf}); - }, - - /** - * Applies the contents of two or more objects together into the first object. - * @param {object} target - The target object in which all objects are merged into. - * @param {object} arg1 - Object containing additional properties to merge in target. - * @param {object} argN - Additional objects containing properties to merge in target. - * @returns {object} The `target` object. - */ - extend: Object.assign || function(target) { - return helpers.merge(target, [].slice.call(arguments, 1), { - merger: function(key, dst, src) { - dst[key] = src[key]; - } - }); - }, - - /** - * Basic javascript inheritance based on the model created in Backbone.js - */ - inherits: function(extensions) { - var me = this; - var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { - return me.apply(this, arguments); - }; - - var Surrogate = function() { - this.constructor = ChartElement; - }; - - Surrogate.prototype = me.prototype; - ChartElement.prototype = new Surrogate(); - ChartElement.extend = helpers.inherits; - - if (extensions) { - helpers.extend(ChartElement.prototype, extensions); - } - - ChartElement.__super__ = me.prototype; - return ChartElement; - }, - - _deprecated: function(scope, value, previous, current) { - if (value !== undefined) { - console.warn(scope + ': "' + previous + - '" is deprecated. Please use "' + current + '" instead'); - } - } -}; - -var helpers_core = helpers; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.callback instead. - * @function Chart.helpers.callCallback - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -helpers.callCallback = helpers.callback; - -/** - * Provided for backward compatibility, use Array.prototype.indexOf instead. - * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ - * @function Chart.helpers.indexOf - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.indexOf = function(array, item, fromIndex) { - return Array.prototype.indexOf.call(array, item, fromIndex); -}; - -/** - * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. - * @function Chart.helpers.getValueOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueOrDefault = helpers.valueOrDefault; - -/** - * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. - * @function Chart.helpers.getValueAtIndexOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; - -/** - * Easing functions adapted from Robert Penner's easing equations. - * @namespace Chart.helpers.easingEffects - * @see http://www.robertpenner.com/easing/ - */ -var effects = { - linear: function(t) { - return t; - }, - - easeInQuad: function(t) { - return t * t; - }, - - easeOutQuad: function(t) { - return -t * (t - 2); - }, - - easeInOutQuad: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t; - } - return -0.5 * ((--t) * (t - 2) - 1); - }, - - easeInCubic: function(t) { - return t * t * t; - }, - - easeOutCubic: function(t) { - return (t = t - 1) * t * t + 1; - }, - - easeInOutCubic: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t; - } - return 0.5 * ((t -= 2) * t * t + 2); - }, - - easeInQuart: function(t) { - return t * t * t * t; - }, - - easeOutQuart: function(t) { - return -((t = t - 1) * t * t * t - 1); - }, - - easeInOutQuart: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t; - } - return -0.5 * ((t -= 2) * t * t * t - 2); - }, - - easeInQuint: function(t) { - return t * t * t * t * t; - }, - - easeOutQuint: function(t) { - return (t = t - 1) * t * t * t * t + 1; - }, - - easeInOutQuint: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t * t; - } - return 0.5 * ((t -= 2) * t * t * t * t + 2); - }, - - easeInSine: function(t) { - return -Math.cos(t * (Math.PI / 2)) + 1; - }, - - easeOutSine: function(t) { - return Math.sin(t * (Math.PI / 2)); - }, - - easeInOutSine: function(t) { - return -0.5 * (Math.cos(Math.PI * t) - 1); - }, - - easeInExpo: function(t) { - return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); - }, - - easeOutExpo: function(t) { - return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; - }, - - easeInOutExpo: function(t) { - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if ((t /= 0.5) < 1) { - return 0.5 * Math.pow(2, 10 * (t - 1)); - } - return 0.5 * (-Math.pow(2, -10 * --t) + 2); - }, - - easeInCirc: function(t) { - if (t >= 1) { - return t; - } - return -(Math.sqrt(1 - t * t) - 1); - }, - - easeOutCirc: function(t) { - return Math.sqrt(1 - (t = t - 1) * t); - }, - - easeInOutCirc: function(t) { - if ((t /= 0.5) < 1) { - return -0.5 * (Math.sqrt(1 - t * t) - 1); - } - return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - - easeInElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - }, - - easeOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; - }, - - easeInOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 0.5) === 2) { - return 1; - } - if (!p) { - p = 0.45; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - if (t < 1) { - return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function(t) { - var s = 1.70158; - return t * t * ((s + 1) * t - s); - }, - - easeOutBack: function(t) { - var s = 1.70158; - return (t = t - 1) * t * ((s + 1) * t + s) + 1; - }, - - easeInOutBack: function(t) { - var s = 1.70158; - if ((t /= 0.5) < 1) { - return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); - } - return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - - easeInBounce: function(t) { - return 1 - effects.easeOutBounce(1 - t); - }, - - easeOutBounce: function(t) { - if (t < (1 / 2.75)) { - return 7.5625 * t * t; - } - if (t < (2 / 2.75)) { - return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; - } - if (t < (2.5 / 2.75)) { - return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; - } - return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; - }, - - easeInOutBounce: function(t) { - if (t < 0.5) { - return effects.easeInBounce(t * 2) * 0.5; - } - return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; - } -}; - -var helpers_easing = { - effects: effects -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.easing.effects instead. - * @function Chart.helpers.easingEffects - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.easingEffects = effects; - -var PI = Math.PI; -var RAD_PER_DEG = PI / 180; -var DOUBLE_PI = PI * 2; -var HALF_PI = PI / 2; -var QUARTER_PI = PI / 4; -var TWO_THIRDS_PI = PI * 2 / 3; - -/** - * @namespace Chart.helpers.canvas - */ -var exports$1 = { - /** - * Clears the entire canvas associated to the given `chart`. - * @param {Chart} chart - The chart for which to clear the canvas. - */ - clear: function(chart) { - chart.ctx.clearRect(0, 0, chart.width, chart.height); - }, - - /** - * Creates a "path" for a rectangle with rounded corners at position (x, y) with a - * given size (width, height) and the same `radius` for all corners. - * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. - * @param {number} x - The x axis of the coordinate for the rectangle starting point. - * @param {number} y - The y axis of the coordinate for the rectangle starting point. - * @param {number} width - The rectangle's width. - * @param {number} height - The rectangle's height. - * @param {number} radius - The rounded amount (in pixels) for the four corners. - * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? - */ - roundedRect: function(ctx, x, y, width, height, radius) { - if (radius) { - var r = Math.min(radius, height / 2, width / 2); - var left = x + r; - var top = y + r; - var right = x + width - r; - var bottom = y + height - r; - - ctx.moveTo(x, top); - if (left < right && top < bottom) { - ctx.arc(left, top, r, -PI, -HALF_PI); - ctx.arc(right, top, r, -HALF_PI, 0); - ctx.arc(right, bottom, r, 0, HALF_PI); - ctx.arc(left, bottom, r, HALF_PI, PI); - } else if (left < right) { - ctx.moveTo(left, y); - ctx.arc(right, top, r, -HALF_PI, HALF_PI); - ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); - } else if (top < bottom) { - ctx.arc(left, top, r, -PI, 0); - ctx.arc(left, bottom, r, 0, PI); - } else { - ctx.arc(left, top, r, -PI, PI); - } - ctx.closePath(); - ctx.moveTo(x, y); - } else { - ctx.rect(x, y, width, height); - } - }, - - drawPoint: function(ctx, style, radius, x, y, rotation) { - var type, xOffset, yOffset, size, cornerRadius; - var rad = (rotation || 0) * RAD_PER_DEG; - - if (style && typeof style === 'object') { - type = style.toString(); - if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.save(); - ctx.translate(x, y); - ctx.rotate(rad); - ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); - ctx.restore(); - return; - } - } - - if (isNaN(radius) || radius <= 0) { - return; - } - - ctx.beginPath(); - - switch (style) { - // Default includes circle - default: - ctx.arc(x, y, radius, 0, DOUBLE_PI); - ctx.closePath(); - break; - case 'triangle': - ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - ctx.closePath(); - break; - case 'rectRounded': - // NOTE: the rounded rect implementation changed to use `arc` instead of - // `quadraticCurveTo` since it generates better results when rect is - // almost a circle. 0.516 (instead of 0.5) produces results with visually - // closer proportion to the previous impl and it is inscribed in the - // circle with `radius`. For more details, see the following PRs: - // https://github.com/chartjs/Chart.js/issues/5597 - // https://github.com/chartjs/Chart.js/issues/5858 - cornerRadius = radius * 0.516; - size = radius - cornerRadius; - xOffset = Math.cos(rad + QUARTER_PI) * size; - yOffset = Math.sin(rad + QUARTER_PI) * size; - ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); - ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); - ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); - ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); - ctx.closePath(); - break; - case 'rect': - if (!rotation) { - size = Math.SQRT1_2 * radius; - ctx.rect(x - size, y - size, 2 * size, 2 * size); - break; - } - rad += QUARTER_PI; - /* falls through */ - case 'rectRot': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + yOffset, y - xOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.lineTo(x - yOffset, y + xOffset); - ctx.closePath(); - break; - case 'crossRot': - rad += QUARTER_PI; - /* falls through */ - case 'cross': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'star': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - rad += QUARTER_PI; - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'line': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - break; - case 'dash': - ctx.moveTo(x, y); - ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); - break; - } - - ctx.fill(); - ctx.stroke(); - }, - - /** - * Returns true if the point is inside the rectangle - * @param {object} point - The point to test - * @param {object} area - The rectangle - * @returns {boolean} - * @private - */ - _isPointInArea: function(point, area) { - var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. - - return point.x > area.left - epsilon && point.x < area.right + epsilon && - point.y > area.top - epsilon && point.y < area.bottom + epsilon; - }, - - clipArea: function(ctx, area) { - ctx.save(); - ctx.beginPath(); - ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); - ctx.clip(); - }, - - unclipArea: function(ctx) { - ctx.restore(); - }, - - lineTo: function(ctx, previous, target, flip) { - var stepped = target.steppedLine; - if (stepped) { - if (stepped === 'middle') { - var midpoint = (previous.x + target.x) / 2.0; - ctx.lineTo(midpoint, flip ? target.y : previous.y); - ctx.lineTo(midpoint, flip ? previous.y : target.y); - } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { - ctx.lineTo(previous.x, target.y); - } else { - ctx.lineTo(target.x, previous.y); - } - ctx.lineTo(target.x, target.y); - return; - } - - if (!target.tension) { - ctx.lineTo(target.x, target.y); - return; - } - - ctx.bezierCurveTo( - flip ? previous.controlPointPreviousX : previous.controlPointNextX, - flip ? previous.controlPointPreviousY : previous.controlPointNextY, - flip ? target.controlPointNextX : target.controlPointPreviousX, - flip ? target.controlPointNextY : target.controlPointPreviousY, - target.x, - target.y); - } -}; - -var helpers_canvas = exports$1; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. - * @namespace Chart.helpers.clear - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.clear = exports$1.clear; - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. - * @namespace Chart.helpers.drawRoundedRectangle - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.drawRoundedRectangle = function(ctx) { - ctx.beginPath(); - exports$1.roundedRect.apply(exports$1, arguments); -}; - -var defaults = { - /** - * @private - */ - _set: function(scope, values) { - return helpers_core.merge(this[scope] || (this[scope] = {}), values); - } -}; - -// TODO(v3): remove 'global' from namespace. all default are global and -// there's inconsistency around which options are under 'global' -defaults._set('global', { - defaultColor: 'rgba(0,0,0,0.1)', - defaultFontColor: '#666', - defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - defaultFontSize: 12, - defaultFontStyle: 'normal', - defaultLineHeight: 1.2, - showLines: true -}); - -var core_defaults = defaults; - -var valueOrDefault = helpers_core.valueOrDefault; - -/** - * Converts the given font object into a CSS font string. - * @param {object} font - A font object. - * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font - * @private - */ -function toFontString(font) { - if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { - return null; - } - - return (font.style ? font.style + ' ' : '') - + (font.weight ? font.weight + ' ' : '') - + font.size + 'px ' - + font.family; -} - -/** - * @alias Chart.helpers.options - * @namespace - */ -var helpers_options = { - /** - * Converts the given line height `value` in pixels for a specific font `size`. - * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). - * @param {number} size - The font size (in pixels) used to resolve relative `value`. - * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - * @since 2.7.0 - */ - toLineHeight: function(value, size) { - var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); - if (!matches || matches[1] === 'normal') { - return size * 1.2; - } - - value = +matches[2]; - - switch (matches[3]) { - case 'px': - return value; - case '%': - value /= 100; - break; - } - - return size * value; - }, - - /** - * Converts the given value into a padding object with pre-computed width/height. - * @param {number|object} value - If a number, set the value to all TRBL component, - * else, if and object, use defined properties and sets undefined ones to 0. - * @returns {object} The padding values (top, right, bottom, left, width, height) - * @since 2.7.0 - */ - toPadding: function(value) { - var t, r, b, l; - - if (helpers_core.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - top: t, - right: r, - bottom: b, - left: l, - height: t + b, - width: l + r - }; - }, - - /** - * Parses font options and returns the font object. - * @param {object} options - A object that contains font options to be parsed. - * @return {object} The font object. - * @todo Support font.* options and renamed to toFont(). - * @private - */ - _parseFont: function(options) { - var globalDefaults = core_defaults.global; - var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); - var font = { - family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), - lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), - size: size, - style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), - weight: null, - string: '' - }; - - font.string = toFontString(font); - return font; - }, - - /** - * Evaluates the given `inputs` sequentially and returns the first defined value. - * @param {Array} inputs - An array of values, falling back to the last value. - * @param {object} [context] - If defined and the current value is a function, the value - * is called with `context` as first argument and the result becomes the new input. - * @param {number} [index] - If defined and the current value is an array, the value - * at `index` become the new input. - * @param {object} [info] - object to return information about resolution in - * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. - * @since 2.7.0 - */ - resolve: function(inputs, context, index, info) { - var cacheable = true; - var i, ilen, value; - - for (i = 0, ilen = inputs.length; i < ilen; ++i) { - value = inputs[i]; - if (value === undefined) { - continue; - } - if (context !== undefined && typeof value === 'function') { - value = value(context); - cacheable = false; - } - if (index !== undefined && helpers_core.isArray(value)) { - value = value[index]; - cacheable = false; - } - if (value !== undefined) { - if (info && !cacheable) { - info.cacheable = false; - } - return value; - } - } - } -}; - -/** - * @alias Chart.helpers.math - * @namespace - */ -var exports$2 = { - /** - * Returns an array of factors sorted from 1 to sqrt(value) - * @private - */ - _factorize: function(value) { - var result = []; - var sqrt = Math.sqrt(value); - var i; - - for (i = 1; i < sqrt; i++) { - if (value % i === 0) { - result.push(i); - result.push(value / i); - } - } - if (sqrt === (sqrt | 0)) { // if value is a square number - result.push(sqrt); - } - - result.sort(function(a, b) { - return a - b; - }).pop(); - return result; - }, - - log10: Math.log10 || function(x) { - var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. - // Check for whole powers of 10, - // which due to floating point rounding error should be corrected. - var powerOf10 = Math.round(exponent); - var isPowerOf10 = x === Math.pow(10, powerOf10); - - return isPowerOf10 ? powerOf10 : exponent; - } -}; - -var helpers_math = exports$2; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.math.log10 instead. - * @namespace Chart.helpers.log10 - * @deprecated since version 2.9.0 - * @todo remove at version 3 - * @private - */ -helpers_core.log10 = exports$2.log10; - -var getRtlAdapter = function(rectX, width) { - return { - x: function(x) { - return rectX + rectX + width - x; - }, - setWidth: function(w) { - width = w; - }, - textAlign: function(align) { - if (align === 'center') { - return align; - } - return align === 'right' ? 'left' : 'right'; - }, - xPlus: function(x, value) { - return x - value; - }, - leftForLtr: function(x, itemWidth) { - return x - itemWidth; - }, - }; -}; - -var getLtrAdapter = function() { - return { - x: function(x) { - return x; - }, - setWidth: function(w) { // eslint-disable-line no-unused-vars - }, - textAlign: function(align) { - return align; - }, - xPlus: function(x, value) { - return x + value; - }, - leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars - return x; - }, - }; -}; - -var getAdapter = function(rtl, rectX, width) { - return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter(); -}; - -var overrideTextDirection = function(ctx, direction) { - var style, original; - if (direction === 'ltr' || direction === 'rtl') { - style = ctx.canvas.style; - original = [ - style.getPropertyValue('direction'), - style.getPropertyPriority('direction'), - ]; - - style.setProperty('direction', direction, 'important'); - ctx.prevTextDirection = original; - } -}; - -var restoreTextDirection = function(ctx) { - var original = ctx.prevTextDirection; - if (original !== undefined) { - delete ctx.prevTextDirection; - ctx.canvas.style.setProperty('direction', original[0], original[1]); - } -}; - -var helpers_rtl = { - getRtlAdapter: getAdapter, - overrideTextDirection: overrideTextDirection, - restoreTextDirection: restoreTextDirection, -}; - -var helpers$1 = helpers_core; -var easing = helpers_easing; -var canvas = helpers_canvas; -var options = helpers_options; -var math = helpers_math; -var rtl = helpers_rtl; -helpers$1.easing = easing; -helpers$1.canvas = canvas; -helpers$1.options = options; -helpers$1.math = math; -helpers$1.rtl = rtl; - -function interpolate(start, view, model, ease) { - var keys = Object.keys(model); - var i, ilen, key, actual, origin, target, type, c0, c1; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - - target = model[key]; - - // if a value is added to the model after pivot() has been called, the view - // doesn't contain it, so let's initialize the view to the target value. - if (!view.hasOwnProperty(key)) { - view[key] = target; - } - - actual = view[key]; - - if (actual === target || key[0] === '_') { - continue; - } - - if (!start.hasOwnProperty(key)) { - start[key] = actual; - } - - origin = start[key]; - - type = typeof target; - - if (type === typeof origin) { - if (type === 'string') { - c0 = chartjsColor(origin); - if (c0.valid) { - c1 = chartjsColor(target); - if (c1.valid) { - view[key] = c1.mix(c0, ease).rgbString(); - continue; - } - } - } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { - view[key] = origin + (target - origin) * ease; - continue; - } - } - - view[key] = target; - } -} - -var Element = function(configuration) { - helpers$1.extend(this, configuration); - this.initialize.apply(this, arguments); -}; - -helpers$1.extend(Element.prototype, { - _type: undefined, - - initialize: function() { - this.hidden = false; - }, - - pivot: function() { - var me = this; - if (!me._view) { - me._view = helpers$1.extend({}, me._model); - } - me._start = {}; - return me; - }, - - transition: function(ease) { - var me = this; - var model = me._model; - var start = me._start; - var view = me._view; - - // No animation -> No Transition - if (!model || ease === 1) { - me._view = helpers$1.extend({}, model); - me._start = null; - return me; - } - - if (!view) { - view = me._view = {}; - } - - if (!start) { - start = me._start = {}; - } - - interpolate(start, view, model, ease); - - return me; - }, - - tooltipPosition: function() { - return { - x: this._model.x, - y: this._model.y - }; - }, - - hasValue: function() { - return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); - } -}); - -Element.extend = helpers$1.inherits; - -var core_element = Element; - -var exports$3 = core_element.extend({ - chart: null, // the animation associated chart instance - currentStep: 0, // the current animation step - numSteps: 60, // default number of steps - easing: '', // the easing to use for this animation - render: null, // render function used by the animation service - - onAnimationProgress: null, // user specified callback to fire on each step of the animation - onAnimationComplete: null, // user specified callback to fire when the animation finishes -}); - -var core_animation = exports$3; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.Animation instead - * @prop Chart.Animation#animationObject - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$3.prototype, 'animationObject', { - get: function() { - return this; - } -}); - -/** - * Provided for backward compatibility, use Chart.Animation#chart instead - * @prop Chart.Animation#chartInstance - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$3.prototype, 'chartInstance', { - get: function() { - return this.chart; - }, - set: function(value) { - this.chart = value; - } -}); - -core_defaults._set('global', { - animation: { - duration: 1000, - easing: 'easeOutQuart', - onProgress: helpers$1.noop, - onComplete: helpers$1.noop - } -}); - -var core_animations = { - animations: [], - request: null, - - /** - * @param {Chart} chart - The chart to animate. - * @param {Chart.Animation} animation - The animation that we will animate. - * @param {number} duration - The animation duration in ms. - * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions - */ - addAnimation: function(chart, animation, duration, lazy) { - var animations = this.animations; - var i, ilen; - - animation.chart = chart; - animation.startTime = Date.now(); - animation.duration = duration; - - if (!lazy) { - chart.animating = true; - } - - for (i = 0, ilen = animations.length; i < ilen; ++i) { - if (animations[i].chart === chart) { - animations[i] = animation; - return; - } - } - - animations.push(animation); - - // If there are no animations queued, manually kickstart a digest, for lack of a better word - if (animations.length === 1) { - this.requestAnimationFrame(); - } - }, - - cancelAnimation: function(chart) { - var index = helpers$1.findIndex(this.animations, function(animation) { - return animation.chart === chart; - }); - - if (index !== -1) { - this.animations.splice(index, 1); - chart.animating = false; - } - }, - - requestAnimationFrame: function() { - var me = this; - if (me.request === null) { - // Skip animation frame requests until the active one is executed. - // This can happen when processing mouse events, e.g. 'mousemove' - // and 'mouseout' events will trigger multiple renders. - me.request = helpers$1.requestAnimFrame.call(window, function() { - me.request = null; - me.startDigest(); - }); - } - }, - - /** - * @private - */ - startDigest: function() { - var me = this; - - me.advance(); - - // Do we have more stuff to animate? - if (me.animations.length > 0) { - me.requestAnimationFrame(); - } - }, - - /** - * @private - */ - advance: function() { - var animations = this.animations; - var animation, chart, numSteps, nextStep; - var i = 0; - - // 1 animation per chart, so we are looping charts here - while (i < animations.length) { - animation = animations[i]; - chart = animation.chart; - numSteps = animation.numSteps; - - // Make sure that currentStep starts at 1 - // https://github.com/chartjs/Chart.js/issues/6104 - nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; - animation.currentStep = Math.min(nextStep, numSteps); - - helpers$1.callback(animation.render, [chart, animation], chart); - helpers$1.callback(animation.onAnimationProgress, [animation], chart); - - if (animation.currentStep >= numSteps) { - helpers$1.callback(animation.onAnimationComplete, [animation], chart); - chart.animating = false; - animations.splice(i, 1); - } else { - ++i; - } - } - } -}; - -var resolve = helpers$1.options.resolve; - -var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; - -/** - * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', - * 'unshift') and notify the listener AFTER the array has been altered. Listeners are - * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. - */ -function listenArrayEvents(array, listener) { - if (array._chartjs) { - array._chartjs.listeners.push(listener); - return; - } - - Object.defineProperty(array, '_chartjs', { - configurable: true, - enumerable: false, - value: { - listeners: [listener] - } - }); - - arrayEvents.forEach(function(key) { - var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); - var base = array[key]; - - Object.defineProperty(array, key, { - configurable: true, - enumerable: false, - value: function() { - var args = Array.prototype.slice.call(arguments); - var res = base.apply(this, args); - - helpers$1.each(array._chartjs.listeners, function(object) { - if (typeof object[method] === 'function') { - object[method].apply(object, args); - } - }); - - return res; - } - }); - }); -} - -/** - * Removes the given array event listener and cleanup extra attached properties (such as - * the _chartjs stub and overridden methods) if array doesn't have any more listeners. - */ -function unlistenArrayEvents(array, listener) { - var stub = array._chartjs; - if (!stub) { - return; - } - - var listeners = stub.listeners; - var index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - - if (listeners.length > 0) { - return; - } - - arrayEvents.forEach(function(key) { - delete array[key]; - }); - - delete array._chartjs; -} - -// Base class for all dataset controllers (line, bar, etc) -var DatasetController = function(chart, datasetIndex) { - this.initialize(chart, datasetIndex); -}; - -helpers$1.extend(DatasetController.prototype, { - - /** - * Element type used to generate a meta dataset (e.g. Chart.element.Line). - * @type {Chart.core.element} - */ - datasetElementType: null, - - /** - * Element type used to generate a meta data (e.g. Chart.element.Point). - * @type {Chart.core.element} - */ - dataElementType: null, - - /** - * Dataset element option keys to be resolved in _resolveDatasetElementOptions. - * A derived controller may override this to resolve controller-specific options. - * The keys defined here are for backward compatibility for legend styles. - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderCapStyle', - 'borderColor', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'borderWidth' - ], - - /** - * Data element option keys to be resolved in _resolveDataElementOptions. - * A derived controller may override this to resolve controller-specific options. - * The keys defined here are for backward compatibility for legend styles. - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'pointStyle' - ], - - initialize: function(chart, datasetIndex) { - var me = this; - me.chart = chart; - me.index = datasetIndex; - me.linkScales(); - me.addElements(); - me._type = me.getMeta().type; - }, - - updateIndex: function(datasetIndex) { - this.index = datasetIndex; - }, - - linkScales: function() { - var me = this; - var meta = me.getMeta(); - var chart = me.chart; - var scales = chart.scales; - var dataset = me.getDataset(); - var scalesOpts = chart.options.scales; - - if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) { - meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id; - } - if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) { - meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id; - } - }, - - getDataset: function() { - return this.chart.data.datasets[this.index]; - }, - - getMeta: function() { - return this.chart.getDatasetMeta(this.index); - }, - - getScaleForId: function(scaleID) { - return this.chart.scales[scaleID]; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().yAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getValueScale: function() { - return this.getScaleForId(this._getValueScaleId()); - }, - - /** - * @private - */ - _getIndexScale: function() { - return this.getScaleForId(this._getIndexScaleId()); - }, - - reset: function() { - this._update(true); - }, - - /** - * @private - */ - destroy: function() { - if (this._data) { - unlistenArrayEvents(this._data, this); - } - }, - - createMetaDataset: function() { - var me = this; - var type = me.datasetElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index - }); - }, - - createMetaData: function(index) { - var me = this; - var type = me.dataElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index, - _index: index - }); - }, - - addElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data || []; - var metaData = meta.data; - var i, ilen; - - for (i = 0, ilen = data.length; i < ilen; ++i) { - metaData[i] = metaData[i] || me.createMetaData(i); - } - - meta.dataset = meta.dataset || me.createMetaDataset(); - }, - - addElementAndReset: function(index) { - var element = this.createMetaData(index); - this.getMeta().data.splice(index, 0, element); - this.updateElement(element, index, true); - }, - - buildOrUpdateElements: function() { - var me = this; - var dataset = me.getDataset(); - var data = dataset.data || (dataset.data = []); - - // In order to correctly handle data addition/deletion animation (an thus simulate - // real-time charts), we need to monitor these data modifications and synchronize - // the internal meta data accordingly. - if (me._data !== data) { - if (me._data) { - // This case happens when the user replaced the data array instance. - unlistenArrayEvents(me._data, me); - } - - if (data && Object.isExtensible(data)) { - listenArrayEvents(data, me); - } - me._data = data; - } - - // Re-sync meta data in case the user replaced the data array or if we missed - // any updates and so make sure that we handle number of datapoints changing. - me.resyncElements(); - }, - - /** - * Returns the merged user-supplied and default dataset-level options - * @private - */ - _configure: function() { - var me = this; - me._config = helpers$1.merge({}, [ - me.chart.options.datasets[me._type], - me.getDataset(), - ], { - merger: function(key, target, source) { - if (key !== '_meta' && key !== 'data') { - helpers$1._merger(key, target, source); - } - } - }); - }, - - _update: function(reset) { - var me = this; - me._configure(); - me._cachedDataOpts = null; - me.update(reset); - }, - - update: helpers$1.noop, - - transition: function(easingValue) { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - for (; i < ilen; ++i) { - elements[i].transition(easingValue); - } - - if (meta.dataset) { - meta.dataset.transition(easingValue); - } - }, - - draw: function() { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - if (meta.dataset) { - meta.dataset.draw(); - } - - for (; i < ilen; ++i) { - elements[i].draw(); - } - }, - - /** - * Returns a set of predefined style properties that should be used to represent the dataset - * or the data if the index is specified - * @param {number} index - data index - * @return {IStyleInterface} style object - */ - getStyle: function(index) { - var me = this; - var meta = me.getMeta(); - var dataset = meta.dataset; - var style; - - me._configure(); - if (dataset && index === undefined) { - style = me._resolveDatasetElementOptions(dataset || {}); - } else { - index = index || 0; - style = me._resolveDataElementOptions(meta.data[index] || {}, index); - } - - if (style.fill === false || style.fill === null) { - style.backgroundColor = style.borderColor; - } - - return style; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function(element, hover) { - var me = this; - var chart = me.chart; - var datasetOpts = me._config; - var custom = element.custom || {}; - var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; - var elementOptions = me._datasetElementOptions; - var values = {}; - var i, ilen, key, readKey; - - // Scriptable options - var context = { - chart: chart, - dataset: me.getDataset(), - datasetIndex: me.index, - hover: hover - }; - - for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { - key = elementOptions[i]; - readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key; - values[key] = resolve([ - custom[readKey], - datasetOpts[readKey], - options[readKey] - ], context); - } - - return values; - }, - - /** - * @private - */ - _resolveDataElementOptions: function(element, index) { - var me = this; - var custom = element && element.custom; - var cached = me._cachedDataOpts; - if (cached && !custom) { - return cached; - } - var chart = me.chart; - var datasetOpts = me._config; - var options = chart.options.elements[me.dataElementType.prototype._type] || {}; - var elementOptions = me._dataElementOptions; - var values = {}; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: me.getDataset(), - datasetIndex: me.index - }; - - // `resolve` sets cacheable to `false` if any option is indexed or scripted - var info = {cacheable: !custom}; - - var keys, i, ilen, key; - - custom = custom || {}; - - if (helpers$1.isArray(elementOptions)) { - for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { - key = elementOptions[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index, info); - } - } else { - keys = Object.keys(elementOptions); - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[elementOptions[key]], - datasetOpts[key], - options[key] - ], context, index, info); - } - } - - if (info.cacheable) { - me._cachedDataOpts = Object.freeze(values); - } - - return values; - }, - - removeHoverStyle: function(element) { - helpers$1.merge(element._model, element.$previousStyle || {}); - delete element.$previousStyle; - }, - - setHoverStyle: function(element) { - var dataset = this.chart.data.datasets[element._datasetIndex]; - var index = element._index; - var custom = element.custom || {}; - var model = element._model; - var getHoverColor = helpers$1.getHoverColor; - - element.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth - }; - - model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); - model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); - model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); - }, - - /** - * @private - */ - _removeDatasetHoverStyle: function() { - var element = this.getMeta().dataset; - - if (element) { - this.removeHoverStyle(element); - } - }, - - /** - * @private - */ - _setDatasetHoverStyle: function() { - var element = this.getMeta().dataset; - var prev = {}; - var i, ilen, key, keys, hoverOptions, model; - - if (!element) { - return; - } - - model = element._model; - hoverOptions = this._resolveDatasetElementOptions(element, true); - - keys = Object.keys(hoverOptions); - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - prev[key] = model[key]; - model[key] = hoverOptions[key]; - } - - element.$previousStyle = prev; - }, - - /** - * @private - */ - resyncElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data; - var numMeta = meta.data.length; - var numData = data.length; - - if (numData < numMeta) { - meta.data.splice(numData, numMeta - numData); - } else if (numData > numMeta) { - me.insertElements(numMeta, numData - numMeta); - } - }, - - /** - * @private - */ - insertElements: function(start, count) { - for (var i = 0; i < count; ++i) { - this.addElementAndReset(start + i); - } - }, - - /** - * @private - */ - onDataPush: function() { - var count = arguments.length; - this.insertElements(this.getDataset().data.length - count, count); - }, - - /** - * @private - */ - onDataPop: function() { - this.getMeta().data.pop(); - }, - - /** - * @private - */ - onDataShift: function() { - this.getMeta().data.shift(); - }, - - /** - * @private - */ - onDataSplice: function(start, count) { - this.getMeta().data.splice(start, count); - this.insertElements(start, arguments.length - 2); - }, - - /** - * @private - */ - onDataUnshift: function() { - this.insertElements(0, arguments.length); - } -}); - -DatasetController.extend = helpers$1.inherits; - -var core_datasetController = DatasetController; - -var TAU = Math.PI * 2; - -core_defaults._set('global', { - elements: { - arc: { - backgroundColor: core_defaults.global.defaultColor, - borderColor: '#fff', - borderWidth: 2, - borderAlign: 'center' - } - } -}); - -function clipArc(ctx, arc) { - var startAngle = arc.startAngle; - var endAngle = arc.endAngle; - var pixelMargin = arc.pixelMargin; - var angleMargin = pixelMargin / arc.outerRadius; - var x = arc.x; - var y = arc.y; - - // Draw an inner border by cliping the arc and drawing a double-width border - // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders - ctx.beginPath(); - ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin); - if (arc.innerRadius > pixelMargin) { - angleMargin = pixelMargin / arc.innerRadius; - ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true); - } else { - ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2); - } - ctx.closePath(); - ctx.clip(); -} - -function drawFullCircleBorders(ctx, vm, arc, inner) { - var endAngle = arc.endAngle; - var i; - - if (inner) { - arc.endAngle = arc.startAngle + TAU; - clipArc(ctx, arc); - arc.endAngle = endAngle; - if (arc.endAngle === arc.startAngle && arc.fullCircles) { - arc.endAngle += TAU; - arc.fullCircles--; - } - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.stroke(); - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.stroke(); - } -} - -function drawBorder(ctx, vm, arc) { - var inner = vm.borderAlign === 'inner'; - - if (inner) { - ctx.lineWidth = vm.borderWidth * 2; - ctx.lineJoin = 'round'; - } else { - ctx.lineWidth = vm.borderWidth; - ctx.lineJoin = 'bevel'; - } - - if (arc.fullCircles) { - drawFullCircleBorders(ctx, vm, arc, inner); - } - - if (inner) { - clipArc(ctx, arc); - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - ctx.stroke(); -} - -var element_arc = core_element.extend({ - _type: 'arc', - - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); - } - return false; - }, - - inRange: function(chartX, chartY) { - var vm = this._view; - - if (vm) { - var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); - var angle = pointRelativePosition.angle; - var distance = pointRelativePosition.distance; - - // Sanitise angle range - var startAngle = vm.startAngle; - var endAngle = vm.endAngle; - while (endAngle < startAngle) { - endAngle += TAU; - } - while (angle > endAngle) { - angle -= TAU; - } - while (angle < startAngle) { - angle += TAU; - } - - // Check if within the range of the open/close angle - var betweenAngles = (angle >= startAngle && angle <= endAngle); - var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); - - return (betweenAngles && withinRadius); - } - return false; - }, - - getCenterPoint: function() { - var vm = this._view; - var halfAngle = (vm.startAngle + vm.endAngle) / 2; - var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; - return { - x: vm.x + Math.cos(halfAngle) * halfRadius, - y: vm.y + Math.sin(halfAngle) * halfRadius - }; - }, - - getArea: function() { - var vm = this._view; - return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); - }, - - tooltipPosition: function() { - var vm = this._view; - var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); - var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; - - return { - x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), - y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; - var arc = { - x: vm.x, - y: vm.y, - innerRadius: vm.innerRadius, - outerRadius: Math.max(vm.outerRadius - pixelMargin, 0), - pixelMargin: pixelMargin, - startAngle: vm.startAngle, - endAngle: vm.endAngle, - fullCircles: Math.floor(vm.circumference / TAU) - }; - var i; - - ctx.save(); - - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - - if (arc.fullCircles) { - arc.endAngle = arc.startAngle + TAU; - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.fill(); - } - arc.endAngle = arc.startAngle + vm.circumference % TAU; - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - ctx.fill(); - - if (vm.borderWidth) { - drawBorder(ctx, vm, arc); - } - - ctx.restore(); - } -}); - -var valueOrDefault$1 = helpers$1.valueOrDefault; - -var defaultColor = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - line: { - tension: 0.4, - backgroundColor: defaultColor, - borderWidth: 3, - borderColor: defaultColor, - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - capBezierPoints: true, - fill: true, // do we fill in the area between the line and its base axis - } - } -}); - -var element_line = core_element.extend({ - _type: 'line', - - draw: function() { - var me = this; - var vm = me._view; - var ctx = me._chart.ctx; - var spanGaps = vm.spanGaps; - var points = me._children.slice(); // clone array - var globalDefaults = core_defaults.global; - var globalOptionLineElements = globalDefaults.elements.line; - var lastDrawnIndex = -1; - var closePath = me._loop; - var index, previous, currentVM; - - if (!points.length) { - return; - } - - if (me._loop) { - for (index = 0; index < points.length; ++index) { - previous = helpers$1.previousItem(points, index); - // If the line has an open path, shift the point array - if (!points[index]._view.skip && previous._view.skip) { - points = points.slice(index).concat(points.slice(0, index)); - closePath = spanGaps; - break; - } - } - // If the line has a close path, add the first point again - if (closePath) { - points.push(points[0]); - } - } - - ctx.save(); - - // Stroke Line Options - ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; - - // IE 9 and 10 do not support line dash - if (ctx.setLineDash) { - ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); - } - - ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); - ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; - ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); - ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; - - // Stroke Line - ctx.beginPath(); - - // First point moves to it's starting position no matter what - currentVM = points[0]._view; - if (!currentVM.skip) { - ctx.moveTo(currentVM.x, currentVM.y); - lastDrawnIndex = 0; - } - - for (index = 1; index < points.length; ++index) { - currentVM = points[index]._view; - previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex]; - - if (!currentVM.skip) { - if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { - // There was a gap and this is the first point after the gap - ctx.moveTo(currentVM.x, currentVM.y); - } else { - // Line to next point - helpers$1.canvas.lineTo(ctx, previous._view, currentVM); - } - lastDrawnIndex = index; - } - } - - if (closePath) { - ctx.closePath(); - } - - ctx.stroke(); - ctx.restore(); - } -}); - -var valueOrDefault$2 = helpers$1.valueOrDefault; - -var defaultColor$1 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - point: { - radius: 3, - pointStyle: 'circle', - backgroundColor: defaultColor$1, - borderColor: defaultColor$1, - borderWidth: 1, - // Hover - hitRadius: 1, - hoverRadius: 4, - hoverBorderWidth: 1 - } - } -}); - -function xRange(mouseX) { - var vm = this._view; - return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; -} - -function yRange(mouseY) { - var vm = this._view; - return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; -} - -var element_point = core_element.extend({ - _type: 'point', - - inRange: function(mouseX, mouseY) { - var vm = this._view; - return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; - }, - - inLabelRange: xRange, - inXRange: xRange, - inYRange: yRange, - - getCenterPoint: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - }, - - getArea: function() { - return Math.PI * Math.pow(this._view.radius, 2); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y, - padding: vm.radius + vm.borderWidth - }; - }, - - draw: function(chartArea) { - var vm = this._view; - var ctx = this._chart.ctx; - var pointStyle = vm.pointStyle; - var rotation = vm.rotation; - var radius = vm.radius; - var x = vm.x; - var y = vm.y; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow - - if (vm.skip) { - return; - } - - // Clipping for Points. - if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { - ctx.strokeStyle = vm.borderColor || defaultColor; - ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); - ctx.fillStyle = vm.backgroundColor || defaultColor; - helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); - } - } -}); - -var defaultColor$2 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - rectangle: { - backgroundColor: defaultColor$2, - borderColor: defaultColor$2, - borderSkipped: 'bottom', - borderWidth: 0 - } - } -}); - -function isVertical(vm) { - return vm && vm.width !== undefined; -} - -/** - * Helper function to get the bounds of the bar regardless of the orientation - * @param bar {Chart.Element.Rectangle} the bar - * @return {Bounds} bounds of the bar - * @private - */ -function getBarBounds(vm) { - var x1, x2, y1, y2, half; - - if (isVertical(vm)) { - half = vm.width / 2; - x1 = vm.x - half; - x2 = vm.x + half; - y1 = Math.min(vm.y, vm.base); - y2 = Math.max(vm.y, vm.base); - } else { - half = vm.height / 2; - x1 = Math.min(vm.x, vm.base); - x2 = Math.max(vm.x, vm.base); - y1 = vm.y - half; - y2 = vm.y + half; - } - - return { - left: x1, - top: y1, - right: x2, - bottom: y2 - }; -} - -function swap(orig, v1, v2) { - return orig === v1 ? v2 : orig === v2 ? v1 : orig; -} - -function parseBorderSkipped(vm) { - var edge = vm.borderSkipped; - var res = {}; - - if (!edge) { - return res; - } - - if (vm.horizontal) { - if (vm.base > vm.x) { - edge = swap(edge, 'left', 'right'); - } - } else if (vm.base < vm.y) { - edge = swap(edge, 'bottom', 'top'); - } - - res[edge] = true; - return res; -} - -function parseBorderWidth(vm, maxW, maxH) { - var value = vm.borderWidth; - var skip = parseBorderSkipped(vm); - var t, r, b, l; - - if (helpers$1.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, - r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, - b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, - l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l - }; -} - -function boundingRects(vm) { - var bounds = getBarBounds(vm); - var width = bounds.right - bounds.left; - var height = bounds.bottom - bounds.top; - var border = parseBorderWidth(vm, width / 2, height / 2); - - return { - outer: { - x: bounds.left, - y: bounds.top, - w: width, - h: height - }, - inner: { - x: bounds.left + border.l, - y: bounds.top + border.t, - w: width - border.l - border.r, - h: height - border.t - border.b - } - }; -} - -function inRange(vm, x, y) { - var skipX = x === null; - var skipY = y === null; - var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); - - return bounds - && (skipX || x >= bounds.left && x <= bounds.right) - && (skipY || y >= bounds.top && y <= bounds.bottom); -} - -var element_rectangle = core_element.extend({ - _type: 'rectangle', - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var rects = boundingRects(vm); - var outer = rects.outer; - var inner = rects.inner; - - ctx.fillStyle = vm.backgroundColor; - ctx.fillRect(outer.x, outer.y, outer.w, outer.h); - - if (outer.w === inner.w && outer.h === inner.h) { - return; - } - - ctx.save(); - ctx.beginPath(); - ctx.rect(outer.x, outer.y, outer.w, outer.h); - ctx.clip(); - ctx.fillStyle = vm.borderColor; - ctx.rect(inner.x, inner.y, inner.w, inner.h); - ctx.fill('evenodd'); - ctx.restore(); - }, - - height: function() { - var vm = this._view; - return vm.base - vm.y; - }, - - inRange: function(mouseX, mouseY) { - return inRange(this._view, mouseX, mouseY); - }, - - inLabelRange: function(mouseX, mouseY) { - var vm = this._view; - return isVertical(vm) - ? inRange(vm, mouseX, null) - : inRange(vm, null, mouseY); - }, - - inXRange: function(mouseX) { - return inRange(this._view, mouseX, null); - }, - - inYRange: function(mouseY) { - return inRange(this._view, null, mouseY); - }, - - getCenterPoint: function() { - var vm = this._view; - var x, y; - if (isVertical(vm)) { - x = vm.x; - y = (vm.y + vm.base) / 2; - } else { - x = (vm.x + vm.base) / 2; - y = vm.y; - } - - return {x: x, y: y}; - }, - - getArea: function() { - var vm = this._view; - - return isVertical(vm) - ? vm.width * Math.abs(vm.y - vm.base) - : vm.height * Math.abs(vm.x - vm.base); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - } -}); - -var elements = {}; -var Arc = element_arc; -var Line = element_line; -var Point = element_point; -var Rectangle = element_rectangle; -elements.Arc = Arc; -elements.Line = Line; -elements.Point = Point; -elements.Rectangle = Rectangle; - -var deprecated = helpers$1._deprecated; -var valueOrDefault$3 = helpers$1.valueOrDefault; - -core_defaults._set('bar', { - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - offset: true, - gridLines: { - offsetGridLines: true - } - }], - - yAxes: [{ - type: 'linear' - }] - } -}); - -core_defaults._set('global', { - datasets: { - bar: { - categoryPercentage: 0.8, - barPercentage: 0.9 - } - } -}); - -/** - * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. - * @private - */ -function computeMinSampleSize(scale, pixels) { - var min = scale._length; - var prev, curr, i, ilen; - - for (i = 1, ilen = pixels.length; i < ilen; ++i) { - min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); - } - - for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) { - curr = scale.getPixelForTick(i); - min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min; - prev = curr; - } - - return min; -} - -/** - * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, - * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This - * mode currently always generates bars equally sized (until we introduce scriptable options?). - * @private - */ -function computeFitCategoryTraits(index, ruler, options) { - var thickness = options.barThickness; - var count = ruler.stackCount; - var curr = ruler.pixels[index]; - var min = helpers$1.isNullOrUndef(thickness) - ? computeMinSampleSize(ruler.scale, ruler.pixels) - : -1; - var size, ratio; - - if (helpers$1.isNullOrUndef(thickness)) { - size = min * options.categoryPercentage; - ratio = options.barPercentage; - } else { - // When bar thickness is enforced, category and bar percentages are ignored. - // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') - // and deprecate barPercentage since this value is ignored when thickness is absolute. - size = thickness * count; - ratio = 1; - } - - return { - chunk: size / count, - ratio: ratio, - start: curr - (size / 2) - }; -} - -/** - * Computes an "optimal" category that globally arranges bars side by side (no gap when - * percentage options are 1), based on the previous and following categories. This mode - * generates bars with different widths when data are not evenly spaced. - * @private - */ -function computeFlexCategoryTraits(index, ruler, options) { - var pixels = ruler.pixels; - var curr = pixels[index]; - var prev = index > 0 ? pixels[index - 1] : null; - var next = index < pixels.length - 1 ? pixels[index + 1] : null; - var percent = options.categoryPercentage; - var start, size; - - if (prev === null) { - // first data: its size is double based on the next point or, - // if it's also the last data, we use the scale size. - prev = curr - (next === null ? ruler.end - ruler.start : next - curr); - } - - if (next === null) { - // last data: its size is also double based on the previous point. - next = curr + curr - prev; - } - - start = curr - (curr - Math.min(prev, next)) / 2 * percent; - size = Math.abs(next - prev) / 2 * percent; - - return { - chunk: size / ruler.stackCount, - ratio: options.barPercentage, - start: start - }; -} - -var controller_bar = core_datasetController.extend({ - - dataElementType: elements.Rectangle, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderSkipped', - 'borderWidth', - 'barPercentage', - 'barThickness', - 'categoryPercentage', - 'maxBarThickness', - 'minBarLength' - ], - - initialize: function() { - var me = this; - var meta, scaleOpts; - - core_datasetController.prototype.initialize.apply(me, arguments); - - meta = me.getMeta(); - meta.stack = me.getDataset().stack; - meta.bar = true; - - scaleOpts = me._getIndexScale().options; - deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage'); - deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness'); - deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage'); - deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength'); - deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness'); - }, - - update: function(reset) { - var me = this; - var rects = me.getMeta().data; - var i, ilen; - - me._ruler = me.getRuler(); - - for (i = 0, ilen = rects.length; i < ilen; ++i) { - me.updateElement(rects[i], i, reset); - } - }, - - updateElement: function(rectangle, index, reset) { - var me = this; - var meta = me.getMeta(); - var dataset = me.getDataset(); - var options = me._resolveDataElementOptions(rectangle, index); - - rectangle._xScale = me.getScaleForId(meta.xAxisID); - rectangle._yScale = me.getScaleForId(meta.yAxisID); - rectangle._datasetIndex = me.index; - rectangle._index = index; - rectangle._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderSkipped: options.borderSkipped, - borderWidth: options.borderWidth, - datasetLabel: dataset.label, - label: me.chart.data.labels[index] - }; - - if (helpers$1.isArray(dataset.data[index])) { - rectangle._model.borderSkipped = null; - } - - me._updateElementGeometry(rectangle, index, reset, options); - - rectangle.pivot(); - }, - - /** - * @private - */ - _updateElementGeometry: function(rectangle, index, reset, options) { - var me = this; - var model = rectangle._model; - var vscale = me._getValueScale(); - var base = vscale.getBasePixel(); - var horizontal = vscale.isHorizontal(); - var ruler = me._ruler || me.getRuler(); - var vpixels = me.calculateBarValuePixels(me.index, index, options); - var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options); - - model.horizontal = horizontal; - model.base = reset ? base : vpixels.base; - model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; - model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; - model.height = horizontal ? ipixels.size : undefined; - model.width = horizontal ? undefined : ipixels.size; - }, - - /** - * Returns the stacks based on groups and bar visibility. - * @param {number} [last] - The dataset index - * @returns {string[]} The list of stack IDs - * @private - */ - _getStacks: function(last) { - var me = this; - var scale = me._getIndexScale(); - var metasets = scale._getMatchingVisibleMetas(me._type); - var stacked = scale.options.stacked; - var ilen = metasets.length; - var stacks = []; - var i, meta; - - for (i = 0; i < ilen; ++i) { - meta = metasets[i]; - // stacked | meta.stack - // | found | not found | undefined - // false | x | x | x - // true | | x | - // undefined | | x | x - if (stacked === false || stacks.indexOf(meta.stack) === -1 || - (stacked === undefined && meta.stack === undefined)) { - stacks.push(meta.stack); - } - if (meta.index === last) { - break; - } - } - - return stacks; - }, - - /** - * Returns the effective number of stacks based on groups and bar visibility. - * @private - */ - getStackCount: function() { - return this._getStacks().length; - }, - - /** - * Returns the stack index for the given dataset based on groups and bar visibility. - * @param {number} [datasetIndex] - The dataset index - * @param {string} [name] - The stack name to find - * @returns {number} The stack index - * @private - */ - getStackIndex: function(datasetIndex, name) { - var stacks = this._getStacks(datasetIndex); - var index = (name !== undefined) - ? stacks.indexOf(name) - : -1; // indexOf returns -1 if element is not present - - return (index === -1) - ? stacks.length - 1 - : index; - }, - - /** - * @private - */ - getRuler: function() { - var me = this; - var scale = me._getIndexScale(); - var pixels = []; - var i, ilen; - - for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { - pixels.push(scale.getPixelForValue(null, i, me.index)); - } - - return { - pixels: pixels, - start: scale._startPixel, - end: scale._endPixel, - stackCount: me.getStackCount(), - scale: scale - }; - }, - - /** - * Note: pixel values are not clamped to the scale area. - * @private - */ - calculateBarValuePixels: function(datasetIndex, index, options) { - var me = this; - var chart = me.chart; - var scale = me._getValueScale(); - var isHorizontal = scale.isHorizontal(); - var datasets = chart.data.datasets; - var metasets = scale._getMatchingVisibleMetas(me._type); - var value = scale._parseValue(datasets[datasetIndex].data[index]); - var minBarLength = options.minBarLength; - var stacked = scale.options.stacked; - var stack = me.getMeta().stack; - var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; - var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; - var ilen = metasets.length; - var i, imeta, ivalue, base, head, size, stackLength; - - if (stacked || (stacked === undefined && stack !== undefined)) { - for (i = 0; i < ilen; ++i) { - imeta = metasets[i]; - - if (imeta.index === datasetIndex) { - break; - } - - if (imeta.stack === stack) { - stackLength = scale._parseValue(datasets[imeta.index].data[index]); - ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; - - if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { - start += ivalue; - } - } - } - } - - base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + length); - size = head - base; - - if (minBarLength !== undefined && Math.abs(size) < minBarLength) { - size = minBarLength; - if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { - head = base - minBarLength; - } else { - head = base + minBarLength; - } - } - - return { - size: size, - base: base, - head: head, - center: head + size / 2 - }; - }, - - /** - * @private - */ - calculateBarIndexPixels: function(datasetIndex, index, ruler, options) { - var me = this; - var range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options) - : computeFitCategoryTraits(index, ruler, options); - - var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); - var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - var size = Math.min( - valueOrDefault$3(options.maxBarThickness, Infinity), - range.chunk * range.ratio); - - return { - base: center - size / 2, - head: center + size / 2, - center: center, - size: size - }; - }, - - draw: function() { - var me = this; - var chart = me.chart; - var scale = me._getValueScale(); - var rects = me.getMeta().data; - var dataset = me.getDataset(); - var ilen = rects.length; - var i = 0; - - helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); - - for (; i < ilen; ++i) { - var val = scale._parseValue(dataset.data[i]); - if (!isNaN(val.min) && !isNaN(val.max)) { - rects[i].draw(); - } - } - - helpers$1.canvas.unclipArea(chart.ctx); - }, - - /** - * @private - */ - _resolveDataElementOptions: function() { - var me = this; - var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments)); - var indexOpts = me._getIndexScale().options; - var valueOpts = me._getValueScale().options; - - values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage); - values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness); - values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage); - values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness); - values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength); - - return values; - } - -}); - -var valueOrDefault$4 = helpers$1.valueOrDefault; -var resolve$1 = helpers$1.options.resolve; - -core_defaults._set('bubble', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - type: 'linear', // bubble should probably use a linear scale by default - position: 'bottom', - id: 'x-axis-0' // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: 'linear', - position: 'left', - id: 'y-axis-0' - }] - }, - - tooltips: { - callbacks: { - title: function() { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(item, data) { - var datasetLabel = data.datasets[item.datasetIndex].label || ''; - var dataPoint = data.datasets[item.datasetIndex].data[item.index]; - return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; - } - } - } -}); - -var controller_bubble = core_datasetController.extend({ - /** - * @protected - */ - dataElementType: elements.Point, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - 'hoverRadius', - 'hitRadius', - 'pointStyle', - 'rotation' - ], - - /** - * @protected - */ - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var points = meta.data; - - // Update Points - helpers$1.each(points, function(point, index) { - me.updateElement(point, index, reset); - }); - }, - - /** - * @protected - */ - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var xScale = me.getScaleForId(meta.xAxisID); - var yScale = me.getScaleForId(meta.yAxisID); - var options = me._resolveDataElementOptions(point, index); - var data = me.getDataset().data[index]; - var dsIndex = me.index; - - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); - var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); - - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = dsIndex; - point._index = index; - point._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - hitRadius: options.hitRadius, - pointStyle: options.pointStyle, - rotation: options.rotation, - radius: reset ? 0 : options.radius, - skip: custom.skip || isNaN(x) || isNaN(y), - x: x, - y: y, - }; - - point.pivot(); - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); - model.radius = options.radius + options.hoverRadius; - }, - - /** - * @private - */ - _resolveDataElementOptions: function(point, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var custom = point.custom || {}; - var data = dataset.data[index] || {}; - var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments); - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - // In case values were cached (and thus frozen), we need to clone the values - if (me._cachedDataOpts === values) { - values = helpers$1.extend({}, values); - } - - // Custom radius resolution - values.radius = resolve$1([ - custom.radius, - data.r, - me._config.radius, - chart.options.elements.point.radius - ], context, index); - - return values; - } -}); - -var valueOrDefault$5 = helpers$1.valueOrDefault; - -var PI$1 = Math.PI; -var DOUBLE_PI$1 = PI$1 * 2; -var HALF_PI$1 = PI$1 / 2; - -core_defaults._set('doughnut', { - animation: { - // Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - // Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false - }, - hover: { - mode: 'single' - }, - legendCallback: function(chart) { - var list = document.createElement('ul'); - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - if (datasets.length) { - for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; - if (labels[i]) { - listItem.appendChild(document.createTextNode(labels[i])); - } - } - } - - return list.outerHTML; - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var style = meta.controller.getStyle(i); - - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - // toggle visibility of index if exists - if (meta.data[index]) { - meta.data[index].hidden = !meta.data[index].hidden; - } - } - - chart.update(); - } - }, - - // The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, - - // The rotation of the chart, where the first data arc begins. - rotation: -HALF_PI$1, - - // The total circumference of the chart. - circumference: DOUBLE_PI$1, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - var dataLabel = data.labels[tooltipItem.index]; - var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - - if (helpers$1.isArray(dataLabel)) { - // show value on first line of multiline label - // need to clone because we are changing the value - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - - return dataLabel; - } - } - } -}); - -var controller_doughnut = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ], - - // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly - getRingIndex: function(datasetIndex) { - var ringIndex = 0; - - for (var j = 0; j < datasetIndex; ++j) { - if (this.chart.isDatasetVisible(j)) { - ++ringIndex; - } - } - - return ringIndex; - }, - - update: function(reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var ratioX = 1; - var ratioY = 1; - var offsetX = 0; - var offsetY = 0; - var meta = me.getMeta(); - var arcs = meta.data; - var cutout = opts.cutoutPercentage / 100 || 0; - var circumference = opts.circumference; - var chartWeight = me._getRingWeight(me.index); - var maxWidth, maxHeight, i, ilen; - - // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc - if (circumference < DOUBLE_PI$1) { - var startAngle = opts.rotation % DOUBLE_PI$1; - startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0; - var endAngle = startAngle + circumference; - var startX = Math.cos(startAngle); - var startY = Math.sin(startAngle); - var endX = Math.cos(endAngle); - var endY = Math.sin(endAngle); - var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1; - var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1; - var contains180 = startAngle === -PI$1 || endAngle >= PI$1; - var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1; - var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout); - var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout); - var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout); - var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout); - ratioX = (maxX - minX) / 2; - ratioY = (maxY - minY) / 2; - offsetX = -(maxX + minX) / 2; - offsetY = -(maxY + minY) / 2; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); - } - - chart.borderWidth = me.getMaxBorderWidth(); - maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX; - maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY; - chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); - chart.innerRadius = Math.max(chart.outerRadius * cutout, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); - chart.offsetX = offsetX * chart.outerRadius; - chart.offsetY = offsetY * chart.outerRadius; - - meta.total = me.calculateTotal(); - - me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); - me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - me.updateElement(arcs[i], i, reset); - } - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var animationOpts = opts.animation; - var centerX = (chartArea.left + chartArea.right) / 2; - var centerY = (chartArea.top + chartArea.bottom) / 2; - var startAngle = opts.rotation; // non reset case handled later - var endAngle = opts.rotation; // non reset case handled later - var dataset = me.getDataset(); - var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1); - var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; - var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX + chart.offsetX, - y: centerY + chart.offsetY, - startAngle: startAngle, - endAngle: endAngle, - circumference: circumference, - outerRadius: outerRadius, - innerRadius: innerRadius, - label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) - } - }); - - var model = arc._model; - - // Set correct angles if not resetting - if (!reset || !animationOpts.animateRotate) { - if (index === 0) { - model.startAngle = opts.rotation; - } else { - model.startAngle = me.getMeta().data[index - 1]._model.endAngle; - } - - model.endAngle = model.startAngle + model.circumference; - } - - arc.pivot(); - }, - - calculateTotal: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var total = 0; - var value; - - helpers$1.each(meta.data, function(element, index) { - value = dataset.data[index]; - if (!isNaN(value) && !element.hidden) { - total += Math.abs(value); - } - }); - - /* if (total === 0) { - total = NaN; - }*/ - - return total; - }, - - calculateCircumference: function(value) { - var total = this.getMeta().total; - if (total > 0 && !isNaN(value)) { - return DOUBLE_PI$1 * (Math.abs(value) / total); - } - return 0; - }, - - // gets the max border or hover width to properly scale pie charts - getMaxBorderWidth: function(arcs) { - var me = this; - var max = 0; - var chart = me.chart; - var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; - - if (!arcs) { - // Find the outmost visible dataset - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - arcs = meta.data; - if (i !== me.index) { - controller = meta.controller; - } - break; - } - } - } - - if (!arcs) { - return 0; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arc = arcs[i]; - if (controller) { - controller._configure(); - options = controller._resolveDataElementOptions(arc, i); - } else { - options = arc._options; - } - if (options.borderAlign !== 'inner') { - borderWidth = options.borderWidth; - hoverWidth = options.hoverBorderWidth; - - max = borderWidth > max ? borderWidth : max; - max = hoverWidth > max ? hoverWidth : max; - } - } - return max; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly - * @private - */ - _getRingWeightOffset: function(datasetIndex) { - var ringWeightOffset = 0; - - for (var i = 0; i < datasetIndex; ++i) { - if (this.chart.isDatasetVisible(i)) { - ringWeightOffset += this._getRingWeight(i); - } - } - - return ringWeightOffset; - }, - - /** - * @private - */ - _getRingWeight: function(dataSetIndex) { - return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0); - }, - - /** - * Returns the sum of all visibile data set weights. This value can be 0. - * @private - */ - _getVisibleDatasetWeightTotal: function() { - return this._getRingWeightOffset(this.chart.data.datasets.length); - } -}); - -core_defaults._set('horizontalBar', { - hover: { - mode: 'index', - axis: 'y' - }, - - scales: { - xAxes: [{ - type: 'linear', - position: 'bottom' - }], - - yAxes: [{ - type: 'category', - position: 'left', - offset: true, - gridLines: { - offsetGridLines: true - } - }] - }, - - elements: { - rectangle: { - borderSkipped: 'left' - } - }, - - tooltips: { - mode: 'index', - axis: 'y' - } -}); - -core_defaults._set('global', { - datasets: { - horizontalBar: { - categoryPercentage: 0.8, - barPercentage: 0.9 - } - } -}); - -var controller_horizontalBar = controller_bar.extend({ - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().yAxisID; - } -}); - -var valueOrDefault$6 = helpers$1.valueOrDefault; -var resolve$2 = helpers$1.options.resolve; -var isPointInArea = helpers$1.canvas._isPointInArea; - -core_defaults._set('line', { - showLines: true, - spanGaps: false, - - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - id: 'x-axis-0' - }], - yAxes: [{ - type: 'linear', - id: 'y-axis-0' - }] - } -}); - -function scaleClip(scale, halfBorderWidth) { - var tickOpts = scale && scale.options.ticks || {}; - var reverse = tickOpts.reverse; - var min = tickOpts.min === undefined ? halfBorderWidth : 0; - var max = tickOpts.max === undefined ? halfBorderWidth : 0; - return { - start: reverse ? max : min, - end: reverse ? min : max - }; -} - -function defaultClip(xScale, yScale, borderWidth) { - var halfBorderWidth = borderWidth / 2; - var x = scaleClip(xScale, halfBorderWidth); - var y = scaleClip(yScale, halfBorderWidth); - - return { - top: y.end, - right: x.end, - bottom: y.start, - left: x.start - }; -} - -function toClip(value) { - var t, r, b, l; - - if (helpers$1.isObject(value)) { - t = value.top; - r = value.right; - b = value.bottom; - l = value.left; - } else { - t = r = b = l = value; - } - - return { - top: t, - right: r, - bottom: b, - left: l - }; -} - - -var controller_line = core_datasetController.extend({ - - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - /** - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderCapStyle', - 'borderColor', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'borderWidth', - 'cubicInterpolationMode', - 'fill' - ], - - /** - * @private - */ - _dataElementOptions: { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var options = me.chart.options; - var config = me._config; - var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines); - var i, ilen; - - me._xScale = me.getScaleForId(meta.xAxisID); - me._yScale = me.getScaleForId(meta.yAxisID); - - // Update Line - if (showLine) { - // Compatibility: If the properties are defined with only the old name, use those values - if (config.tension !== undefined && config.lineTension === undefined) { - config.lineTension = config.tension; - } - - // Utility - line._scale = me._yScale; - line._datasetIndex = me.index; - // Data - line._children = points; - // Model - line._model = me._resolveDatasetElementOptions(line); - - line.pivot(); - } - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - if (showLine && line._model.tension !== 0) { - me.updateBezierControlPoints(); - } - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var dataset = me.getDataset(); - var datasetIndex = me.index; - var value = dataset.data[index]; - var xScale = me._xScale; - var yScale = me._yScale; - var lineModel = meta.dataset._model; - var x, y; - - var options = me._resolveDataElementOptions(point, index); - - x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); - y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); - - // Utility - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = datasetIndex; - point._index = index; - - // Desired view properties - point._model = { - x: x, - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), - steppedLine: lineModel ? lineModel.steppedLine : false, - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function(element) { - var me = this; - var config = me._config; - var custom = element.custom || {}; - var options = me.chart.options; - var lineOptions = options.elements.line; - var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); - - // The default behavior of lines is to break at null values, according - // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 - // This option gives lines the ability to span gaps - values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps); - values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension); - values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]); - values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth))); - - return values; - }, - - calculatePointY: function(value, index, datasetIndex) { - var me = this; - var chart = me.chart; - var yScale = me._yScale; - var sumPos = 0; - var sumNeg = 0; - var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen; - - if (yScale.options.stacked) { - rightValue = +yScale.getRightValue(value); - metasets = chart._getSortedVisibleDatasetMetas(); - ilen = metasets.length; - - for (i = 0; i < ilen; ++i) { - dsMeta = metasets[i]; - if (dsMeta.index === datasetIndex) { - break; - } - - ds = chart.data.datasets[dsMeta.index]; - if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) { - stackedRightValue = +yScale.getRightValue(ds.data[index]); - if (stackedRightValue < 0) { - sumNeg += stackedRightValue || 0; - } else { - sumPos += stackedRightValue || 0; - } - } - } - - if (rightValue < 0) { - return yScale.getPixelForValue(sumNeg + rightValue); - } - return yScale.getPixelForValue(sumPos + rightValue); - } - return yScale.getPixelForValue(value); - }, - - updateBezierControlPoints: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var lineModel = meta.dataset._model; - var area = chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - // Only consider points that are drawn in case the spanGaps option is used - if (lineModel.spanGaps) { - points = points.filter(function(pt) { - return !pt._model.skip; - }); - } - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - if (lineModel.cubicInterpolationMode === 'monotone') { - helpers$1.splineCurveMonotone(points); - } else { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i)._model, - model, - helpers$1.nextItem(points, i)._model, - lineModel.tension - ); - model.controlPointPreviousX = controlPoints.previous.x; - model.controlPointPreviousY = controlPoints.previous.y; - model.controlPointNextX = controlPoints.next.x; - model.controlPointNextY = controlPoints.next.y; - } - } - - if (chart.options.elements.line.capBezierPoints) { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - if (isPointInArea(model, area)) { - if (i > 0 && isPointInArea(points[i - 1]._model, area)) { - model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); - model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); - } - if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { - model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); - model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); - } - } - } - } - }, - - draw: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var points = meta.data || []; - var area = chart.chartArea; - var canvas = chart.canvas; - var i = 0; - var ilen = points.length; - var clip; - - if (me._showLine) { - clip = meta.dataset._model.clip; - - helpers$1.canvas.clipArea(chart.ctx, { - left: clip.left === false ? 0 : area.left - clip.left, - right: clip.right === false ? canvas.width : area.right + clip.right, - top: clip.top === false ? 0 : area.top - clip.top, - bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom - }); - - meta.dataset.draw(); - - helpers$1.canvas.unclipArea(chart.ctx); - } - - // Draw the points - for (; i < ilen; ++i) { - points[i].draw(area); - } - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$6(options.hoverRadius, options.radius); - }, -}); - -var resolve$3 = helpers$1.options.resolve; - -core_defaults._set('polarArea', { - scale: { - type: 'radialLinear', - angleLines: { - display: false - }, - gridLines: { - circular: true - }, - pointLabels: { - display: false - }, - ticks: { - beginAtZero: true - } - }, - - // Boolean - Whether to animate the rotation of the chart - animation: { - animateRotate: true, - animateScale: true - }, - - startAngle: -0.5 * Math.PI, - legendCallback: function(chart) { - var list = document.createElement('ul'); - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - if (datasets.length) { - for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; - if (labels[i]) { - listItem.appendChild(document.createTextNode(labels[i])); - } - } - } - - return list.outerHTML; - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var style = meta.controller.getStyle(i); - - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - meta.data[index].hidden = !meta.data[index].hidden; - } - - chart.update(); - } - }, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(item, data) { - return data.labels[item.index] + ': ' + item.yLabel; - } - } - } -}); - -var controller_polarArea = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ], - - /** - * @private - */ - _getIndexScaleId: function() { - return this.chart.scale.id; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.chart.scale.id; - }, - - update: function(reset) { - var me = this; - var dataset = me.getDataset(); - var meta = me.getMeta(); - var start = me.chart.options.startAngle || 0; - var starts = me._starts = []; - var angles = me._angles = []; - var arcs = meta.data; - var i, ilen, angle; - - me._updateRadius(); - - meta.count = me.countVisibleElements(); - - for (i = 0, ilen = dataset.data.length; i < ilen; i++) { - starts[i] = start; - angle = me._computeAngle(i); - angles[i] = angle; - start += angle; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); - me.updateElement(arcs[i], i, reset); - } - }, - - /** - * @private - */ - _updateRadius: function() { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - - chart.outerRadius = Math.max(minSize / 2, 0); - chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); - - me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); - me.innerRadius = me.outerRadius - chart.radiusLength; - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var opts = chart.options; - var animationOpts = opts.animation; - var scale = chart.scale; - var labels = chart.data.labels; - - var centerX = scale.xCenter; - var centerY = scale.yCenter; - - // var negHalfPI = -0.5 * Math.PI; - var datasetStartAngle = opts.startAngle; - var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var startAngle = me._starts[index]; - var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); - - var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - _scale: scale, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius: reset ? resetRadius : distance, - startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, - endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, - label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) - } - }); - - arc.pivot(); - }, - - countVisibleElements: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var count = 0; - - helpers$1.each(meta.data, function(element, index) { - if (!isNaN(dataset.data[index]) && !element.hidden) { - count++; - } - }); - - return count; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - var valueOrDefault = helpers$1.valueOrDefault; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * @private - */ - _computeAngle: function(index) { - var me = this; - var count = this.getMeta().count; - var dataset = me.getDataset(); - var meta = me.getMeta(); - - if (isNaN(dataset.data[index]) || meta.data[index].hidden) { - return 0; - } - - // Scriptable options - var context = { - chart: me.chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - return resolve$3([ - me.chart.options.elements.arc.angle, - (2 * Math.PI) / count - ], context, index); - } -}); - -core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); -core_defaults._set('pie', { - cutoutPercentage: 0 -}); - -// Pie charts are Doughnut chart with different defaults -var controller_pie = controller_doughnut; - -var valueOrDefault$7 = helpers$1.valueOrDefault; - -core_defaults._set('radar', { - spanGaps: false, - scale: { - type: 'radialLinear' - }, - elements: { - line: { - fill: 'start', - tension: 0 // no bezier in radar - } - } -}); - -var controller_radar = core_datasetController.extend({ - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderWidth', - 'borderColor', - 'borderCapStyle', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'fill' - ], - - /** - * @private - */ - _dataElementOptions: { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.chart.scale.id; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.chart.scale.id; - }, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var scale = me.chart.scale; - var config = me._config; - var i, ilen; - - // Compatibility: If the properties are defined with only the old name, use those values - if (config.tension !== undefined && config.lineTension === undefined) { - config.lineTension = config.tension; - } - - // Utility - line._scale = scale; - line._datasetIndex = me.index; - // Data - line._children = points; - line._loop = true; - // Model - line._model = me._resolveDatasetElementOptions(line); - - line.pivot(); - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - // Update bezier control points - me.updateBezierControlPoints(); - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var custom = point.custom || {}; - var dataset = me.getDataset(); - var scale = me.chart.scale; - var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); - var options = me._resolveDataElementOptions(point, index); - var lineModel = me.getMeta().dataset._model; - var x = reset ? scale.xCenter : pointPosition.x; - var y = reset ? scale.yCenter : pointPosition.y; - - // Utility - point._scale = scale; - point._options = options; - point._datasetIndex = me.index; - point._index = index; - - // Desired view properties - point._model = { - x: x, // value not used in dataset scale, but we want a consistent API between scales - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0), - - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function() { - var me = this; - var config = me._config; - var options = me.chart.options; - var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); - - values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps); - values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension); - - return values; - }, - - updateBezierControlPoints: function() { - var me = this; - var meta = me.getMeta(); - var area = me.chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - // Only consider points that are drawn in case the spanGaps option is used - if (meta.dataset._model.spanGaps) { - points = points.filter(function(pt) { - return !pt._model.skip; - }); - } - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i, true)._model, - model, - helpers$1.nextItem(points, i, true)._model, - model.tension - ); - - // Prevent the bezier going outside of the bounds of the graph - model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); - model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); - model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); - model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); - } - }, - - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$7(options.hoverRadius, options.radius); - } -}); - -core_defaults._set('scatter', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - id: 'x-axis-1', // need an ID so datasets can reference the scale - type: 'linear', // scatter should not use a category axis - position: 'bottom' - }], - yAxes: [{ - id: 'y-axis-1', - type: 'linear', - position: 'left' - }] - }, - - tooltips: { - callbacks: { - title: function() { - return ''; // doesn't make sense for scatter since data are formatted as a point - }, - label: function(item) { - return '(' + item.xLabel + ', ' + item.yLabel + ')'; - } - } - } -}); - -core_defaults._set('global', { - datasets: { - scatter: { - showLine: false - } - } -}); - -// Scatter charts use line controllers -var controller_scatter = controller_line; - -// NOTE export a map in which the key represents the controller type, not -// the class, and so must be CamelCase in order to be correctly retrieved -// by the controller in core.controller.js (`controllers[meta.type]`). - -var controllers = { - bar: controller_bar, - bubble: controller_bubble, - doughnut: controller_doughnut, - horizontalBar: controller_horizontalBar, - line: controller_line, - polarArea: controller_polarArea, - pie: controller_pie, - radar: controller_radar, - scatter: controller_scatter -}; - -/** - * Helper function to get relative position for an event - * @param {Event|IEvent} event - The event to get the position for - * @param {Chart} chart - The chart - * @returns {object} the event position - */ -function getRelativePosition(e, chart) { - if (e.native) { - return { - x: e.x, - y: e.y - }; - } - - return helpers$1.getRelativePosition(e, chart); -} - -/** - * Helper function to traverse all of the visible elements in the chart - * @param {Chart} chart - the chart - * @param {function} handler - the callback to execute for each visible item - */ -function parseVisibleItems(chart, handler) { - var metasets = chart._getSortedVisibleDatasetMetas(); - var metadata, i, j, ilen, jlen, element; - - for (i = 0, ilen = metasets.length; i < ilen; ++i) { - metadata = metasets[i].data; - for (j = 0, jlen = metadata.length; j < jlen; ++j) { - element = metadata[j]; - if (!element._view.skip) { - handler(element); - } - } - } -} - -/** - * Helper function to get the items that intersect the event position - * @param {ChartElement[]} items - elements to filter - * @param {object} position - the point to be nearest to - * @return {ChartElement[]} the nearest items - */ -function getIntersectItems(chart, position) { - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - } - }); - - return elements; -} - -/** - * Helper function to get the items nearest to the event position considering all visible items in teh chart - * @param {Chart} chart - the chart to look at elements from - * @param {object} position - the point to be nearest to - * @param {boolean} intersect - if true, only consider items that intersect the position - * @param {function} distanceMetric - function to provide the distance between points - * @return {ChartElement[]} the nearest items - */ -function getNearestItems(chart, position, intersect, distanceMetric) { - var minDistance = Number.POSITIVE_INFINITY; - var nearestItems = []; - - parseVisibleItems(chart, function(element) { - if (intersect && !element.inRange(position.x, position.y)) { - return; - } - - var center = element.getCenterPoint(); - var distance = distanceMetric(position, center); - if (distance < minDistance) { - nearestItems = [element]; - minDistance = distance; - } else if (distance === minDistance) { - // Can have multiple items at the same distance in which case we sort by size - nearestItems.push(element); - } - }); - - return nearestItems; -} - -/** - * Get a distance metric function for two points based on the - * axis mode setting - * @param {string} axis - the axis mode. x|y|xy - */ -function getDistanceMetricForAxis(axis) { - var useX = axis.indexOf('x') !== -1; - var useY = axis.indexOf('y') !== -1; - - return function(pt1, pt2) { - var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; - var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; - return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); - }; -} - -function indexMode(chart, e, options) { - var position = getRelativePosition(e, chart); - // Default axis for index mode is 'x' to match old behaviour - options.axis = options.axis || 'x'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - var elements = []; - - if (!items.length) { - return []; - } - - chart._getSortedVisibleDatasetMetas().forEach(function(meta) { - var element = meta.data[items[0]._index]; - - // don't count items that are skipped (null data) - if (element && !element._view.skip) { - elements.push(element); - } - }); - - return elements; -} - -/** - * @interface IInteractionOptions - */ -/** - * If true, only consider items that intersect the point - * @name IInterfaceOptions#boolean - * @type Boolean - */ - -/** - * Contains interaction related functions - * @namespace Chart.Interaction - */ -var core_interaction = { - // Helper function for different modes - modes: { - single: function(chart, e) { - var position = getRelativePosition(e, chart); - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - return elements; - } - }); - - return elements.slice(0, 1); - }, - - /** - * @function Chart.Interaction.modes.label - * @deprecated since version 2.4.0 - * @todo remove at version 3 - * @private - */ - label: indexMode, - - /** - * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item - * @function Chart.Interaction.modes.index - * @since v2.4.0 - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - index: indexMode, - - /** - * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect is false, we find the nearest item and return the items in that dataset - * @function Chart.Interaction.modes.dataset - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - dataset: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - - if (items.length > 0) { - items = chart.getDatasetMeta(items[0]._datasetIndex).data; - } - - return items; - }, - - /** - * @function Chart.Interaction.modes.x-axis - * @deprecated since version 2.4.0. Use index mode and intersect == true - * @todo remove at version 3 - * @private - */ - 'x-axis': function(chart, e) { - return indexMode(chart, e, {intersect: false}); - }, - - /** - * Point mode returns all elements that hit test based on the event position - * of the event - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - point: function(chart, e) { - var position = getRelativePosition(e, chart); - return getIntersectItems(chart, position); - }, - - /** - * nearest mode returns the element closest to the point - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - nearest: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - return getNearestItems(chart, position, options.intersect, distanceMetric); - }, - - /** - * x mode returns the elements that hit-test at the current x coordinate - * @function Chart.Interaction.modes.x - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - x: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inXRange(position.x)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - }, - - /** - * y mode returns the elements that hit-test at the current y coordinate - * @function Chart.Interaction.modes.y - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - y: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inYRange(position.y)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - } - } -}; - -var extend = helpers$1.extend; - -function filterByPosition(array, position) { - return helpers$1.where(array, function(v) { - return v.pos === position; - }); -} - -function sortByWeight(array, reverse) { - return array.sort(function(a, b) { - var v0 = reverse ? b : a; - var v1 = reverse ? a : b; - return v0.weight === v1.weight ? - v0.index - v1.index : - v0.weight - v1.weight; - }); -} - -function wrapBoxes(boxes) { - var layoutBoxes = []; - var i, ilen, box; - - for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) { - box = boxes[i]; - layoutBoxes.push({ - index: i, - box: box, - pos: box.position, - horizontal: box.isHorizontal(), - weight: box.weight - }); - } - return layoutBoxes; -} - -function setLayoutDims(layouts, params) { - var i, ilen, layout; - for (i = 0, ilen = layouts.length; i < ilen; ++i) { - layout = layouts[i]; - // store width used instead of chartArea.w in fitBoxes - layout.width = layout.horizontal - ? layout.box.fullWidth && params.availableWidth - : params.vBoxMaxWidth; - // store height used instead of chartArea.h in fitBoxes - layout.height = layout.horizontal && params.hBoxMaxHeight; - } -} - -function buildLayoutBoxes(boxes) { - var layoutBoxes = wrapBoxes(boxes); - var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true); - var right = sortByWeight(filterByPosition(layoutBoxes, 'right')); - var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true); - var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom')); - - return { - leftAndTop: left.concat(top), - rightAndBottom: right.concat(bottom), - chartArea: filterByPosition(layoutBoxes, 'chartArea'), - vertical: left.concat(right), - horizontal: top.concat(bottom) - }; -} - -function getCombinedMax(maxPadding, chartArea, a, b) { - return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]); -} - -function updateDims(chartArea, params, layout) { - var box = layout.box; - var maxPadding = chartArea.maxPadding; - var newWidth, newHeight; - - if (layout.size) { - // this layout was already counted for, lets first reduce old size - chartArea[layout.pos] -= layout.size; - } - layout.size = layout.horizontal ? box.height : box.width; - chartArea[layout.pos] += layout.size; - - if (box.getPadding) { - var boxPadding = box.getPadding(); - maxPadding.top = Math.max(maxPadding.top, boxPadding.top); - maxPadding.left = Math.max(maxPadding.left, boxPadding.left); - maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom); - maxPadding.right = Math.max(maxPadding.right, boxPadding.right); - } - - newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'); - newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'); - - if (newWidth !== chartArea.w || newHeight !== chartArea.h) { - chartArea.w = newWidth; - chartArea.h = newHeight; - - // return true if chart area changed in layout's direction - return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h; - } -} - -function handleMaxPadding(chartArea) { - var maxPadding = chartArea.maxPadding; - - function updatePos(pos) { - var change = Math.max(maxPadding[pos] - chartArea[pos], 0); - chartArea[pos] += change; - return change; - } - chartArea.y += updatePos('top'); - chartArea.x += updatePos('left'); - updatePos('right'); - updatePos('bottom'); -} - -function getMargins(horizontal, chartArea) { - var maxPadding = chartArea.maxPadding; - - function marginForPositions(positions) { - var margin = {left: 0, top: 0, right: 0, bottom: 0}; - positions.forEach(function(pos) { - margin[pos] = Math.max(chartArea[pos], maxPadding[pos]); - }); - return margin; - } - - return horizontal - ? marginForPositions(['left', 'right']) - : marginForPositions(['top', 'bottom']); -} - -function fitBoxes(boxes, chartArea, params) { - var refitBoxes = []; - var i, ilen, layout, box, refit, changed; - - for (i = 0, ilen = boxes.length; i < ilen; ++i) { - layout = boxes[i]; - box = layout.box; - - box.update( - layout.width || chartArea.w, - layout.height || chartArea.h, - getMargins(layout.horizontal, chartArea) - ); - if (updateDims(chartArea, params, layout)) { - changed = true; - if (refitBoxes.length) { - // Dimensions changed and there were non full width boxes before this - // -> we have to refit those - refit = true; - } - } - if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case - refitBoxes.push(layout); - } - } - - return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed; -} - -function placeBoxes(boxes, chartArea, params) { - var userPadding = params.padding; - var x = chartArea.x; - var y = chartArea.y; - var i, ilen, layout, box; - - for (i = 0, ilen = boxes.length; i < ilen; ++i) { - layout = boxes[i]; - box = layout.box; - if (layout.horizontal) { - box.left = box.fullWidth ? userPadding.left : chartArea.left; - box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w; - box.top = y; - box.bottom = y + box.height; - box.width = box.right - box.left; - y = box.bottom; - } else { - box.left = x; - box.right = x + box.width; - box.top = chartArea.top; - box.bottom = chartArea.top + chartArea.h; - box.height = box.bottom - box.top; - x = box.right; - } - } - - chartArea.x = x; - chartArea.y = y; -} - -core_defaults._set('global', { - layout: { - padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - } -}); - -/** - * @interface ILayoutItem - * @prop {string} position - The position of the item in the chart layout. Possible values are - * 'left', 'top', 'right', 'bottom', and 'chartArea' - * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area - * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down - * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) - * @prop {function} update - Takes two parameters: width and height. Returns size of item - * @prop {function} getPadding - Returns an object with padding on the edges - * @prop {number} width - Width of item. Must be valid after update() - * @prop {number} height - Height of item. Must be valid after update() - * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update - * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update - * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update - * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update - */ - -// The layout service is very self explanatory. It's responsible for the layout within a chart. -// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need -// It is this service's responsibility of carrying out that layout. -var core_layouts = { - defaults: {}, - - /** - * Register a box to a chart. - * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. - * @param {Chart} chart - the chart to use - * @param {ILayoutItem} item - the item to add to be layed out - */ - addBox: function(chart, item) { - if (!chart.boxes) { - chart.boxes = []; - } - - // initialize item with default values - item.fullWidth = item.fullWidth || false; - item.position = item.position || 'top'; - item.weight = item.weight || 0; - item._layers = item._layers || function() { - return [{ - z: 0, - draw: function() { - item.draw.apply(item, arguments); - } - }]; - }; - - chart.boxes.push(item); - }, - - /** - * Remove a layoutItem from a chart - * @param {Chart} chart - the chart to remove the box from - * @param {ILayoutItem} layoutItem - the item to remove from the layout - */ - removeBox: function(chart, layoutItem) { - var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; - if (index !== -1) { - chart.boxes.splice(index, 1); - } - }, - - /** - * Sets (or updates) options on the given `item`. - * @param {Chart} chart - the chart in which the item lives (or will be added to) - * @param {ILayoutItem} item - the item to configure with the given options - * @param {object} options - the new item options. - */ - configure: function(chart, item, options) { - var props = ['fullWidth', 'position', 'weight']; - var ilen = props.length; - var i = 0; - var prop; - - for (; i < ilen; ++i) { - prop = props[i]; - if (options.hasOwnProperty(prop)) { - item[prop] = options[prop]; - } - } - }, - - /** - * Fits boxes of the given chart into the given size by having each box measure itself - * then running a fitting algorithm - * @param {Chart} chart - the chart - * @param {number} width - the width to fit into - * @param {number} height - the height to fit into - */ - update: function(chart, width, height) { - if (!chart) { - return; - } - - var layoutOptions = chart.options.layout || {}; - var padding = helpers$1.options.toPadding(layoutOptions.padding); - - var availableWidth = width - padding.width; - var availableHeight = height - padding.height; - var boxes = buildLayoutBoxes(chart.boxes); - var verticalBoxes = boxes.vertical; - var horizontalBoxes = boxes.horizontal; - - // Essentially we now have any number of boxes on each of the 4 sides. - // Our canvas looks like the following. - // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and - // B1 is the bottom axis - // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays - // These locations are single-box locations only, when trying to register a chartArea location that is already taken, - // an error will be thrown. - // - // |----------------------------------------------------| - // | T1 (Full Width) | - // |----------------------------------------------------| - // | | | T2 | | - // | |----|-------------------------------------|----| - // | | | C1 | | C2 | | - // | | |----| |----| | - // | | | | | - // | L1 | L2 | ChartArea (C0) | R1 | - // | | | | | - // | | |----| |----| | - // | | | C3 | | C4 | | - // | |----|-------------------------------------|----| - // | | | B1 | | - // |----------------------------------------------------| - // | B2 (Full Width) | - // |----------------------------------------------------| - // - - var params = Object.freeze({ - outerWidth: width, - outerHeight: height, - padding: padding, - availableWidth: availableWidth, - vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length, - hBoxMaxHeight: availableHeight / 2 - }); - var chartArea = extend({ - maxPadding: extend({}, padding), - w: availableWidth, - h: availableHeight, - x: padding.left, - y: padding.top - }, padding); - - setLayoutDims(verticalBoxes.concat(horizontalBoxes), params); - - // First fit vertical boxes - fitBoxes(verticalBoxes, chartArea, params); - - // Then fit horizontal boxes - if (fitBoxes(horizontalBoxes, chartArea, params)) { - // if the area changed, re-fit vertical boxes - fitBoxes(verticalBoxes, chartArea, params); - } - - handleMaxPadding(chartArea); - - // Finally place the boxes to correct coordinates - placeBoxes(boxes.leftAndTop, chartArea, params); - - // Move to opposite side of chart - chartArea.x += chartArea.w; - chartArea.y += chartArea.h; - - placeBoxes(boxes.rightAndBottom, chartArea, params); - - chart.chartArea = { - left: chartArea.left, - top: chartArea.top, - right: chartArea.left + chartArea.w, - bottom: chartArea.top + chartArea.h - }; - - // Finally update boxes in chartArea (radial scale for example) - helpers$1.each(boxes.chartArea, function(layout) { - var box = layout.box; - extend(box, chart.chartArea); - box.update(chartArea.w, chartArea.h); - }); - } -}; - -/** - * Platform fallback implementation (minimal). - * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 - */ - -var platform_basic = { - acquireContext: function(item) { - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - return item && item.getContext('2d') || null; - } -}; - -var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; - -var platform_dom$1 = /*#__PURE__*/Object.freeze({ -__proto__: null, -'default': platform_dom -}); - -var stylesheet = getCjsExportFromNamespace(platform_dom$1); - -var EXPANDO_KEY = '$chartjs'; -var CSS_PREFIX = 'chartjs-'; -var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; -var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; -var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; -var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; - -/** - * DOM event types -> Chart.js event types. - * Note: only events with different types are mapped. - * @see https://developer.mozilla.org/en-US/docs/Web/Events - */ -var EVENT_TYPES = { - touchstart: 'mousedown', - touchmove: 'mousemove', - touchend: 'mouseup', - pointerenter: 'mouseenter', - pointerdown: 'mousedown', - pointermove: 'mousemove', - pointerup: 'mouseup', - pointerleave: 'mouseout', - pointerout: 'mouseout' -}; - -/** - * The "used" size is the final value of a dimension property after all calculations have - * been performed. This method uses the computed style of `element` but returns undefined - * if the computed style is not expressed in pixels. That can happen in some cases where - * `element` has a size relative to its parent and this last one is not yet displayed, - * for example because of `display: none` on a parent node. - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value - * @returns {number} Size in pixels or undefined if unknown. - */ -function readUsedSize(element, property) { - var value = helpers$1.getStyle(element, property); - var matches = value && value.match(/^(\d+)(\.\d+)?px$/); - return matches ? Number(matches[1]) : undefined; -} - -/** - * Initializes the canvas style and render size without modifying the canvas display size, - * since responsiveness is handled by the controller.resize() method. The config is used - * to determine the aspect ratio to apply in case no explicit height has been specified. - */ -function initCanvas(canvas, config) { - var style = canvas.style; - - // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it - // returns null or '' if no explicit value has been set to the canvas attribute. - var renderHeight = canvas.getAttribute('height'); - var renderWidth = canvas.getAttribute('width'); - - // Chart.js modifies some canvas values that we want to restore on destroy - canvas[EXPANDO_KEY] = { - initial: { - height: renderHeight, - width: renderWidth, - style: { - display: style.display, - height: style.height, - width: style.width - } - } - }; - - // Force canvas to display as block to avoid extra space caused by inline - // elements, which would interfere with the responsive resize process. - // https://github.com/chartjs/Chart.js/issues/2538 - style.display = style.display || 'block'; - - if (renderWidth === null || renderWidth === '') { - var displayWidth = readUsedSize(canvas, 'width'); - if (displayWidth !== undefined) { - canvas.width = displayWidth; - } - } - - if (renderHeight === null || renderHeight === '') { - if (canvas.style.height === '') { - // If no explicit render height and style height, let's apply the aspect ratio, - // which one can be specified by the user but also by charts as default option - // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. - canvas.height = canvas.width / (config.options.aspectRatio || 2); - } else { - var displayHeight = readUsedSize(canvas, 'height'); - if (displayWidth !== undefined) { - canvas.height = displayHeight; - } - } - } - - return canvas; -} - -/** - * Detects support for options object argument in addEventListener. - * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support - * @private - */ -var supportsEventListenerOptions = (function() { - var supports = false; - try { - var options = Object.defineProperty({}, 'passive', { - // eslint-disable-next-line getter-return - get: function() { - supports = true; - } - }); - window.addEventListener('e', null, options); - } catch (e) { - // continue regardless of error - } - return supports; -}()); - -// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. -// https://github.com/chartjs/Chart.js/issues/4287 -var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; - -function addListener(node, type, listener) { - node.addEventListener(type, listener, eventListenerOptions); -} - -function removeListener(node, type, listener) { - node.removeEventListener(type, listener, eventListenerOptions); -} - -function createEvent(type, chart, x, y, nativeEvent) { - return { - type: type, - chart: chart, - native: nativeEvent || null, - x: x !== undefined ? x : null, - y: y !== undefined ? y : null, - }; -} - -function fromNativeEvent(event, chart) { - var type = EVENT_TYPES[event.type] || event.type; - var pos = helpers$1.getRelativePosition(event, chart); - return createEvent(type, chart, pos.x, pos.y, event); -} - -function throttled(fn, thisArg) { - var ticking = false; - var args = []; - - return function() { - args = Array.prototype.slice.call(arguments); - thisArg = thisArg || this; - - if (!ticking) { - ticking = true; - helpers$1.requestAnimFrame.call(window, function() { - ticking = false; - fn.apply(thisArg, args); - }); - } - }; -} - -function createDiv(cls) { - var el = document.createElement('div'); - el.className = cls || ''; - return el; -} - -// Implementation based on https://github.com/marcj/css-element-queries -function createResizer(handler) { - var maxSize = 1000000; - - // NOTE(SB) Don't use innerHTML because it could be considered unsafe. - // https://github.com/chartjs/Chart.js/issues/5902 - var resizer = createDiv(CSS_SIZE_MONITOR); - var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); - var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); - - expand.appendChild(createDiv()); - shrink.appendChild(createDiv()); - - resizer.appendChild(expand); - resizer.appendChild(shrink); - resizer._reset = function() { - expand.scrollLeft = maxSize; - expand.scrollTop = maxSize; - shrink.scrollLeft = maxSize; - shrink.scrollTop = maxSize; - }; - - var onScroll = function() { - resizer._reset(); - handler(); - }; - - addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); - addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); - - return resizer; -} - -// https://davidwalsh.name/detect-node-insertion -function watchForRender(node, handler) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - var proxy = expando.renderProxy = function(e) { - if (e.animationName === CSS_RENDER_ANIMATION) { - handler(); - } - }; - - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - addListener(node, type, proxy); - }); - - // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class - // is removed then added back immediately (same animation frame?). Accessing the - // `offsetParent` property will force a reflow and re-evaluate the CSS animation. - // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics - // https://github.com/chartjs/Chart.js/issues/4737 - expando.reflow = !!node.offsetParent; - - node.classList.add(CSS_RENDER_MONITOR); -} - -function unwatchForRender(node) { - var expando = node[EXPANDO_KEY] || {}; - var proxy = expando.renderProxy; - - if (proxy) { - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - removeListener(node, type, proxy); - }); - - delete expando.renderProxy; - } - - node.classList.remove(CSS_RENDER_MONITOR); -} - -function addResizeListener(node, listener, chart) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - - // Let's keep track of this added resizer and thus avoid DOM query when removing it. - var resizer = expando.resizer = createResizer(throttled(function() { - if (expando.resizer) { - var container = chart.options.maintainAspectRatio && node.parentNode; - var w = container ? container.clientWidth : 0; - listener(createEvent('resize', chart)); - if (container && container.clientWidth < w && chart.canvas) { - // If the container size shrank during chart resize, let's assume - // scrollbar appeared. So we resize again with the scrollbar visible - - // effectively making chart smaller and the scrollbar hidden again. - // Because we are inside `throttled`, and currently `ticking`, scroll - // events are ignored during this whole 2 resize process. - // If we assumed wrong and something else happened, we are resizing - // twice in a frame (potential performance issue) - listener(createEvent('resize', chart)); - } - } - })); - - // The resizer needs to be attached to the node parent, so we first need to be - // sure that `node` is attached to the DOM before injecting the resizer element. - watchForRender(node, function() { - if (expando.resizer) { - var container = node.parentNode; - if (container && container !== resizer.parentNode) { - container.insertBefore(resizer, container.firstChild); - } - - // The container size might have changed, let's reset the resizer state. - resizer._reset(); - } - }); -} - -function removeResizeListener(node) { - var expando = node[EXPANDO_KEY] || {}; - var resizer = expando.resizer; - - delete expando.resizer; - unwatchForRender(node); - - if (resizer && resizer.parentNode) { - resizer.parentNode.removeChild(resizer); - } -} - -/** - * Injects CSS styles inline if the styles are not already present. - * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>. - * @param {string} css - the CSS to be injected. - */ -function injectCSS(rootNode, css) { - // https://stackoverflow.com/q/3922139 - var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {}); - if (!expando.containsStyles) { - expando.containsStyles = true; - css = '/* Chart.js */\n' + css; - var style = document.createElement('style'); - style.setAttribute('type', 'text/css'); - style.appendChild(document.createTextNode(css)); - rootNode.appendChild(style); - } -} - -var platform_dom$2 = { - /** - * When `true`, prevents the automatic injection of the stylesheet required to - * correctly detect when the chart is added to the DOM and then resized. This - * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) - * to be manually imported to make this library compatible with any CSP. - * See https://github.com/chartjs/Chart.js/issues/5208 - */ - disableCSSInjection: false, - - /** - * This property holds whether this platform is enabled for the current environment. - * Currently used by platform.js to select the proper implementation. - * @private - */ - _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', - - /** - * Initializes resources that depend on platform options. - * @param {HTMLCanvasElement} canvas - The Canvas element. - * @private - */ - _ensureLoaded: function(canvas) { - if (!this.disableCSSInjection) { - // If the canvas is in a shadow DOM, then the styles must also be inserted - // into the same shadow DOM. - // https://github.com/chartjs/Chart.js/issues/5763 - var root = canvas.getRootNode ? canvas.getRootNode() : document; - var targetNode = root.host ? root : document.head; - injectCSS(targetNode, stylesheet); - } - }, - - acquireContext: function(item, config) { - if (typeof item === 'string') { - item = document.getElementById(item); - } else if (item.length) { - // Support for array based queries (such as jQuery) - item = item[0]; - } - - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - // To prevent canvas fingerprinting, some add-ons undefine the getContext - // method, for example: https://github.com/kkapsner/CanvasBlocker - // https://github.com/chartjs/Chart.js/issues/2807 - var context = item && item.getContext && item.getContext('2d'); - - // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is - // inside an iframe or when running in a protected environment. We could guess the - // types from their toString() value but let's keep things flexible and assume it's - // a sufficient condition if the item has a context2D which has item as `canvas`. - // https://github.com/chartjs/Chart.js/issues/3887 - // https://github.com/chartjs/Chart.js/issues/4102 - // https://github.com/chartjs/Chart.js/issues/4152 - if (context && context.canvas === item) { - // Load platform resources on first chart creation, to make it possible to - // import the library before setting platform options. - this._ensureLoaded(item); - initCanvas(item, config); - return context; - } - - return null; - }, - - releaseContext: function(context) { - var canvas = context.canvas; - if (!canvas[EXPANDO_KEY]) { - return; - } - - var initial = canvas[EXPANDO_KEY].initial; - ['height', 'width'].forEach(function(prop) { - var value = initial[prop]; - if (helpers$1.isNullOrUndef(value)) { - canvas.removeAttribute(prop); - } else { - canvas.setAttribute(prop, value); - } - }); - - helpers$1.each(initial.style || {}, function(value, key) { - canvas.style[key] = value; - }); - - // The canvas render size might have been changed (and thus the state stack discarded), - // we can't use save() and restore() to restore the initial state. So make sure that at - // least the canvas context is reset to the default state by setting the canvas width. - // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html - // eslint-disable-next-line no-self-assign - canvas.width = canvas.width; - - delete canvas[EXPANDO_KEY]; - }, - - addEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - addResizeListener(canvas, listener, chart); - return; - } - - var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); - var proxies = expando.proxies || (expando.proxies = {}); - var proxy = proxies[chart.id + '_' + type] = function(event) { - listener(fromNativeEvent(event, chart)); - }; - - addListener(canvas, type, proxy); - }, - - removeEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - removeResizeListener(canvas); - return; - } - - var expando = listener[EXPANDO_KEY] || {}; - var proxies = expando.proxies || {}; - var proxy = proxies[chart.id + '_' + type]; - if (!proxy) { - return; - } - - removeListener(canvas, type, proxy); - } -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use EventTarget.addEventListener instead. - * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener - * @function Chart.helpers.addEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.addEvent = addListener; - -/** - * Provided for backward compatibility, use EventTarget.removeEventListener instead. - * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener - * @function Chart.helpers.removeEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.removeEvent = removeListener; - -// @TODO Make possible to select another platform at build time. -var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; - -/** - * @namespace Chart.platform - * @see https://chartjs.gitbooks.io/proposals/content/Platform.html - * @since 2.4.0 - */ -var platform = helpers$1.extend({ - /** - * @since 2.7.0 - */ - initialize: function() {}, - - /** - * Called at chart construction time, returns a context2d instance implementing - * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. - * @param {*} item - The native item from which to acquire context (platform specific) - * @param {object} options - The chart options - * @returns {CanvasRenderingContext2D} context2d instance - */ - acquireContext: function() {}, - - /** - * Called at chart destruction time, releases any resources associated to the context - * previously returned by the acquireContext() method. - * @param {CanvasRenderingContext2D} context - The context2d instance - * @returns {boolean} true if the method succeeded, else false - */ - releaseContext: function() {}, - - /** - * Registers the specified listener on the given chart. - * @param {Chart} chart - Chart from which to listen for event - * @param {string} type - The ({@link IEvent}) type to listen for - * @param {function} listener - Receives a notification (an object that implements - * the {@link IEvent} interface) when an event of the specified type occurs. - */ - addEventListener: function() {}, - - /** - * Removes the specified listener previously registered with addEventListener. - * @param {Chart} chart - Chart from which to remove the listener - * @param {string} type - The ({@link IEvent}) type to remove - * @param {function} listener - The listener function to remove from the event target. - */ - removeEventListener: function() {} - -}, implementation); - -core_defaults._set('global', { - plugins: {} -}); - -/** - * The plugin service singleton - * @namespace Chart.plugins - * @since 2.1.0 - */ -var core_plugins = { - /** - * Globally registered plugins. - * @private - */ - _plugins: [], - - /** - * This identifier is used to invalidate the descriptors cache attached to each chart - * when a global plugin is registered or unregistered. In this case, the cache ID is - * incremented and descriptors are regenerated during following API calls. - * @private - */ - _cacheId: 0, - - /** - * Registers the given plugin(s) if not already registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - register: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - if (p.indexOf(plugin) === -1) { - p.push(plugin); - } - }); - - this._cacheId++; - }, - - /** - * Unregisters the given plugin(s) only if registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - unregister: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - var idx = p.indexOf(plugin); - if (idx !== -1) { - p.splice(idx, 1); - } - }); - - this._cacheId++; - }, - - /** - * Remove all registered plugins. - * @since 2.1.5 - */ - clear: function() { - this._plugins = []; - this._cacheId++; - }, - - /** - * Returns the number of registered plugins? - * @returns {number} - * @since 2.1.5 - */ - count: function() { - return this._plugins.length; - }, - - /** - * Returns all registered plugin instances. - * @returns {IPlugin[]} array of plugin objects. - * @since 2.1.5 - */ - getAll: function() { - return this._plugins; - }, - - /** - * Calls enabled plugins for `chart` on the specified hook and with the given args. - * This method immediately returns as soon as a plugin explicitly returns false. The - * returned value can be used, for instance, to interrupt the current action. - * @param {Chart} chart - The chart instance for which plugins should be called. - * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). - * @param {Array} [args] - Extra arguments to apply to the hook call. - * @returns {boolean} false if any of the plugins return false, else returns true. - */ - notify: function(chart, hook, args) { - var descriptors = this.descriptors(chart); - var ilen = descriptors.length; - var i, descriptor, plugin, params, method; - - for (i = 0; i < ilen; ++i) { - descriptor = descriptors[i]; - plugin = descriptor.plugin; - method = plugin[hook]; - if (typeof method === 'function') { - params = [chart].concat(args || []); - params.push(descriptor.options); - if (method.apply(plugin, params) === false) { - return false; - } - } - } - - return true; - }, - - /** - * Returns descriptors of enabled plugins for the given chart. - * @returns {object[]} [{ plugin, options }] - * @private - */ - descriptors: function(chart) { - var cache = chart.$plugins || (chart.$plugins = {}); - if (cache.id === this._cacheId) { - return cache.descriptors; - } - - var plugins = []; - var descriptors = []; - var config = (chart && chart.config) || {}; - var options = (config.options && config.options.plugins) || {}; - - this._plugins.concat(config.plugins || []).forEach(function(plugin) { - var idx = plugins.indexOf(plugin); - if (idx !== -1) { - return; - } - - var id = plugin.id; - var opts = options[id]; - if (opts === false) { - return; - } - - if (opts === true) { - opts = helpers$1.clone(core_defaults.global.plugins[id]); - } - - plugins.push(plugin); - descriptors.push({ - plugin: plugin, - options: opts || {} - }); - }); - - cache.descriptors = descriptors; - cache.id = this._cacheId; - return descriptors; - }, - - /** - * Invalidates cache for the given chart: descriptors hold a reference on plugin option, - * but in some cases, this reference can be changed by the user when updating options. - * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - * @private - */ - _invalidate: function(chart) { - delete chart.$plugins; - } -}; - -var core_scaleService = { - // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then - // use the new chart options to grab the correct scale - constructors: {}, - // Use a registration function so that we can move to an ES6 map when we no longer need to support - // old browsers - - // Scale config defaults - defaults: {}, - registerScaleType: function(type, scaleConstructor, scaleDefaults) { - this.constructors[type] = scaleConstructor; - this.defaults[type] = helpers$1.clone(scaleDefaults); - }, - getScaleConstructor: function(type) { - return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; - }, - getScaleDefaults: function(type) { - // Return the scale defaults merged with the global settings so that we always use the latest ones - return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; - }, - updateScaleDefaults: function(type, additions) { - var me = this; - if (me.defaults.hasOwnProperty(type)) { - me.defaults[type] = helpers$1.extend(me.defaults[type], additions); - } - }, - addScalesToLayout: function(chart) { - // Adds each scale to the chart.boxes array to be sized accordingly - helpers$1.each(chart.scales, function(scale) { - // Set ILayoutItem parameters for backwards compatibility - scale.fullWidth = scale.options.fullWidth; - scale.position = scale.options.position; - scale.weight = scale.options.weight; - core_layouts.addBox(chart, scale); - }); - } -}; - -var valueOrDefault$8 = helpers$1.valueOrDefault; -var getRtlHelper = helpers$1.rtl.getRtlAdapter; - -core_defaults._set('global', { - tooltips: { - enabled: true, - custom: null, - mode: 'nearest', - position: 'average', - intersect: true, - backgroundColor: 'rgba(0,0,0,0.8)', - titleFontStyle: 'bold', - titleSpacing: 2, - titleMarginBottom: 6, - titleFontColor: '#fff', - titleAlign: 'left', - bodySpacing: 2, - bodyFontColor: '#fff', - bodyAlign: 'left', - footerFontStyle: 'bold', - footerSpacing: 2, - footerMarginTop: 6, - footerFontColor: '#fff', - footerAlign: 'left', - yPadding: 6, - xPadding: 6, - caretPadding: 2, - caretSize: 5, - cornerRadius: 6, - multiKeyBackground: '#fff', - displayColors: true, - borderColor: 'rgba(0,0,0,0)', - borderWidth: 0, - callbacks: { - // Args are: (tooltipItems, data) - beforeTitle: helpers$1.noop, - title: function(tooltipItems, data) { - var title = ''; - var labels = data.labels; - var labelCount = labels ? labels.length : 0; - - if (tooltipItems.length > 0) { - var item = tooltipItems[0]; - if (item.label) { - title = item.label; - } else if (item.xLabel) { - title = item.xLabel; - } else if (labelCount > 0 && item.index < labelCount) { - title = labels[item.index]; - } - } - - return title; - }, - afterTitle: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeBody: helpers$1.noop, - - // Args are: (tooltipItem, data) - beforeLabel: helpers$1.noop, - label: function(tooltipItem, data) { - var label = data.datasets[tooltipItem.datasetIndex].label || ''; - - if (label) { - label += ': '; - } - if (!helpers$1.isNullOrUndef(tooltipItem.value)) { - label += tooltipItem.value; - } else { - label += tooltipItem.yLabel; - } - return label; - }, - labelColor: function(tooltipItem, chart) { - var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); - var activeElement = meta.data[tooltipItem.index]; - var view = activeElement._view; - return { - borderColor: view.borderColor, - backgroundColor: view.backgroundColor - }; - }, - labelTextColor: function() { - return this._options.bodyFontColor; - }, - afterLabel: helpers$1.noop, - - // Args are: (tooltipItems, data) - afterBody: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeFooter: helpers$1.noop, - footer: helpers$1.noop, - afterFooter: helpers$1.noop - } - } -}); - -var positioners = { - /** - * Average mode places the tooltip at the average position of the elements shown - * @function Chart.Tooltip.positioners.average - * @param elements {ChartElement[]} the elements being displayed in the tooltip - * @returns {object} tooltip position - */ - average: function(elements) { - if (!elements.length) { - return false; - } - - var i, len; - var x = 0; - var y = 0; - var count = 0; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var pos = el.tooltipPosition(); - x += pos.x; - y += pos.y; - ++count; - } - } - - return { - x: x / count, - y: y / count - }; - }, - - /** - * Gets the tooltip position nearest of the item nearest to the event position - * @function Chart.Tooltip.positioners.nearest - * @param elements {Chart.Element[]} the tooltip elements - * @param eventPosition {object} the position of the event in canvas coordinates - * @returns {object} the tooltip position - */ - nearest: function(elements, eventPosition) { - var x = eventPosition.x; - var y = eventPosition.y; - var minDistance = Number.POSITIVE_INFINITY; - var i, len, nearestElement; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var center = el.getCenterPoint(); - var d = helpers$1.distanceBetweenPoints(eventPosition, center); - - if (d < minDistance) { - minDistance = d; - nearestElement = el; - } - } - } - - if (nearestElement) { - var tp = nearestElement.tooltipPosition(); - x = tp.x; - y = tp.y; - } - - return { - x: x, - y: y - }; - } -}; - -// Helper to push or concat based on if the 2nd parameter is an array or not -function pushOrConcat(base, toPush) { - if (toPush) { - if (helpers$1.isArray(toPush)) { - // base = base.concat(toPush); - Array.prototype.push.apply(base, toPush); - } else { - base.push(toPush); - } - } - - return base; -} - -/** - * Returns array of strings split by newline - * @param {string} value - The value to split by newline. - * @returns {string[]} value if newline present - Returned from String split() method - * @function - */ -function splitNewlines(str) { - if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { - return str.split('\n'); - } - return str; -} - - -/** - * Private helper to create a tooltip item model - * @param element - the chart element (point, arc, bar) to create the tooltip item for - * @return new tooltip item - */ -function createTooltipItem(element) { - var xScale = element._xScale; - var yScale = element._yScale || element._scale; // handle radar || polarArea charts - var index = element._index; - var datasetIndex = element._datasetIndex; - var controller = element._chart.getDatasetMeta(datasetIndex).controller; - var indexScale = controller._getIndexScale(); - var valueScale = controller._getValueScale(); - - return { - xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', - yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', - label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', - value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', - index: index, - datasetIndex: datasetIndex, - x: element._model.x, - y: element._model.y - }; -} - -/** - * Helper to get the reset model for the tooltip - * @param tooltipOpts {object} the tooltip options - */ -function getBaseModel(tooltipOpts) { - var globalDefaults = core_defaults.global; - - return { - // Positioning - xPadding: tooltipOpts.xPadding, - yPadding: tooltipOpts.yPadding, - xAlign: tooltipOpts.xAlign, - yAlign: tooltipOpts.yAlign, - - // Drawing direction and text direction - rtl: tooltipOpts.rtl, - textDirection: tooltipOpts.textDirection, - - // Body - bodyFontColor: tooltipOpts.bodyFontColor, - _bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), - _bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), - _bodyAlign: tooltipOpts.bodyAlign, - bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), - bodySpacing: tooltipOpts.bodySpacing, - - // Title - titleFontColor: tooltipOpts.titleFontColor, - _titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), - _titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), - titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), - _titleAlign: tooltipOpts.titleAlign, - titleSpacing: tooltipOpts.titleSpacing, - titleMarginBottom: tooltipOpts.titleMarginBottom, - - // Footer - footerFontColor: tooltipOpts.footerFontColor, - _footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), - _footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), - footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), - _footerAlign: tooltipOpts.footerAlign, - footerSpacing: tooltipOpts.footerSpacing, - footerMarginTop: tooltipOpts.footerMarginTop, - - // Appearance - caretSize: tooltipOpts.caretSize, - cornerRadius: tooltipOpts.cornerRadius, - backgroundColor: tooltipOpts.backgroundColor, - opacity: 0, - legendColorBackground: tooltipOpts.multiKeyBackground, - displayColors: tooltipOpts.displayColors, - borderColor: tooltipOpts.borderColor, - borderWidth: tooltipOpts.borderWidth - }; -} - -/** - * Get the size of the tooltip - */ -function getTooltipSize(tooltip, model) { - var ctx = tooltip._chart.ctx; - - var height = model.yPadding * 2; // Tooltip Padding - var width = 0; - - // Count of all lines in the body - var body = model.body; - var combinedBodyLength = body.reduce(function(count, bodyItem) { - return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; - }, 0); - combinedBodyLength += model.beforeBody.length + model.afterBody.length; - - var titleLineCount = model.title.length; - var footerLineCount = model.footer.length; - var titleFontSize = model.titleFontSize; - var bodyFontSize = model.bodyFontSize; - var footerFontSize = model.footerFontSize; - - height += titleLineCount * titleFontSize; // Title Lines - height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing - height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin - height += combinedBodyLength * bodyFontSize; // Body Lines - height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing - height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin - height += footerLineCount * (footerFontSize); // Footer Lines - height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing - - // Title width - var widthPadding = 0; - var maxLineWidth = function(line) { - width = Math.max(width, ctx.measureText(line).width + widthPadding); - }; - - ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); - helpers$1.each(model.title, maxLineWidth); - - // Body width - ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); - helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); - - // Body lines may include some extra width due to the color box - widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; - helpers$1.each(body, function(bodyItem) { - helpers$1.each(bodyItem.before, maxLineWidth); - helpers$1.each(bodyItem.lines, maxLineWidth); - helpers$1.each(bodyItem.after, maxLineWidth); - }); - - // Reset back to 0 - widthPadding = 0; - - // Footer width - ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); - helpers$1.each(model.footer, maxLineWidth); - - // Add padding - width += 2 * model.xPadding; - - return { - width: width, - height: height - }; -} - -/** - * Helper to get the alignment of a tooltip given the size - */ -function determineAlignment(tooltip, size) { - var model = tooltip._model; - var chart = tooltip._chart; - var chartArea = tooltip._chart.chartArea; - var xAlign = 'center'; - var yAlign = 'center'; - - if (model.y < size.height) { - yAlign = 'top'; - } else if (model.y > (chart.height - size.height)) { - yAlign = 'bottom'; - } - - var lf, rf; // functions to determine left, right alignment - var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart - var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges - var midX = (chartArea.left + chartArea.right) / 2; - var midY = (chartArea.top + chartArea.bottom) / 2; - - if (yAlign === 'center') { - lf = function(x) { - return x <= midX; - }; - rf = function(x) { - return x > midX; - }; - } else { - lf = function(x) { - return x <= (size.width / 2); - }; - rf = function(x) { - return x >= (chart.width - (size.width / 2)); - }; - } - - olf = function(x) { - return x + size.width + model.caretSize + model.caretPadding > chart.width; - }; - orf = function(x) { - return x - size.width - model.caretSize - model.caretPadding < 0; - }; - yf = function(y) { - return y <= midY ? 'top' : 'bottom'; - }; - - if (lf(model.x)) { - xAlign = 'left'; - - // Is tooltip too wide and goes over the right side of the chart.? - if (olf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } else if (rf(model.x)) { - xAlign = 'right'; - - // Is tooltip too wide and goes outside left edge of canvas? - if (orf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } - - var opts = tooltip._options; - return { - xAlign: opts.xAlign ? opts.xAlign : xAlign, - yAlign: opts.yAlign ? opts.yAlign : yAlign - }; -} - -/** - * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment - */ -function getBackgroundPoint(vm, size, alignment, chart) { - // Background Position - var x = vm.x; - var y = vm.y; - - var caretSize = vm.caretSize; - var caretPadding = vm.caretPadding; - var cornerRadius = vm.cornerRadius; - var xAlign = alignment.xAlign; - var yAlign = alignment.yAlign; - var paddingAndSize = caretSize + caretPadding; - var radiusAndPadding = cornerRadius + caretPadding; - - if (xAlign === 'right') { - x -= size.width; - } else if (xAlign === 'center') { - x -= (size.width / 2); - if (x + size.width > chart.width) { - x = chart.width - size.width; - } - if (x < 0) { - x = 0; - } - } - - if (yAlign === 'top') { - y += paddingAndSize; - } else if (yAlign === 'bottom') { - y -= size.height + paddingAndSize; - } else { - y -= (size.height / 2); - } - - if (yAlign === 'center') { - if (xAlign === 'left') { - x += paddingAndSize; - } else if (xAlign === 'right') { - x -= paddingAndSize; - } - } else if (xAlign === 'left') { - x -= radiusAndPadding; - } else if (xAlign === 'right') { - x += radiusAndPadding; - } - - return { - x: x, - y: y - }; -} - -function getAlignedX(vm, align) { - return align === 'center' - ? vm.x + vm.width / 2 - : align === 'right' - ? vm.x + vm.width - vm.xPadding - : vm.x + vm.xPadding; -} - -/** - * Helper to build before and after body lines - */ -function getBeforeAfterBodyLines(callback) { - return pushOrConcat([], splitNewlines(callback)); -} - -var exports$4 = core_element.extend({ - initialize: function() { - this._model = getBaseModel(this._options); - this._lastActive = []; - }, - - // Get the title - // Args are: (tooltipItem, data) - getTitle: function() { - var me = this; - var opts = me._options; - var callbacks = opts.callbacks; - - var beforeTitle = callbacks.beforeTitle.apply(me, arguments); - var title = callbacks.title.apply(me, arguments); - var afterTitle = callbacks.afterTitle.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeTitle)); - lines = pushOrConcat(lines, splitNewlines(title)); - lines = pushOrConcat(lines, splitNewlines(afterTitle)); - - return lines; - }, - - // Args are: (tooltipItem, data) - getBeforeBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); - }, - - // Args are: (tooltipItem, data) - getBody: function(tooltipItems, data) { - var me = this; - var callbacks = me._options.callbacks; - var bodyItems = []; - - helpers$1.each(tooltipItems, function(tooltipItem) { - var bodyItem = { - before: [], - lines: [], - after: [] - }; - pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); - pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); - pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); - - bodyItems.push(bodyItem); - }); - - return bodyItems; - }, - - // Args are: (tooltipItem, data) - getAfterBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); - }, - - // Get the footer and beforeFooter and afterFooter lines - // Args are: (tooltipItem, data) - getFooter: function() { - var me = this; - var callbacks = me._options.callbacks; - - var beforeFooter = callbacks.beforeFooter.apply(me, arguments); - var footer = callbacks.footer.apply(me, arguments); - var afterFooter = callbacks.afterFooter.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeFooter)); - lines = pushOrConcat(lines, splitNewlines(footer)); - lines = pushOrConcat(lines, splitNewlines(afterFooter)); - - return lines; - }, - - update: function(changed) { - var me = this; - var opts = me._options; - - // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition - // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time - // which breaks any animations. - var existingModel = me._model; - var model = me._model = getBaseModel(opts); - var active = me._active; - - var data = me._data; - - // In the case where active.length === 0 we need to keep these at existing values for good animations - var alignment = { - xAlign: existingModel.xAlign, - yAlign: existingModel.yAlign - }; - var backgroundPoint = { - x: existingModel.x, - y: existingModel.y - }; - var tooltipSize = { - width: existingModel.width, - height: existingModel.height - }; - var tooltipPosition = { - x: existingModel.caretX, - y: existingModel.caretY - }; - - var i, len; - - if (active.length) { - model.opacity = 1; - - var labelColors = []; - var labelTextColors = []; - tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); - - var tooltipItems = []; - for (i = 0, len = active.length; i < len; ++i) { - tooltipItems.push(createTooltipItem(active[i])); - } - - // If the user provided a filter function, use it to modify the tooltip items - if (opts.filter) { - tooltipItems = tooltipItems.filter(function(a) { - return opts.filter(a, data); - }); - } - - // If the user provided a sorting function, use it to modify the tooltip items - if (opts.itemSort) { - tooltipItems = tooltipItems.sort(function(a, b) { - return opts.itemSort(a, b, data); - }); - } - - // Determine colors for boxes - helpers$1.each(tooltipItems, function(tooltipItem) { - labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); - labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); - }); - - - // Build the Text Lines - model.title = me.getTitle(tooltipItems, data); - model.beforeBody = me.getBeforeBody(tooltipItems, data); - model.body = me.getBody(tooltipItems, data); - model.afterBody = me.getAfterBody(tooltipItems, data); - model.footer = me.getFooter(tooltipItems, data); - - // Initial positioning and colors - model.x = tooltipPosition.x; - model.y = tooltipPosition.y; - model.caretPadding = opts.caretPadding; - model.labelColors = labelColors; - model.labelTextColors = labelTextColors; - - // data points - model.dataPoints = tooltipItems; - - // We need to determine alignment of the tooltip - tooltipSize = getTooltipSize(this, model); - alignment = determineAlignment(this, tooltipSize); - // Final Size and Position - backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); - } else { - model.opacity = 0; - } - - model.xAlign = alignment.xAlign; - model.yAlign = alignment.yAlign; - model.x = backgroundPoint.x; - model.y = backgroundPoint.y; - model.width = tooltipSize.width; - model.height = tooltipSize.height; - - // Point where the caret on the tooltip points to - model.caretX = tooltipPosition.x; - model.caretY = tooltipPosition.y; - - me._model = model; - - if (changed && opts.custom) { - opts.custom.call(me, model); - } - - return me; - }, - - drawCaret: function(tooltipPoint, size) { - var ctx = this._chart.ctx; - var vm = this._view; - var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); - - ctx.lineTo(caretPosition.x1, caretPosition.y1); - ctx.lineTo(caretPosition.x2, caretPosition.y2); - ctx.lineTo(caretPosition.x3, caretPosition.y3); - }, - getCaretPosition: function(tooltipPoint, size, vm) { - var x1, x2, x3, y1, y2, y3; - var caretSize = vm.caretSize; - var cornerRadius = vm.cornerRadius; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var ptX = tooltipPoint.x; - var ptY = tooltipPoint.y; - var width = size.width; - var height = size.height; - - if (yAlign === 'center') { - y2 = ptY + (height / 2); - - if (xAlign === 'left') { - x1 = ptX; - x2 = x1 - caretSize; - x3 = x1; - - y1 = y2 + caretSize; - y3 = y2 - caretSize; - } else { - x1 = ptX + width; - x2 = x1 + caretSize; - x3 = x1; - - y1 = y2 - caretSize; - y3 = y2 + caretSize; - } - } else { - if (xAlign === 'left') { - x2 = ptX + cornerRadius + (caretSize); - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else if (xAlign === 'right') { - x2 = ptX + width - cornerRadius - caretSize; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else { - x2 = vm.caretX; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } - if (yAlign === 'top') { - y1 = ptY; - y2 = y1 - caretSize; - y3 = y1; - } else { - y1 = ptY + height; - y2 = y1 + caretSize; - y3 = y1; - // invert drawing order - var tmp = x3; - x3 = x1; - x1 = tmp; - } - } - return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; - }, - - drawTitle: function(pt, vm, ctx) { - var title = vm.title; - var length = title.length; - var titleFontSize, titleSpacing, i; - - if (length) { - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - pt.x = getAlignedX(vm, vm._titleAlign); - - ctx.textAlign = rtlHelper.textAlign(vm._titleAlign); - ctx.textBaseline = 'middle'; - - titleFontSize = vm.titleFontSize; - titleSpacing = vm.titleSpacing; - - ctx.fillStyle = vm.titleFontColor; - ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); - - for (i = 0; i < length; ++i) { - ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2); - pt.y += titleFontSize + titleSpacing; // Line Height and spacing - - if (i + 1 === length) { - pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing - } - } - } - }, - - drawBody: function(pt, vm, ctx) { - var bodyFontSize = vm.bodyFontSize; - var bodySpacing = vm.bodySpacing; - var bodyAlign = vm._bodyAlign; - var body = vm.body; - var drawColorBoxes = vm.displayColors; - var xLinePadding = 0; - var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; - - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - var fillLineOfText = function(line) { - ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2); - pt.y += bodyFontSize + bodySpacing; - }; - - var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen; - var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); - - ctx.textAlign = bodyAlign; - ctx.textBaseline = 'middle'; - ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); - - pt.x = getAlignedX(vm, bodyAlignForCalculation); - - // Before body lines - ctx.fillStyle = vm.bodyFontColor; - helpers$1.each(vm.beforeBody, fillLineOfText); - - xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right' - ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) - : 0; - - // Draw body lines now - for (i = 0, ilen = body.length; i < ilen; ++i) { - bodyItem = body[i]; - textColor = vm.labelTextColors[i]; - labelColors = vm.labelColors[i]; - - ctx.fillStyle = textColor; - helpers$1.each(bodyItem.before, fillLineOfText); - - lines = bodyItem.lines; - for (j = 0, jlen = lines.length; j < jlen; ++j) { - // Draw Legend-like boxes if needed - if (drawColorBoxes) { - var rtlColorX = rtlHelper.x(colorX); - - // Fill a white rect so that colours merge nicely if the opacity is < 1 - ctx.fillStyle = vm.legendColorBackground; - ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); - - // Border - ctx.lineWidth = 1; - ctx.strokeStyle = labelColors.borderColor; - ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); - - // Inner square - ctx.fillStyle = labelColors.backgroundColor; - ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); - ctx.fillStyle = textColor; - } - - fillLineOfText(lines[j]); - } - - helpers$1.each(bodyItem.after, fillLineOfText); - } - - // Reset back to 0 for after body - xLinePadding = 0; - - // After body lines - helpers$1.each(vm.afterBody, fillLineOfText); - pt.y -= bodySpacing; // Remove last body spacing - }, - - drawFooter: function(pt, vm, ctx) { - var footer = vm.footer; - var length = footer.length; - var footerFontSize, i; - - if (length) { - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - pt.x = getAlignedX(vm, vm._footerAlign); - pt.y += vm.footerMarginTop; - - ctx.textAlign = rtlHelper.textAlign(vm._footerAlign); - ctx.textBaseline = 'middle'; - - footerFontSize = vm.footerFontSize; - - ctx.fillStyle = vm.footerFontColor; - ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); - - for (i = 0; i < length; ++i) { - ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2); - pt.y += footerFontSize + vm.footerSpacing; - } - } - }, - - drawBackground: function(pt, vm, ctx, tooltipSize) { - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var x = pt.x; - var y = pt.y; - var width = tooltipSize.width; - var height = tooltipSize.height; - var radius = vm.cornerRadius; - - ctx.beginPath(); - ctx.moveTo(x + radius, y); - if (yAlign === 'top') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - if (yAlign === 'center' && xAlign === 'right') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - if (yAlign === 'bottom') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - if (yAlign === 'center' && xAlign === 'left') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - - ctx.fill(); - - if (vm.borderWidth > 0) { - ctx.stroke(); - } - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - - if (vm.opacity === 0) { - return; - } - - var tooltipSize = { - width: vm.width, - height: vm.height - }; - var pt = { - x: vm.x, - y: vm.y - }; - - // IE11/Edge does not like very small opacities, so snap to 0 - var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; - - // Truthy/falsey value for empty tooltip - var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; - - if (this._options.enabled && hasTooltipContent) { - ctx.save(); - ctx.globalAlpha = opacity; - - // Draw Background - this.drawBackground(pt, vm, ctx, tooltipSize); - - // Draw Title, Body, and Footer - pt.y += vm.yPadding; - - helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection); - - // Titles - this.drawTitle(pt, vm, ctx); - - // Body - this.drawBody(pt, vm, ctx); - - // Footer - this.drawFooter(pt, vm, ctx); - - helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection); - - ctx.restore(); - } - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - * @returns {boolean} true if the tooltip changed - */ - handleEvent: function(e) { - var me = this; - var options = me._options; - var changed = false; - - me._lastActive = me._lastActive || []; - - // Find Active Elements for tooltips - if (e.type === 'mouseout') { - me._active = []; - } else { - me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); - if (options.reverse) { - me._active.reverse(); - } - } - - // Remember Last Actives - changed = !helpers$1.arrayEquals(me._active, me._lastActive); - - // Only handle target event on tooltip change - if (changed) { - me._lastActive = me._active; - - if (options.enabled || options.custom) { - me._eventPosition = { - x: e.x, - y: e.y - }; - - me.update(true); - me.pivot(); - } - } - - return changed; - } -}); - -/** - * @namespace Chart.Tooltip.positioners - */ -var positioners_1 = positioners; - -var core_tooltip = exports$4; -core_tooltip.positioners = positioners_1; - -var valueOrDefault$9 = helpers$1.valueOrDefault; - -core_defaults._set('global', { - elements: {}, - events: [ - 'mousemove', - 'mouseout', - 'click', - 'touchstart', - 'touchmove' - ], - hover: { - onHover: null, - mode: 'nearest', - intersect: true, - animationDuration: 400 - }, - onClick: null, - maintainAspectRatio: true, - responsive: true, - responsiveAnimationDuration: 0 -}); - -/** - * Recursively merge the given config objects representing the `scales` option - * by incorporating scale defaults in `xAxes` and `yAxes` array items, then - * returns a deep copy of the result, thus doesn't alter inputs. - */ -function mergeScaleConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - if (key === 'xAxes' || key === 'yAxes') { - var slen = source[key].length; - var i, type, scale; - - if (!target[key]) { - target[key] = []; - } - - for (i = 0; i < slen; ++i) { - scale = source[key][i]; - type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear'); - - if (i >= target[key].length) { - target[key].push({}); - } - - if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { - // new/untyped scale or type changed: let's apply the new defaults - // then merge source scale to correctly overwrite the defaults. - helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); - } else { - // scales type are the same - helpers$1.merge(target[key][i], scale); - } - } - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -/** - * Recursively merge the given config objects as the root options by handling - * default scale options for the `scales` and `scale` properties, then returns - * a deep copy of the result, thus doesn't alter inputs. - */ -function mergeConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - var tval = target[key] || {}; - var sval = source[key]; - - if (key === 'scales') { - // scale config merging is complex. Add our own function here for that - target[key] = mergeScaleConfig(tval, sval); - } else if (key === 'scale') { - // used in polar area & radar charts since there is only one scale - target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -function initConfig(config) { - config = config || {}; - - // Do NOT use mergeConfig for the data object because this method merges arrays - // and so would change references to labels and datasets, preventing data updates. - var data = config.data = config.data || {}; - data.datasets = data.datasets || []; - data.labels = data.labels || []; - - config.options = mergeConfig( - core_defaults.global, - core_defaults[config.type], - config.options || {}); - - return config; -} - -function updateConfig(chart) { - var newOptions = chart.options; - - helpers$1.each(chart.scales, function(scale) { - core_layouts.removeBox(chart, scale); - }); - - newOptions = mergeConfig( - core_defaults.global, - core_defaults[chart.config.type], - newOptions); - - chart.options = chart.config.options = newOptions; - chart.ensureScalesHaveIDs(); - chart.buildOrUpdateScales(); - - // Tooltip - chart.tooltip._options = newOptions.tooltips; - chart.tooltip.initialize(); -} - -function nextAvailableScaleId(axesOpts, prefix, index) { - var id; - var hasId = function(obj) { - return obj.id === id; - }; - - do { - id = prefix + index++; - } while (helpers$1.findIndex(axesOpts, hasId) >= 0); - - return id; -} - -function positionIsHorizontal(position) { - return position === 'top' || position === 'bottom'; -} - -function compare2Level(l1, l2) { - return function(a, b) { - return a[l1] === b[l1] - ? a[l2] - b[l2] - : a[l1] - b[l1]; - }; -} - -var Chart = function(item, config) { - this.construct(item, config); - return this; -}; - -helpers$1.extend(Chart.prototype, /** @lends Chart */ { - /** - * @private - */ - construct: function(item, config) { - var me = this; - - config = initConfig(config); - - var context = platform.acquireContext(item, config); - var canvas = context && context.canvas; - var height = canvas && canvas.height; - var width = canvas && canvas.width; - - me.id = helpers$1.uid(); - me.ctx = context; - me.canvas = canvas; - me.config = config; - me.width = width; - me.height = height; - me.aspectRatio = height ? width / height : null; - me.options = config.options; - me._bufferedRender = false; - me._layers = []; - - /** - * Provided for backward compatibility, Chart and Chart.Controller have been merged, - * the "instance" still need to be defined since it might be called from plugins. - * @prop Chart#chart - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ - me.chart = me; - me.controller = me; // chart.chart.controller #inception - - // Add the chart instance to the global namespace - Chart.instances[me.id] = me; - - // Define alias to the config data: `chart.data === chart.config.data` - Object.defineProperty(me, 'data', { - get: function() { - return me.config.data; - }, - set: function(value) { - me.config.data = value; - } - }); - - if (!context || !canvas) { - // The given item is not a compatible context2d element, let's return before finalizing - // the chart initialization but after setting basic chart / controller properties that - // can help to figure out that the chart is not valid (e.g chart.canvas !== null); - // https://github.com/chartjs/Chart.js/issues/2807 - console.error("Failed to create chart: can't acquire context from the given item"); - return; - } - - me.initialize(); - me.update(); - }, - - /** - * @private - */ - initialize: function() { - var me = this; - - // Before init plugin notification - core_plugins.notify(me, 'beforeInit'); - - helpers$1.retinaScale(me, me.options.devicePixelRatio); - - me.bindEvents(); - - if (me.options.responsive) { - // Initial resize before chart draws (must be silent to preserve initial animations). - me.resize(true); - } - - me.initToolTip(); - - // After init plugin notification - core_plugins.notify(me, 'afterInit'); - - return me; - }, - - clear: function() { - helpers$1.canvas.clear(this); - return this; - }, - - stop: function() { - // Stops any current animation loop occurring - core_animations.cancelAnimation(this); - return this; - }, - - resize: function(silent) { - var me = this; - var options = me.options; - var canvas = me.canvas; - var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; - - // the canvas render width and height will be casted to integers so make sure that - // the canvas display style uses the same integer values to avoid blurring effect. - - // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed - var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); - var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); - - if (me.width === newWidth && me.height === newHeight) { - return; - } - - canvas.width = me.width = newWidth; - canvas.height = me.height = newHeight; - canvas.style.width = newWidth + 'px'; - canvas.style.height = newHeight + 'px'; - - helpers$1.retinaScale(me, options.devicePixelRatio); - - if (!silent) { - // Notify any plugins about the resize - var newSize = {width: newWidth, height: newHeight}; - core_plugins.notify(me, 'resize', [newSize]); - - // Notify of resize - if (options.onResize) { - options.onResize(me, newSize); - } - - me.stop(); - me.update({ - duration: options.responsiveAnimationDuration - }); - } - }, - - ensureScalesHaveIDs: function() { - var options = this.options; - var scalesOptions = options.scales || {}; - var scaleOptions = options.scale; - - helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { - if (!xAxisOptions.id) { - xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index); - } - }); - - helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { - if (!yAxisOptions.id) { - yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index); - } - }); - - if (scaleOptions) { - scaleOptions.id = scaleOptions.id || 'scale'; - } - }, - - /** - * Builds a map of scale ID to scale object for future lookup. - */ - buildOrUpdateScales: function() { - var me = this; - var options = me.options; - var scales = me.scales || {}; - var items = []; - var updated = Object.keys(scales).reduce(function(obj, id) { - obj[id] = false; - return obj; - }, {}); - - if (options.scales) { - items = items.concat( - (options.scales.xAxes || []).map(function(xAxisOptions) { - return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; - }), - (options.scales.yAxes || []).map(function(yAxisOptions) { - return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; - }) - ); - } - - if (options.scale) { - items.push({ - options: options.scale, - dtype: 'radialLinear', - isDefault: true, - dposition: 'chartArea' - }); - } - - helpers$1.each(items, function(item) { - var scaleOptions = item.options; - var id = scaleOptions.id; - var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype); - - if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { - scaleOptions.position = item.dposition; - } - - updated[id] = true; - var scale = null; - if (id in scales && scales[id].type === scaleType) { - scale = scales[id]; - scale.options = scaleOptions; - scale.ctx = me.ctx; - scale.chart = me; - } else { - var scaleClass = core_scaleService.getScaleConstructor(scaleType); - if (!scaleClass) { - return; - } - scale = new scaleClass({ - id: id, - type: scaleType, - options: scaleOptions, - ctx: me.ctx, - chart: me - }); - scales[scale.id] = scale; - } - - scale.mergeTicksOptions(); - - // TODO(SB): I think we should be able to remove this custom case (options.scale) - // and consider it as a regular scale part of the "scales"" map only! This would - // make the logic easier and remove some useless? custom code. - if (item.isDefault) { - me.scale = scale; - } - }); - // clear up discarded scales - helpers$1.each(updated, function(hasUpdated, id) { - if (!hasUpdated) { - delete scales[id]; - } - }); - - me.scales = scales; - - core_scaleService.addScalesToLayout(this); - }, - - buildOrUpdateControllers: function() { - var me = this; - var newControllers = []; - var datasets = me.data.datasets; - var i, ilen; - - for (i = 0, ilen = datasets.length; i < ilen; i++) { - var dataset = datasets[i]; - var meta = me.getDatasetMeta(i); - var type = dataset.type || me.config.type; - - if (meta.type && meta.type !== type) { - me.destroyDatasetMeta(i); - meta = me.getDatasetMeta(i); - } - meta.type = type; - meta.order = dataset.order || 0; - meta.index = i; - - if (meta.controller) { - meta.controller.updateIndex(i); - meta.controller.linkScales(); - } else { - var ControllerClass = controllers[meta.type]; - if (ControllerClass === undefined) { - throw new Error('"' + meta.type + '" is not a chart type.'); - } - - meta.controller = new ControllerClass(me, i); - newControllers.push(meta.controller); - } - } - - return newControllers; - }, - - /** - * Reset the elements of all datasets - * @private - */ - resetElements: function() { - var me = this; - helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { - me.getDatasetMeta(datasetIndex).controller.reset(); - }, me); - }, - - /** - * Resets the chart back to it's state before the initial animation - */ - reset: function() { - this.resetElements(); - this.tooltip.initialize(); - }, - - update: function(config) { - var me = this; - var i, ilen; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - updateConfig(me); - - // plugins options references might have change, let's invalidate the cache - // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - core_plugins._invalidate(me); - - if (core_plugins.notify(me, 'beforeUpdate') === false) { - return; - } - - // In case the entire data object changed - me.tooltip._data = me.data; - - // Make sure dataset controllers are updated and new controllers are reset - var newControllers = me.buildOrUpdateControllers(); - - // Make sure all dataset controllers have correct meta data counts - for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) { - me.getDatasetMeta(i).controller.buildOrUpdateElements(); - } - - me.updateLayout(); - - // Can only reset the new controllers after the scales have been updated - if (me.options.animation && me.options.animation.duration) { - helpers$1.each(newControllers, function(controller) { - controller.reset(); - }); - } - - me.updateDatasets(); - - // Need to reset tooltip in case it is displayed with elements that are removed - // after update. - me.tooltip.initialize(); - - // Last active contains items that were previously in the tooltip. - // When we reset the tooltip, we need to clear it - me.lastActive = []; - - // Do this before render so that any plugins that need final scale updates can use it - core_plugins.notify(me, 'afterUpdate'); - - me._layers.sort(compare2Level('z', '_idx')); - - if (me._bufferedRender) { - me._bufferedRequest = { - duration: config.duration, - easing: config.easing, - lazy: config.lazy - }; - } else { - me.render(config); - } - }, - - /** - * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` - * hook, in which case, plugins will not be called on `afterLayout`. - * @private - */ - updateLayout: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeLayout') === false) { - return; - } - - core_layouts.update(this, this.width, this.height); - - me._layers = []; - helpers$1.each(me.boxes, function(box) { - // _configure is called twice, once in core.scale.update and once here. - // Here the boxes are fully updated and at their final positions. - if (box._configure) { - box._configure(); - } - me._layers.push.apply(me._layers, box._layers()); - }, me); - - me._layers.forEach(function(item, index) { - item._idx = index; - }); - - /** - * Provided for backward compatibility, use `afterLayout` instead. - * @method IPlugin#afterScaleUpdate - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ - core_plugins.notify(me, 'afterScaleUpdate'); - core_plugins.notify(me, 'afterLayout'); - }, - - /** - * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` - * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. - * @private - */ - updateDatasets: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { - return; - } - - for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.updateDataset(i); - } - - core_plugins.notify(me, 'afterDatasetsUpdate'); - }, - - /** - * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` - * hook, in which case, plugins will not be called on `afterDatasetUpdate`. - * @private - */ - updateDataset: function(index) { - var me = this; - var meta = me.getDatasetMeta(index); - var args = { - meta: meta, - index: index - }; - - if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { - return; - } - - meta.controller._update(); - - core_plugins.notify(me, 'afterDatasetUpdate', [args]); - }, - - render: function(config) { - var me = this; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - var animationOptions = me.options.animation; - var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration); - var lazy = config.lazy; - - if (core_plugins.notify(me, 'beforeRender') === false) { - return; - } - - var onComplete = function(animation) { - core_plugins.notify(me, 'afterRender'); - helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); - }; - - if (animationOptions && duration) { - var animation = new core_animation({ - numSteps: duration / 16.66, // 60 fps - easing: config.easing || animationOptions.easing, - - render: function(chart, animationObject) { - var easingFunction = helpers$1.easing.effects[animationObject.easing]; - var currentStep = animationObject.currentStep; - var stepDecimal = currentStep / animationObject.numSteps; - - chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); - }, - - onAnimationProgress: animationOptions.onProgress, - onAnimationComplete: onComplete - }); - - core_animations.addAnimation(me, animation, duration, lazy); - } else { - me.draw(); - - // See https://github.com/chartjs/Chart.js/issues/3781 - onComplete(new core_animation({numSteps: 0, chart: me})); - } - - return me; - }, - - draw: function(easingValue) { - var me = this; - var i, layers; - - me.clear(); - - if (helpers$1.isNullOrUndef(easingValue)) { - easingValue = 1; - } - - me.transition(easingValue); - - if (me.width <= 0 || me.height <= 0) { - return; - } - - if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { - return; - } - - // Because of plugin hooks (before/afterDatasetsDraw), datasets can't - // currently be part of layers. Instead, we draw - // layers <= 0 before(default, backward compat), and the rest after - layers = me._layers; - for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { - layers[i].draw(me.chartArea); - } - - me.drawDatasets(easingValue); - - // Rest of layers - for (; i < layers.length; ++i) { - layers[i].draw(me.chartArea); - } - - me._drawTooltip(easingValue); - - core_plugins.notify(me, 'afterDraw', [easingValue]); - }, - - /** - * @private - */ - transition: function(easingValue) { - var me = this; - - for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { - if (me.isDatasetVisible(i)) { - me.getDatasetMeta(i).controller.transition(easingValue); - } - } - - me.tooltip.transition(easingValue); - }, - - /** - * @private - */ - _getSortedDatasetMetas: function(filterVisible) { - var me = this; - var datasets = me.data.datasets || []; - var result = []; - var i, ilen; - - for (i = 0, ilen = datasets.length; i < ilen; ++i) { - if (!filterVisible || me.isDatasetVisible(i)) { - result.push(me.getDatasetMeta(i)); - } - } - - result.sort(compare2Level('order', 'index')); - - return result; - }, - - /** - * @private - */ - _getSortedVisibleDatasetMetas: function() { - return this._getSortedDatasetMetas(true); - }, - - /** - * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` - * hook, in which case, plugins will not be called on `afterDatasetsDraw`. - * @private - */ - drawDatasets: function(easingValue) { - var me = this; - var metasets, i; - - if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { - return; - } - - metasets = me._getSortedVisibleDatasetMetas(); - for (i = metasets.length - 1; i >= 0; --i) { - me.drawDataset(metasets[i], easingValue); - } - - core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); - }, - - /** - * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` - * hook, in which case, plugins will not be called on `afterDatasetDraw`. - * @private - */ - drawDataset: function(meta, easingValue) { - var me = this; - var args = { - meta: meta, - index: meta.index, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { - return; - } - - meta.controller.draw(easingValue); - - core_plugins.notify(me, 'afterDatasetDraw', [args]); - }, - - /** - * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` - * hook, in which case, plugins will not be called on `afterTooltipDraw`. - * @private - */ - _drawTooltip: function(easingValue) { - var me = this; - var tooltip = me.tooltip; - var args = { - tooltip: tooltip, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { - return; - } - - tooltip.draw(); - - core_plugins.notify(me, 'afterTooltipDraw', [args]); - }, - - /** - * Get the single element that was clicked on - * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw - */ - getElementAtEvent: function(e) { - return core_interaction.modes.single(this, e); - }, - - getElementsAtEvent: function(e) { - return core_interaction.modes.label(this, e, {intersect: true}); - }, - - getElementsAtXAxis: function(e) { - return core_interaction.modes['x-axis'](this, e, {intersect: true}); - }, - - getElementsAtEventForMode: function(e, mode, options) { - var method = core_interaction.modes[mode]; - if (typeof method === 'function') { - return method(this, e, options); - } - - return []; - }, - - getDatasetAtEvent: function(e) { - return core_interaction.modes.dataset(this, e, {intersect: true}); - }, - - getDatasetMeta: function(datasetIndex) { - var me = this; - var dataset = me.data.datasets[datasetIndex]; - if (!dataset._meta) { - dataset._meta = {}; - } - - var meta = dataset._meta[me.id]; - if (!meta) { - meta = dataset._meta[me.id] = { - type: null, - data: [], - dataset: null, - controller: null, - hidden: null, // See isDatasetVisible() comment - xAxisID: null, - yAxisID: null, - order: dataset.order || 0, - index: datasetIndex - }; - } - - return meta; - }, - - getVisibleDatasetCount: function() { - var count = 0; - for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { - if (this.isDatasetVisible(i)) { - count++; - } - } - return count; - }, - - isDatasetVisible: function(datasetIndex) { - var meta = this.getDatasetMeta(datasetIndex); - - // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, - // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. - return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; - }, - - generateLegend: function() { - return this.options.legendCallback(this); - }, - - /** - * @private - */ - destroyDatasetMeta: function(datasetIndex) { - var id = this.id; - var dataset = this.data.datasets[datasetIndex]; - var meta = dataset._meta && dataset._meta[id]; - - if (meta) { - meta.controller.destroy(); - delete dataset._meta[id]; - } - }, - - destroy: function() { - var me = this; - var canvas = me.canvas; - var i, ilen; - - me.stop(); - - // dataset controllers need to cleanup associated data - for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.destroyDatasetMeta(i); - } - - if (canvas) { - me.unbindEvents(); - helpers$1.canvas.clear(me); - platform.releaseContext(me.ctx); - me.canvas = null; - me.ctx = null; - } - - core_plugins.notify(me, 'destroy'); - - delete Chart.instances[me.id]; - }, - - toBase64Image: function() { - return this.canvas.toDataURL.apply(this.canvas, arguments); - }, - - initToolTip: function() { - var me = this; - me.tooltip = new core_tooltip({ - _chart: me, - _chartInstance: me, // deprecated, backward compatibility - _data: me.data, - _options: me.options.tooltips - }, me); - }, - - /** - * @private - */ - bindEvents: function() { - var me = this; - var listeners = me._listeners = {}; - var listener = function() { - me.eventHandler.apply(me, arguments); - }; - - helpers$1.each(me.options.events, function(type) { - platform.addEventListener(me, type, listener); - listeners[type] = listener; - }); - - // Elements used to detect size change should not be injected for non responsive charts. - // See https://github.com/chartjs/Chart.js/issues/2210 - if (me.options.responsive) { - listener = function() { - me.resize(); - }; - - platform.addEventListener(me, 'resize', listener); - listeners.resize = listener; - } - }, - - /** - * @private - */ - unbindEvents: function() { - var me = this; - var listeners = me._listeners; - if (!listeners) { - return; - } - - delete me._listeners; - helpers$1.each(listeners, function(listener, type) { - platform.removeEventListener(me, type, listener); - }); - }, - - updateHoverStyle: function(elements, mode, enabled) { - var prefix = enabled ? 'set' : 'remove'; - var element, i, ilen; - - for (i = 0, ilen = elements.length; i < ilen; ++i) { - element = elements[i]; - if (element) { - this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element); - } - } - - if (mode === 'dataset') { - this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle'](); - } - }, - - /** - * @private - */ - eventHandler: function(e) { - var me = this; - var tooltip = me.tooltip; - - if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { - return; - } - - // Buffer any update calls so that renders do not occur - me._bufferedRender = true; - me._bufferedRequest = null; - - var changed = me.handleEvent(e); - // for smooth tooltip animations issue #4989 - // the tooltip should be the source of change - // Animation check workaround: - // tooltip._start will be null when tooltip isn't animating - if (tooltip) { - changed = tooltip._start - ? tooltip.handleEvent(e) - : changed | tooltip.handleEvent(e); - } - - core_plugins.notify(me, 'afterEvent', [e]); - - var bufferedRequest = me._bufferedRequest; - if (bufferedRequest) { - // If we have an update that was triggered, we need to do a normal render - me.render(bufferedRequest); - } else if (changed && !me.animating) { - // If entering, leaving, or changing elements, animate the change via pivot - me.stop(); - - // We only need to render at this point. Updating will cause scales to be - // recomputed generating flicker & using more memory than necessary. - me.render({ - duration: me.options.hover.animationDuration, - lazy: true - }); - } - - me._bufferedRender = false; - me._bufferedRequest = null; - - return me; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event the event to handle - * @return {boolean} true if the chart needs to re-render - */ - handleEvent: function(e) { - var me = this; - var options = me.options || {}; - var hoverOptions = options.hover; - var changed = false; - - me.lastActive = me.lastActive || []; - - // Find Active Elements for hover and tooltips - if (e.type === 'mouseout') { - me.active = []; - } else { - me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); - } - - // Invoke onHover hook - // Need to call with native event here to not break backwards compatibility - helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); - - if (e.type === 'mouseup' || e.type === 'click') { - if (options.onClick) { - // Use e.native here for backwards compatibility - options.onClick.call(me, e.native, me.active); - } - } - - // Remove styling for last active (even if it may still be active) - if (me.lastActive.length) { - me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); - } - - // Built in hover styling - if (me.active.length && hoverOptions.mode) { - me.updateHoverStyle(me.active, hoverOptions.mode, true); - } - - changed = !helpers$1.arrayEquals(me.active, me.lastActive); - - // Remember Last Actives - me.lastActive = me.active; - - return changed; - } -}); - -/** - * NOTE(SB) We actually don't use this container anymore but we need to keep it - * for backward compatibility. Though, it can still be useful for plugins that - * would need to work on multiple charts?! - */ -Chart.instances = {}; - -var core_controller = Chart; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart instead. - * @class Chart.Controller - * @deprecated since version 2.6 - * @todo remove at version 3 - * @private - */ -Chart.Controller = Chart; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -Chart.types = {}; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.configMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.configMerge = mergeConfig; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.scaleMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.scaleMerge = mergeScaleConfig; - -var core_helpers = function() { - - // -- Basic js utility methods - - helpers$1.where = function(collection, filterCallback) { - if (helpers$1.isArray(collection) && Array.prototype.filter) { - return collection.filter(filterCallback); - } - var filtered = []; - - helpers$1.each(collection, function(item) { - if (filterCallback(item)) { - filtered.push(item); - } - }); - - return filtered; - }; - helpers$1.findIndex = Array.prototype.findIndex ? - function(array, callback, scope) { - return array.findIndex(callback, scope); - } : - function(array, callback, scope) { - scope = scope === undefined ? array : scope; - for (var i = 0, ilen = array.length; i < ilen; ++i) { - if (callback.call(scope, array[i], i, array)) { - return i; - } - } - return -1; - }; - helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to start of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = -1; - } - for (var i = startIndex + 1; i < arrayToSearch.length; i++) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to end of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = arrayToSearch.length; - } - for (var i = startIndex - 1; i >= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - - // -- Math methods - helpers$1.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - helpers$1.almostEquals = function(x, y, epsilon) { - return Math.abs(x - y) < epsilon; - }; - helpers$1.almostWhole = function(x, epsilon) { - var rounded = Math.round(x); - return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x); - }; - helpers$1.max = function(array) { - return array.reduce(function(max, value) { - if (!isNaN(value)) { - return Math.max(max, value); - } - return max; - }, Number.NEGATIVE_INFINITY); - }; - helpers$1.min = function(array) { - return array.reduce(function(min, value) { - if (!isNaN(value)) { - return Math.min(min, value); - } - return min; - }, Number.POSITIVE_INFINITY); - }; - helpers$1.sign = Math.sign ? - function(x) { - return Math.sign(x); - } : - function(x) { - x = +x; // convert to a number - if (x === 0 || isNaN(x)) { - return x; - } - return x > 0 ? 1 : -1; - }; - helpers$1.toRadians = function(degrees) { - return degrees * (Math.PI / 180); - }; - helpers$1.toDegrees = function(radians) { - return radians * (180 / Math.PI); - }; - - /** - * Returns the number of decimal places - * i.e. the number of digits after the decimal point, of the value of this Number. - * @param {number} x - A number. - * @returns {number} The number of decimal places. - * @private - */ - helpers$1._decimalPlaces = function(x) { - if (!helpers$1.isFinite(x)) { - return; - } - var e = 1; - var p = 0; - while (Math.round(x * e) / e !== x) { - e *= 10; - p++; - } - return p; - }; - - // Gets the angle from vertical upright to the point about a centre. - helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { - var distanceFromXCenter = anglePoint.x - centrePoint.x; - var distanceFromYCenter = anglePoint.y - centrePoint.y; - var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); - - if (angle < (-0.5 * Math.PI)) { - angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }; - helpers$1.distanceBetweenPoints = function(pt1, pt2) { - return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); - }; - - /** - * Provided for backward compatibility, not available anymore - * @function Chart.helpers.aliasPixel - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ - helpers$1.aliasPixel = function(pixelWidth) { - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }; - - /** - * Returns the aligned pixel value to avoid anti-aliasing blur - * @param {Chart} chart - The chart instance. - * @param {number} pixel - A pixel value. - * @param {number} width - The width of the element. - * @returns {number} The aligned pixel value. - * @private - */ - helpers$1._alignPixel = function(chart, pixel, width) { - var devicePixelRatio = chart.currentDevicePixelRatio; - var halfWidth = width / 2; - return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; - }; - - helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { - // Props to Rob Spencer at scaled innovation for his post on splining between points - // http://scaledinnovation.com/analytics/splines/aboutSplines.html - - // This function must also respect "skipped" points - - var previous = firstPoint.skip ? middlePoint : firstPoint; - var current = middlePoint; - var next = afterPoint.skip ? middlePoint : afterPoint; - - var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); - var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); - - var s01 = d01 / (d01 + d12); - var s12 = d12 / (d01 + d12); - - // If all points are the same, s01 & s02 will be inf - s01 = isNaN(s01) ? 0 : s01; - s12 = isNaN(s12) ? 0 : s12; - - var fa = t * s01; // scaling factor for triangle Ta - var fb = t * s12; - - return { - previous: { - x: current.x - fa * (next.x - previous.x), - y: current.y - fa * (next.y - previous.y) - }, - next: { - x: current.x + fb * (next.x - previous.x), - y: current.y + fb * (next.y - previous.y) - } - }; - }; - helpers$1.EPSILON = Number.EPSILON || 1e-14; - helpers$1.splineCurveMonotone = function(points) { - // This function calculates Bézier control points in a similar way than |splineCurve|, - // but preserves monotonicity of the provided data and ensures no local extremums are added - // between the dataset discrete points due to the interpolation. - // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation - - var pointsWithTangents = (points || []).map(function(point) { - return { - model: point._model, - deltaK: 0, - mK: 0 - }; - }); - - // Calculate slopes (deltaK) and initialize tangents (mK) - var pointsLen = pointsWithTangents.length; - var i, pointBefore, pointCurrent, pointAfter; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointAfter && !pointAfter.model.skip) { - var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); - - // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 - pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; - } - - if (!pointBefore || pointBefore.model.skip) { - pointCurrent.mK = pointCurrent.deltaK; - } else if (!pointAfter || pointAfter.model.skip) { - pointCurrent.mK = pointBefore.deltaK; - } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { - pointCurrent.mK = 0; - } else { - pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; - } - } - - // Adjust tangents to ensure monotonic properties - var alphaK, betaK, tauK, squaredMagnitude; - for (i = 0; i < pointsLen - 1; ++i) { - pointCurrent = pointsWithTangents[i]; - pointAfter = pointsWithTangents[i + 1]; - if (pointCurrent.model.skip || pointAfter.model.skip) { - continue; - } - - if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { - pointCurrent.mK = pointAfter.mK = 0; - continue; - } - - alphaK = pointCurrent.mK / pointCurrent.deltaK; - betaK = pointAfter.mK / pointCurrent.deltaK; - squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); - if (squaredMagnitude <= 9) { - continue; - } - - tauK = 3 / Math.sqrt(squaredMagnitude); - pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; - pointAfter.mK = betaK * tauK * pointCurrent.deltaK; - } - - // Compute control points - var deltaX; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointBefore && !pointBefore.model.skip) { - deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; - pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; - pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; - } - if (pointAfter && !pointAfter.model.skip) { - deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; - pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; - pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; - } - } - }; - helpers$1.nextItem = function(collection, index, loop) { - if (loop) { - return index >= collection.length - 1 ? collection[0] : collection[index + 1]; - } - return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; - }; - helpers$1.previousItem = function(collection, index, loop) { - if (loop) { - return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; - } - return index <= 0 ? collection[0] : collection[index - 1]; - }; - // Implementation of the nice number algorithm used in determining where axis labels will go - helpers$1.niceNum = function(range, round) { - var exponent = Math.floor(helpers$1.log10(range)); - var fraction = range / Math.pow(10, exponent); - var niceFraction; - - if (round) { - if (fraction < 1.5) { - niceFraction = 1; - } else if (fraction < 3) { - niceFraction = 2; - } else if (fraction < 7) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } else if (fraction <= 1.0) { - niceFraction = 1; - } else if (fraction <= 2) { - niceFraction = 2; - } else if (fraction <= 5) { - niceFraction = 5; - } else { - niceFraction = 10; - } - - return niceFraction * Math.pow(10, exponent); - }; - // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - helpers$1.requestAnimFrame = (function() { - if (typeof window === 'undefined') { - return function(callback) { - callback(); - }; - } - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - }()); - // -- DOM methods - helpers$1.getRelativePosition = function(evt, chart) { - var mouseX, mouseY; - var e = evt.originalEvent || evt; - var canvas = evt.target || evt.srcElement; - var boundingRect = canvas.getBoundingClientRect(); - - var touches = e.touches; - if (touches && touches.length > 0) { - mouseX = touches[0].clientX; - mouseY = touches[0].clientY; - - } else { - mouseX = e.clientX; - mouseY = e.clientY; - } - - // Scale mouse coordinates into canvas coordinates - // by following the pattern laid out by 'jerryj' in the comments of - // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ - var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); - var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); - var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); - var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); - var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; - var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; - - // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However - // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here - mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); - mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); - - return { - x: mouseX, - y: mouseY - }; - - }; - - // Private helper function to convert max-width/max-height values that may be percentages into a number - function parseMaxStyle(styleValue, node, parentProperty) { - var valueInPixels; - if (typeof styleValue === 'string') { - valueInPixels = parseInt(styleValue, 10); - - if (styleValue.indexOf('%') !== -1) { - // percentage * size in dimension - valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; - } - } else { - valueInPixels = styleValue; - } - - return valueInPixels; - } - - /** - * Returns if the given value contains an effective constraint. - * @private - */ - function isConstrainedValue(value) { - return value !== undefined && value !== null && value !== 'none'; - } - - /** - * Returns the max width or height of the given DOM node in a cross-browser compatible fashion - * @param {HTMLElement} domNode - the node to check the constraint on - * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') - * @param {string} percentageProperty - property of parent to use when calculating width as a percentage - * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} - */ - function getConstraintDimension(domNode, maxStyle, percentageProperty) { - var view = document.defaultView; - var parentNode = helpers$1._getParentNode(domNode); - var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; - var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; - var hasCNode = isConstrainedValue(constrainedNode); - var hasCContainer = isConstrainedValue(constrainedContainer); - var infinity = Number.POSITIVE_INFINITY; - - if (hasCNode || hasCContainer) { - return Math.min( - hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, - hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); - } - - return 'none'; - } - // returns Number or undefined if no constraint - helpers$1.getConstraintWidth = function(domNode) { - return getConstraintDimension(domNode, 'max-width', 'clientWidth'); - }; - // returns Number or undefined if no constraint - helpers$1.getConstraintHeight = function(domNode) { - return getConstraintDimension(domNode, 'max-height', 'clientHeight'); - }; - /** - * @private - */ - helpers$1._calculatePadding = function(container, padding, parentDimension) { - padding = helpers$1.getStyle(container, padding); - - return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); - }; - /** - * @private - */ - helpers$1._getParentNode = function(domNode) { - var parent = domNode.parentNode; - if (parent && parent.toString() === '[object ShadowRoot]') { - parent = parent.host; - } - return parent; - }; - helpers$1.getMaximumWidth = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientWidth; - } - - var clientWidth = container.clientWidth; - var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); - var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); - - var w = clientWidth - paddingLeft - paddingRight; - var cw = helpers$1.getConstraintWidth(domNode); - return isNaN(cw) ? w : Math.min(w, cw); - }; - helpers$1.getMaximumHeight = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientHeight; - } - - var clientHeight = container.clientHeight; - var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); - var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); - - var h = clientHeight - paddingTop - paddingBottom; - var ch = helpers$1.getConstraintHeight(domNode); - return isNaN(ch) ? h : Math.min(h, ch); - }; - helpers$1.getStyle = function(el, property) { - return el.currentStyle ? - el.currentStyle[property] : - document.defaultView.getComputedStyle(el, null).getPropertyValue(property); - }; - helpers$1.retinaScale = function(chart, forceRatio) { - var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; - if (pixelRatio === 1) { - return; - } - - var canvas = chart.canvas; - var height = chart.height; - var width = chart.width; - - canvas.height = height * pixelRatio; - canvas.width = width * pixelRatio; - chart.ctx.scale(pixelRatio, pixelRatio); - - // If no style has been set on the canvas, the render size is used as display size, - // making the chart visually bigger, so let's enforce it to the "correct" values. - // See https://github.com/chartjs/Chart.js/issues/3575 - if (!canvas.style.height && !canvas.style.width) { - canvas.style.height = height + 'px'; - canvas.style.width = width + 'px'; - } - }; - // -- Canvas methods - helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { - return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; - }; - helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { - cache = cache || {}; - var data = cache.data = cache.data || {}; - var gc = cache.garbageCollect = cache.garbageCollect || []; - - if (cache.font !== font) { - data = cache.data = {}; - gc = cache.garbageCollect = []; - cache.font = font; - } - - ctx.font = font; - var longest = 0; - var ilen = arrayOfThings.length; - var i, j, jlen, thing, nestedThing; - for (i = 0; i < ilen; i++) { - thing = arrayOfThings[i]; - - // Undefined strings and arrays should not be measured - if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { - longest = helpers$1.measureText(ctx, data, gc, longest, thing); - } else if (helpers$1.isArray(thing)) { - // if it is an array lets measure each element - // to do maybe simplify this function a bit so we can do this more recursively? - for (j = 0, jlen = thing.length; j < jlen; j++) { - nestedThing = thing[j]; - // Undefined strings and arrays should not be measured - if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { - longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); - } - } - } - } - - var gcLen = gc.length / 2; - if (gcLen > arrayOfThings.length) { - for (i = 0; i < gcLen; i++) { - delete data[gc[i]]; - } - gc.splice(0, gcLen); - } - return longest; - }; - helpers$1.measureText = function(ctx, data, gc, longest, string) { - var textWidth = data[string]; - if (!textWidth) { - textWidth = data[string] = ctx.measureText(string).width; - gc.push(string); - } - if (textWidth > longest) { - longest = textWidth; - } - return longest; - }; - - /** - * @deprecated - */ - helpers$1.numberOfLabelLines = function(arrayOfThings) { - var numberOfLines = 1; - helpers$1.each(arrayOfThings, function(thing) { - if (helpers$1.isArray(thing)) { - if (thing.length > numberOfLines) { - numberOfLines = thing.length; - } - } - }); - return numberOfLines; - }; - - helpers$1.color = !chartjsColor ? - function(value) { - console.error('Color.js not found!'); - return value; - } : - function(value) { - /* global CanvasGradient */ - if (value instanceof CanvasGradient) { - value = core_defaults.global.defaultColor; - } - - return chartjsColor(value); - }; - - helpers$1.getHoverColor = function(colorValue) { - /* global CanvasPattern */ - return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? - colorValue : - helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); - }; -}; - -function abstract() { - throw new Error( - 'This method is not implemented: either no adapter can ' + - 'be found or an incomplete integration was provided.' - ); -} - -/** - * Date adapter (current used by the time scale) - * @namespace Chart._adapters._date - * @memberof Chart._adapters - * @private - */ - -/** - * Currently supported unit string values. - * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} - * @memberof Chart._adapters._date - * @name Unit - */ - -/** - * @class - */ -function DateAdapter(options) { - this.options = options || {}; -} - -helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { - /** - * Returns a map of time formats for the supported formatting units defined - * in Unit as well as 'datetime' representing a detailed date/time string. - * @returns {{string: string}} - */ - formats: abstract, - - /** - * Parses the given `value` and return the associated timestamp. - * @param {any} value - the value to parse (usually comes from the data) - * @param {string} [format] - the expected data format - * @returns {(number|null)} - * @function - */ - parse: abstract, - - /** - * Returns the formatted date in the specified `format` for a given `timestamp`. - * @param {number} timestamp - the timestamp to format - * @param {string} format - the date/time token - * @return {string} - * @function - */ - format: abstract, - - /** - * Adds the specified `amount` of `unit` to the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {number} amount - the amount to add - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - add: abstract, - - /** - * Returns the number of `unit` between the given timestamps. - * @param {number} max - the input timestamp (reference) - * @param {number} min - the timestamp to substract - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - diff: abstract, - - /** - * Returns start of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @param {number} [weekday] - the ISO day of the week with 1 being Monday - * and 7 being Sunday (only needed if param *unit* is `isoWeek`). - * @function - */ - startOf: abstract, - - /** - * Returns end of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @function - */ - endOf: abstract, - - // DEPRECATIONS - - /** - * Provided for backward compatibility for scale.getValueForPixel(), - * this method should be overridden only by the moment adapter. - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(value) { - return value; - } -}); - -DateAdapter.override = function(members) { - helpers$1.extend(DateAdapter.prototype, members); -}; - -var _date = DateAdapter; - -var core_adapters = { - _date: _date -}; - -/** - * Namespace to hold static tick generation functions - * @namespace Chart.Ticks - */ -var core_ticks = { - /** - * Namespace to hold formatters for different types of ticks - * @namespace Chart.Ticks.formatters - */ - formatters: { - /** - * Formatter for value labels - * @method Chart.Ticks.formatters.values - * @param value the value to display - * @return {string|string[]} the label to display - */ - values: function(value) { - return helpers$1.isArray(value) ? value : '' + value; - }, - - /** - * Formatter for linear numeric ticks - * @method Chart.Ticks.formatters.linear - * @param tickValue {number} the value to be formatted - * @param index {number} the position of the tickValue parameter in the ticks array - * @param ticks {number[]} the list of ticks being converted - * @return {string} string representation of the tickValue parameter - */ - linear: function(tickValue, index, ticks) { - // If we have lots of ticks, don't use the ones - var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; - - // If we have a number like 2.5 as the delta, figure out how many decimal places we need - if (Math.abs(delta) > 1) { - if (tickValue !== Math.floor(tickValue)) { - // not an integer - delta = tickValue - Math.floor(tickValue); - } - } - - var logDelta = helpers$1.log10(Math.abs(delta)); - var tickString = ''; - - if (tickValue !== 0) { - var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); - if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation - var logTick = helpers$1.log10(Math.abs(tickValue)); - var numExponential = Math.floor(logTick) - Math.floor(logDelta); - numExponential = Math.max(Math.min(numExponential, 20), 0); - tickString = tickValue.toExponential(numExponential); - } else { - var numDecimal = -1 * Math.floor(logDelta); - numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places - tickString = tickValue.toFixed(numDecimal); - } - } else { - tickString = '0'; // never show decimal places for 0 - } - - return tickString; - }, - - logarithmic: function(tickValue, index, ticks) { - var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); - - if (tickValue === 0) { - return '0'; - } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { - return tickValue.toExponential(); - } - return ''; - } - } -}; - -var isArray = helpers$1.isArray; -var isNullOrUndef = helpers$1.isNullOrUndef; -var valueOrDefault$a = helpers$1.valueOrDefault; -var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; - -core_defaults._set('scale', { - display: true, - position: 'left', - offset: false, - - // grid line settings - gridLines: { - display: true, - color: 'rgba(0,0,0,0.1)', - lineWidth: 1, - drawBorder: true, - drawOnChartArea: true, - drawTicks: true, - tickMarkLength: 10, - zeroLineWidth: 1, - zeroLineColor: 'rgba(0,0,0,0.25)', - zeroLineBorderDash: [], - zeroLineBorderDashOffset: 0.0, - offsetGridLines: false, - borderDash: [], - borderDashOffset: 0.0 - }, - - // scale label - scaleLabel: { - // display property - display: false, - - // actual label - labelString: '', - - // top/bottom padding - padding: { - top: 4, - bottom: 4 - } - }, - - // label settings - ticks: { - beginAtZero: false, - minRotation: 0, - maxRotation: 50, - mirror: false, - padding: 0, - reverse: false, - display: true, - autoSkip: true, - autoSkipPadding: 0, - labelOffset: 0, - // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. - callback: core_ticks.formatters.values, - minor: {}, - major: {} - } -}); - -/** Returns a new array containing numItems from arr */ -function sample(arr, numItems) { - var result = []; - var increment = arr.length / numItems; - var i = 0; - var len = arr.length; - - for (; i < len; i += increment) { - result.push(arr[Math.floor(i)]); - } - return result; -} - -function getPixelForGridLine(scale, index, offsetGridLines) { - var length = scale.getTicks().length; - var validIndex = Math.min(index, length - 1); - var lineValue = scale.getPixelForTick(validIndex); - var start = scale._startPixel; - var end = scale._endPixel; - var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. - var offset; - - if (offsetGridLines) { - if (length === 1) { - offset = Math.max(lineValue - start, end - lineValue); - } else if (index === 0) { - offset = (scale.getPixelForTick(1) - lineValue) / 2; - } else { - offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; - } - lineValue += validIndex < index ? offset : -offset; - - // Return undefined if the pixel is out of the range - if (lineValue < start - epsilon || lineValue > end + epsilon) { - return; - } - } - return lineValue; -} - -function garbageCollect(caches, length) { - helpers$1.each(caches, function(cache) { - var gc = cache.gc; - var gcLen = gc.length / 2; - var i; - if (gcLen > length) { - for (i = 0; i < gcLen; ++i) { - delete cache.data[gc[i]]; - } - gc.splice(0, gcLen); - } - }); -} - -/** - * Returns {width, height, offset} objects for the first, last, widest, highest tick - * labels where offset indicates the anchor point offset from the top in pixels. - */ -function computeLabelSizes(ctx, tickFonts, ticks, caches) { - var length = ticks.length; - var widths = []; - var heights = []; - var offsets = []; - var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest; - - for (i = 0; i < length; ++i) { - label = ticks[i].label; - tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor; - ctx.font = fontString = tickFont.string; - cache = caches[fontString] = caches[fontString] || {data: {}, gc: []}; - lineHeight = tickFont.lineHeight; - width = height = 0; - // Undefined labels and arrays should not be measured - if (!isNullOrUndef(label) && !isArray(label)) { - width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label); - height = lineHeight; - } else if (isArray(label)) { - // if it is an array let's measure each element - for (j = 0, jlen = label.length; j < jlen; ++j) { - nestedLabel = label[j]; - // Undefined labels and arrays should not be measured - if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { - width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel); - height += lineHeight; - } - } - } - widths.push(width); - heights.push(height); - offsets.push(lineHeight / 2); - } - garbageCollect(caches, length); - - widest = widths.indexOf(Math.max.apply(null, widths)); - highest = heights.indexOf(Math.max.apply(null, heights)); - - function valueAt(idx) { - return { - width: widths[idx] || 0, - height: heights[idx] || 0, - offset: offsets[idx] || 0 - }; - } - - return { - first: valueAt(0), - last: valueAt(length - 1), - widest: valueAt(widest), - highest: valueAt(highest) - }; -} - -function getTickMarkLength(options) { - return options.drawTicks ? options.tickMarkLength : 0; -} - -function getScaleLabelHeight(options) { - var font, padding; - - if (!options.display) { - return 0; - } - - font = helpers$1.options._parseFont(options); - padding = helpers$1.options.toPadding(options.padding); - - return font.lineHeight + padding.height; -} - -function parseFontOptions(options, nestedOpts) { - return helpers$1.extend(helpers$1.options._parseFont({ - fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily), - fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize), - fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle), - lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight) - }), { - color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor]) - }); -} - -function parseTickFontOptions(options) { - var minor = parseFontOptions(options, options.minor); - var major = options.major.enabled ? parseFontOptions(options, options.major) : minor; - - return {minor: minor, major: major}; -} - -function nonSkipped(ticksToFilter) { - var filtered = []; - var item, index, len; - for (index = 0, len = ticksToFilter.length; index < len; ++index) { - item = ticksToFilter[index]; - if (typeof item._index !== 'undefined') { - filtered.push(item); - } - } - return filtered; -} - -function getEvenSpacing(arr) { - var len = arr.length; - var i, diff; - - if (len < 2) { - return false; - } - - for (diff = arr[0], i = 1; i < len; ++i) { - if (arr[i] - arr[i - 1] !== diff) { - return false; - } - } - return diff; -} - -function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) { - var evenMajorSpacing = getEvenSpacing(majorIndices); - var spacing = (ticks.length - 1) / ticksLimit; - var factors, factor, i, ilen; - - // If the major ticks are evenly spaced apart, place the minor ticks - // so that they divide the major ticks into even chunks - if (!evenMajorSpacing) { - return Math.max(spacing, 1); - } - - factors = helpers$1.math._factorize(evenMajorSpacing); - for (i = 0, ilen = factors.length - 1; i < ilen; i++) { - factor = factors[i]; - if (factor > spacing) { - return factor; - } - } - return Math.max(spacing, 1); -} - -function getMajorIndices(ticks) { - var result = []; - var i, ilen; - for (i = 0, ilen = ticks.length; i < ilen; i++) { - if (ticks[i].major) { - result.push(i); - } - } - return result; -} - -function skipMajors(ticks, majorIndices, spacing) { - var count = 0; - var next = majorIndices[0]; - var i, tick; - - spacing = Math.ceil(spacing); - for (i = 0; i < ticks.length; i++) { - tick = ticks[i]; - if (i === next) { - tick._index = i; - count++; - next = majorIndices[count * spacing]; - } else { - delete tick.label; - } - } -} - -function skip(ticks, spacing, majorStart, majorEnd) { - var start = valueOrDefault$a(majorStart, 0); - var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length); - var count = 0; - var length, i, tick, next; - - spacing = Math.ceil(spacing); - if (majorEnd) { - length = majorEnd - majorStart; - spacing = length / Math.floor(length / spacing); - } - - next = start; - - while (next < 0) { - count++; - next = Math.round(start + count * spacing); - } - - for (i = Math.max(start, 0); i < end; i++) { - tick = ticks[i]; - if (i === next) { - tick._index = i; - count++; - next = Math.round(start + count * spacing); - } else { - delete tick.label; - } - } -} - -var Scale = core_element.extend({ - - zeroLineIndex: 0, - - /** - * Get the padding needed for the scale - * @method getPadding - * @private - * @returns {Padding} the necessary padding - */ - getPadding: function() { - var me = this; - return { - left: me.paddingLeft || 0, - top: me.paddingTop || 0, - right: me.paddingRight || 0, - bottom: me.paddingBottom || 0 - }; - }, - - /** - * Returns the scale tick objects ({label, major}) - * @since 2.7 - */ - getTicks: function() { - return this._ticks; - }, - - /** - * @private - */ - _getLabels: function() { - var data = this.chart.data; - return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || []; - }, - - // These methods are ordered by lifecyle. Utilities then follow. - // Any function defined here is inherited by all scale types. - // Any function can be extended by the scale type - - /** - * Provided for backward compatibility, not available anymore - * @function Chart.Scale.mergeTicksOptions - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ - mergeTicksOptions: function() { - // noop - }, - - beforeUpdate: function() { - helpers$1.callback(this.options.beforeUpdate, [this]); - }, - - /** - * @param {number} maxWidth - the max width in pixels - * @param {number} maxHeight - the max height in pixels - * @param {object} margins - the space between the edge of the other scales and edge of the chart - * This space comes from two sources: - * - padding - space that's required to show the labels at the edges of the scale - * - thickness of scales or legends in another orientation - */ - update: function(maxWidth, maxHeight, margins) { - var me = this; - var tickOpts = me.options.ticks; - var sampleSize = tickOpts.sampleSize; - var i, ilen, labels, ticks, samplingEnabled; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = helpers$1.extend({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }, margins); - - me._ticks = null; - me.ticks = null; - me._labelSizes = null; - me._maxLabelLines = 0; - me.longestLabelWidth = 0; - me.longestTextCache = me.longestTextCache || {}; - me._gridLineItems = null; - me._labelItems = null; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - - // Data min/max - me.beforeDataLimits(); - me.determineDataLimits(); - me.afterDataLimits(); - - // Ticks - `this.ticks` is now DEPRECATED! - // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member - // and must not be accessed directly from outside this class. `this.ticks` being - // around for long time and not marked as private, we can't change its structure - // without unexpected breaking changes. If you need to access the scale ticks, - // use scale.getTicks() instead. - - me.beforeBuildTicks(); - - // New implementations should return an array of objects but for BACKWARD COMPAT, - // we still support no return (`this.ticks` internally set by calling this method). - ticks = me.buildTicks() || []; - - // Allow modification of ticks in callback. - ticks = me.afterBuildTicks(ticks) || ticks; - - // Ensure ticks contains ticks in new tick format - if ((!ticks || !ticks.length) && me.ticks) { - ticks = []; - for (i = 0, ilen = me.ticks.length; i < ilen; ++i) { - ticks.push({ - value: me.ticks[i], - major: false - }); - } - } - - me._ticks = ticks; - - // Compute tick rotation and fit using a sampled subset of labels - // We generally don't need to compute the size of every single label for determining scale size - samplingEnabled = sampleSize < ticks.length; - labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks); - - // _configure is called twice, once here, once from core.controller.updateLayout. - // Here we haven't been positioned yet, but dimensions are correct. - // Variables set in _configure are needed for calculateTickRotation, and - // it's ok that coordinates are not correct there, only dimensions matter. - me._configure(); - - // Tick Rotation - me.beforeCalculateTickRotation(); - me.calculateTickRotation(); - me.afterCalculateTickRotation(); - - me.beforeFit(); - me.fit(); - me.afterFit(); - - // Auto-skip - me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks; - - if (samplingEnabled) { - // Generate labels using all non-skipped ticks - labels = me._convertTicksToLabels(me._ticksToDraw); - } - - me.ticks = labels; // BACKWARD COMPATIBILITY - - // IMPORTANT: after this point, we consider that `this.ticks` will NEVER change! - - me.afterUpdate(); - - // TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused - // make maxWidth and maxHeight private - return me.minSize; - }, - - /** - * @private - */ - _configure: function() { - var me = this; - var reversePixels = me.options.ticks.reverse; - var startPixel, endPixel; - - if (me.isHorizontal()) { - startPixel = me.left; - endPixel = me.right; - } else { - startPixel = me.top; - endPixel = me.bottom; - // by default vertical scales are from bottom to top, so pixels are reversed - reversePixels = !reversePixels; - } - me._startPixel = startPixel; - me._endPixel = endPixel; - me._reversePixels = reversePixels; - me._length = endPixel - startPixel; - }, - - afterUpdate: function() { - helpers$1.callback(this.options.afterUpdate, [this]); - }, - - // - - beforeSetDimensions: function() { - helpers$1.callback(this.options.beforeSetDimensions, [this]); - }, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - }, - afterSetDimensions: function() { - helpers$1.callback(this.options.afterSetDimensions, [this]); - }, - - // Data limits - beforeDataLimits: function() { - helpers$1.callback(this.options.beforeDataLimits, [this]); - }, - determineDataLimits: helpers$1.noop, - afterDataLimits: function() { - helpers$1.callback(this.options.afterDataLimits, [this]); - }, - - // - beforeBuildTicks: function() { - helpers$1.callback(this.options.beforeBuildTicks, [this]); - }, - buildTicks: helpers$1.noop, - afterBuildTicks: function(ticks) { - var me = this; - // ticks is empty for old axis implementations here - if (isArray(ticks) && ticks.length) { - return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); - } - // Support old implementations (that modified `this.ticks` directly in buildTicks) - me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; - return ticks; - }, - - beforeTickToLabelConversion: function() { - helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); - }, - convertTicksToLabels: function() { - var me = this; - // Convert ticks to strings - var tickOpts = me.options.ticks; - me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); - }, - afterTickToLabelConversion: function() { - helpers$1.callback(this.options.afterTickToLabelConversion, [this]); - }, - - // - - beforeCalculateTickRotation: function() { - helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); - }, - calculateTickRotation: function() { - var me = this; - var options = me.options; - var tickOpts = options.ticks; - var numTicks = me.getTicks().length; - var minRotation = tickOpts.minRotation || 0; - var maxRotation = tickOpts.maxRotation; - var labelRotation = minRotation; - var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal; - - if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) { - me.labelRotation = minRotation; - return; - } - - labelSizes = me._getLabelSizes(); - maxLabelWidth = labelSizes.widest.width; - maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset; - - // Estimate the width of each grid based on the canvas width, the maximum - // label width and the number of tick intervals - maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth); - tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1); - - // Allow 3 pixels x2 padding either side for label readability - if (maxLabelWidth + 6 > tickWidth) { - tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1)); - maxHeight = me.maxHeight - getTickMarkLength(options.gridLines) - - tickOpts.padding - getScaleLabelHeight(options.scaleLabel); - maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight); - labelRotation = helpers$1.toDegrees(Math.min( - Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)), - Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal) - )); - labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation)); - } - - me.labelRotation = labelRotation; - }, - afterCalculateTickRotation: function() { - helpers$1.callback(this.options.afterCalculateTickRotation, [this]); - }, - - // - - beforeFit: function() { - helpers$1.callback(this.options.beforeFit, [this]); - }, - fit: function() { - var me = this; - // Reset - var minSize = me.minSize = { - width: 0, - height: 0 - }; - - var chart = me.chart; - var opts = me.options; - var tickOpts = opts.ticks; - var scaleLabelOpts = opts.scaleLabel; - var gridLineOpts = opts.gridLines; - var display = me._isVisible(); - var isBottom = opts.position === 'bottom'; - var isHorizontal = me.isHorizontal(); - - // Width - if (isHorizontal) { - minSize.width = me.maxWidth; - } else if (display) { - minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); - } - - // height - if (!isHorizontal) { - minSize.height = me.maxHeight; // fill all the height - } else if (display) { - minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); - } - - // Don't bother fitting the ticks if we are not showing the labels - if (tickOpts.display && display) { - var tickFonts = parseTickFontOptions(tickOpts); - var labelSizes = me._getLabelSizes(); - var firstLabelSize = labelSizes.first; - var lastLabelSize = labelSizes.last; - var widestLabelSize = labelSizes.widest; - var highestLabelSize = labelSizes.highest; - var lineSpace = tickFonts.minor.lineHeight * 0.4; - var tickPadding = tickOpts.padding; - - if (isHorizontal) { - // A horizontal axis is more constrained by the height. - var isRotated = me.labelRotation !== 0; - var angleRadians = helpers$1.toRadians(me.labelRotation); - var cosRotation = Math.cos(angleRadians); - var sinRotation = Math.sin(angleRadians); - - var labelHeight = sinRotation * widestLabelSize.width - + cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0)) - + (isRotated ? 0 : lineSpace); // padding - - minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); - - var offsetLeft = me.getPixelForTick(0) - me.left; - var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1); - var paddingLeft, paddingRight; - - // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned - // which means that the right padding is dominated by the font height - if (isRotated) { - paddingLeft = isBottom ? - cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset : - sinRotation * (firstLabelSize.height - firstLabelSize.offset); - paddingRight = isBottom ? - sinRotation * (lastLabelSize.height - lastLabelSize.offset) : - cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset; - } else { - paddingLeft = firstLabelSize.width / 2; - paddingRight = lastLabelSize.width / 2; - } - - // Adjust padding taking into account changes in offsets - // and add 3 px to move away from canvas edges - me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3; - me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3; - } else { - // A vertical axis is more constrained by the width. Labels are the - // dominant factor here, so get that length first and account for padding - var labelWidth = tickOpts.mirror ? 0 : - // use lineSpace for consistency with horizontal axis - // tickPadding is not implemented for horizontal - widestLabelSize.width + tickPadding + lineSpace; - - minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth); - - me.paddingTop = firstLabelSize.height / 2; - me.paddingBottom = lastLabelSize.height / 2; - } - } - - me.handleMargins(); - - if (isHorizontal) { - me.width = me._length = chart.width - me.margins.left - me.margins.right; - me.height = minSize.height; - } else { - me.width = minSize.width; - me.height = me._length = chart.height - me.margins.top - me.margins.bottom; - } - }, - - /** - * Handle margins and padding interactions - * @private - */ - handleMargins: function() { - var me = this; - if (me.margins) { - me.margins.left = Math.max(me.paddingLeft, me.margins.left); - me.margins.top = Math.max(me.paddingTop, me.margins.top); - me.margins.right = Math.max(me.paddingRight, me.margins.right); - me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom); - } - }, - - afterFit: function() { - helpers$1.callback(this.options.afterFit, [this]); - }, - - // Shared Methods - isHorizontal: function() { - var pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - }, - isFullWidth: function() { - return this.options.fullWidth; - }, - - // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not - getRightValue: function(rawValue) { - // Null and undefined values first - if (isNullOrUndef(rawValue)) { - return NaN; - } - // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values - if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { - return NaN; - } - - // If it is in fact an object, dive in one more level - if (rawValue) { - if (this.isHorizontal()) { - if (rawValue.x !== undefined) { - return this.getRightValue(rawValue.x); - } - } else if (rawValue.y !== undefined) { - return this.getRightValue(rawValue.y); - } - } - - // Value is good, return it - return rawValue; - }, - - _convertTicksToLabels: function(ticks) { - var me = this; - var labels, i, ilen; - - me.ticks = ticks.map(function(tick) { - return tick.value; - }); - - me.beforeTickToLabelConversion(); - - // New implementations should return the formatted tick labels but for BACKWARD - // COMPAT, we still support no return (`this.ticks` internally changed by calling - // this method and supposed to contain only string values). - labels = me.convertTicksToLabels(ticks) || me.ticks; - - me.afterTickToLabelConversion(); - - // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - ticks[i].label = labels[i]; - } - - return labels; - }, - - /** - * @private - */ - _getLabelSizes: function() { - var me = this; - var labelSizes = me._labelSizes; - - if (!labelSizes) { - me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache); - me.longestLabelWidth = labelSizes.widest.width; - } - - return labelSizes; - }, - - /** - * @private - */ - _parseValue: function(value) { - var start, end, min, max; - - if (isArray(value)) { - start = +this.getRightValue(value[0]); - end = +this.getRightValue(value[1]); - min = Math.min(start, end); - max = Math.max(start, end); - } else { - value = +this.getRightValue(value); - start = undefined; - end = value; - min = value; - max = value; - } - - return { - min: min, - max: max, - start: start, - end: end - }; - }, - - /** - * @private - */ - _getScaleLabel: function(rawValue) { - var v = this._parseValue(rawValue); - if (v.start !== undefined) { - return '[' + v.start + ', ' + v.end + ']'; - } - - return +this.getRightValue(rawValue); - }, - - /** - * Used to get the value to display in the tooltip for the data at the given index - * @param index - * @param datasetIndex - */ - getLabelForIndex: helpers$1.noop, - - /** - * Returns the location of the given data point. Value can either be an index or a numerical value - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param value - * @param index - * @param datasetIndex - */ - getPixelForValue: helpers$1.noop, - - /** - * Used to get the data value from a given pixel. This is the inverse of getPixelForValue - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param pixel - */ - getValueForPixel: helpers$1.noop, - - /** - * Returns the location of the tick at the given index - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForTick: function(index) { - var me = this; - var offset = me.options.offset; - var numTicks = me._ticks.length; - var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1); - - return index < 0 || index > numTicks - 1 - ? null - : me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0)); - }, - - /** - * Utility for getting the pixel location of a percentage of scale - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForDecimal: function(decimal) { - var me = this; - - if (me._reversePixels) { - decimal = 1 - decimal; - } - - return me._startPixel + decimal * me._length; - }, - - getDecimalForPixel: function(pixel) { - var decimal = (pixel - this._startPixel) / this._length; - return this._reversePixels ? 1 - decimal : decimal; - }, - - /** - * Returns the pixel for the minimum chart value - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getBasePixel: function() { - return this.getPixelForValue(this.getBaseValue()); - }, - - getBaseValue: function() { - var me = this; - var min = me.min; - var max = me.max; - - return me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0; - }, - - /** - * Returns a subset of ticks to be plotted to avoid overlapping labels. - * @private - */ - _autoSkip: function(ticks) { - var me = this; - var tickOpts = me.options.ticks; - var axisLength = me._length; - var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1; - var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; - var numMajorIndices = majorIndices.length; - var first = majorIndices[0]; - var last = majorIndices[numMajorIndices - 1]; - var i, ilen, spacing, avgMajorSpacing; - - // If there are too many major ticks to display them all - if (numMajorIndices > ticksLimit) { - skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit); - return nonSkipped(ticks); - } - - spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit); - - if (numMajorIndices > 0) { - for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) { - skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]); - } - avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null; - skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); - skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); - return nonSkipped(ticks); - } - skip(ticks, spacing); - return nonSkipped(ticks); - }, - - /** - * @private - */ - _tickSize: function() { - var me = this; - var optionTicks = me.options.ticks; - - // Calculate space needed by label in axis direction. - var rot = helpers$1.toRadians(me.labelRotation); - var cos = Math.abs(Math.cos(rot)); - var sin = Math.abs(Math.sin(rot)); - - var labelSizes = me._getLabelSizes(); - var padding = optionTicks.autoSkipPadding || 0; - var w = labelSizes ? labelSizes.widest.width + padding : 0; - var h = labelSizes ? labelSizes.highest.height + padding : 0; - - // Calculate space needed for 1 tick in axis direction. - return me.isHorizontal() - ? h * cos > w * sin ? w / cos : h / sin - : h * sin < w * cos ? h / cos : w / sin; - }, - - /** - * @private - */ - _isVisible: function() { - var me = this; - var chart = me.chart; - var display = me.options.display; - var i, ilen, meta; - - if (display !== 'auto') { - return !!display; - } - - // When 'auto', the scale is visible if at least one associated dataset is visible. - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - if (meta.xAxisID === me.id || meta.yAxisID === me.id) { - return true; - } - } - } - - return false; - }, - - /** - * @private - */ - _computeGridLineItems: function(chartArea) { - var me = this; - var chart = me.chart; - var options = me.options; - var gridLines = options.gridLines; - var position = options.position; - var offsetGridLines = gridLines.offsetGridLines; - var isHorizontal = me.isHorizontal(); - var ticks = me._ticksToDraw; - var ticksLength = ticks.length + (offsetGridLines ? 1 : 0); - - var tl = getTickMarkLength(gridLines); - var items = []; - var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; - var axisHalfWidth = axisWidth / 2; - var alignPixel = helpers$1._alignPixel; - var alignBorderValue = function(pixel) { - return alignPixel(chart, pixel, axisWidth); - }; - var borderValue, i, tick, lineValue, alignedLineValue; - var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset; - - if (position === 'top') { - borderValue = alignBorderValue(me.bottom); - ty1 = me.bottom - tl; - ty2 = borderValue - axisHalfWidth; - y1 = alignBorderValue(chartArea.top) + axisHalfWidth; - y2 = chartArea.bottom; - } else if (position === 'bottom') { - borderValue = alignBorderValue(me.top); - y1 = chartArea.top; - y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; - ty1 = borderValue + axisHalfWidth; - ty2 = me.top + tl; - } else if (position === 'left') { - borderValue = alignBorderValue(me.right); - tx1 = me.right - tl; - tx2 = borderValue - axisHalfWidth; - x1 = alignBorderValue(chartArea.left) + axisHalfWidth; - x2 = chartArea.right; - } else { - borderValue = alignBorderValue(me.left); - x1 = chartArea.left; - x2 = alignBorderValue(chartArea.right) - axisHalfWidth; - tx1 = borderValue + axisHalfWidth; - tx2 = me.left + tl; - } - - for (i = 0; i < ticksLength; ++i) { - tick = ticks[i] || {}; - - // autoskipper skipped this tick (#4635) - if (isNullOrUndef(tick.label) && i < ticks.length) { - continue; - } - - if (i === me.zeroLineIndex && options.offset === offsetGridLines) { - // Draw the first index specially - lineWidth = gridLines.zeroLineWidth; - lineColor = gridLines.zeroLineColor; - borderDash = gridLines.zeroLineBorderDash || []; - borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; - } else { - lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1); - lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)'); - borderDash = gridLines.borderDash || []; - borderDashOffset = gridLines.borderDashOffset || 0.0; - } - - lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines); - - // Skip if the pixel is out of the range - if (lineValue === undefined) { - continue; - } - - alignedLineValue = alignPixel(chart, lineValue, lineWidth); - - if (isHorizontal) { - tx1 = tx2 = x1 = x2 = alignedLineValue; - } else { - ty1 = ty2 = y1 = y2 = alignedLineValue; - } - - items.push({ - tx1: tx1, - ty1: ty1, - tx2: tx2, - ty2: ty2, - x1: x1, - y1: y1, - x2: x2, - y2: y2, - width: lineWidth, - color: lineColor, - borderDash: borderDash, - borderDashOffset: borderDashOffset, - }); - } - - items.ticksLength = ticksLength; - items.borderValue = borderValue; - - return items; - }, - - /** - * @private - */ - _computeLabelItems: function() { - var me = this; - var options = me.options; - var optionTicks = options.ticks; - var position = options.position; - var isMirrored = optionTicks.mirror; - var isHorizontal = me.isHorizontal(); - var ticks = me._ticksToDraw; - var fonts = parseTickFontOptions(optionTicks); - var tickPadding = optionTicks.padding; - var tl = getTickMarkLength(options.gridLines); - var rotation = -helpers$1.toRadians(me.labelRotation); - var items = []; - var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; - - if (position === 'top') { - y = me.bottom - tl - tickPadding; - textAlign = !rotation ? 'center' : 'left'; - } else if (position === 'bottom') { - y = me.top + tl + tickPadding; - textAlign = !rotation ? 'center' : 'right'; - } else if (position === 'left') { - x = me.right - (isMirrored ? 0 : tl) - tickPadding; - textAlign = isMirrored ? 'left' : 'right'; - } else { - x = me.left + (isMirrored ? 0 : tl) + tickPadding; - textAlign = isMirrored ? 'right' : 'left'; - } - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - tick = ticks[i]; - label = tick.label; - - // autoskipper skipped this tick (#4635) - if (isNullOrUndef(label)) { - continue; - } - - pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset; - font = tick.major ? fonts.major : fonts.minor; - lineHeight = font.lineHeight; - lineCount = isArray(label) ? label.length : 1; - - if (isHorizontal) { - x = pixel; - textOffset = position === 'top' - ? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight - : (!rotation ? 0.5 : 0) * lineHeight; - } else { - y = pixel; - textOffset = (1 - lineCount) * lineHeight / 2; - } - - items.push({ - x: x, - y: y, - rotation: rotation, - label: label, - font: font, - textOffset: textOffset, - textAlign: textAlign - }); - } - - return items; - }, - - /** - * @private - */ - _drawGrid: function(chartArea) { - var me = this; - var gridLines = me.options.gridLines; - - if (!gridLines.display) { - return; - } - - var ctx = me.ctx; - var chart = me.chart; - var alignPixel = helpers$1._alignPixel; - var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; - var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea)); - var width, color, i, ilen, item; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - width = item.width; - color = item.color; - - if (width && color) { - ctx.save(); - ctx.lineWidth = width; - ctx.strokeStyle = color; - if (ctx.setLineDash) { - ctx.setLineDash(item.borderDash); - ctx.lineDashOffset = item.borderDashOffset; - } - - ctx.beginPath(); - - if (gridLines.drawTicks) { - ctx.moveTo(item.tx1, item.ty1); - ctx.lineTo(item.tx2, item.ty2); - } - - if (gridLines.drawOnChartArea) { - ctx.moveTo(item.x1, item.y1); - ctx.lineTo(item.x2, item.y2); - } - - ctx.stroke(); - ctx.restore(); - } - } - - if (axisWidth) { - // Draw the line at the edge of the axis - var firstLineWidth = axisWidth; - var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1); - var borderValue = items.borderValue; - var x1, x2, y1, y2; - - if (me.isHorizontal()) { - x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; - x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; - y1 = y2 = borderValue; - } else { - y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; - y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; - x1 = x2 = borderValue; - } - - ctx.lineWidth = axisWidth; - ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - } - }, - - /** - * @private - */ - _drawLabels: function() { - var me = this; - var optionTicks = me.options.ticks; - - if (!optionTicks.display) { - return; - } - - var ctx = me.ctx; - var items = me._labelItems || (me._labelItems = me._computeLabelItems()); - var i, j, ilen, jlen, item, tickFont, label, y; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - tickFont = item.font; - - // Make sure we draw text in the correct color and font - ctx.save(); - ctx.translate(item.x, item.y); - ctx.rotate(item.rotation); - ctx.font = tickFont.string; - ctx.fillStyle = tickFont.color; - ctx.textBaseline = 'middle'; - ctx.textAlign = item.textAlign; - - label = item.label; - y = item.textOffset; - if (isArray(label)) { - for (j = 0, jlen = label.length; j < jlen; ++j) { - // We just make sure the multiline element is a string here.. - ctx.fillText('' + label[j], 0, y); - y += tickFont.lineHeight; - } - } else { - ctx.fillText(label, 0, y); - } - ctx.restore(); - } - }, - - /** - * @private - */ - _drawTitle: function() { - var me = this; - var ctx = me.ctx; - var options = me.options; - var scaleLabel = options.scaleLabel; - - if (!scaleLabel.display) { - return; - } - - var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor); - var scaleLabelFont = helpers$1.options._parseFont(scaleLabel); - var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); - var halfLineHeight = scaleLabelFont.lineHeight / 2; - var position = options.position; - var rotation = 0; - var scaleLabelX, scaleLabelY; - - if (me.isHorizontal()) { - scaleLabelX = me.left + me.width / 2; // midpoint of the width - scaleLabelY = position === 'bottom' - ? me.bottom - halfLineHeight - scaleLabelPadding.bottom - : me.top + halfLineHeight + scaleLabelPadding.top; - } else { - var isLeft = position === 'left'; - scaleLabelX = isLeft - ? me.left + halfLineHeight + scaleLabelPadding.top - : me.right - halfLineHeight - scaleLabelPadding.top; - scaleLabelY = me.top + me.height / 2; - rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; - } - - ctx.save(); - ctx.translate(scaleLabelX, scaleLabelY); - ctx.rotate(rotation); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = scaleLabelFontColor; // render in correct colour - ctx.font = scaleLabelFont.string; - ctx.fillText(scaleLabel.labelString, 0, 0); - ctx.restore(); - }, - - draw: function(chartArea) { - var me = this; - - if (!me._isVisible()) { - return; - } - - me._drawGrid(chartArea); - me._drawTitle(); - me._drawLabels(); - }, - - /** - * @private - */ - _layers: function() { - var me = this; - var opts = me.options; - var tz = opts.ticks && opts.ticks.z || 0; - var gz = opts.gridLines && opts.gridLines.z || 0; - - if (!me._isVisible() || tz === gz || me.draw !== me._draw) { - // backward compatibility: draw has been overridden by custom scale - return [{ - z: tz, - draw: function() { - me.draw.apply(me, arguments); - } - }]; - } - - return [{ - z: gz, - draw: function() { - me._drawGrid.apply(me, arguments); - me._drawTitle.apply(me, arguments); - } - }, { - z: tz, - draw: function() { - me._drawLabels.apply(me, arguments); - } - }]; - }, - - /** - * @private - */ - _getMatchingVisibleMetas: function(type) { - var me = this; - var isHorizontal = me.isHorizontal(); - return me.chart._getSortedVisibleDatasetMetas() - .filter(function(meta) { - return (!type || meta.type === type) - && (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id); - }); - } -}); - -Scale.prototype._draw = Scale.prototype.draw; - -var core_scale = Scale; - -var isNullOrUndef$1 = helpers$1.isNullOrUndef; - -var defaultConfig = { - position: 'bottom' -}; - -var scale_category = core_scale.extend({ - determineDataLimits: function() { - var me = this; - var labels = me._getLabels(); - var ticksOpts = me.options.ticks; - var min = ticksOpts.min; - var max = ticksOpts.max; - var minIndex = 0; - var maxIndex = labels.length - 1; - var findIndex; - - if (min !== undefined) { - // user specified min value - findIndex = labels.indexOf(min); - if (findIndex >= 0) { - minIndex = findIndex; - } - } - - if (max !== undefined) { - // user specified max value - findIndex = labels.indexOf(max); - if (findIndex >= 0) { - maxIndex = findIndex; - } - } - - me.minIndex = minIndex; - me.maxIndex = maxIndex; - me.min = labels[minIndex]; - me.max = labels[maxIndex]; - }, - - buildTicks: function() { - var me = this; - var labels = me._getLabels(); - var minIndex = me.minIndex; - var maxIndex = me.maxIndex; - - // If we are viewing some subset of labels, slice the original array - me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var chart = me.chart; - - if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { - return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); - } - - return me._getLabels()[index]; - }, - - _configure: function() { - var me = this; - var offset = me.options.offset; - var ticks = me.ticks; - - core_scale.prototype._configure.call(me); - - if (!me.isHorizontal()) { - // For backward compatibility, vertical category scale reverse is inverted. - me._reversePixels = !me._reversePixels; - } - - if (!ticks) { - return; - } - - me._startValue = me.minIndex - (offset ? 0.5 : 0); - me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1); - }, - - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: function(value, index, datasetIndex) { - var me = this; - var valueCategory, labels, idx; - - if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) { - value = me.chart.data.datasets[datasetIndex].data[index]; - } - - // If value is a data object, then index is the index in the data array, - // not the index of the scale. We need to change that. - if (!isNullOrUndef$1(value)) { - valueCategory = me.isHorizontal() ? value.x : value.y; - } - if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { - labels = me._getLabels(); - value = helpers$1.valueOrDefault(valueCategory, value); - idx = labels.indexOf(value); - index = idx !== -1 ? idx : index; - if (isNaN(index)) { - index = value; - } - } - return me.getPixelForDecimal((index - me._startValue) / me._valueRange); - }, - - getPixelForTick: function(index) { - var ticks = this.ticks; - return index < 0 || index > ticks.length - 1 - ? null - : this.getPixelForValue(ticks[index], index + this.minIndex); - }, - - getValueForPixel: function(pixel) { - var me = this; - var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange); - return Math.min(Math.max(value, 0), me.ticks.length - 1); - }, - - getBasePixel: function() { - return this.bottom; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults = defaultConfig; -scale_category._defaults = _defaults; - -var noop = helpers$1.noop; -var isNullOrUndef$2 = helpers$1.isNullOrUndef; - -/** - * Generate a set of linear ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks(generationOptions, dataRange) { - var ticks = []; - // To get a "nice" value for the tick spacing, we will use the appropriately named - // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks - // for details. - - var MIN_SPACING = 1e-14; - var stepSize = generationOptions.stepSize; - var unit = stepSize || 1; - var maxNumSpaces = generationOptions.maxTicks - 1; - var min = generationOptions.min; - var max = generationOptions.max; - var precision = generationOptions.precision; - var rmin = dataRange.min; - var rmax = dataRange.max; - var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; - var factor, niceMin, niceMax, numSpaces; - - // Beyond MIN_SPACING floating point numbers being to lose precision - // such that we can't do the math necessary to generate ticks - if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) { - return [rmin, rmax]; - } - - numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); - if (numSpaces > maxNumSpaces) { - // If the calculated num of spaces exceeds maxNumSpaces, recalculate it - spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; - } - - if (stepSize || isNullOrUndef$2(precision)) { - // If a precision is not specified, calculate factor based on spacing - factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); - } else { - // If the user specified a precision, round to that number of decimal places - factor = Math.pow(10, precision); - spacing = Math.ceil(spacing * factor) / factor; - } - - niceMin = Math.floor(rmin / spacing) * spacing; - niceMax = Math.ceil(rmax / spacing) * spacing; - - // If min, max and stepSize is set and they make an evenly spaced scale use it. - if (stepSize) { - // If very close to our whole number, use it. - if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { - niceMin = min; - } - if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { - niceMax = max; - } - } - - numSpaces = (niceMax - niceMin) / spacing; - // If very close to our rounded value, use it. - if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { - numSpaces = Math.round(numSpaces); - } else { - numSpaces = Math.ceil(numSpaces); - } - - niceMin = Math.round(niceMin * factor) / factor; - niceMax = Math.round(niceMax * factor) / factor; - ticks.push(isNullOrUndef$2(min) ? niceMin : min); - for (var j = 1; j < numSpaces; ++j) { - ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); - } - ticks.push(isNullOrUndef$2(max) ? niceMax : max); - - return ticks; -} - -var scale_linearbase = core_scale.extend({ - getRightValue: function(value) { - if (typeof value === 'string') { - return +value; - } - return core_scale.prototype.getRightValue.call(this, value); - }, - - handleTickRangeOptions: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, - // do nothing since that would make the chart weird. If the user really wants a weird chart - // axis, they can manually override it - if (tickOpts.beginAtZero) { - var minSign = helpers$1.sign(me.min); - var maxSign = helpers$1.sign(me.max); - - if (minSign < 0 && maxSign < 0) { - // move the top up to 0 - me.max = 0; - } else if (minSign > 0 && maxSign > 0) { - // move the bottom down to 0 - me.min = 0; - } - } - - var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; - var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; - - if (tickOpts.min !== undefined) { - me.min = tickOpts.min; - } else if (tickOpts.suggestedMin !== undefined) { - if (me.min === null) { - me.min = tickOpts.suggestedMin; - } else { - me.min = Math.min(me.min, tickOpts.suggestedMin); - } - } - - if (tickOpts.max !== undefined) { - me.max = tickOpts.max; - } else if (tickOpts.suggestedMax !== undefined) { - if (me.max === null) { - me.max = tickOpts.suggestedMax; - } else { - me.max = Math.max(me.max, tickOpts.suggestedMax); - } - } - - if (setMin !== setMax) { - // We set the min or the max but not both. - // So ensure that our range is good - // Inverted or 0 length range can happen when - // ticks.min is set, and no datasets are visible - if (me.min >= me.max) { - if (setMin) { - me.max = me.min + 1; - } else { - me.min = me.max - 1; - } - } - } - - if (me.min === me.max) { - me.max++; - - if (!tickOpts.beginAtZero) { - me.min--; - } - } - }, - - getTickLimit: function() { - var me = this; - var tickOpts = me.options.ticks; - var stepSize = tickOpts.stepSize; - var maxTicksLimit = tickOpts.maxTicksLimit; - var maxTicks; - - if (stepSize) { - maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; - } else { - maxTicks = me._computeTickLimit(); - maxTicksLimit = maxTicksLimit || 11; - } - - if (maxTicksLimit) { - maxTicks = Math.min(maxTicksLimit, maxTicks); - } - - return maxTicks; - }, - - _computeTickLimit: function() { - return Number.POSITIVE_INFINITY; - }, - - handleDirectionalChanges: noop, - - buildTicks: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // Figure out what the max number of ticks we can support it is based on the size of - // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 - // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on - // the graph. Make sure we always have at least 2 ticks - var maxTicks = me.getTickLimit(); - maxTicks = Math.max(2, maxTicks); - - var numericGeneratorOptions = { - maxTicks: maxTicks, - min: tickOpts.min, - max: tickOpts.max, - precision: tickOpts.precision, - stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) - }; - var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); - - me.handleDirectionalChanges(); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - ticks.reverse(); - - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - }, - - convertTicksToLabels: function() { - var me = this; - me.ticksAsNumbers = me.ticks.slice(); - me.zeroLineIndex = me.ticks.indexOf(0); - - core_scale.prototype.convertTicksToLabels.call(me); - }, - - _configure: function() { - var me = this; - var ticks = me.getTicks(); - var start = me.min; - var end = me.max; - var offset; - - core_scale.prototype._configure.call(me); - - if (me.options.offset && ticks.length) { - offset = (end - start) / Math.max(ticks.length - 1, 1) / 2; - start -= offset; - end += offset; - } - me._startValue = start; - me._endValue = end; - me._valueRange = end - start; - } -}); - -var defaultConfig$1 = { - position: 'left', - ticks: { - callback: core_ticks.formatters.linear - } -}; - -var DEFAULT_MIN = 0; -var DEFAULT_MAX = 1; - -function getOrCreateStack(stacks, stacked, meta) { - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - stacked === undefined && meta.stack === undefined ? meta.index : '', - meta.stack - ].join('.'); - - if (stacks[key] === undefined) { - stacks[key] = { - pos: [], - neg: [] - }; - } - - return stacks[key]; -} - -function stackData(scale, stacks, meta, data) { - var opts = scale.options; - var stacked = opts.stacked; - var stack = getOrCreateStack(stacks, stacked, meta); - var pos = stack.pos; - var neg = stack.neg; - var ilen = data.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = scale._parseValue(data[i]); - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { - continue; - } - - pos[i] = pos[i] || 0; - neg[i] = neg[i] || 0; - - if (opts.relativePoints) { - pos[i] = 100; - } else if (value.min < 0 || value.max < 0) { - neg[i] += value.min; - } else { - pos[i] += value.max; - } - } -} - -function updateMinMax(scale, meta, data) { - var ilen = data.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = scale._parseValue(data[i]); - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { - continue; - } - - scale.min = Math.min(scale.min, value.min); - scale.max = Math.max(scale.max, value.max); - } -} - -var scale_linear = scale_linearbase.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var datasets = chart.data.datasets; - var metasets = me._getMatchingVisibleMetas(); - var hasStacks = opts.stacked; - var stacks = {}; - var ilen = metasets.length; - var i, meta, data, values; - - me.min = Number.POSITIVE_INFINITY; - me.max = Number.NEGATIVE_INFINITY; - - if (hasStacks === undefined) { - for (i = 0; !hasStacks && i < ilen; ++i) { - meta = metasets[i]; - hasStacks = meta.stack !== undefined; - } - } - - for (i = 0; i < ilen; ++i) { - meta = metasets[i]; - data = datasets[meta.index].data; - if (hasStacks) { - stackData(me, stacks, meta, data); - } else { - updateMinMax(me, meta, data); - } - } - - helpers$1.each(stacks, function(stackValues) { - values = stackValues.pos.concat(stackValues.neg); - me.min = Math.min(me.min, helpers$1.min(values)); - me.max = Math.max(me.max, helpers$1.max(values)); - }); - - me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; - me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - me.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - var me = this; - var tickFont; - - if (me.isHorizontal()) { - return Math.ceil(me.width / 40); - } - tickFont = helpers$1.options._parseFont(me.options.ticks); - return Math.ceil(me.height / tickFont.lineHeight); - }, - - // Called after the ticks are built. We need - handleDirectionalChanges: function() { - if (!this.isHorizontal()) { - // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); - } - }, - - getLabelForIndex: function(index, datasetIndex) { - return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); - }, - - // Utils - getPixelForValue: function(value) { - var me = this; - return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange); - }, - - getValueForPixel: function(pixel) { - return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange; - }, - - getPixelForTick: function(index) { - var ticks = this.ticksAsNumbers; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index]); - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$1 = defaultConfig$1; -scale_linear._defaults = _defaults$1; - -var valueOrDefault$b = helpers$1.valueOrDefault; -var log10 = helpers$1.math.log10; - -/** - * Generate a set of logarithmic ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks$1(generationOptions, dataRange) { - var ticks = []; - - var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); - - var endExp = Math.floor(log10(dataRange.max)); - var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); - var exp, significand; - - if (tickVal === 0) { - exp = Math.floor(log10(dataRange.minNotZero)); - significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); - - ticks.push(tickVal); - tickVal = significand * Math.pow(10, exp); - } else { - exp = Math.floor(log10(tickVal)); - significand = Math.floor(tickVal / Math.pow(10, exp)); - } - var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; - - do { - ticks.push(tickVal); - - ++significand; - if (significand === 10) { - significand = 1; - ++exp; - precision = exp >= 0 ? 1 : precision; - } - - tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; - } while (exp < endExp || (exp === endExp && significand < endSignificand)); - - var lastTick = valueOrDefault$b(generationOptions.max, tickVal); - ticks.push(lastTick); - - return ticks; -} - -var defaultConfig$2 = { - position: 'left', - - // label settings - ticks: { - callback: core_ticks.formatters.logarithmic - } -}; - -// TODO(v3): change this to positiveOrDefault -function nonNegativeOrDefault(value, defaultValue) { - return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; -} - -var scale_logarithmic = core_scale.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var datasets = chart.data.datasets; - var isHorizontal = me.isHorizontal(); - function IDMatches(meta) { - return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; - } - var datasetIndex, meta, value, data, i, ilen; - - // Calculate Range - me.min = Number.POSITIVE_INFINITY; - me.max = Number.NEGATIVE_INFINITY; - me.minNotZero = Number.POSITIVE_INFINITY; - - var hasStacks = opts.stacked; - if (hasStacks === undefined) { - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && - meta.stack !== undefined) { - hasStacks = true; - break; - } - } - } - - if (opts.stacked || hasStacks) { - var valuesPerStack = {}; - - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), - meta.stack - ].join('.'); - - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - if (valuesPerStack[key] === undefined) { - valuesPerStack[key] = []; - } - - data = datasets[datasetIndex].data; - for (i = 0, ilen = data.length; i < ilen; i++) { - var values = valuesPerStack[key]; - value = me._parseValue(data[i]); - // invalid, hidden and negative values are ignored - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { - continue; - } - values[i] = values[i] || 0; - values[i] += value.max; - } - } - } - - helpers$1.each(valuesPerStack, function(valuesForType) { - if (valuesForType.length > 0) { - var minVal = helpers$1.min(valuesForType); - var maxVal = helpers$1.max(valuesForType); - me.min = Math.min(me.min, minVal); - me.max = Math.max(me.max, maxVal); - } - }); - - } else { - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - data = datasets[datasetIndex].data; - for (i = 0, ilen = data.length; i < ilen; i++) { - value = me._parseValue(data[i]); - // invalid, hidden and negative values are ignored - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { - continue; - } - - me.min = Math.min(value.min, me.min); - me.max = Math.max(value.max, me.max); - - if (value.min !== 0) { - me.minNotZero = Math.min(value.min, me.minNotZero); - } - } - } - } - } - - me.min = helpers$1.isFinite(me.min) ? me.min : null; - me.max = helpers$1.isFinite(me.max) ? me.max : null; - me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null; - - // Common base implementation to handle ticks.min, ticks.max - this.handleTickRangeOptions(); - }, - - handleTickRangeOptions: function() { - var me = this; - var tickOpts = me.options.ticks; - var DEFAULT_MIN = 1; - var DEFAULT_MAX = 10; - - me.min = nonNegativeOrDefault(tickOpts.min, me.min); - me.max = nonNegativeOrDefault(tickOpts.max, me.max); - - if (me.min === me.max) { - if (me.min !== 0 && me.min !== null) { - me.min = Math.pow(10, Math.floor(log10(me.min)) - 1); - me.max = Math.pow(10, Math.floor(log10(me.max)) + 1); - } else { - me.min = DEFAULT_MIN; - me.max = DEFAULT_MAX; - } - } - if (me.min === null) { - me.min = Math.pow(10, Math.floor(log10(me.max)) - 1); - } - if (me.max === null) { - me.max = me.min !== 0 - ? Math.pow(10, Math.floor(log10(me.min)) + 1) - : DEFAULT_MAX; - } - if (me.minNotZero === null) { - if (me.min > 0) { - me.minNotZero = me.min; - } else if (me.max < 1) { - me.minNotZero = Math.pow(10, Math.floor(log10(me.max))); - } else { - me.minNotZero = DEFAULT_MIN; - } - } - }, - - buildTicks: function() { - var me = this; - var tickOpts = me.options.ticks; - var reverse = !me.isHorizontal(); - - var generationOptions = { - min: nonNegativeOrDefault(tickOpts.min), - max: nonNegativeOrDefault(tickOpts.max) - }; - var ticks = me.ticks = generateTicks$1(generationOptions, me); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - reverse = !reverse; - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - if (reverse) { - ticks.reverse(); - } - }, - - convertTicksToLabels: function() { - this.tickValues = this.ticks.slice(); - - core_scale.prototype.convertTicksToLabels.call(this); - }, - - // Get the correct tooltip label - getLabelForIndex: function(index, datasetIndex) { - return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); - }, - - getPixelForTick: function(index) { - var ticks = this.tickValues; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index]); - }, - - /** - * Returns the value of the first tick. - * @param {number} value - The minimum not zero value. - * @return {number} The first tick value. - * @private - */ - _getFirstTickValue: function(value) { - var exp = Math.floor(log10(value)); - var significand = Math.floor(value / Math.pow(10, exp)); - - return significand * Math.pow(10, exp); - }, - - _configure: function() { - var me = this; - var start = me.min; - var offset = 0; - - core_scale.prototype._configure.call(me); - - if (start === 0) { - start = me._getFirstTickValue(me.minNotZero); - offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length; - } - - me._startValue = log10(start); - me._valueOffset = offset; - me._valueRange = (log10(me.max) - log10(start)) / (1 - offset); - }, - - getPixelForValue: function(value) { - var me = this; - var decimal = 0; - - value = +me.getRightValue(value); - - if (value > me.min && value > 0) { - decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset; - } - return me.getPixelForDecimal(decimal); - }, - - getValueForPixel: function(pixel) { - var me = this; - var decimal = me.getDecimalForPixel(pixel); - return decimal === 0 && me.min === 0 - ? 0 - : Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange); - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$2 = defaultConfig$2; -scale_logarithmic._defaults = _defaults$2; - -var valueOrDefault$c = helpers$1.valueOrDefault; -var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; -var resolve$4 = helpers$1.options.resolve; - -var defaultConfig$3 = { - display: true, - - // Boolean - Whether to animate scaling the chart from the centre - animate: true, - position: 'chartArea', - - angleLines: { - display: true, - color: 'rgba(0,0,0,0.1)', - lineWidth: 1, - borderDash: [], - borderDashOffset: 0.0 - }, - - gridLines: { - circular: false - }, - - // label settings - ticks: { - // Boolean - Show a backdrop to the scale label - showLabelBackdrop: true, - - // String - The colour of the label backdrop - backdropColor: 'rgba(255,255,255,0.75)', - - // Number - The backdrop padding above & below the label in pixels - backdropPaddingY: 2, - - // Number - The backdrop padding to the side of the label in pixels - backdropPaddingX: 2, - - callback: core_ticks.formatters.linear - }, - - pointLabels: { - // Boolean - if true, show point labels - display: true, - - // Number - Point label font size in pixels - fontSize: 10, - - // Function - Used to convert point labels - callback: function(label) { - return label; - } - } -}; - -function getTickBackdropHeight(opts) { - var tickOpts = opts.ticks; - - if (tickOpts.display && opts.display) { - return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; - } - return 0; -} - -function measureLabelSize(ctx, lineHeight, label) { - if (helpers$1.isArray(label)) { - return { - w: helpers$1.longestText(ctx, ctx.font, label), - h: label.length * lineHeight - }; - } - - return { - w: ctx.measureText(label).width, - h: lineHeight - }; -} - -function determineLimits(angle, pos, size, min, max) { - if (angle === min || angle === max) { - return { - start: pos - (size / 2), - end: pos + (size / 2) - }; - } else if (angle < min || angle > max) { - return { - start: pos - size, - end: pos - }; - } - - return { - start: pos, - end: pos + size - }; -} - -/** - * Helper function to fit a radial linear scale with point labels - */ -function fitWithPointLabels(scale) { - - // Right, this is really confusing and there is a lot of maths going on here - // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - // - // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - // - // Solution: - // - // We assume the radius of the polygon is half the size of the canvas at first - // at each index we check if the text overlaps. - // - // Where it does, we store that angle and that index. - // - // After finding the largest index and angle we calculate how much we need to remove - // from the shape radius to move the point inwards by that x. - // - // We average the left and right distances to get the maximum shape radius that can fit in the box - // along with labels. - // - // Once we have that, we can find the centre point for the chart, by taking the x text protrusion - // on each side, removing that from the size, halving it and adding the left x protrusion width. - // - // This will mean we have a shape fitted to the canvas, as large as it can be with the labels - // and position it in the most space efficient manner - // - // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - - var plFont = helpers$1.options._parseFont(scale.options.pointLabels); - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var furthestLimits = { - l: 0, - r: scale.width, - t: 0, - b: scale.height - scale.paddingTop - }; - var furthestAngles = {}; - var i, textSize, pointPosition; - - scale.ctx.font = plFont.string; - scale._pointLabelSizes = []; - - var valueCount = scale.chart.data.labels.length; - for (i = 0; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); - textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]); - scale._pointLabelSizes[i] = textSize; - - // Add quarter circle to make degree 0 mean top of circle - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians) % 360; - var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); - var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); - - if (hLimits.start < furthestLimits.l) { - furthestLimits.l = hLimits.start; - furthestAngles.l = angleRadians; - } - - if (hLimits.end > furthestLimits.r) { - furthestLimits.r = hLimits.end; - furthestAngles.r = angleRadians; - } - - if (vLimits.start < furthestLimits.t) { - furthestLimits.t = vLimits.start; - furthestAngles.t = angleRadians; - } - - if (vLimits.end > furthestLimits.b) { - furthestLimits.b = vLimits.end; - furthestAngles.b = angleRadians; - } - } - - scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); -} - -function getTextAlignForAngle(angle) { - if (angle === 0 || angle === 180) { - return 'center'; - } else if (angle < 180) { - return 'left'; - } - - return 'right'; -} - -function fillText(ctx, text, position, lineHeight) { - var y = position.y + lineHeight / 2; - var i, ilen; - - if (helpers$1.isArray(text)) { - for (i = 0, ilen = text.length; i < ilen; ++i) { - ctx.fillText(text[i], position.x, y); - y += lineHeight; - } - } else { - ctx.fillText(text, position.x, y); - } -} - -function adjustPointPositionForLabelHeight(angle, textSize, position) { - if (angle === 90 || angle === 270) { - position.y -= (textSize.h / 2); - } else if (angle > 270 || angle < 90) { - position.y -= textSize.h; - } -} - -function drawPointLabels(scale) { - var ctx = scale.ctx; - var opts = scale.options; - var pointLabelOpts = opts.pointLabels; - var tickBackdropHeight = getTickBackdropHeight(opts); - var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); - var plFont = helpers$1.options._parseFont(pointLabelOpts); - - ctx.save(); - - ctx.font = plFont.string; - ctx.textBaseline = 'middle'; - - for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) { - // Extra pixels out for some label spacing - var extra = (i === 0 ? tickBackdropHeight / 2 : 0); - var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); - - // Keep this in loop since we may support array properties here - var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); - ctx.fillStyle = pointLabelFontColor; - - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians); - ctx.textAlign = getTextAlignForAngle(angle); - adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); - fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight); - } - ctx.restore(); -} - -function drawRadiusLine(scale, gridLineOpts, radius, index) { - var ctx = scale.ctx; - var circular = gridLineOpts.circular; - var valueCount = scale.chart.data.labels.length; - var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); - var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); - var pointPosition; - - if ((!circular && !valueCount) || !lineColor || !lineWidth) { - return; - } - - ctx.save(); - ctx.strokeStyle = lineColor; - ctx.lineWidth = lineWidth; - if (ctx.setLineDash) { - ctx.setLineDash(gridLineOpts.borderDash || []); - ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; - } - - ctx.beginPath(); - if (circular) { - // Draw circular arcs between the points - ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); - } else { - // Draw straight lines connecting each index - pointPosition = scale.getPointPosition(0, radius); - ctx.moveTo(pointPosition.x, pointPosition.y); - - for (var i = 1; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, radius); - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } - ctx.closePath(); - ctx.stroke(); - ctx.restore(); -} - -function numberOrZero(param) { - return helpers$1.isNumber(param) ? param : 0; -} - -var scale_radialLinear = scale_linearbase.extend({ - setDimensions: function() { - var me = this; - - // Set the unconstrained dimension before label rotation - me.width = me.maxWidth; - me.height = me.maxHeight; - me.paddingTop = getTickBackdropHeight(me.options) / 2; - me.xCenter = Math.floor(me.width / 2); - me.yCenter = Math.floor((me.height - me.paddingTop) / 2); - me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var min = Number.POSITIVE_INFINITY; - var max = Number.NEGATIVE_INFINITY; - - helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { - if (chart.isDatasetVisible(datasetIndex)) { - var meta = chart.getDatasetMeta(datasetIndex); - - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { - return; - } - - min = Math.min(value, min); - max = Math.max(value, max); - }); - } - }); - - me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); - me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - me.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); - }, - - convertTicksToLabels: function() { - var me = this; - - scale_linearbase.prototype.convertTicksToLabels.call(me); - - // Point labels - me.pointLabels = me.chart.data.labels.map(function() { - var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me); - return label || label === 0 ? label : ''; - }); - }, - - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - - fit: function() { - var me = this; - var opts = me.options; - - if (opts.display && opts.pointLabels.display) { - fitWithPointLabels(me); - } else { - me.setCenterPoint(0, 0, 0, 0); - } - }, - - /** - * Set radius reductions and determine new radius and center point - * @private - */ - setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { - var me = this; - var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); - var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); - var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); - var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); - - radiusReductionLeft = numberOrZero(radiusReductionLeft); - radiusReductionRight = numberOrZero(radiusReductionRight); - radiusReductionTop = numberOrZero(radiusReductionTop); - radiusReductionBottom = numberOrZero(radiusReductionBottom); - - me.drawingArea = Math.min( - Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), - Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); - me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); - }, - - setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { - var me = this; - var maxRight = me.width - rightMovement - me.drawingArea; - var maxLeft = leftMovement + me.drawingArea; - var maxTop = topMovement + me.drawingArea; - var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; - - me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); - me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); - }, - - getIndexAngle: function(index) { - var chart = this.chart; - var angleMultiplier = 360 / chart.data.labels.length; - var options = chart.options || {}; - var startAngle = options.startAngle || 0; - - // Start from the top instead of right, so remove a quarter of the circle - var angle = (index * angleMultiplier + startAngle) % 360; - - return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360; - }, - - getDistanceFromCenterForValue: function(value) { - var me = this; - - if (helpers$1.isNullOrUndef(value)) { - return NaN; - } - - // Take into account half font size + the yPadding of the top value - var scalingFactor = me.drawingArea / (me.max - me.min); - if (me.options.ticks.reverse) { - return (me.max - value) * scalingFactor; - } - return (value - me.min) * scalingFactor; - }, - - getPointPosition: function(index, distanceFromCenter) { - var me = this; - var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); - return { - x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, - y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter - }; - }, - - getPointPositionForValue: function(index, value) { - return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); - }, - - getBasePosition: function(index) { - var me = this; - var min = me.min; - var max = me.max; - - return me.getPointPositionForValue(index || 0, - me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0); - }, - - /** - * @private - */ - _drawGrid: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - var gridLineOpts = opts.gridLines; - var angleLineOpts = opts.angleLines; - var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth); - var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color); - var i, offset, position; - - if (opts.pointLabels.display) { - drawPointLabels(me); - } - - if (gridLineOpts.display) { - helpers$1.each(me.ticks, function(label, index) { - if (index !== 0) { - offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); - drawRadiusLine(me, gridLineOpts, offset, index); - } - }); - } - - if (angleLineOpts.display && lineWidth && lineColor) { - ctx.save(); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = lineColor; - if (ctx.setLineDash) { - ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); - ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); - } - - for (i = me.chart.data.labels.length - 1; i >= 0; i--) { - offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max); - position = me.getPointPosition(i, offset); - ctx.beginPath(); - ctx.moveTo(me.xCenter, me.yCenter); - ctx.lineTo(position.x, position.y); - ctx.stroke(); - } - - ctx.restore(); - } - }, - - /** - * @private - */ - _drawLabels: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - var tickOpts = opts.ticks; - - if (!tickOpts.display) { - return; - } - - var startAngle = me.getIndexAngle(0); - var tickFont = helpers$1.options._parseFont(tickOpts); - var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor); - var offset, width; - - ctx.save(); - ctx.font = tickFont.string; - ctx.translate(me.xCenter, me.yCenter); - ctx.rotate(startAngle); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - helpers$1.each(me.ticks, function(label, index) { - if (index === 0 && !tickOpts.reverse) { - return; - } - - offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); - - if (tickOpts.showLabelBackdrop) { - width = ctx.measureText(label).width; - ctx.fillStyle = tickOpts.backdropColor; - - ctx.fillRect( - -width / 2 - tickOpts.backdropPaddingX, - -offset - tickFont.size / 2 - tickOpts.backdropPaddingY, - width + tickOpts.backdropPaddingX * 2, - tickFont.size + tickOpts.backdropPaddingY * 2 - ); - } - - ctx.fillStyle = tickFontColor; - ctx.fillText(label, 0, -offset); - }); - - ctx.restore(); - }, - - /** - * @private - */ - _drawTitle: helpers$1.noop -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$3 = defaultConfig$3; -scale_radialLinear._defaults = _defaults$3; - -var deprecated$1 = helpers$1._deprecated; -var resolve$5 = helpers$1.options.resolve; -var valueOrDefault$d = helpers$1.valueOrDefault; - -// Integer constants are from the ES6 spec. -var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; -var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; - -var INTERVALS = { - millisecond: { - common: true, - size: 1, - steps: 1000 - }, - second: { - common: true, - size: 1000, - steps: 60 - }, - minute: { - common: true, - size: 60000, - steps: 60 - }, - hour: { - common: true, - size: 3600000, - steps: 24 - }, - day: { - common: true, - size: 86400000, - steps: 30 - }, - week: { - common: false, - size: 604800000, - steps: 4 - }, - month: { - common: true, - size: 2.628e9, - steps: 12 - }, - quarter: { - common: false, - size: 7.884e9, - steps: 4 - }, - year: { - common: true, - size: 3.154e10 - } -}; - -var UNITS = Object.keys(INTERVALS); - -function sorter(a, b) { - return a - b; -} - -function arrayUnique(items) { - var hash = {}; - var out = []; - var i, ilen, item; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - if (!hash[item]) { - hash[item] = true; - out.push(item); - } - } - - return out; -} - -function getMin(options) { - return helpers$1.valueOrDefault(options.time.min, options.ticks.min); -} - -function getMax(options) { - return helpers$1.valueOrDefault(options.time.max, options.ticks.max); -} - -/** - * Returns an array of {time, pos} objects used to interpolate a specific `time` or position - * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is - * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other - * extremity (left + width or top + height). Note that it would be more optimized to directly - * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need - * to create the lookup table. The table ALWAYS contains at least two items: min and max. - * - * @param {number[]} timestamps - timestamps sorted from lowest to highest. - * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min - * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. - * If 'series', timestamps will be positioned at the same distance from each other. In this - * case, only timestamps that break the time linearity are registered, meaning that in the - * best case, all timestamps are linear, the table contains only min and max. - */ -function buildLookupTable(timestamps, min, max, distribution) { - if (distribution === 'linear' || !timestamps.length) { - return [ - {time: min, pos: 0}, - {time: max, pos: 1} - ]; - } - - var table = []; - var items = [min]; - var i, ilen, prev, curr, next; - - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - curr = timestamps[i]; - if (curr > min && curr < max) { - items.push(curr); - } - } - - items.push(max); - - for (i = 0, ilen = items.length; i < ilen; ++i) { - next = items[i + 1]; - prev = items[i - 1]; - curr = items[i]; - - // only add points that breaks the scale linearity - if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { - table.push({time: curr, pos: i / (ilen - 1)}); - } - } - - return table; -} - -// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ -function lookup(table, key, value) { - var lo = 0; - var hi = table.length - 1; - var mid, i0, i1; - - while (lo >= 0 && lo <= hi) { - mid = (lo + hi) >> 1; - i0 = table[mid - 1] || null; - i1 = table[mid]; - - if (!i0) { - // given value is outside table (before first item) - return {lo: null, hi: i1}; - } else if (i1[key] < value) { - lo = mid + 1; - } else if (i0[key] > value) { - hi = mid - 1; - } else { - return {lo: i0, hi: i1}; - } - } - - // given value is outside table (after last item) - return {lo: i1, hi: null}; -} - -/** - * Linearly interpolates the given source `value` using the table items `skey` values and - * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') - * returns the position for a timestamp equal to 42. If value is out of bounds, values at - * index [0, 1] or [n - 1, n] are used for the interpolation. - */ -function interpolate$1(table, skey, sval, tkey) { - var range = lookup(table, skey, sval); - - // Note: the lookup table ALWAYS contains at least 2 items (min and max) - var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; - var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; - - var span = next[skey] - prev[skey]; - var ratio = span ? (sval - prev[skey]) / span : 0; - var offset = (next[tkey] - prev[tkey]) * ratio; - - return prev[tkey] + offset; -} - -function toTimestamp(scale, input) { - var adapter = scale._adapter; - var options = scale.options.time; - var parser = options.parser; - var format = parser || options.format; - var value = input; - - if (typeof parser === 'function') { - value = parser(value); - } - - // Only parse if its not a timestamp already - if (!helpers$1.isFinite(value)) { - value = typeof format === 'string' - ? adapter.parse(value, format) - : adapter.parse(value); - } - - if (value !== null) { - return +value; - } - - // Labels are in an incompatible format and no `parser` has been provided. - // The user might still use the deprecated `format` option for parsing. - if (!parser && typeof format === 'function') { - value = format(input); - - // `format` could return something else than a timestamp, if so, parse it - if (!helpers$1.isFinite(value)) { - value = adapter.parse(value); - } - } - - return value; -} - -function parse(scale, input) { - if (helpers$1.isNullOrUndef(input)) { - return null; - } - - var options = scale.options.time; - var value = toTimestamp(scale, scale.getRightValue(input)); - if (value === null) { - return value; - } - - if (options.round) { - value = +scale._adapter.startOf(value, options.round); - } - - return value; -} - -/** - * Figures out what unit results in an appropriate number of auto-generated ticks - */ -function determineUnitForAutoTicks(minUnit, min, max, capacity) { - var ilen = UNITS.length; - var i, interval, factor; - - for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { - interval = INTERVALS[UNITS[i]]; - factor = interval.steps ? interval.steps : MAX_INTEGER; - - if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { - return UNITS[i]; - } - } - - return UNITS[ilen - 1]; -} - -/** - * Figures out what unit to format a set of ticks with - */ -function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { - var i, unit; - - for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { - unit = UNITS[i]; - if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { - return unit; - } - } - - return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; -} - -function determineMajorUnit(unit) { - for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { - if (INTERVALS[UNITS[i]].common) { - return UNITS[i]; - } - } -} - -/** - * Generates a maximum of `capacity` timestamps between min and max, rounded to the - * `minor` unit using the given scale time `options`. - * Important: this method can return ticks outside the min and max range, it's the - * responsibility of the calling code to clamp values if needed. - */ -function generate(scale, min, max, capacity) { - var adapter = scale._adapter; - var options = scale.options; - var timeOpts = options.time; - var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); - var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]); - var weekday = minor === 'week' ? timeOpts.isoWeekday : false; - var first = min; - var ticks = []; - var time; - - // For 'week' unit, handle the first day of week option - if (weekday) { - first = +adapter.startOf(first, 'isoWeek', weekday); - } - - // Align first ticks on unit - first = +adapter.startOf(first, weekday ? 'day' : minor); - - // Prevent browser from freezing in case user options request millions of milliseconds - if (adapter.diff(max, min, minor) > 100000 * stepSize) { - throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor; - } - - for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) { - ticks.push(time); - } - - if (time === max || options.bounds === 'ticks') { - ticks.push(time); - } - - return ticks; -} - -/** - * Returns the start and end offsets from edges in the form of {start, end} - * where each value is a relative width to the scale and ranges between 0 and 1. - * They add extra margins on the both sides by scaling down the original scale. - * Offsets are added when the `offset` option is true. - */ -function computeOffsets(table, ticks, min, max, options) { - var start = 0; - var end = 0; - var first, last; - - if (options.offset && ticks.length) { - first = interpolate$1(table, 'time', ticks[0], 'pos'); - if (ticks.length === 1) { - start = 1 - first; - } else { - start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; - } - last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); - if (ticks.length === 1) { - end = last; - } else { - end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; - } - } - - return {start: start, end: end, factor: 1 / (start + 1 + end)}; -} - -function setMajorTicks(scale, ticks, map, majorUnit) { - var adapter = scale._adapter; - var first = +adapter.startOf(ticks[0].value, majorUnit); - var last = ticks[ticks.length - 1].value; - var major, index; - - for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { - index = map[major]; - if (index >= 0) { - ticks[index].major = true; - } - } - return ticks; -} - -function ticksFromTimestamps(scale, values, majorUnit) { - var ticks = []; - var map = {}; - var ilen = values.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = values[i]; - map[value] = i; - - ticks.push({ - value: value, - major: false - }); - } - - // We set the major ticks separately from the above loop because calling startOf for every tick - // is expensive when there is a large number of ticks - return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); -} - -var defaultConfig$4 = { - position: 'bottom', - - /** - * Data distribution along the scale: - * - 'linear': data are spread according to their time (distances can vary), - * - 'series': data are spread at the same distance from each other. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - distribution: 'linear', - - /** - * Scale boundary strategy (bypassed by min/max time options) - * - `data`: make sure data are fully visible, ticks outside are removed - * - `ticks`: make sure ticks are fully visible, data outside are truncated - * @see https://github.com/chartjs/Chart.js/pull/4556 - * @since 2.7.0 - */ - bounds: 'data', - - adapters: {}, - time: { - parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment - unit: false, // false == automatic or override with week, month, year, etc. - round: false, // none, or override with week, month, year, etc. - displayFormat: false, // DEPRECATED - isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ - minUnit: 'millisecond', - displayFormats: {} - }, - ticks: { - autoSkip: false, - - /** - * Ticks generation input values: - * - 'auto': generates "optimal" ticks based on scale size and time options. - * - 'data': generates ticks from data (including labels from data {t|x|y} objects). - * - 'labels': generates ticks from user given `data.labels` values ONLY. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - source: 'auto', - - major: { - enabled: false - } - } -}; - -var scale_time = core_scale.extend({ - initialize: function() { - this.mergeTicksOptions(); - core_scale.prototype.initialize.call(this); - }, - - update: function() { - var me = this; - var options = me.options; - var time = options.time || (options.time = {}); - var adapter = me._adapter = new core_adapters._date(options.adapters.date); - - // DEPRECATIONS: output a message only one time per update - deprecated$1('time scale', time.format, 'time.format', 'time.parser'); - deprecated$1('time scale', time.min, 'time.min', 'ticks.min'); - deprecated$1('time scale', time.max, 'time.max', 'ticks.max'); - - // Backward compatibility: before introducing adapter, `displayFormats` was - // supposed to contain *all* unit/string pairs but this can't be resolved - // when loading the scale (adapters are loaded afterward), so let's populate - // missing formats on update - helpers$1.mergeIf(time.displayFormats, adapter.formats()); - - return core_scale.prototype.update.apply(me, arguments); - }, - - /** - * Allows data to be referenced via 't' attribute - */ - getRightValue: function(rawValue) { - if (rawValue && rawValue.t !== undefined) { - rawValue = rawValue.t; - } - return core_scale.prototype.getRightValue.call(this, rawValue); - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var adapter = me._adapter; - var options = me.options; - var unit = options.time.unit || 'day'; - var min = MAX_INTEGER; - var max = MIN_INTEGER; - var timestamps = []; - var datasets = []; - var labels = []; - var i, j, ilen, jlen, data, timestamp, labelsAdded; - var dataLabels = me._getLabels(); - - for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { - labels.push(parse(me, dataLabels[i])); - } - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - data = chart.data.datasets[i].data; - - // Let's consider that all data have the same format. - if (helpers$1.isObject(data[0])) { - datasets[i] = []; - - for (j = 0, jlen = data.length; j < jlen; ++j) { - timestamp = parse(me, data[j]); - timestamps.push(timestamp); - datasets[i][j] = timestamp; - } - } else { - datasets[i] = labels.slice(0); - if (!labelsAdded) { - timestamps = timestamps.concat(labels); - labelsAdded = true; - } - } - } else { - datasets[i] = []; - } - } - - if (labels.length) { - min = Math.min(min, labels[0]); - max = Math.max(max, labels[labels.length - 1]); - } - - if (timestamps.length) { - timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter); - min = Math.min(min, timestamps[0]); - max = Math.max(max, timestamps[timestamps.length - 1]); - } - - min = parse(me, getMin(options)) || min; - max = parse(me, getMax(options)) || max; - - // In case there is no valid min/max, set limits based on unit time option - min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; - max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; - - // Make sure that max is strictly higher than min (required by the lookup table) - me.min = Math.min(min, max); - me.max = Math.max(min + 1, max); - - // PRIVATE - me._table = []; - me._timestamps = { - data: timestamps, - datasets: datasets, - labels: labels - }; - }, - - buildTicks: function() { - var me = this; - var min = me.min; - var max = me.max; - var options = me.options; - var tickOpts = options.ticks; - var timeOpts = options.time; - var timestamps = me._timestamps; - var ticks = []; - var capacity = me.getLabelCapacity(min); - var source = tickOpts.source; - var distribution = options.distribution; - var i, ilen, timestamp; - - if (source === 'data' || (source === 'auto' && distribution === 'series')) { - timestamps = timestamps.data; - } else if (source === 'labels') { - timestamps = timestamps.labels; - } else { - timestamps = generate(me, min, max, capacity); - } - - if (options.bounds === 'ticks' && timestamps.length) { - min = timestamps[0]; - max = timestamps[timestamps.length - 1]; - } - - // Enforce limits with user min/max options - min = parse(me, getMin(options)) || min; - max = parse(me, getMax(options)) || max; - - // Remove ticks outside the min/max range - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - timestamp = timestamps[i]; - if (timestamp >= min && timestamp <= max) { - ticks.push(timestamp); - } - } - - me.min = min; - me.max = max; - - // PRIVATE - // determineUnitForFormatting relies on the number of ticks so we don't use it when - // autoSkip is enabled because we don't yet know what the final number of ticks will be - me._unit = timeOpts.unit || (tickOpts.autoSkip - ? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity) - : determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max)); - me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined - : determineMajorUnit(me._unit); - me._table = buildLookupTable(me._timestamps.data, min, max, distribution); - me._offsets = computeOffsets(me._table, ticks, min, max, options); - - if (tickOpts.reverse) { - ticks.reverse(); - } - - return ticksFromTimestamps(me, ticks, me._majorUnit); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var adapter = me._adapter; - var data = me.chart.data; - var timeOpts = me.options.time; - var label = data.labels && index < data.labels.length ? data.labels[index] : ''; - var value = data.datasets[datasetIndex].data[index]; - - if (helpers$1.isObject(value)) { - label = me.getRightValue(value); - } - if (timeOpts.tooltipFormat) { - return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); - } - if (typeof label === 'string') { - return label; - } - return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); - }, - - /** - * Function to format an individual tick mark - * @private - */ - tickFormatFunction: function(time, index, ticks, format) { - var me = this; - var adapter = me._adapter; - var options = me.options; - var formats = options.time.displayFormats; - var minorFormat = formats[me._unit]; - var majorUnit = me._majorUnit; - var majorFormat = formats[majorUnit]; - var tick = ticks[index]; - var tickOpts = options.ticks; - var major = majorUnit && majorFormat && tick && tick.major; - var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); - var nestedTickOpts = major ? tickOpts.major : tickOpts.minor; - var formatter = resolve$5([ - nestedTickOpts.callback, - nestedTickOpts.userCallback, - tickOpts.callback, - tickOpts.userCallback - ]); - - return formatter ? formatter(label, index, ticks) : label; - }, - - convertTicksToLabels: function(ticks) { - var labels = []; - var i, ilen; - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); - } - - return labels; - }, - - /** - * @private - */ - getPixelForOffset: function(time) { - var me = this; - var offsets = me._offsets; - var pos = interpolate$1(me._table, 'time', time, 'pos'); - return me.getPixelForDecimal((offsets.start + pos) * offsets.factor); - }, - - getPixelForValue: function(value, index, datasetIndex) { - var me = this; - var time = null; - - if (index !== undefined && datasetIndex !== undefined) { - time = me._timestamps.datasets[datasetIndex][index]; - } - - if (time === null) { - time = parse(me, value); - } - - if (time !== null) { - return me.getPixelForOffset(time); - } - }, - - getPixelForTick: function(index) { - var ticks = this.getTicks(); - return index >= 0 && index < ticks.length ? - this.getPixelForOffset(ticks[index].value) : - null; - }, - - getValueForPixel: function(pixel) { - var me = this; - var offsets = me._offsets; - var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end; - var time = interpolate$1(me._table, 'pos', pos, 'time'); - - // DEPRECATION, we should return time directly - return me._adapter._create(time); - }, - - /** - * @private - */ - _getLabelSize: function(label) { - var me = this; - var ticksOpts = me.options.ticks; - var tickLabelWidth = me.ctx.measureText(label).width; - var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); - var cosRotation = Math.cos(angle); - var sinRotation = Math.sin(angle); - var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize); - - return { - w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation), - h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation) - }; - }, - - /** - * Crude approximation of what the label width might be - * @private - */ - getLabelWidth: function(label) { - return this._getLabelSize(label).w; - }, - - /** - * @private - */ - getLabelCapacity: function(exampleTime) { - var me = this; - var timeOpts = me.options.time; - var displayFormats = timeOpts.displayFormats; - - // pick the longest format (milliseconds) for guestimation - var format = displayFormats[timeOpts.unit] || displayFormats.millisecond; - var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format); - var size = me._getLabelSize(exampleLabel); - var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h); - - if (me.options.offset) { - capacity--; - } - - return capacity > 0 ? capacity : 1; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$4 = defaultConfig$4; -scale_time._defaults = _defaults$4; - -var scales = { - category: scale_category, - linear: scale_linear, - logarithmic: scale_logarithmic, - radialLinear: scale_radialLinear, - time: scale_time -}; - -var moment = createCommonjsModule(function (module, exports) { -(function (global, factory) { - module.exports = factory() ; -}(commonjsGlobal, (function () { - var hookCallback; - - function hooks () { - return hookCallback.apply(null, arguments); - } - - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback (callback) { - hookCallback = callback; - } - - function isArray(input) { - return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; - } - - function isObject(input) { - // IE8 will treat undefined and null as object if it wasn't for - // input != null - return input != null && Object.prototype.toString.call(input) === '[object Object]'; - } - - function isObjectEmpty(obj) { - if (Object.getOwnPropertyNames) { - return (Object.getOwnPropertyNames(obj).length === 0); - } else { - var k; - for (k in obj) { - if (obj.hasOwnProperty(k)) { - return false; - } - } - return true; - } - } - - function isUndefined(input) { - return input === void 0; - } - - function isNumber(input) { - return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; - } - - function isDate(input) { - return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; - } - - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } - - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; - } - - function createUTC (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } - - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso : false, - parsedDateParts : [], - meridiem : null, - rfc2822 : false, - weekdayMismatch : false - }; - } - - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - - var some; - if (Array.prototype.some) { - some = Array.prototype.some; - } else { - some = function (fun) { - var t = Object(this); - var len = t.length >>> 0; - - for (var i = 0; i < len; i++) { - if (i in t && fun.call(this, t[i], i, t)) { - return true; - } - } - - return false; - }; - } - - function isValid(m) { - if (m._isValid == null) { - var flags = getParsingFlags(m); - var parsedParts = some.call(flags.parsedDateParts, function (i) { - return i != null; - }); - var isNowValid = !isNaN(m._d.getTime()) && - flags.overflow < 0 && - !flags.empty && - !flags.invalidMonth && - !flags.invalidWeekday && - !flags.weekdayMismatch && - !flags.nullInput && - !flags.invalidFormat && - !flags.userInvalidated && - (!flags.meridiem || (flags.meridiem && parsedParts)); - - if (m._strict) { - isNowValid = isNowValid && - flags.charsLeftOver === 0 && - flags.unusedTokens.length === 0 && - flags.bigHour === undefined; - } - - if (Object.isFrozen == null || !Object.isFrozen(m)) { - m._isValid = isNowValid; - } - else { - return isNowValid; - } - } - return m._isValid; - } - - function createInvalid (flags) { - var m = createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); - } - else { - getParsingFlags(m).userInvalidated = true; - } - - return m; - } - - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - var momentProperties = hooks.momentProperties = []; - - function copyConfig(to, from) { - var i, prop, val; - - if (!isUndefined(from._isAMomentObject)) { - to._isAMomentObject = from._isAMomentObject; - } - if (!isUndefined(from._i)) { - to._i = from._i; - } - if (!isUndefined(from._f)) { - to._f = from._f; - } - if (!isUndefined(from._l)) { - to._l = from._l; - } - if (!isUndefined(from._strict)) { - to._strict = from._strict; - } - if (!isUndefined(from._tzm)) { - to._tzm = from._tzm; - } - if (!isUndefined(from._isUTC)) { - to._isUTC = from._isUTC; - } - if (!isUndefined(from._offset)) { - to._offset = from._offset; - } - if (!isUndefined(from._pf)) { - to._pf = getParsingFlags(from); - } - if (!isUndefined(from._locale)) { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i = 0; i < momentProperties.length; i++) { - prop = momentProperties[i]; - val = from[prop]; - if (!isUndefined(val)) { - to[prop] = val; - } - } - } - - return to; - } - - var updateInProgress = false; - - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - if (!this.isValid()) { - this._d = new Date(NaN); - } - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - hooks.updateOffset(this); - updateInProgress = false; - } - } - - function isMoment (obj) { - return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); - } - - function absFloor (number) { - if (number < 0) { - // -0 -> 0 - return Math.ceil(number) || 0; - } else { - return Math.floor(number); - } - } - - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; - - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - - return value; - } - - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } - - function warn(msg) { - if (hooks.suppressDeprecationWarnings === false && - (typeof console !== 'undefined') && console.warn) { - console.warn('Deprecation warning: ' + msg); - } - } - - function deprecate(msg, fn) { - var firstTime = true; - - return extend(function () { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(null, msg); - } - if (firstTime) { - var args = []; - var arg; - for (var i = 0; i < arguments.length; i++) { - arg = ''; - if (typeof arguments[i] === 'object') { - arg += '\n[' + i + '] '; - for (var key in arguments[0]) { - arg += key + ': ' + arguments[0][key] + ', '; - } - arg = arg.slice(0, -2); // Remove trailing comma and space - } else { - arg = arguments[i]; - } - args.push(arg); - } - warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - - var deprecations = {}; - - function deprecateSimple(name, msg) { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(name, msg); - } - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } - - hooks.suppressDeprecationWarnings = false; - hooks.deprecationHandler = null; - - function isFunction(input) { - return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; - } - - function set (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (isFunction(prop)) { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - this._config = config; - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. - // TODO: Remove "ordinalParse" fallback in next major release. - this._dayOfMonthOrdinalParseLenient = new RegExp( - (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + - '|' + (/\d{1,2}/).source); - } - - function mergeConfigs(parentConfig, childConfig) { - var res = extend({}, parentConfig), prop; - for (prop in childConfig) { - if (hasOwnProp(childConfig, prop)) { - if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { - res[prop] = {}; - extend(res[prop], parentConfig[prop]); - extend(res[prop], childConfig[prop]); - } else if (childConfig[prop] != null) { - res[prop] = childConfig[prop]; - } else { - delete res[prop]; - } - } - } - for (prop in parentConfig) { - if (hasOwnProp(parentConfig, prop) && - !hasOwnProp(childConfig, prop) && - isObject(parentConfig[prop])) { - // make sure changes to properties don't modify parent config - res[prop] = extend({}, res[prop]); - } - } - return res; - } - - function Locale(config) { - if (config != null) { - this.set(config); - } - } - - var keys; - - if (Object.keys) { - keys = Object.keys; - } else { - keys = function (obj) { - var i, res = []; - for (i in obj) { - if (hasOwnProp(obj, i)) { - res.push(i); - } - } - return res; - }; - } - - var defaultCalendar = { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }; - - function calendar (key, mom, now) { - var output = this._calendar[key] || this._calendar['sameElse']; - return isFunction(output) ? output.call(mom, now) : output; - } - - var defaultLongDateFormat = { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }; - - function longDateFormat (key) { - var format = this._longDateFormat[key], - formatUpper = this._longDateFormat[key.toUpperCase()]; - - if (format || !formatUpper) { - return format; - } - - this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - - return this._longDateFormat[key]; - } - - var defaultInvalidDate = 'Invalid date'; - - function invalidDate () { - return this._invalidDate; - } - - var defaultOrdinal = '%d'; - var defaultDayOfMonthOrdinalParse = /\d{1,2}/; - - function ordinal (number) { - return this._ordinal.replace('%d', number); - } - - var defaultRelativeTime = { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }; - - function relativeTime (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (isFunction(output)) ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - } - - function pastFuture (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return isFunction(format) ? format(output) : format.replace(/%s/i, output); - } - - var aliases = {}; - - function addUnitAlias (unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } - - function normalizeUnits(units) { - return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; - } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; - } - - var priorities = {}; - - function addUnitPriority(unit, priority) { - priorities[unit] = priority; - } - - function getPrioritizedUnits(unitsObj) { - var units = []; - for (var u in unitsObj) { - units.push({unit: u, priority: priorities[u]}); - } - units.sort(function (a, b) { - return a.priority - b.priority; - }); - return units; - } - - function zeroFill(number, targetLength, forceSign) { - var absNumber = '' + Math.abs(number), - zerosToFill = targetLength - absNumber.length, - sign = number >= 0; - return (sign ? (forceSign ? '+' : '') : '-') + - Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; - } - - var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; - - var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; - - var formatFunctions = {}; - - var formatTokenFunctions = {}; - - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken (token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal(func.apply(this, arguments), token); - }; - } - } - - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } - - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - - return function (mom) { - var output = '', i; - for (i = 0; i < length; i++) { - output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - - format = expandFormat(format, m.localeData()); - formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); - - return formatFunctions[format](m); - } - - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - - return format; - } - - var match1 = /\d/; // 0 - 9 - var match2 = /\d\d/; // 00 - 99 - var match3 = /\d{3}/; // 000 - 999 - var match4 = /\d{4}/; // 0000 - 9999 - var match6 = /[+-]?\d{6}/; // -999999 - 999999 - var match1to2 = /\d\d?/; // 0 - 99 - var match3to4 = /\d\d\d\d?/; // 999 - 9999 - var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 - var match1to3 = /\d{1,3}/; // 0 - 999 - var match1to4 = /\d{1,4}/; // 0 - 9999 - var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 - - var matchUnsigned = /\d+/; // 0 - inf - var matchSigned = /[+-]?\d+/; // -inf - inf - - var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z - var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z - - var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 - - // any word (or two) characters or numbers including two/three word month in arabic. - // includes scottish gaelic two word and hyphenated months - var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; - - var regexes = {}; - - function addRegexToken (token, regex, strictRegex) { - regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { - return (isStrict && strictRegex) ? strictRegex : regex; - }; - } - - function getParseRegexForToken (token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } - - return regexes[token](config._strict, config._locale); - } - - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - })); - } - - function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } - - var tokens = {}; - - function addParseToken (token, callback) { - var i, func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (isNumber(callback)) { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; - } - } - - function addWeekParseToken (token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } - - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); - } - } - - var YEAR = 0; - var MONTH = 1; - var DATE = 2; - var HOUR = 3; - var MINUTE = 4; - var SECOND = 5; - var MILLISECOND = 6; - var WEEK = 7; - var WEEKDAY = 8; - - // FORMATTING - - addFormatToken('Y', 0, 0, function () { - var y = this.year(); - return y <= 9999 ? '' + y : '+' + y; - }); - - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); - - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - - // ALIASES - - addUnitAlias('year', 'y'); - - // PRIORITIES - - addUnitPriority('year', 1); - - // PARSING - - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); - - addParseToken(['YYYYY', 'YYYYYY'], YEAR); - addParseToken('YYYY', function (input, array) { - array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken('YY', function (input, array) { - array[YEAR] = hooks.parseTwoDigitYear(input); - }); - addParseToken('Y', function (input, array) { - array[YEAR] = parseInt(input, 10); - }); - - // HELPERS - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } - - // HOOKS - - hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - // MOMENTS - - var getSetYear = makeGetSet('FullYear', true); - - function getIsLeapYear () { - return isLeapYear(this.year()); - } - - function makeGetSet (unit, keepTime) { - return function (value) { - if (value != null) { - set$1(this, unit, value); - hooks.updateOffset(this, keepTime); - return this; - } else { - return get(this, unit); - } - }; - } - - function get (mom, unit) { - return mom.isValid() ? - mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; - } - - function set$1 (mom, unit, value) { - if (mom.isValid() && !isNaN(value)) { - if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); - } - else { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } - } - - // MOMENTS - - function stringGet (units) { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](); - } - return this; - } - - - function stringSet (units, value) { - if (typeof units === 'object') { - units = normalizeObjectUnits(units); - var prioritized = getPrioritizedUnits(units); - for (var i = 0; i < prioritized.length; i++) { - this[prioritized[i].unit](units[prioritized[i].unit]); - } - } else { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](value); - } - } - return this; - } - - function mod(n, x) { - return ((n % x) + x) % x; - } - - var indexOf; - - if (Array.prototype.indexOf) { - indexOf = Array.prototype.indexOf; - } else { - indexOf = function (o) { - // I know - var i; - for (i = 0; i < this.length; ++i) { - if (this[i] === o) { - return i; - } - } - return -1; - }; - } - - function daysInMonth(year, month) { - if (isNaN(year) || isNaN(month)) { - return NaN; - } - var modMonth = mod(month, 12); - year += (month - modMonth) / 12; - return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); - } - - // FORMATTING - - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); - - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PRIORITY - - addUnitPriority('month', 8); - - // PARSING - - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', function (isStrict, locale) { - return locale.monthsShortRegex(isStrict); - }); - addRegexToken('MMMM', function (isStrict, locale) { - return locale.monthsRegex(isStrict); - }); - - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); - - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; - } else { - getParsingFlags(config).invalidMonth = input; - } - }); - - // LOCALES - - var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); - function localeMonths (m, format) { - if (!m) { - return isArray(this._months) ? this._months : - this._months['standalone']; - } - return isArray(this._months) ? this._months[m.month()] : - this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; - } - - var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); - function localeMonthsShort (m, format) { - if (!m) { - return isArray(this._monthsShort) ? this._monthsShort : - this._monthsShort['standalone']; - } - return isArray(this._monthsShort) ? this._monthsShort[m.month()] : - this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; - } - - function handleStrictParse(monthName, format, strict) { - var i, ii, mom, llc = monthName.toLocaleLowerCase(); - if (!this._monthsParse) { - // this is not used - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - for (i = 0; i < 12; ++i) { - mom = createUTC([2000, i]); - this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); - this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeMonthsParse (monthName, format, strict) { - var i, mom, regex; - - if (this._monthsParseExact) { - return handleStrictParse.call(this, monthName, format, strict); - } - - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - - // TODO: add sorting - // Sorting makes sure if one month (or abbr) is a prefix of another - // see sorting in computeMonthsParse - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - } - - // MOMENTS - - function setMonth (mom, value) { - var dayOfMonth; - - if (!mom.isValid()) { - // No op - return mom; - } - - if (typeof value === 'string') { - if (/^\d+$/.test(value)) { - value = toInt(value); - } else { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (!isNumber(value)) { - return mom; - } - } - } - - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } - - function getSetMonth (value) { - if (value != null) { - setMonth(this, value); - hooks.updateOffset(this, true); - return this; - } else { - return get(this, 'Month'); - } - } - - function getDaysInMonth () { - return daysInMonth(this.year(), this.month()); - } - - var defaultMonthsShortRegex = matchWord; - function monthsShortRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsShortStrictRegex; - } else { - return this._monthsShortRegex; - } - } else { - if (!hasOwnProp(this, '_monthsShortRegex')) { - this._monthsShortRegex = defaultMonthsShortRegex; - } - return this._monthsShortStrictRegex && isStrict ? - this._monthsShortStrictRegex : this._monthsShortRegex; - } - } - - var defaultMonthsRegex = matchWord; - function monthsRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsStrictRegex; - } else { - return this._monthsRegex; - } - } else { - if (!hasOwnProp(this, '_monthsRegex')) { - this._monthsRegex = defaultMonthsRegex; - } - return this._monthsStrictRegex && isStrict ? - this._monthsStrictRegex : this._monthsRegex; - } - } - - function computeMonthsParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var shortPieces = [], longPieces = [], mixedPieces = [], - i, mom; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - shortPieces.push(this.monthsShort(mom, '')); - longPieces.push(this.months(mom, '')); - mixedPieces.push(this.months(mom, '')); - mixedPieces.push(this.monthsShort(mom, '')); - } - // Sorting makes sure if one month (or abbr) is a prefix of another it - // will match the longer piece. - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 12; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - } - for (i = 0; i < 24; i++) { - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._monthsShortRegex = this._monthsRegex; - this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); - } - - function createDate (y, m, d, h, M, s, ms) { - // can't just apply() to create a date: - // https://stackoverflow.com/q/181348 - var date; - // the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - date = new Date(y + 400, m, d, h, M, s, ms); - if (isFinite(date.getFullYear())) { - date.setFullYear(y); - } - } else { - date = new Date(y, m, d, h, M, s, ms); - } - - return date; - } - - function createUTCDate (y) { - var date; - // the Date.UTC function remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - var args = Array.prototype.slice.call(arguments); - // preserve leap years using a full 400 year cycle, then reset - args[0] = y + 400; - date = new Date(Date.UTC.apply(null, args)); - if (isFinite(date.getUTCFullYear())) { - date.setUTCFullYear(y); - } - } else { - date = new Date(Date.UTC.apply(null, arguments)); - } - - return date; - } - - // start-of-first-week - start-of-year - function firstWeekOffset(year, dow, doy) { - var // first-week day -- which january is always in the first week (4 for iso, 1 for other) - fwd = 7 + dow - doy, - // first-week day local weekday -- which local weekday is fwd - fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; - - return -fwdlw + fwd - 1; - } - - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, dow, doy) { - var localWeekday = (7 + weekday - dow) % 7, - weekOffset = firstWeekOffset(year, dow, doy), - dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, - resYear, resDayOfYear; - - if (dayOfYear <= 0) { - resYear = year - 1; - resDayOfYear = daysInYear(resYear) + dayOfYear; - } else if (dayOfYear > daysInYear(year)) { - resYear = year + 1; - resDayOfYear = dayOfYear - daysInYear(year); - } else { - resYear = year; - resDayOfYear = dayOfYear; - } - - return { - year: resYear, - dayOfYear: resDayOfYear - }; - } - - function weekOfYear(mom, dow, doy) { - var weekOffset = firstWeekOffset(mom.year(), dow, doy), - week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, - resWeek, resYear; - - if (week < 1) { - resYear = mom.year() - 1; - resWeek = week + weeksInYear(resYear, dow, doy); - } else if (week > weeksInYear(mom.year(), dow, doy)) { - resWeek = week - weeksInYear(mom.year(), dow, doy); - resYear = mom.year() + 1; - } else { - resYear = mom.year(); - resWeek = week; - } - - return { - week: resWeek, - year: resYear - }; - } - - function weeksInYear(year, dow, doy) { - var weekOffset = firstWeekOffset(year, dow, doy), - weekOffsetNext = firstWeekOffset(year + 1, dow, doy); - return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; - } - - // FORMATTING - - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PRIORITIES - - addUnitPriority('week', 5); - addUnitPriority('isoWeek', 5); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // LOCALES - - function localeWeek (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - } - - var defaultLocaleWeek = { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 6th is the first week of the year. - }; - - function localeFirstDayOfWeek () { - return this._week.dow; - } - - function localeFirstDayOfYear () { - return this._week.doy; - } - - // MOMENTS - - function getSetWeek (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - function getSetISOWeek (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - // FORMATTING - - addFormatToken('d', 0, 'do', 'day'); - - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); - - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); - - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); - - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); - - // ALIASES - - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); - - // PRIORITY - addUnitPriority('day', 11); - addUnitPriority('weekday', 11); - addUnitPriority('isoWeekday', 11); - - // PARSING - - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', function (isStrict, locale) { - return locale.weekdaysMinRegex(isStrict); - }); - addRegexToken('ddd', function (isStrict, locale) { - return locale.weekdaysShortRegex(isStrict); - }); - addRegexToken('dddd', function (isStrict, locale) { - return locale.weekdaysRegex(isStrict); - }); - - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { - var weekday = config._locale.weekdaysParse(input, token, config._strict); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input !== 'string') { - return input; - } - - if (!isNaN(input)) { - return parseInt(input, 10); - } - - input = locale.weekdaysParse(input); - if (typeof input === 'number') { - return input; - } - - return null; - } - - function parseIsoWeekday(input, locale) { - if (typeof input === 'string') { - return locale.weekdaysParse(input) % 7 || 7; - } - return isNaN(input) ? null : input; - } - - // LOCALES - function shiftWeekdays (ws, n) { - return ws.slice(n, 7).concat(ws.slice(0, n)); - } - - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); - function localeWeekdays (m, format) { - var weekdays = isArray(this._weekdays) ? this._weekdays : - this._weekdays[(m && m !== true && this._weekdays.isFormat.test(format)) ? 'format' : 'standalone']; - return (m === true) ? shiftWeekdays(weekdays, this._week.dow) - : (m) ? weekdays[m.day()] : weekdays; - } - - var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); - function localeWeekdaysShort (m) { - return (m === true) ? shiftWeekdays(this._weekdaysShort, this._week.dow) - : (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; - } - - var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); - function localeWeekdaysMin (m) { - return (m === true) ? shiftWeekdays(this._weekdaysMin, this._week.dow) - : (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; - } - - function handleStrictParse$1(weekdayName, format, strict) { - var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._shortWeekdaysParse = []; - this._minWeekdaysParse = []; - - for (i = 0; i < 7; ++i) { - mom = createUTC([2000, 1]).day(i); - this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); - this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); - this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeWeekdaysParse (weekdayName, format, strict) { - var i, mom, regex; - - if (this._weekdaysParseExact) { - return handleStrictParse$1.call(this, weekdayName, format, strict); - } - - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._minWeekdaysParse = []; - this._shortWeekdaysParse = []; - this._fullWeekdaysParse = []; - } - - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - - mom = createUTC([2000, 1]).day(i); - if (strict && !this._fullWeekdaysParse[i]) { - this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i'); - this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i'); - this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i'); - } - if (!this._weekdaysParse[i]) { - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - - // MOMENTS - - function getSetDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - } - - function getSetLocaleDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } - - function getSetISODayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - - if (input != null) { - var weekday = parseIsoWeekday(input, this.localeData()); - return this.day(this.day() % 7 ? weekday : weekday - 7); - } else { - return this.day() || 7; - } - } - - var defaultWeekdaysRegex = matchWord; - function weekdaysRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysStrictRegex; - } else { - return this._weekdaysRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysRegex')) { - this._weekdaysRegex = defaultWeekdaysRegex; - } - return this._weekdaysStrictRegex && isStrict ? - this._weekdaysStrictRegex : this._weekdaysRegex; - } - } - - var defaultWeekdaysShortRegex = matchWord; - function weekdaysShortRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysShortStrictRegex; - } else { - return this._weekdaysShortRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysShortRegex')) { - this._weekdaysShortRegex = defaultWeekdaysShortRegex; - } - return this._weekdaysShortStrictRegex && isStrict ? - this._weekdaysShortStrictRegex : this._weekdaysShortRegex; - } - } - - var defaultWeekdaysMinRegex = matchWord; - function weekdaysMinRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysMinStrictRegex; - } else { - return this._weekdaysMinRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysMinRegex')) { - this._weekdaysMinRegex = defaultWeekdaysMinRegex; - } - return this._weekdaysMinStrictRegex && isStrict ? - this._weekdaysMinStrictRegex : this._weekdaysMinRegex; - } - } - - - function computeWeekdaysParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], - i, mom, minp, shortp, longp; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, 1]).day(i); - minp = this.weekdaysMin(mom, ''); - shortp = this.weekdaysShort(mom, ''); - longp = this.weekdays(mom, ''); - minPieces.push(minp); - shortPieces.push(shortp); - longPieces.push(longp); - mixedPieces.push(minp); - mixedPieces.push(shortp); - mixedPieces.push(longp); - } - // Sorting makes sure if one weekday (or abbr) is a prefix of another it - // will match the longer piece. - minPieces.sort(cmpLenRev); - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 7; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._weekdaysShortRegex = this._weekdaysRegex; - this._weekdaysMinRegex = this._weekdaysRegex; - - this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); - this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); - } - - // FORMATTING - - function hFormat() { - return this.hours() % 12 || 12; - } - - function kFormat() { - return this.hours() || 24; - } - - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, hFormat); - addFormatToken('k', ['kk', 2], 0, kFormat); - - addFormatToken('hmm', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); - }); - - addFormatToken('hmmss', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - addFormatToken('Hmm', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2); - }); - - addFormatToken('Hmmss', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - function meridiem (token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); - }); - } - - meridiem('a', true); - meridiem('A', false); - - // ALIASES - - addUnitAlias('hour', 'h'); - - // PRIORITY - addUnitPriority('hour', 13); - - // PARSING - - function matchMeridiem (isStrict, locale) { - return locale._meridiemParse; - } - - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('k', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); - addRegexToken('kk', match1to2, match2); - - addRegexToken('hmm', match3to4); - addRegexToken('hmmss', match5to6); - addRegexToken('Hmm', match3to4); - addRegexToken('Hmmss', match5to6); - - addParseToken(['H', 'HH'], HOUR); - addParseToken(['k', 'kk'], function (input, array, config) { - var kInput = toInt(input); - array[HOUR] = kInput === 24 ? 0 : kInput; - }); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('Hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - }); - addParseToken('Hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - }); - - // LOCALES - - function localeIsPM (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - } - - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; - function localeMeridiem (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - } - - - // MOMENTS - - // Setting the hour should keep the time, because the user explicitly - // specified which hour they want. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - var getSetHour = makeGetSet('Hours', true); - - var baseConfig = { - calendar: defaultCalendar, - longDateFormat: defaultLongDateFormat, - invalidDate: defaultInvalidDate, - ordinal: defaultOrdinal, - dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, - relativeTime: defaultRelativeTime, - - months: defaultLocaleMonths, - monthsShort: defaultLocaleMonthsShort, - - week: defaultLocaleWeek, - - weekdays: defaultLocaleWeekdays, - weekdaysMin: defaultLocaleWeekdaysMin, - weekdaysShort: defaultLocaleWeekdaysShort, - - meridiemParse: defaultLocaleMeridiemParse - }; - - // internal storage for locale config files - var locales = {}; - var localeFamilies = {}; - var globalLocale; - - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return globalLocale; - } - - function loadLocale(name) { - var oldLocale = null; - // TODO: Find a better way to register and load all the locales in Node - if (!locales[name] && ('object' !== 'undefined') && - module && module.exports) { - try { - oldLocale = globalLocale._abbr; - var aliasedRequire = commonjsRequire; - aliasedRequire('./locale/' + name); - getSetGlobalLocale(oldLocale); - } catch (e) {} - } - return locales[name]; - } - - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function getSetGlobalLocale (key, values) { - var data; - if (key) { - if (isUndefined(values)) { - data = getLocale(key); - } - else { - data = defineLocale(key, values); - } - - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } - else { - if ((typeof console !== 'undefined') && console.warn) { - //warn user if arguments are passed but the locale could not be set - console.warn('Locale ' + key + ' not found. Did you forget to load it?'); - } - } - } - - return globalLocale._abbr; - } - - function defineLocale (name, config) { - if (config !== null) { - var locale, parentConfig = baseConfig; - config.abbr = name; - if (locales[name] != null) { - deprecateSimple('defineLocaleOverride', - 'use moment.updateLocale(localeName, config) to change ' + - 'an existing locale. moment.defineLocale(localeName, ' + - 'config) should only be used for creating a new locale ' + - 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); - parentConfig = locales[name]._config; - } else if (config.parentLocale != null) { - if (locales[config.parentLocale] != null) { - parentConfig = locales[config.parentLocale]._config; - } else { - locale = loadLocale(config.parentLocale); - if (locale != null) { - parentConfig = locale._config; - } else { - if (!localeFamilies[config.parentLocale]) { - localeFamilies[config.parentLocale] = []; - } - localeFamilies[config.parentLocale].push({ - name: name, - config: config - }); - return null; - } - } - } - locales[name] = new Locale(mergeConfigs(parentConfig, config)); - - if (localeFamilies[name]) { - localeFamilies[name].forEach(function (x) { - defineLocale(x.name, x.config); - }); - } - - // backwards compat for now: also set the locale - // make sure we set the locale AFTER all child locales have been - // created, so we won't end up with the child locale set. - getSetGlobalLocale(name); - - - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - } - - function updateLocale(name, config) { - if (config != null) { - var locale, tmpLocale, parentConfig = baseConfig; - // MERGE - tmpLocale = loadLocale(name); - if (tmpLocale != null) { - parentConfig = tmpLocale._config; - } - config = mergeConfigs(parentConfig, config); - locale = new Locale(config); - locale.parentLocale = locales[name]; - locales[name] = locale; - - // backwards compat for now: also set the locale - getSetGlobalLocale(name); - } else { - // pass null for config to unupdate, useful for tests - if (locales[name] != null) { - if (locales[name].parentLocale != null) { - locales[name] = locales[name].parentLocale; - } else if (locales[name] != null) { - delete locales[name]; - } - } - } - return locales[name]; - } - - // returns locale data - function getLocale (key) { - var locale; - - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - - if (!key) { - return globalLocale; - } - - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } - - return chooseLocale(key); - } - - function listLocales() { - return keys(locales); - } - - function checkOverflow (m) { - var overflow; - var a = m._a; - - if (a && getParsingFlags(m).overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : - a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : - a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : - a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : - a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : - a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : - -1; - - if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - if (getParsingFlags(m)._overflowWeeks && overflow === -1) { - overflow = WEEK; - } - if (getParsingFlags(m)._overflowWeekday && overflow === -1) { - overflow = WEEKDAY; - } - - getParsingFlags(m).overflow = overflow; - } - - return m; - } - - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - - function currentDateArray(config) { - // hooks is actually the exported moment object - var nowValue = new Date(hooks.now()); - if (config._useUTC) { - return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; - } - return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray (config) { - var i, date, input = [], currentDate, expectedWeekday, yearToUse; - - if (config._d) { - return; - } - - currentDate = currentDateArray(config); - - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - - //if the day of the year is set, figure out what it is - if (config._dayOfYear != null) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - - if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { - getParsingFlags(config)._overflowDayOfYear = true; - } - - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } - - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } - - config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); - expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); - - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - - if (config._nextDay) { - config._a[HOUR] = 24; - } - - // check for mismatching day of week - if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { - getParsingFlags(config).weekdayMismatch = true; - } - } - - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; - - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - if (weekday < 1 || weekday > 7) { - weekdayOverflow = true; - } - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - - var curWeek = weekOfYear(createLocal(), dow, doy); - - weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); - - // Default to current week. - week = defaults(w.w, curWeek.week); - - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < 0 || weekday > 6) { - weekdayOverflow = true; - } - } else if (w.e != null) { - // local weekday -- counting starts from beginning of week - weekday = w.e + dow; - if (w.e < 0 || w.e > 6) { - weekdayOverflow = true; - } - } else { - // default to beginning of week - weekday = dow; - } - } - if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { - getParsingFlags(config)._overflowWeeks = true; - } else if (weekdayOverflow != null) { - getParsingFlags(config)._overflowWeekday = true; - } else { - temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - } - - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - - var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; - - var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], - ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], - ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], - ['GGGG-[W]WW', /\d{4}-W\d\d/, false], - ['YYYY-DDD', /\d{4}-\d{3}/], - ['YYYY-MM', /\d{4}-\d\d/, false], - ['YYYYYYMMDD', /[+-]\d{10}/], - ['YYYYMMDD', /\d{8}/], - // YYYYMM is NOT allowed by the standard - ['GGGG[W]WWE', /\d{4}W\d{3}/], - ['GGGG[W]WW', /\d{4}W\d{2}/, false], - ['YYYYDDD', /\d{7}/] - ]; - - // iso time formats and regexes - var isoTimes = [ - ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], - ['HH:mm:ss', /\d\d:\d\d:\d\d/], - ['HH:mm', /\d\d:\d\d/], - ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], - ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], - ['HHmmss', /\d\d\d\d\d\d/], - ['HHmm', /\d\d\d\d/], - ['HH', /\d\d/] - ]; - - var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; - - // date from iso format - function configFromISO(config) { - var i, l, - string = config._i, - match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), - allowTime, dateFormat, timeFormat, tzFormat; - - if (match) { - getParsingFlags(config).iso = true; - - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(match[1])) { - dateFormat = isoDates[i][0]; - allowTime = isoDates[i][2] !== false; - break; - } - } - if (dateFormat == null) { - config._isValid = false; - return; - } - if (match[3]) { - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - // match[2] should be 'T' or space - timeFormat = (match[2] || ' ') + isoTimes[i][0]; - break; - } - } - if (timeFormat == null) { - config._isValid = false; - return; - } - } - if (!allowTime && timeFormat != null) { - config._isValid = false; - return; - } - if (match[4]) { - if (tzRegex.exec(match[4])) { - tzFormat = 'Z'; - } else { - config._isValid = false; - return; - } - } - config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); - configFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - - // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 - var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; - - function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { - var result = [ - untruncateYear(yearStr), - defaultLocaleMonthsShort.indexOf(monthStr), - parseInt(dayStr, 10), - parseInt(hourStr, 10), - parseInt(minuteStr, 10) - ]; - - if (secondStr) { - result.push(parseInt(secondStr, 10)); - } - - return result; - } - - function untruncateYear(yearStr) { - var year = parseInt(yearStr, 10); - if (year <= 49) { - return 2000 + year; - } else if (year <= 999) { - return 1900 + year; - } - return year; - } - - function preprocessRFC2822(s) { - // Remove comments and folding whitespace and replace multiple-spaces with a single space - return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - } - - function checkWeekday(weekdayStr, parsedInput, config) { - if (weekdayStr) { - // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. - var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), - weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); - if (weekdayProvided !== weekdayActual) { - getParsingFlags(config).weekdayMismatch = true; - config._isValid = false; - return false; - } - } - return true; - } - - var obsOffsets = { - UT: 0, - GMT: 0, - EDT: -4 * 60, - EST: -5 * 60, - CDT: -5 * 60, - CST: -6 * 60, - MDT: -6 * 60, - MST: -7 * 60, - PDT: -7 * 60, - PST: -8 * 60 - }; - - function calculateOffset(obsOffset, militaryOffset, numOffset) { - if (obsOffset) { - return obsOffsets[obsOffset]; - } else if (militaryOffset) { - // the only allowed military tz is Z - return 0; - } else { - var hm = parseInt(numOffset, 10); - var m = hm % 100, h = (hm - m) / 100; - return h * 60 + m; - } - } - - // date and time from ref 2822 format - function configFromRFC2822(config) { - var match = rfc2822.exec(preprocessRFC2822(config._i)); - if (match) { - var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); - if (!checkWeekday(match[1], parsedArray, config)) { - return; - } - - config._a = parsedArray; - config._tzm = calculateOffset(match[8], match[9], match[10]); - - config._d = createUTCDate.apply(null, config._a); - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - - getParsingFlags(config).rfc2822 = true; - } else { - config._isValid = false; - } - } - - // date from iso format or fallback - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - - if (matched !== null) { - config._d = new Date(+matched[1]); - return; - } - - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - configFromRFC2822(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - // Final attempt, use Input Fallback - hooks.createFromInputFallback(config); - } - - hooks.createFromInputFallback = deprecate( - 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + - 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + - 'discouraged and will be removed in an upcoming major release. Please refer to ' + - 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); - - // constant that refers to the ISO standard - hooks.ISO_8601 = function () {}; - - // constant that refers to the RFC 2822 form - hooks.RFC_2822 = function () {}; - - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === hooks.ISO_8601) { - configFromISO(config); - return; - } - if (config._f === hooks.RFC_2822) { - configFromRFC2822(config); - return; - } - config._a = []; - getParsingFlags(config).empty = true; - - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; - - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - // console.log('token', token, 'parsedInput', parsedInput, - // 'regex', getParseRegexForToken(token, config)); - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } - else { - getParsingFlags(config).unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - getParsingFlags(config).unusedTokens.push(token); - } - } - - // add remaining unparsed input length to the string - getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - - // clear _12h flag if hour is <= 12 - if (config._a[HOUR] <= 12 && - getParsingFlags(config).bigHour === true && - config._a[HOUR] > 0) { - getParsingFlags(config).bigHour = undefined; - } - - getParsingFlags(config).parsedDateParts = config._a.slice(0); - getParsingFlags(config).meridiem = config._meridiem; - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - - configFromArray(config); - checkOverflow(config); - } - - - function meridiemFixWrap (locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; - } - } - - // date from string and array of format strings - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, - - scoreToBeat, - i, - currentScore; - - if (config._f.length === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = new Date(NaN); - return; - } - - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - - if (!isValid(tempConfig)) { - continue; - } - - // if there is any input that was not parsed add a penalty for that format - currentScore += getParsingFlags(tempConfig).charsLeftOver; - - //or tokens - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - - getParsingFlags(tempConfig).score = currentScore; - - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - - extend(config, bestMoment || tempConfig); - } - - function configFromObject(config) { - if (config._d) { - return; - } - - var i = normalizeObjectUnits(config._i); - config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { - return obj && parseInt(obj, 10); - }); - - configFromArray(config); - } - - function createFromConfig (config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } - - return res; - } - - function prepareConfig (config) { - var input = config._i, - format = config._f; - - config._locale = config._locale || getLocale(config._l); - - if (input === null || (format === undefined && input === '')) { - return createInvalid({nullInput: true}); - } - - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } - - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isDate(input)) { - config._d = input; - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else { - configFromInput(config); - } - - if (!isValid(config)) { - config._d = null; - } - - return config; - } - - function configFromInput(config) { - var input = config._i; - if (isUndefined(input)) { - config._d = new Date(hooks.now()); - } else if (isDate(input)) { - config._d = new Date(input.valueOf()); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (isObject(input)) { - configFromObject(config); - } else if (isNumber(input)) { - // from milliseconds - config._d = new Date(input); - } else { - hooks.createFromInputFallback(config); - } - } - - function createLocalOrUTC (input, format, locale, strict, isUTC) { - var c = {}; - - if (locale === true || locale === false) { - strict = locale; - locale = undefined; - } - - if ((isObject(input) && isObjectEmpty(input)) || - (isArray(input) && input.length === 0)) { - input = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - - return createFromConfig(c); - } - - function createLocal (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); - } - - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other < this ? this : other; - } else { - return createInvalid(); - } - } - ); - - var prototypeMax = deprecate( - 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other > this ? this : other; - } else { - return createInvalid(); - } - } - ); - - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - - // TODO: Use [].sort instead? - function min () { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - } - - function max () { - var args = [].slice.call(arguments, 0); - - return pickBy('isAfter', args); - } - - var now = function () { - return Date.now ? Date.now() : +(new Date()); - }; - - var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; - - function isDurationValid(m) { - for (var key in m) { - if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { - return false; - } - } - - var unitHasDecimal = false; - for (var i = 0; i < ordering.length; ++i) { - if (m[ordering[i]]) { - if (unitHasDecimal) { - return false; // only allow non-integers for smallest unit - } - if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { - unitHasDecimal = true; - } - } - } - - return true; - } - - function isValid$1() { - return this._isValid; - } - - function createInvalid$1() { - return createDuration(NaN); - } - - function Duration (duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || normalizedInput.isoWeek || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - this._isValid = isDurationValid(normalizedInput); - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible to translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = getLocale(); - - this._bubble(); - } - - function isDuration (obj) { - return obj instanceof Duration; - } - - function absRound (number) { - if (number < 0) { - return Math.round(-1 * number) * -1; - } else { - return Math.round(number); - } - } - - // FORMATTING - - function offset (token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(); - var sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); - }); - } - - offset('Z', ':'); - offset('ZZ', ''); - - // PARSING - - addRegexToken('Z', matchShortOffset); - addRegexToken('ZZ', matchShortOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(matchShortOffset, input); - }); - - // HELPERS - - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; - - function offsetFromString(matcher, string) { - var matches = (string || '').match(matcher); - - if (matches === null) { - return null; - } - - var chunk = matches[matches.length - 1] || []; - var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - var minutes = +(parts[1] * 60) + toInt(parts[2]); - - return minutes === 0 ? - 0 : - parts[0] === '+' ? minutes : -minutes; - } - - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); - // Use low-level api, because this fn is low-level api. - res._d.setTime(res._d.valueOf() + diff); - hooks.updateOffset(res, false); - return res; - } else { - return createLocal(input).local(); - } - } - - function getDateOffset (m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset() / 15) * 15; - } - - // HOOKS - - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - hooks.updateOffset = function () {}; - - // MOMENTS - - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset (input, keepLocalTime, keepMinutes) { - var offset = this._offset || 0, - localAdjust; - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(matchShortOffset, input); - if (input === null) { - return this; - } - } else if (Math.abs(input) < 16 && !keepMinutes) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addSubtract(this, createDuration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : getDateOffset(this); - } - } - - function getSetZone (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); - - return this; - } else { - return -this.utcOffset(); - } - } - - function setOffsetToUTC (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } - - function setOffsetToLocal (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; - - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); - } - } - return this; - } - - function setOffsetToParsedOffset () { - if (this._tzm != null) { - this.utcOffset(this._tzm, false, true); - } else if (typeof this._i === 'string') { - var tZone = offsetFromString(matchOffset, this._i); - if (tZone != null) { - this.utcOffset(tZone); - } - else { - this.utcOffset(0, true); - } - } - return this; - } - - function hasAlignedHourOffset (input) { - if (!this.isValid()) { - return false; - } - input = input ? createLocal(input).utcOffset() : 0; - - return (this.utcOffset() - input) % 60 === 0; - } - - function isDaylightSavingTime () { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); - } - - function isDaylightSavingTimeShifted () { - if (!isUndefined(this._isDSTShifted)) { - return this._isDSTShifted; - } - - var c = {}; - - copyConfig(c, this); - c = prepareConfig(c); - - if (c._a) { - var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); - this._isDSTShifted = this.isValid() && - compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; - } - - return this._isDSTShifted; - } - - function isLocal () { - return this.isValid() ? !this._isUTC : false; - } - - function isUtcOffset () { - return this.isValid() ? this._isUTC : false; - } - - function isUtc () { - return this.isValid() ? this._isUTC && this._offset === 0 : false; - } - - // ASP.NET json date format regex - var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; - - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - // and further modified to allow for strings containing both week and day - var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; - - function createDuration (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; - - if (isDuration(input)) { - duration = { - ms : input._milliseconds, - d : input._days, - M : input._months - }; - } else if (isNumber(input)) { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : 0, - d : toInt(match[DATE]) * sign, - h : toInt(match[HOUR]) * sign, - m : toInt(match[MINUTE]) * sign, - s : toInt(match[SECOND]) * sign, - ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match - }; - } else if (!!(match = isoRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : parseIso(match[2], sign), - M : parseIso(match[3], sign), - w : parseIso(match[4], sign), - d : parseIso(match[5], sign), - h : parseIso(match[6], sign), - m : parseIso(match[7], sign), - s : parseIso(match[8], sign) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); - - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - - ret = new Duration(duration); - - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - - return ret; - } - - createDuration.fn = Duration.prototype; - createDuration.invalid = createInvalid$1; - - function parseIso (inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } - - function positiveMomentsDifference(base, other) { - var res = {}; - - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } - - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - - return res; - } - - function momentsDifference(base, other) { - var res; - if (!(base.isValid() && other.isValid())) { - return {milliseconds: 0, months: 0}; - } - - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - - return res; - } - - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + - 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); - tmp = val; val = period; period = tmp; - } - - val = typeof val === 'string' ? +val : val; - dur = createDuration(val, period); - addSubtract(this, dur, direction); - return this; - }; - } - - function addSubtract (mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = absRound(duration._days), - months = absRound(duration._months); - - if (!mom.isValid()) { - // No op - return; - } - - updateOffset = updateOffset == null ? true : updateOffset; - - if (months) { - setMonth(mom, get(mom, 'Month') + months * isAdding); - } - if (days) { - set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); - } - if (milliseconds) { - mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); - } - if (updateOffset) { - hooks.updateOffset(mom, days || months); - } - } - - var add = createAdder(1, 'add'); - var subtract = createAdder(-1, 'subtract'); - - function getCalendarFormat(myMoment, now) { - var diff = myMoment.diff(now, 'days', true); - return diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - } - - function calendar$1 (time, formats) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - format = hooks.calendarFormat(this, sod) || 'sameElse'; - - var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); - - return this.format(output || this.localeData().calendar(format, this, createLocal(now))); - } - - function clone () { - return new Moment(this); - } - - function isAfter (input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() > localInput.valueOf(); - } else { - return localInput.valueOf() < this.clone().startOf(units).valueOf(); - } - } - - function isBefore (input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() < localInput.valueOf(); - } else { - return this.clone().endOf(units).valueOf() < localInput.valueOf(); - } - } - - function isBetween (from, to, units, inclusivity) { - var localFrom = isMoment(from) ? from : createLocal(from), - localTo = isMoment(to) ? to : createLocal(to); - if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { - return false; - } - inclusivity = inclusivity || '()'; - return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && - (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units)); - } - - function isSame (input, units) { - var localInput = isMoment(input) ? input : createLocal(input), - inputMs; - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() === localInput.valueOf(); - } else { - inputMs = localInput.valueOf(); - return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); - } - } - - function isSameOrAfter (input, units) { - return this.isSame(input, units) || this.isAfter(input, units); - } - - function isSameOrBefore (input, units) { - return this.isSame(input, units) || this.isBefore(input, units); - } - - function diff (input, units, asFloat) { - var that, - zoneDelta, - output; - - if (!this.isValid()) { - return NaN; - } - - that = cloneWithOffset(input, this); - - if (!that.isValid()) { - return NaN; - } - - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; - - units = normalizeUnits(units); - - switch (units) { - case 'year': output = monthDiff(this, that) / 12; break; - case 'month': output = monthDiff(this, that); break; - case 'quarter': output = monthDiff(this, that) / 3; break; - case 'second': output = (this - that) / 1e3; break; // 1000 - case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 - case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 - case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst - case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst - default: output = this - that; - } - - return asFloat ? output : absFloor(output); - } - - function monthDiff (a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - - //check for negative zero, return zero if negative zero - return -(wholeMonthDiff + adjust) || 0; - } - - hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; - - function toString () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } - - function toISOString(keepOffset) { - if (!this.isValid()) { - return null; - } - var utc = keepOffset !== true; - var m = utc ? this.clone().utc() : this; - if (m.year() < 0 || m.year() > 9999) { - return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); - } - if (isFunction(Date.prototype.toISOString)) { - // native implementation is ~50x faster, use it when we can - if (utc) { - return this.toDate().toISOString(); - } else { - return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z')); - } - } - return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); - } - - /** - * Return a human readable representation of a moment that can - * also be evaluated to get a new moment which is the same - * - * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects - */ - function inspect () { - if (!this.isValid()) { - return 'moment.invalid(/* ' + this._i + ' */)'; - } - var func = 'moment'; - var zone = ''; - if (!this.isLocal()) { - func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; - zone = 'Z'; - } - var prefix = '[' + func + '("]'; - var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; - var datetime = '-MM-DD[T]HH:mm:ss.SSS'; - var suffix = zone + '[")]'; - - return this.format(prefix + year + datetime + suffix); - } - - function format (inputString) { - if (!inputString) { - inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; - } - var output = formatMoment(this, inputString); - return this.localeData().postformat(output); - } - - function from (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - createLocal(time).isValid())) { - return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function fromNow (withoutSuffix) { - return this.from(createLocal(), withoutSuffix); - } - - function to (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - createLocal(time).isValid())) { - return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function toNow (withoutSuffix) { - return this.to(createLocal(), withoutSuffix); - } - - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - function locale (key) { - var newLocaleData; - - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - } - - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); - - function localeData () { - return this._locale; - } - - var MS_PER_SECOND = 1000; - var MS_PER_MINUTE = 60 * MS_PER_SECOND; - var MS_PER_HOUR = 60 * MS_PER_MINUTE; - var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; - - // actual modulo - handles negative numbers (for dates before 1970): - function mod$1(dividend, divisor) { - return (dividend % divisor + divisor) % divisor; - } - - function localStartOfDate(y, m, d) { - // the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - return new Date(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return new Date(y, m, d).valueOf(); - } - } - - function utcStartOfDate(y, m, d) { - // Date.UTC remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return Date.UTC(y, m, d); - } - } - - function startOf (units) { - var time; - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond' || !this.isValid()) { - return this; - } - - var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - - switch (units) { - case 'year': - time = startOfDate(this.year(), 0, 1); - break; - case 'quarter': - time = startOfDate(this.year(), this.month() - this.month() % 3, 1); - break; - case 'month': - time = startOfDate(this.year(), this.month(), 1); - break; - case 'week': - time = startOfDate(this.year(), this.month(), this.date() - this.weekday()); - break; - case 'isoWeek': - time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1)); - break; - case 'day': - case 'date': - time = startOfDate(this.year(), this.month(), this.date()); - break; - case 'hour': - time = this._d.valueOf(); - time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR); - break; - case 'minute': - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_MINUTE); - break; - case 'second': - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_SECOND); - break; - } - - this._d.setTime(time); - hooks.updateOffset(this, true); - return this; - } - - function endOf (units) { - var time; - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond' || !this.isValid()) { - return this; - } - - var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - - switch (units) { - case 'year': - time = startOfDate(this.year() + 1, 0, 1) - 1; - break; - case 'quarter': - time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1; - break; - case 'month': - time = startOfDate(this.year(), this.month() + 1, 1) - 1; - break; - case 'week': - time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; - break; - case 'isoWeek': - time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1; - break; - case 'day': - case 'date': - time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; - break; - case 'hour': - time = this._d.valueOf(); - time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1; - break; - case 'minute': - time = this._d.valueOf(); - time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; - break; - case 'second': - time = this._d.valueOf(); - time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; - break; - } - - this._d.setTime(time); - hooks.updateOffset(this, true); - return this; - } - - function valueOf () { - return this._d.valueOf() - ((this._offset || 0) * 60000); - } - - function unix () { - return Math.floor(this.valueOf() / 1000); - } - - function toDate () { - return new Date(this.valueOf()); - } - - function toArray () { - var m = this; - return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; - } - - function toObject () { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds() - }; - } - - function toJSON () { - // new Date(NaN).toJSON() === null - return this.isValid() ? this.toISOString() : null; - } - - function isValid$2 () { - return isValid(this); - } - - function parsingFlags () { - return extend({}, getParsingFlags(this)); - } - - function invalidAt () { - return getParsingFlags(this).overflow; - } - - function creationData() { - return { - input: this._i, - format: this._f, - locale: this._locale, - isUTC: this._isUTC, - strict: this._strict - }; - } - - // FORMATTING - - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken (token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } - - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - - // ALIASES - - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); - - // PRIORITY - - addUnitPriority('weekYear', 1); - addUnitPriority('isoWeekYear', 1); - - - // PARSING - - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); - - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { - week[token.substr(0, 2)] = toInt(input); - }); - - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = hooks.parseTwoDigitYear(input); - }); - - // MOMENTS - - function getSetWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, - this.week(), - this.weekday(), - this.localeData()._week.dow, - this.localeData()._week.doy); - } - - function getSetISOWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, this.isoWeek(), this.isoWeekday(), 1, 4); - } - - function getISOWeeksInYear () { - return weeksInYear(this.year(), 1, 4); - } - - function getWeeksInYear () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - - function getSetWeekYearHelper(input, week, weekday, dow, doy) { - var weeksTarget; - if (input == null) { - return weekOfYear(this, dow, doy).year; - } else { - weeksTarget = weeksInYear(input, dow, doy); - if (week > weeksTarget) { - week = weeksTarget; - } - return setWeekAll.call(this, input, week, weekday, dow, doy); - } - } - - function setWeekAll(weekYear, week, weekday, dow, doy) { - var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), - date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - - this.year(date.getUTCFullYear()); - this.month(date.getUTCMonth()); - this.date(date.getUTCDate()); - return this; - } - - // FORMATTING - - addFormatToken('Q', 0, 'Qo', 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); - - // PRIORITY - - addUnitPriority('quarter', 7); - - // PARSING - - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - - // MOMENTS - - function getSetQuarter (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } - - // FORMATTING - - addFormatToken('D', ['DD', 2], 'Do', 'date'); - - // ALIASES - - addUnitAlias('date', 'D'); - - // PRIORITY - addUnitPriority('date', 9); - - // PARSING - - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - // TODO: Remove "ordinalParse" fallback in next major release. - return isStrict ? - (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : - locale._dayOfMonthOrdinalParseLenient; - }); - - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0]); - }); - - // MOMENTS - - var getSetDayOfMonth = makeGetSet('Date', true); - - // FORMATTING - - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - - // ALIASES - - addUnitAlias('dayOfYear', 'DDD'); - - // PRIORITY - addUnitPriority('dayOfYear', 4); - - // PARSING - - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); - - // HELPERS - - // MOMENTS - - function getSetDayOfYear (input) { - var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - } - - // FORMATTING - - addFormatToken('m', ['mm', 2], 0, 'minute'); - - // ALIASES - - addUnitAlias('minute', 'm'); - - // PRIORITY - - addUnitPriority('minute', 14); - - // PARSING - - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); - - // MOMENTS - - var getSetMinute = makeGetSet('Minutes', false); - - // FORMATTING - - addFormatToken('s', ['ss', 2], 0, 'second'); - - // ALIASES - - addUnitAlias('second', 's'); - - // PRIORITY - - addUnitPriority('second', 15); - - // PARSING - - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - - // MOMENTS - - var getSetSecond = makeGetSet('Seconds', false); - - // FORMATTING - - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); - - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); - - addFormatToken(0, ['SSS', 3], 0, 'millisecond'); - addFormatToken(0, ['SSSS', 4], 0, function () { - return this.millisecond() * 10; - }); - addFormatToken(0, ['SSSSS', 5], 0, function () { - return this.millisecond() * 100; - }); - addFormatToken(0, ['SSSSSS', 6], 0, function () { - return this.millisecond() * 1000; - }); - addFormatToken(0, ['SSSSSSS', 7], 0, function () { - return this.millisecond() * 10000; - }); - addFormatToken(0, ['SSSSSSSS', 8], 0, function () { - return this.millisecond() * 100000; - }); - addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { - return this.millisecond() * 1000000; - }); - - - // ALIASES - - addUnitAlias('millisecond', 'ms'); - - // PRIORITY - - addUnitPriority('millisecond', 16); - - // PARSING - - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - - var token; - for (token = 'SSSS'; token.length <= 9; token += 'S') { - addRegexToken(token, matchUnsigned); - } - - function parseMs(input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - } - - for (token = 'S'; token.length <= 9; token += 'S') { - addParseToken(token, parseMs); - } - // MOMENTS - - var getSetMillisecond = makeGetSet('Milliseconds', false); - - // FORMATTING - - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); - - // MOMENTS - - function getZoneAbbr () { - return this._isUTC ? 'UTC' : ''; - } - - function getZoneName () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - } - - var proto = Moment.prototype; - - proto.add = add; - proto.calendar = calendar$1; - proto.clone = clone; - proto.diff = diff; - proto.endOf = endOf; - proto.format = format; - proto.from = from; - proto.fromNow = fromNow; - proto.to = to; - proto.toNow = toNow; - proto.get = stringGet; - proto.invalidAt = invalidAt; - proto.isAfter = isAfter; - proto.isBefore = isBefore; - proto.isBetween = isBetween; - proto.isSame = isSame; - proto.isSameOrAfter = isSameOrAfter; - proto.isSameOrBefore = isSameOrBefore; - proto.isValid = isValid$2; - proto.lang = lang; - proto.locale = locale; - proto.localeData = localeData; - proto.max = prototypeMax; - proto.min = prototypeMin; - proto.parsingFlags = parsingFlags; - proto.set = stringSet; - proto.startOf = startOf; - proto.subtract = subtract; - proto.toArray = toArray; - proto.toObject = toObject; - proto.toDate = toDate; - proto.toISOString = toISOString; - proto.inspect = inspect; - proto.toJSON = toJSON; - proto.toString = toString; - proto.unix = unix; - proto.valueOf = valueOf; - proto.creationData = creationData; - proto.year = getSetYear; - proto.isLeapYear = getIsLeapYear; - proto.weekYear = getSetWeekYear; - proto.isoWeekYear = getSetISOWeekYear; - proto.quarter = proto.quarters = getSetQuarter; - proto.month = getSetMonth; - proto.daysInMonth = getDaysInMonth; - proto.week = proto.weeks = getSetWeek; - proto.isoWeek = proto.isoWeeks = getSetISOWeek; - proto.weeksInYear = getWeeksInYear; - proto.isoWeeksInYear = getISOWeeksInYear; - proto.date = getSetDayOfMonth; - proto.day = proto.days = getSetDayOfWeek; - proto.weekday = getSetLocaleDayOfWeek; - proto.isoWeekday = getSetISODayOfWeek; - proto.dayOfYear = getSetDayOfYear; - proto.hour = proto.hours = getSetHour; - proto.minute = proto.minutes = getSetMinute; - proto.second = proto.seconds = getSetSecond; - proto.millisecond = proto.milliseconds = getSetMillisecond; - proto.utcOffset = getSetOffset; - proto.utc = setOffsetToUTC; - proto.local = setOffsetToLocal; - proto.parseZone = setOffsetToParsedOffset; - proto.hasAlignedHourOffset = hasAlignedHourOffset; - proto.isDST = isDaylightSavingTime; - proto.isLocal = isLocal; - proto.isUtcOffset = isUtcOffset; - proto.isUtc = isUtc; - proto.isUTC = isUtc; - proto.zoneAbbr = getZoneAbbr; - proto.zoneName = getZoneName; - proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); - proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); - proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); - proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); - proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); - - function createUnix (input) { - return createLocal(input * 1000); - } - - function createInZone () { - return createLocal.apply(null, arguments).parseZone(); - } - - function preParsePostFormat (string) { - return string; - } - - var proto$1 = Locale.prototype; - - proto$1.calendar = calendar; - proto$1.longDateFormat = longDateFormat; - proto$1.invalidDate = invalidDate; - proto$1.ordinal = ordinal; - proto$1.preparse = preParsePostFormat; - proto$1.postformat = preParsePostFormat; - proto$1.relativeTime = relativeTime; - proto$1.pastFuture = pastFuture; - proto$1.set = set; - - proto$1.months = localeMonths; - proto$1.monthsShort = localeMonthsShort; - proto$1.monthsParse = localeMonthsParse; - proto$1.monthsRegex = monthsRegex; - proto$1.monthsShortRegex = monthsShortRegex; - proto$1.week = localeWeek; - proto$1.firstDayOfYear = localeFirstDayOfYear; - proto$1.firstDayOfWeek = localeFirstDayOfWeek; - - proto$1.weekdays = localeWeekdays; - proto$1.weekdaysMin = localeWeekdaysMin; - proto$1.weekdaysShort = localeWeekdaysShort; - proto$1.weekdaysParse = localeWeekdaysParse; - - proto$1.weekdaysRegex = weekdaysRegex; - proto$1.weekdaysShortRegex = weekdaysShortRegex; - proto$1.weekdaysMinRegex = weekdaysMinRegex; - - proto$1.isPM = localeIsPM; - proto$1.meridiem = localeMeridiem; - - function get$1 (format, index, field, setter) { - var locale = getLocale(); - var utc = createUTC().set(setter, index); - return locale[field](utc, format); - } - - function listMonthsImpl (format, index, field) { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - - if (index != null) { - return get$1(format, index, field, 'month'); - } - - var i; - var out = []; - for (i = 0; i < 12; i++) { - out[i] = get$1(format, i, field, 'month'); - } - return out; - } - - // () - // (5) - // (fmt, 5) - // (fmt) - // (true) - // (true, 5) - // (true, fmt, 5) - // (true, fmt) - function listWeekdaysImpl (localeSorted, format, index, field) { - if (typeof localeSorted === 'boolean') { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } else { - format = localeSorted; - index = format; - localeSorted = false; - - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } - - var locale = getLocale(), - shift = localeSorted ? locale._week.dow : 0; - - if (index != null) { - return get$1(format, (index + shift) % 7, field, 'day'); - } - - var i; - var out = []; - for (i = 0; i < 7; i++) { - out[i] = get$1(format, (i + shift) % 7, field, 'day'); - } - return out; - } - - function listMonths (format, index) { - return listMonthsImpl(format, index, 'months'); - } - - function listMonthsShort (format, index) { - return listMonthsImpl(format, index, 'monthsShort'); - } - - function listWeekdays (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); - } - - function listWeekdaysShort (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); - } - - function listWeekdaysMin (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); - } - - getSetGlobalLocale('en', { - dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - // Side effect imports - - hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); - hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); - - var mathAbs = Math.abs; - - function abs () { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function addSubtract$1 (duration, input, value, direction) { - var other = createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function add$1 (input, value) { - return addSubtract$1(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function subtract$1 (input, value) { - return addSubtract$1(this, input, value, -1); - } - - function absCeil (number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); - } - } - - function bubble () { - var milliseconds = this._milliseconds; - var days = this._days; - var months = this._months; - var data = this._data; - var seconds, minutes, hours, years, monthsFromDays; - - // if we have a mix of positive and negative values, bubble down first - // check: https://github.com/moment/moment/issues/2166 - if (!((milliseconds >= 0 && days >= 0 && months >= 0) || - (milliseconds <= 0 && days <= 0 && months <= 0))) { - milliseconds += absCeil(monthsToDays(months) + days) * 864e5; - days = 0; - months = 0; - } - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // convert days to months - monthsFromDays = absFloor(daysToMonths(days)); - months += monthsFromDays; - days -= absCeil(monthsToDays(monthsFromDays)); - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; - } - - function daysToMonths (days) { - // 400 years have 146097 days (taking into account leap year rules) - // 400 years have 12 months === 4800 - return days * 4800 / 146097; - } - - function monthsToDays (months) { - // the reverse of daysToMonths - return months * 146097 / 4800; - } - - function as (units) { - if (!this.isValid()) { - return NaN; - } - var days; - var months; - var milliseconds = this._milliseconds; - - units = normalizeUnits(units); - - if (units === 'month' || units === 'quarter' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToMonths(days); - switch (units) { - case 'month': return months; - case 'quarter': return months / 3; - case 'year': return months / 12; - } - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case 'week' : return days / 7 + milliseconds / 6048e5; - case 'day' : return days + milliseconds / 864e5; - case 'hour' : return days * 24 + milliseconds / 36e5; - case 'minute' : return days * 1440 + milliseconds / 6e4; - case 'second' : return days * 86400 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 864e5) + milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - } - - // TODO: Use this.as('ms')? - function valueOf$1 () { - if (!this.isValid()) { - return NaN; - } - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs (alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'); - var asSeconds = makeAs('s'); - var asMinutes = makeAs('m'); - var asHours = makeAs('h'); - var asDays = makeAs('d'); - var asWeeks = makeAs('w'); - var asMonths = makeAs('M'); - var asQuarters = makeAs('Q'); - var asYears = makeAs('y'); - - function clone$1 () { - return createDuration(this); - } - - function get$2 (units) { - units = normalizeUnits(units); - return this.isValid() ? this[units + 's']() : NaN; - } - - function makeGetter(name) { - return function () { - return this.isValid() ? this._data[name] : NaN; - }; - } - - var milliseconds = makeGetter('milliseconds'); - var seconds = makeGetter('seconds'); - var minutes = makeGetter('minutes'); - var hours = makeGetter('hours'); - var days = makeGetter('days'); - var months = makeGetter('months'); - var years = makeGetter('years'); - - function weeks () { - return absFloor(this.days() / 7); - } - - var round = Math.round; - var thresholds = { - ss: 44, // a few seconds to seconds - s : 45, // seconds to minute - m : 45, // minutes to hour - h : 22, // hours to day - d : 26, // days to month - M : 11 // months to year - }; - - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - - function relativeTime$1 (posNegDuration, withoutSuffix, locale) { - var duration = createDuration(posNegDuration).abs(); - var seconds = round(duration.as('s')); - var minutes = round(duration.as('m')); - var hours = round(duration.as('h')); - var days = round(duration.as('d')); - var months = round(duration.as('M')); - var years = round(duration.as('y')); - - var a = seconds <= thresholds.ss && ['s', seconds] || - seconds < thresholds.s && ['ss', seconds] || - minutes <= 1 && ['m'] || - minutes < thresholds.m && ['mm', minutes] || - hours <= 1 && ['h'] || - hours < thresholds.h && ['hh', hours] || - days <= 1 && ['d'] || - days < thresholds.d && ['dd', days] || - months <= 1 && ['M'] || - months < thresholds.M && ['MM', months] || - years <= 1 && ['y'] || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set the rounding function for relative time strings - function getSetRelativeTimeRounding (roundingFunction) { - if (roundingFunction === undefined) { - return round; - } - if (typeof(roundingFunction) === 'function') { - round = roundingFunction; - return true; - } - return false; - } - - // This function allows you to set a threshold for relative time strings - function getSetRelativeTimeThreshold (threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - if (threshold === 's') { - thresholds.ss = limit - 1; - } - return true; - } - - function humanize (withSuffix) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var locale = this.localeData(); - var output = relativeTime$1(this, !withSuffix, locale); - - if (withSuffix) { - output = locale.pastFuture(+this, output); - } - - return locale.postformat(output); - } - - var abs$1 = Math.abs; - - function sign(x) { - return ((x > 0) - (x < 0)) || +x; - } - - function toISOString$1() { - // for ISO strings we do not use the normal bubbling rules: - // * milliseconds bubble up until they become hours - // * days do not bubble at all - // * months bubble up until they become years - // This is because there is no context-free conversion between hours and days - // (think of clock changes) - // and also not between days and months (28-31 days per month) - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var seconds = abs$1(this._milliseconds) / 1000; - var days = abs$1(this._days); - var months = abs$1(this._months); - var minutes, hours, years; - - // 3600 seconds -> 60 minutes -> 1 hour - minutes = absFloor(seconds / 60); - hours = absFloor(minutes / 60); - seconds %= 60; - minutes %= 60; - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = years; - var M = months; - var D = days; - var h = hours; - var m = minutes; - var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; - var total = this.asSeconds(); - - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } - - var totalSign = total < 0 ? '-' : ''; - var ymSign = sign(this._months) !== sign(total) ? '-' : ''; - var daysSign = sign(this._days) !== sign(total) ? '-' : ''; - var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; - - return totalSign + 'P' + - (Y ? ymSign + Y + 'Y' : '') + - (M ? ymSign + M + 'M' : '') + - (D ? daysSign + D + 'D' : '') + - ((h || m || s) ? 'T' : '') + - (h ? hmsSign + h + 'H' : '') + - (m ? hmsSign + m + 'M' : '') + - (s ? hmsSign + s + 'S' : ''); - } - - var proto$2 = Duration.prototype; - - proto$2.isValid = isValid$1; - proto$2.abs = abs; - proto$2.add = add$1; - proto$2.subtract = subtract$1; - proto$2.as = as; - proto$2.asMilliseconds = asMilliseconds; - proto$2.asSeconds = asSeconds; - proto$2.asMinutes = asMinutes; - proto$2.asHours = asHours; - proto$2.asDays = asDays; - proto$2.asWeeks = asWeeks; - proto$2.asMonths = asMonths; - proto$2.asQuarters = asQuarters; - proto$2.asYears = asYears; - proto$2.valueOf = valueOf$1; - proto$2._bubble = bubble; - proto$2.clone = clone$1; - proto$2.get = get$2; - proto$2.milliseconds = milliseconds; - proto$2.seconds = seconds; - proto$2.minutes = minutes; - proto$2.hours = hours; - proto$2.days = days; - proto$2.weeks = weeks; - proto$2.months = months; - proto$2.years = years; - proto$2.humanize = humanize; - proto$2.toISOString = toISOString$1; - proto$2.toString = toISOString$1; - proto$2.toJSON = toISOString$1; - proto$2.locale = locale; - proto$2.localeData = localeData; - - proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); - proto$2.lang = lang; - - // Side effect imports - - // FORMATTING - - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input, 10) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - // Side effect imports - - - hooks.version = '2.24.0'; - - setHookCallback(createLocal); - - hooks.fn = proto; - hooks.min = min; - hooks.max = max; - hooks.now = now; - hooks.utc = createUTC; - hooks.unix = createUnix; - hooks.months = listMonths; - hooks.isDate = isDate; - hooks.locale = getSetGlobalLocale; - hooks.invalid = createInvalid; - hooks.duration = createDuration; - hooks.isMoment = isMoment; - hooks.weekdays = listWeekdays; - hooks.parseZone = createInZone; - hooks.localeData = getLocale; - hooks.isDuration = isDuration; - hooks.monthsShort = listMonthsShort; - hooks.weekdaysMin = listWeekdaysMin; - hooks.defineLocale = defineLocale; - hooks.updateLocale = updateLocale; - hooks.locales = listLocales; - hooks.weekdaysShort = listWeekdaysShort; - hooks.normalizeUnits = normalizeUnits; - hooks.relativeTimeRounding = getSetRelativeTimeRounding; - hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; - hooks.calendarFormat = getCalendarFormat; - hooks.prototype = proto; - - // currently HTML5 input type only supports 24-hour formats - hooks.HTML5_FMT = { - DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" /> - DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" /> - DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" /> - DATE: 'YYYY-MM-DD', // <input type="date" /> - TIME: 'HH:mm', // <input type="time" /> - TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" /> - TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" /> - WEEK: 'GGGG-[W]WW', // <input type="week" /> - MONTH: 'YYYY-MM' // <input type="month" /> - }; - - return hooks; - -}))); -}); - -var FORMATS = { - datetime: 'MMM D, YYYY, h:mm:ss a', - millisecond: 'h:mm:ss.SSS a', - second: 'h:mm:ss a', - minute: 'h:mm a', - hour: 'hA', - day: 'MMM D', - week: 'll', - month: 'MMM YYYY', - quarter: '[Q]Q - YYYY', - year: 'YYYY' -}; - -core_adapters._date.override(typeof moment === 'function' ? { - _id: 'moment', // DEBUG ONLY - - formats: function() { - return FORMATS; - }, - - parse: function(value, format) { - if (typeof value === 'string' && typeof format === 'string') { - value = moment(value, format); - } else if (!(value instanceof moment)) { - value = moment(value); - } - return value.isValid() ? value.valueOf() : null; - }, - - format: function(time, format) { - return moment(time).format(format); - }, - - add: function(time, amount, unit) { - return moment(time).add(amount, unit).valueOf(); - }, - - diff: function(max, min, unit) { - return moment(max).diff(moment(min), unit); - }, - - startOf: function(time, unit, weekday) { - time = moment(time); - if (unit === 'isoWeek') { - return time.isoWeekday(weekday).valueOf(); - } - return time.startOf(unit).valueOf(); - }, - - endOf: function(time, unit) { - return moment(time).endOf(unit).valueOf(); - }, - - // DEPRECATIONS - - /** - * Provided for backward compatibility with scale.getValueForPixel(). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(time) { - return moment(time); - }, -} : {}); - -core_defaults._set('global', { - plugins: { - filler: { - propagate: true - } - } -}); - -var mappers = { - dataset: function(source) { - var index = source.fill; - var chart = source.chart; - var meta = chart.getDatasetMeta(index); - var visible = meta && chart.isDatasetVisible(index); - var points = (visible && meta.dataset._children) || []; - var length = points.length || 0; - - return !length ? null : function(point, i) { - return (i < length && points[i]._view) || null; - }; - }, - - boundary: function(source) { - var boundary = source.boundary; - var x = boundary ? boundary.x : null; - var y = boundary ? boundary.y : null; - - if (helpers$1.isArray(boundary)) { - return function(point, i) { - return boundary[i]; - }; - } - - return function(point) { - return { - x: x === null ? point.x : x, - y: y === null ? point.y : y, - }; - }; - } -}; - -// @todo if (fill[0] === '#') -function decodeFill(el, index, count) { - var model = el._model || {}; - var fill = model.fill; - var target; - - if (fill === undefined) { - fill = !!model.backgroundColor; - } - - if (fill === false || fill === null) { - return false; - } - - if (fill === true) { - return 'origin'; - } - - target = parseFloat(fill, 10); - if (isFinite(target) && Math.floor(target) === target) { - if (fill[0] === '-' || fill[0] === '+') { - target = index + target; - } - - if (target === index || target < 0 || target >= count) { - return false; - } - - return target; - } - - switch (fill) { - // compatibility - case 'bottom': - return 'start'; - case 'top': - return 'end'; - case 'zero': - return 'origin'; - // supported boundaries - case 'origin': - case 'start': - case 'end': - return fill; - // invalid fill values - default: - return false; - } -} - -function computeLinearBoundary(source) { - var model = source.el._model || {}; - var scale = source.el._scale || {}; - var fill = source.fill; - var target = null; - var horizontal; - - if (isFinite(fill)) { - return null; - } - - // Backward compatibility: until v3, we still need to support boundary values set on - // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and - // controllers might still use it (e.g. the Smith chart). - - if (fill === 'start') { - target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; - } else if (fill === 'end') { - target = model.scaleTop === undefined ? scale.top : model.scaleTop; - } else if (model.scaleZero !== undefined) { - target = model.scaleZero; - } else if (scale.getBasePixel) { - target = scale.getBasePixel(); - } - - if (target !== undefined && target !== null) { - if (target.x !== undefined && target.y !== undefined) { - return target; - } - - if (helpers$1.isFinite(target)) { - horizontal = scale.isHorizontal(); - return { - x: horizontal ? target : null, - y: horizontal ? null : target - }; - } - } - - return null; -} - -function computeCircularBoundary(source) { - var scale = source.el._scale; - var options = scale.options; - var length = scale.chart.data.labels.length; - var fill = source.fill; - var target = []; - var start, end, center, i, point; - - if (!length) { - return null; - } - - start = options.ticks.reverse ? scale.max : scale.min; - end = options.ticks.reverse ? scale.min : scale.max; - center = scale.getPointPositionForValue(0, start); - for (i = 0; i < length; ++i) { - point = fill === 'start' || fill === 'end' - ? scale.getPointPositionForValue(i, fill === 'start' ? start : end) - : scale.getBasePosition(i); - if (options.gridLines.circular) { - point.cx = center.x; - point.cy = center.y; - point.angle = scale.getIndexAngle(i) - Math.PI / 2; - } - target.push(point); - } - return target; -} - -function computeBoundary(source) { - var scale = source.el._scale || {}; - - if (scale.getPointPositionForValue) { - return computeCircularBoundary(source); - } - return computeLinearBoundary(source); -} - -function resolveTarget(sources, index, propagate) { - var source = sources[index]; - var fill = source.fill; - var visited = [index]; - var target; - - if (!propagate) { - return fill; - } - - while (fill !== false && visited.indexOf(fill) === -1) { - if (!isFinite(fill)) { - return fill; - } - - target = sources[fill]; - if (!target) { - return false; - } - - if (target.visible) { - return fill; - } - - visited.push(fill); - fill = target.fill; - } - - return false; -} - -function createMapper(source) { - var fill = source.fill; - var type = 'dataset'; - - if (fill === false) { - return null; - } - - if (!isFinite(fill)) { - type = 'boundary'; - } - - return mappers[type](source); -} - -function isDrawable(point) { - return point && !point.skip; -} - -function drawArea(ctx, curve0, curve1, len0, len1) { - var i, cx, cy, r; - - if (!len0 || !len1) { - return; - } - - // building first area curve (normal) - ctx.moveTo(curve0[0].x, curve0[0].y); - for (i = 1; i < len0; ++i) { - helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); - } - - if (curve1[0].angle !== undefined) { - cx = curve1[0].cx; - cy = curve1[0].cy; - r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2)); - for (i = len1 - 1; i > 0; --i) { - ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true); - } - return; - } - - // joining the two area curves - ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); - - // building opposite area curve (reverse) - for (i = len1 - 1; i > 0; --i) { - helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); - } -} - -function doFill(ctx, points, mapper, view, color, loop) { - var count = points.length; - var span = view.spanGaps; - var curve0 = []; - var curve1 = []; - var len0 = 0; - var len1 = 0; - var i, ilen, index, p0, p1, d0, d1, loopOffset; - - ctx.beginPath(); - - for (i = 0, ilen = count; i < ilen; ++i) { - index = i % count; - p0 = points[index]._view; - p1 = mapper(p0, index, view); - d0 = isDrawable(p0); - d1 = isDrawable(p1); - - if (loop && loopOffset === undefined && d0) { - loopOffset = i + 1; - ilen = count + loopOffset; - } - - if (d0 && d1) { - len0 = curve0.push(p0); - len1 = curve1.push(p1); - } else if (len0 && len1) { - if (!span) { - drawArea(ctx, curve0, curve1, len0, len1); - len0 = len1 = 0; - curve0 = []; - curve1 = []; - } else { - if (d0) { - curve0.push(p0); - } - if (d1) { - curve1.push(p1); - } - } - } - } - - drawArea(ctx, curve0, curve1, len0, len1); - - ctx.closePath(); - ctx.fillStyle = color; - ctx.fill(); -} - -var plugin_filler = { - id: 'filler', - - afterDatasetsUpdate: function(chart, options) { - var count = (chart.data.datasets || []).length; - var propagate = options.propagate; - var sources = []; - var meta, i, el, source; - - for (i = 0; i < count; ++i) { - meta = chart.getDatasetMeta(i); - el = meta.dataset; - source = null; - - if (el && el._model && el instanceof elements.Line) { - source = { - visible: chart.isDatasetVisible(i), - fill: decodeFill(el, i, count), - chart: chart, - el: el - }; - } - - meta.$filler = source; - sources.push(source); - } - - for (i = 0; i < count; ++i) { - source = sources[i]; - if (!source) { - continue; - } - - source.fill = resolveTarget(sources, i, propagate); - source.boundary = computeBoundary(source); - source.mapper = createMapper(source); - } - }, - - beforeDatasetsDraw: function(chart) { - var metasets = chart._getSortedVisibleDatasetMetas(); - var ctx = chart.ctx; - var meta, i, el, view, points, mapper, color; - - for (i = metasets.length - 1; i >= 0; --i) { - meta = metasets[i].$filler; - - if (!meta || !meta.visible) { - continue; - } - - el = meta.el; - view = el._view; - points = el._children || []; - mapper = meta.mapper; - color = view.backgroundColor || core_defaults.global.defaultColor; - - if (mapper && color && points.length) { - helpers$1.canvas.clipArea(ctx, chart.chartArea); - doFill(ctx, points, mapper, view, color, el._loop); - helpers$1.canvas.unclipArea(ctx); - } - } - } -}; - -var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter; -var noop$1 = helpers$1.noop; -var valueOrDefault$e = helpers$1.valueOrDefault; - -core_defaults._set('global', { - legend: { - display: true, - position: 'top', - align: 'center', - fullWidth: true, - reverse: false, - weight: 1000, - - // a callback that will handle - onClick: function(e, legendItem) { - var index = legendItem.datasetIndex; - var ci = this.chart; - var meta = ci.getDatasetMeta(index); - - // See controller.isDatasetVisible comment - meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; - - // We hid a dataset ... rerender the chart - ci.update(); - }, - - onHover: null, - onLeave: null, - - labels: { - boxWidth: 40, - padding: 10, - // Generates labels shown in the legend - // Valid properties to return: - // text : text to display - // fillStyle : fill of coloured box - // strokeStyle: stroke of coloured box - // hidden : if this legend item refers to a hidden item - // lineCap : cap style for line - // lineDash - // lineDashOffset : - // lineJoin : - // lineWidth : - generateLabels: function(chart) { - var datasets = chart.data.datasets; - var options = chart.options.legend || {}; - var usePointStyle = options.labels && options.labels.usePointStyle; - - return chart._getSortedDatasetMetas().map(function(meta) { - var style = meta.controller.getStyle(usePointStyle ? 0 : undefined); - - return { - text: datasets[meta.index].label, - fillStyle: style.backgroundColor, - hidden: !chart.isDatasetVisible(meta.index), - lineCap: style.borderCapStyle, - lineDash: style.borderDash, - lineDashOffset: style.borderDashOffset, - lineJoin: style.borderJoinStyle, - lineWidth: style.borderWidth, - strokeStyle: style.borderColor, - pointStyle: style.pointStyle, - rotation: style.rotation, - - // Below is extra data used for toggling the datasets - datasetIndex: meta.index - }; - }, this); - } - } - }, - - legendCallback: function(chart) { - var list = document.createElement('ul'); - var datasets = chart.data.datasets; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - - for (i = 0, ilen = datasets.length; i < ilen; i++) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[i].backgroundColor; - if (datasets[i].label) { - listItem.appendChild(document.createTextNode(datasets[i].label)); - } - } - - return list.outerHTML; - } -}); - -/** - * Helper function to get the box width based on the usePointStyle option - * @param {object} labelopts - the label options on the legend - * @param {number} fontSize - the label font size - * @return {number} width of the color box area - */ -function getBoxWidth(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? - fontSize : - labelOpts.boxWidth; -} - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Legend = core_element.extend({ - - initialize: function(config) { - var me = this; - helpers$1.extend(me, config); - - // Contains hit boxes for each dataset (in dataset order) - me.legendHitBoxes = []; - - /** - * @private - */ - me._hoveredItem = null; - - // Are we in doughnut mode which has a different data type - me.doughnutMode = false; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - // Any function defined here is inherited by all legend types. - // Any function can be extended by the legend type - - beforeUpdate: noop$1, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - }, - afterUpdate: noop$1, - - // - - beforeSetDimensions: noop$1, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$1, - - // - - beforeBuildLabels: noop$1, - buildLabels: function() { - var me = this; - var labelOpts = me.options.labels || {}; - var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; - - if (labelOpts.filter) { - legendItems = legendItems.filter(function(item) { - return labelOpts.filter(item, me.chart.data); - }); - } - - if (me.options.reverse) { - legendItems.reverse(); - } - - me.legendItems = legendItems; - }, - afterBuildLabels: noop$1, - - // - - beforeFit: noop$1, - fit: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var display = opts.display; - - var ctx = me.ctx; - - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - - // Reset hit boxes - var hitboxes = me.legendHitBoxes = []; - - var minSize = me.minSize; - var isHorizontal = me.isHorizontal(); - - if (isHorizontal) { - minSize.width = me.maxWidth; // fill all the width - minSize.height = display ? 10 : 0; - } else { - minSize.width = display ? 10 : 0; - minSize.height = me.maxHeight; // fill all the height - } - - // Increase sizes here - if (!display) { - me.width = minSize.width = me.height = minSize.height = 0; - return; - } - ctx.font = labelFont.string; - - if (isHorizontal) { - // Labels - - // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one - var lineWidths = me.lineWidths = [0]; - var totalHeight = 0; - - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { - totalHeight += fontSize + labelOpts.padding; - lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; - } - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: width, - height: fontSize - }; - - lineWidths[lineWidths.length - 1] += width + labelOpts.padding; - }); - - minSize.height += totalHeight; - - } else { - var vPadding = labelOpts.padding; - var columnWidths = me.columnWidths = []; - var columnHeights = me.columnHeights = []; - var totalWidth = labelOpts.padding; - var currentColWidth = 0; - var currentColHeight = 0; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - // If too tall, go to new column - if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) { - totalWidth += currentColWidth + labelOpts.padding; - columnWidths.push(currentColWidth); // previous column width - columnHeights.push(currentColHeight); - currentColWidth = 0; - currentColHeight = 0; - } - - // Get max width - currentColWidth = Math.max(currentColWidth, itemWidth); - currentColHeight += fontSize + vPadding; - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: itemWidth, - height: fontSize - }; - }); - - totalWidth += currentColWidth; - columnWidths.push(currentColWidth); - columnHeights.push(currentColHeight); - minSize.width += totalWidth; - } - - me.width = minSize.width; - me.height = minSize.height; - }, - afterFit: noop$1, - - // Shared Methods - isHorizontal: function() { - return this.options.position === 'top' || this.options.position === 'bottom'; - }, - - // Actually draw the legend on the canvas - draw: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; - var lineDefault = globalDefaults.elements.line; - var legendHeight = me.height; - var columnHeights = me.columnHeights; - var legendWidth = me.width; - var lineWidths = me.lineWidths; - - if (!opts.display) { - return; - } - - var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width); - var ctx = me.ctx; - var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor); - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - var cursor; - - // Canvas setup - ctx.textAlign = rtlHelper.textAlign('left'); - ctx.textBaseline = 'middle'; - ctx.lineWidth = 0.5; - ctx.strokeStyle = fontColor; // for strikethrough effect - ctx.fillStyle = fontColor; // render in correct colour - ctx.font = labelFont.string; - - var boxWidth = getBoxWidth(labelOpts, fontSize); - var hitboxes = me.legendHitBoxes; - - // current position - var drawLegendBox = function(x, y, legendItem) { - if (isNaN(boxWidth) || boxWidth <= 0) { - return; - } - - // Set the ctx for the box - ctx.save(); - - var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth); - ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor); - ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle); - ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset); - ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor); - - if (ctx.setLineDash) { - // IE 9 and 10 do not support line dash - ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash)); - } - - if (labelOpts && labelOpts.usePointStyle) { - // Recalculate x and y for drawPoint() because its expecting - // x and y to be center of figure (instead of top left) - var radius = boxWidth * Math.SQRT2 / 2; - var centerX = rtlHelper.xPlus(x, boxWidth / 2); - var centerY = y + fontSize / 2; - - // Draw pointStyle as legend symbol - helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); - } else { - // Draw box as legend symbol - ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); - if (lineWidth !== 0) { - ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); - } - } - - ctx.restore(); - }; - - var fillText = function(x, y, legendItem, textWidth) { - var halfFontSize = fontSize / 2; - var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); - var yMiddle = y + halfFontSize; - - ctx.fillText(legendItem.text, xLeft, yMiddle); - - if (legendItem.hidden) { - // Strikethrough the text if hidden - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.moveTo(xLeft, yMiddle); - ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle); - ctx.stroke(); - } - }; - - var alignmentOffset = function(dimension, blockSize) { - switch (opts.align) { - case 'start': - return labelOpts.padding; - case 'end': - return dimension - blockSize; - default: // center - return (dimension - blockSize + labelOpts.padding) / 2; - } - }; - - // Horizontal - var isHorizontal = me.isHorizontal(); - if (isHorizontal) { - cursor = { - x: me.left + alignmentOffset(legendWidth, lineWidths[0]), - y: me.top + labelOpts.padding, - line: 0 - }; - } else { - cursor = { - x: me.left + labelOpts.padding, - y: me.top + alignmentOffset(legendHeight, columnHeights[0]), - line: 0 - }; - } - - helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection); - - var itemHeight = fontSize + labelOpts.padding; - helpers$1.each(me.legendItems, function(legendItem, i) { - var textWidth = ctx.measureText(legendItem.text).width; - var width = boxWidth + (fontSize / 2) + textWidth; - var x = cursor.x; - var y = cursor.y; - - rtlHelper.setWidth(me.minSize.width); - - // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) - // instead of me.right and me.bottom because me.width and me.height - // may have been changed since me.minSize was calculated - if (isHorizontal) { - if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { - y = cursor.y += itemHeight; - cursor.line++; - x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]); - } - } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { - x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; - cursor.line++; - y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]); - } - - var realX = rtlHelper.x(x); - - drawLegendBox(realX, y, legendItem); - - hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width); - hitboxes[i].top = y; - - // Fill the actual label - fillText(realX, y, legendItem, textWidth); - - if (isHorizontal) { - cursor.x += width + labelOpts.padding; - } else { - cursor.y += itemHeight; - } - }); - - helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection); - }, - - /** - * @private - */ - _getLegendItemAt: function(x, y) { - var me = this; - var i, hitBox, lh; - - if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { - // See if we are touching one of the dataset boxes - lh = me.legendHitBoxes; - for (i = 0; i < lh.length; ++i) { - hitBox = lh[i]; - - if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { - // Touching an element - return me.legendItems[i]; - } - } - } - - return null; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - */ - handleEvent: function(e) { - var me = this; - var opts = me.options; - var type = e.type === 'mouseup' ? 'click' : e.type; - var hoveredItem; - - if (type === 'mousemove') { - if (!opts.onHover && !opts.onLeave) { - return; - } - } else if (type === 'click') { - if (!opts.onClick) { - return; - } - } else { - return; - } - - // Chart event already has relative position in it - hoveredItem = me._getLegendItemAt(e.x, e.y); - - if (type === 'click') { - if (hoveredItem && opts.onClick) { - // use e.native for backwards compatibility - opts.onClick.call(me, e.native, hoveredItem); - } - } else { - if (opts.onLeave && hoveredItem !== me._hoveredItem) { - if (me._hoveredItem) { - opts.onLeave.call(me, e.native, me._hoveredItem); - } - me._hoveredItem = hoveredItem; - } - - if (opts.onHover && hoveredItem) { - // use e.native for backwards compatibility - opts.onHover.call(me, e.native, hoveredItem); - } - } - } -}); - -function createNewLegendAndAttach(chart, legendOpts) { - var legend = new Legend({ - ctx: chart.ctx, - options: legendOpts, - chart: chart - }); - - core_layouts.configure(chart, legend, legendOpts); - core_layouts.addBox(chart, legend); - chart.legend = legend; -} - -var plugin_legend = { - id: 'legend', - - /** - * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making - * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Legend, - - beforeInit: function(chart) { - var legendOpts = chart.options.legend; - - if (legendOpts) { - createNewLegendAndAttach(chart, legendOpts); - } - }, - - beforeUpdate: function(chart) { - var legendOpts = chart.options.legend; - var legend = chart.legend; - - if (legendOpts) { - helpers$1.mergeIf(legendOpts, core_defaults.global.legend); - - if (legend) { - core_layouts.configure(chart, legend, legendOpts); - legend.options = legendOpts; - } else { - createNewLegendAndAttach(chart, legendOpts); - } - } else if (legend) { - core_layouts.removeBox(chart, legend); - delete chart.legend; - } - }, - - afterEvent: function(chart, e) { - var legend = chart.legend; - if (legend) { - legend.handleEvent(e); - } - } -}; - -var noop$2 = helpers$1.noop; - -core_defaults._set('global', { - title: { - display: false, - fontStyle: 'bold', - fullWidth: true, - padding: 10, - position: 'top', - text: '', - weight: 2000 // by default greater than legend (1000) to be above - } -}); - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Title = core_element.extend({ - initialize: function(config) { - var me = this; - helpers$1.extend(me, config); - - // Contains hit boxes for each dataset (in dataset order) - me.legendHitBoxes = []; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - - beforeUpdate: noop$2, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - - }, - afterUpdate: noop$2, - - // - - beforeSetDimensions: noop$2, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$2, - - // - - beforeBuildLabels: noop$2, - buildLabels: noop$2, - afterBuildLabels: noop$2, - - // - - beforeFit: noop$2, - fit: function() { - var me = this; - var opts = me.options; - var minSize = me.minSize = {}; - var isHorizontal = me.isHorizontal(); - var lineCount, textSize; - - if (!opts.display) { - me.width = minSize.width = me.height = minSize.height = 0; - return; - } - - lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; - textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2; - - me.width = minSize.width = isHorizontal ? me.maxWidth : textSize; - me.height = minSize.height = isHorizontal ? textSize : me.maxHeight; - }, - afterFit: noop$2, - - // Shared Methods - isHorizontal: function() { - var pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - }, - - // Actually draw the title block on the canvas - draw: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - - if (!opts.display) { - return; - } - - var fontOpts = helpers$1.options._parseFont(opts); - var lineHeight = fontOpts.lineHeight; - var offset = lineHeight / 2 + opts.padding; - var rotation = 0; - var top = me.top; - var left = me.left; - var bottom = me.bottom; - var right = me.right; - var maxWidth, titleX, titleY; - - ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour - ctx.font = fontOpts.string; - - // Horizontal - if (me.isHorizontal()) { - titleX = left + ((right - left) / 2); // midpoint of the width - titleY = top + offset; - maxWidth = right - left; - } else { - titleX = opts.position === 'left' ? left + offset : right - offset; - titleY = top + ((bottom - top) / 2); - maxWidth = bottom - top; - rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); - } - - ctx.save(); - ctx.translate(titleX, titleY); - ctx.rotate(rotation); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - var text = opts.text; - if (helpers$1.isArray(text)) { - var y = 0; - for (var i = 0; i < text.length; ++i) { - ctx.fillText(text[i], 0, y, maxWidth); - y += lineHeight; - } - } else { - ctx.fillText(text, 0, 0, maxWidth); - } - - ctx.restore(); - } -}); - -function createNewTitleBlockAndAttach(chart, titleOpts) { - var title = new Title({ - ctx: chart.ctx, - options: titleOpts, - chart: chart - }); - - core_layouts.configure(chart, title, titleOpts); - core_layouts.addBox(chart, title); - chart.titleBlock = title; -} - -var plugin_title = { - id: 'title', - - /** - * Backward compatibility: since 2.1.5, the title is registered as a plugin, making - * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Title, - - beforeInit: function(chart) { - var titleOpts = chart.options.title; - - if (titleOpts) { - createNewTitleBlockAndAttach(chart, titleOpts); - } - }, - - beforeUpdate: function(chart) { - var titleOpts = chart.options.title; - var titleBlock = chart.titleBlock; - - if (titleOpts) { - helpers$1.mergeIf(titleOpts, core_defaults.global.title); - - if (titleBlock) { - core_layouts.configure(chart, titleBlock, titleOpts); - titleBlock.options = titleOpts; - } else { - createNewTitleBlockAndAttach(chart, titleOpts); - } - } else if (titleBlock) { - core_layouts.removeBox(chart, titleBlock); - delete chart.titleBlock; - } - } -}; - -var plugins = {}; -var filler = plugin_filler; -var legend = plugin_legend; -var title = plugin_title; -plugins.filler = filler; -plugins.legend = legend; -plugins.title = title; - -/** - * @namespace Chart - */ - - -core_controller.helpers = helpers$1; - -// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! -core_helpers(); - -core_controller._adapters = core_adapters; -core_controller.Animation = core_animation; -core_controller.animationService = core_animations; -core_controller.controllers = controllers; -core_controller.DatasetController = core_datasetController; -core_controller.defaults = core_defaults; -core_controller.Element = core_element; -core_controller.elements = elements; -core_controller.Interaction = core_interaction; -core_controller.layouts = core_layouts; -core_controller.platform = platform; -core_controller.plugins = core_plugins; -core_controller.Scale = core_scale; -core_controller.scaleService = core_scaleService; -core_controller.Ticks = core_ticks; -core_controller.Tooltip = core_tooltip; - -// Register built-in scales - -core_controller.helpers.each(scales, function(scale, type) { - core_controller.scaleService.registerScaleType(type, scale, scale._defaults); -}); - -// Load to register built-in adapters (as side effects) - - -// Loading built-in plugins - -for (var k in plugins) { - if (plugins.hasOwnProperty(k)) { - core_controller.plugins.register(plugins[k]); - } -} - -core_controller.platform.initialize(); - -var src = core_controller; -if (typeof window !== 'undefined') { - window.Chart = core_controller; -} - -// DEPRECATIONS - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Chart - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -core_controller.Chart = core_controller; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Legend - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Legend = plugins.legend._element; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Title - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Title = plugins.title._element; - -/** - * Provided for backward compatibility, use Chart.plugins instead - * @namespace Chart.pluginService - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.pluginService = core_controller.plugins; - -/** - * Provided for backward compatibility, inheriting from Chart.PlugingBase has no - * effect, instead simply create/register plugins via plain JavaScript objects. - * @interface Chart.PluginBase - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ -core_controller.PluginBase = core_controller.Element.extend({}); - -/** - * Provided for backward compatibility, use Chart.helpers.canvas instead. - * @namespace Chart.canvasHelpers - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -core_controller.canvasHelpers = core_controller.helpers.canvas; - -/** - * Provided for backward compatibility, use Chart.layouts instead. - * @namespace Chart.layoutService - * @deprecated since version 2.7.3 - * @todo remove at version 3 - * @private - */ -core_controller.layoutService = core_controller.layouts; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.LinearScaleBase - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -core_controller.LinearScaleBase = scale_linearbase; - -/** - * Provided for backward compatibility, instead we should create a new Chart - * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ -core_controller.helpers.each( - [ - 'Bar', - 'Bubble', - 'Doughnut', - 'Line', - 'PolarArea', - 'Radar', - 'Scatter' - ], - function(klass) { - core_controller[klass] = function(ctx, cfg) { - return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { - type: klass.charAt(0).toLowerCase() + klass.slice(1) - })); - }; - } -); - -return src; - -}))); diff --git a/lib/web/chartjs/Chart.bundle.min.js b/lib/web/chartjs/Chart.bundle.min.js deleted file mode 100644 index 55d9eb03f821a..0000000000000 --- a/lib/web/chartjs/Chart.bundle.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Chart.js v2.9.3 - * https://www.chartjs.org - * (c) 2019 Chart.js Contributors - * Released under the MIT License - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Chart=e()}(this,(function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function t(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function e(t,e){return t(e={exports:{}},e.exports),e.exports}var n={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},i=e((function(t){var e={};for(var i in n)n.hasOwnProperty(i)&&(e[n[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=e[t];if(i)return i;var a,r,o,s=1/0;for(var l in n)if(n.hasOwnProperty(l)){var u=n[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return n[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));i.rgb,i.hsl,i.hsv,i.hwb,i.cmyk,i.xyz,i.lab,i.lch,i.hex,i.keyword,i.ansi16,i.ansi256,i.hcg,i.apple,i.gray;function a(t){var e=function(){for(var t={},e=Object.keys(i),n=e.length,a=0;a<n;a++)t[e[a]]={distance:-1,parent:null};return t}(),n=[t];for(e[t].distance=0;n.length;)for(var a=n.pop(),r=Object.keys(i[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,n.unshift(l))}return e}function r(t,e){return function(n){return e(t(n))}}function o(t,e){for(var n=[e[t].parent,t],a=i[e[t].parent][t],o=e[t].parent;e[o].parent;)n.unshift(e[o].parent),a=r(i[e[o].parent][o],a),o=e[o].parent;return a.conversion=n,a}var s={};Object.keys(i).forEach((function(t){s[t]={},Object.defineProperty(s[t],"channels",{value:i[t].channels}),Object.defineProperty(s[t],"labels",{value:i[t].labels});var e=function(t){for(var e=a(t),n={},i=Object.keys(e),r=i.length,s=0;s<r;s++){var l=i[s];null!==e[l].parent&&(n[l]=o(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];s[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),s[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var l=s,u={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},d={getRgba:h,getHsla:c,getRgb:function(t){var e=h(t);return e&&e.slice(0,3)},getHsl:function(t){var e=c(t);return e&&e.slice(0,3)},getHwb:f,getAlpha:function(t){var e=h(t);if(e)return e[3];if(e=c(t))return e[3];if(e=f(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+b(t[0])+b(t[1])+b(t[2])+(e>=0&&e<1?b(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:g,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return m(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:m,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return y[t.slice(0,3)]}};function h(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=u[i[1]]))return}for(r=0;r<e.length;r++)e[r]=v(e[r],0,255);return n=n||0==n?v(n,0,1):1,e[3]=n,e}}function c(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function f(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function g(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function m(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function v(t,e,n){return Math.min(Math.max(e,t),n)}function b(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var y={};for(var x in u)y[u[x]]=x;var _=function(t){return t instanceof _?t:this instanceof _?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=d.getRgba(t))?this.setValues("rgb",e):(e=d.getHsla(t))?this.setValues("hsl",e):(e=d.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new _(t);var e};_.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return d.hexString(this.values.rgb)},rgbString:function(){return d.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return d.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return d.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return d.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return d.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return d.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return d.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new _,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},_.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},_.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},_.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},_.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,s=1;if(this.valid=!0,"alpha"===t)s=e;else if(e.length)a[t]=e.slice(0,t.length),s=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];s=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];s=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===s?a.alpha:s)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=l[t][d](a[t]));return!0},_.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},_.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=_);var w,k=_,M={noop:function(){},uid:(w=0,function(){return w++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return M.valueOrDefault(M.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(M.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(M.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!M.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(M.isArray(t))return t.map(M.clone);if(M.isObject(t)){for(var e={},n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=M.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){var a=e[t],r=n[t];M.isObject(a)&&M.isObject(r)?M.merge(a,r,i):e[t]=M.clone(r)},_mergerIf:function(t,e,n){var i=e[t],a=n[t];M.isObject(i)&&M.isObject(a)?M.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=M.clone(a))},merge:function(t,e,n){var i,a,r,o,s,l=M.isArray(e)?e:[e],u=l.length;if(!M.isObject(t))return t;for(i=(n=n||{}).merger||M._merger,a=0;a<u;++a)if(e=l[a],M.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return M.merge(t,e,{merger:M._mergerIf})},extend:Object.assign||function(t){return M.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=M.inherits,t&&M.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},S=M;M.callCallback=M.callback,M.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},M.getValueOrDefault=M.valueOrDefault,M.getValueAtIndexOrDefault=M.valueAtIndexOrDefault;var D={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-D.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*D.easeInBounce(2*t):.5*D.easeOutBounce(2*t-1)+.5}},C={effects:D};S.easingEffects=D;var P=Math.PI,T=P/180,O=2*P,A=P/2,F=P/4,I=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-P,-A),t.arc(u,l,o,-A,0),t.arc(u,d,o,0,A),t.arc(s,d,o,A,P)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-A,A),t.arc(s,l,o,A,P+A)):l<d?(t.arc(s,l,o,-P,0),t.arc(s,d,o,0,P)):t.arc(s,l,o,-P,P),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*T;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,O),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+F)*u,l=Math.sin(h+F)*u,t.arc(i-s,a-l,d,h-P,h-A),t.arc(i+l,a-s,d,h-A,h),t.arc(i+s,a+l,d,h,h+A),t.arc(i-l,a+s,d,h+A,h+P),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=F;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=F;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=F,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},R=L;S.clear=L.clear,S.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var N={_set:function(t,e){return S.merge(this[t]||(this[t]={}),e)}};N._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var W=N,Y=S.valueOrDefault;var z={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return S.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=W.global,n=Y(t.fontSize,e.defaultFontSize),i={family:Y(t.fontFamily,e.defaultFontFamily),lineHeight:S.options.toLineHeight(Y(t.lineHeight,e.defaultLineHeight),n),size:n,style:Y(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||S.isNullOrUndef(t.size)||S.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&S.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},E={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},V=E;S.log10=E.log10;var H=S,B=C,j=R,U=z,G=V,q={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};H.easing=B,H.canvas=j,H.options=U,H.math=G,H.rtl=q;var Z=function(t){H.extend(this,t),this.initialize.apply(this,arguments)};H.extend(Z.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=H.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=k(l)).valid&&(c=k(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(H.isFinite(l)&&H.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=H.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return H.isNumber(this._model.x)&&H.isNumber(this._model.y)}}),Z.extend=H.inherits;var $=Z,X=$.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),K=X;Object.defineProperty(X.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(X.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),W._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:H.noop,onComplete:H.noop}});var J={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=H.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=H.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),H.callback(t.render,[e,t],e),H.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(H.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},Q=H.options.resolve,tt=["push","pop","shift","splice","unshift"];function et(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(tt.forEach((function(e){delete t[e]})),delete t._chartjs)}}var nt=function(t,e){this.initialize(t,e)};H.extend(nt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&et(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&et(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),tt.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return H.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=H.merge({},[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&H._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:H.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=Q([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},m={cacheable:!i};if(i=i||{},H.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=Q([i[l],d[l],h[l]],g,e,m);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=Q([i[l],d[c[l]],d[l],h[l]],g,e,m);return m.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){H.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=H.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=Q([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=Q([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=Q([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),nt.extend=H.inherits;var it=nt,at=2*Math.PI;function rt(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function ot(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+at,rt(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=at,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+at,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+at),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&rt(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}W._set("global",{elements:{arc:{backgroundColor:W.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var st=$.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=H.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=at;for(;a>s;)a-=at;for(;a<o;)a+=at;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/at)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+at,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%at}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&ot(e,n,a),e.restore()}}),lt=H.valueOrDefault,ut=W.global.defaultColor;W._set("global",{elements:{line:{tension:.4,backgroundColor:ut,borderWidth:3,borderColor:ut,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var dt=$.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=W.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=H.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=lt(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=lt(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?H.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):H.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),ht=H.valueOrDefault,ct=W.global.defaultColor;function ft(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}W._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ct,borderColor:ct,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var gt=$.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ft,inXRange:ft,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=W.global,u=l.defaultColor;e.skip||(void 0===t||H.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=ht(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,H.canvas.drawPoint(n,i,r,o,s,a))}}),mt=W.global.defaultColor;function pt(t){return t&&void 0!==t.width}function vt(t){var e,n,i,a,r;return pt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function bt(t,e,n){return t===e?n:t===n?e:t}function yt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=bt(e,"left","right")):t.base<t.y&&(e=bt(e,"bottom","top")),n[e]=!0,n):n}(t);return H.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&vt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}W._set("global",{elements:{rectangle:{backgroundColor:mt,borderColor:mt,borderSkipped:"bottom",borderWidth:0}}});var _t=$.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=vt(t),n=e.right-e.left,i=e.bottom-e.top,a=yt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),wt={},kt=st,Mt=dt,St=gt,Dt=_t;wt.Arc=kt,wt.Line=Mt,wt.Point=St,wt.Rectangle=Dt;var Ct=H._deprecated,Pt=H.valueOrDefault;function Tt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=H.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return H.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}W._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),W._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Ot=it.extend({dataElementType:wt.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;it.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},H.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),m=h._parseValue(f[t].data[e]),p=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,y=void 0===m.start?0:m.max>=0&&m.min>=0?m.min:m.max,x=void 0===m.start?m.end:m.max>=0&&m.min>=0?m.max-m.min:m.min-m.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(m.min<0&&r<0||m.max>=0&&r>0)&&(y+=r));return o=h.getPixelForValue(y),l=(s=h.getPixelForValue(y+x))-o,void 0!==p&&Math.abs(l)<p&&(l=p,s=x>=0&&!c||x<0&&c?o-p:o+p),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):Tt(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(Pt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(H.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}H.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=H.extend({},it.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Pt(n.barPercentage,e.barPercentage),e.barThickness=Pt(n.barThickness,e.barThickness),e.categoryPercentage=Pt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Pt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Pt(i.minBarLength,e.minBarLength),e}}),At=H.valueOrDefault,Ft=H.options.resolve;W._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var It=it.extend({dataElementType:wt.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;H.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=At(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=At(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=At(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=it.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=H.extend({},s)),s.radius=Ft([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=H.valueOrDefault,Rt=Math.PI,Nt=2*Rt,Wt=Rt/2;W._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-Wt,circumference:Nt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return H.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Yt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,m=l.cutoutPercentage/100||0,p=l.circumference,v=r._getRingWeight(r.index);if(p<Nt){var b=l.rotation%Nt,y=(b+=b>=Rt?-Nt:b<-Rt?Nt:0)+p,x=Math.cos(b),_=Math.sin(b),w=Math.cos(y),k=Math.sin(y),M=b<=0&&y>=0||y>=Nt,S=b<=Wt&&y>=Wt||y>=Nt+Wt,D=b<=-Wt&&y>=-Wt||y>=Rt+Wt,C=b===-Rt||y>=Rt?-1:Math.min(x,x*m,w,w*m),P=D?-1:Math.min(_,_*m,k,k*m),T=M?1:Math.max(x,x*m,w,w*m),O=S?1:Math.max(_,_*m,k,k*m);u=(T-C)/2,d=(O-P)/2,h=-(T+C)/2,c=-(O+P)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*m,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/Nt),g=n&&s.animateScale?0:i.innerRadius,m=n&&s.animateScale?0:i.outerRadius,p=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:p.backgroundColor,borderColor:p.borderColor,borderWidth:p.borderWidth,borderAlign:p.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:m,innerRadius:g,label:H.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return H.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Nt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});W._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),W._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var zt=Ot.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Et=H.valueOrDefault,Vt=H.options.resolve,Ht=H.canvas._isPointInArea;function Bt(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function jt(t,e,n){var i=n/2,a=Bt(t,i),r=Bt(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function Ut(t){var e,n,i,a;return H.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}W._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Gt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Et(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Et(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=it.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Et(n.spanGaps,a.spanGaps),o.tension=Et(n.lineTension,r.tension),o.steppedLine=Vt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=Ut(Et(n.clip,jt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)H.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=H.splineCurve(H.previousItem(l,t)._model,n,H.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Ht(n,s)&&(t>0&&Ht(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Ht(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,H.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),H.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Et(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Et(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Et(n.hoverBorderWidth,n.borderWidth),e.radius=Et(n.hoverRadius,n.radius)}}),qt=H.options.resolve;W._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Zt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],m=g+(t.hidden?0:i._angles[e]),p=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?p:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:m,label:H.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return H.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor,a=H.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return qt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});W._set("pie",H.clone(W.doughnut)),W._set("pie",{cutoutPercentage:0});var $t=Yt,Xt=H.valueOrDefault;W._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var Kt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,linkScales:H.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Xt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=it.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Xt(e.spanGaps,n.spanGaps),i.tension=Xt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=H.splineCurve(H.previousItem(o,t,!0)._model,n,H.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Xt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Xt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Xt(n.hoverBorderWidth,n.borderWidth),e.radius=Xt(n.hoverRadius,n.radius)}});W._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),W._set("global",{datasets:{scatter:{showLine:!1}}});var Jt={bar:Ot,bubble:It,doughnut:Yt,horizontalBar:zt,line:Gt,polarArea:Zt,pie:$t,radar:Kt,scatter:Gt};function Qt(t,e){return t.native?{x:t.x,y:t.y}:H.getRelativePosition(t,e)}function te(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function ee(t,e){var n=[];return te(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ne(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return te(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ie(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ae(t,e,n){var i=Qt(e,t);n.axis=n.axis||"x";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var re={modes:{single:function(t,e){var n=Qt(e,t),i=[];return te(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ae,index:ae,dataset:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ae(t,e,{intersect:!1})},point:function(t,e){return ee(t,Qt(e,t))},nearest:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis);return ne(t,i,n.intersect,a)},x:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},oe=H.extend;function se(t,e){return H.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function ue(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function de(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-ue(o,t,"left","right"),a=e.outerHeight-ue(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function he(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function ce(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,he(r.horizontal,e)),de(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&ce(u,e,n)||l}function fe(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}W._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ge,me={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=H.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=le(se(e,"left"),!0),i=le(se(e,"right")),a=le(se(e,"top"),!0),r=le(se(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:se(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=oe({maxPadding:oe({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),ce(l,h,d),ce(u,h,d)&&ce(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),fe(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,fe(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},H.each(s.chartArea,(function(e){var n=e.box;oe(n,t.chartArea),n.update(h.w,h.h)}))}}},pe=(ge=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&ge.default||ge,ve="$chartjs",be="chartjs-size-monitor",ye="chartjs-render-monitor",xe="chartjs-render-animation",_e=["animationstart","webkitAnimationStart"],we={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=H.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var Me=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Se(t,e,n){t.addEventListener(e,n,Me)}function De(t,e,n){t.removeEventListener(e,n,Me)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Te(t,e,n){var i,a,r,o,s=t[ve]||(t[ve]={}),l=s.resizer=function(t){var e=Pe(be),n=Pe(be+"-expand"),i=Pe(be+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Se(n,"scroll",a.bind(n,"expand")),Se(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Ce("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,H.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[ve]||(t[ve]={}),i=n.renderProxy=function(t){t.animationName===xe&&e()};H.each(_e,(function(e){Se(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(ye)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function Oe(t){var e=t[ve]||{},n=e.resizer;delete e.resizer,function(t){var e=t[ve]||{},n=e.renderProxy;n&&(H.each(_e,(function(e){De(t,e,n)})),delete e.renderProxy),t.classList.remove(ye)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Ae={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[ve]||(t[ve]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[ve]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=ke(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=ke(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[ve]){var n=e[ve].initial;["height","width"].forEach((function(t){var i=n[t];H.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),H.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[ve]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[ve]||(n[ve]={});Se(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=we[t.type]||t.type,i=H.getRelativePosition(t,e);return Ce(n,e,i.x,i.y,t)}(e,t))})}else Te(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[ve]||{}).proxies||{})[t.id+"_"+e];a&&De(i,e,a)}else Oe(i)}};H.addEvent=Se,H.removeEvent=De;var Fe=Ae._enabled?Ae:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Ie=H.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Fe);W._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=H.clone(W.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Re={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=H.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?H.merge({},[W.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=H.extend(this.defaults[t],e))},addScalesToLayout:function(t){H.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,me.addBox(t,e)}))}},Ne=H.valueOrDefault,We=H.rtl.getRtlAdapter;W._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:H.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:H.noop,beforeBody:H.noop,beforeLabel:H.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),H.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:H.noop,afterBody:H.noop,beforeFooter:H.noop,footer:H.noop,afterFooter:H.noop}}});var Ye={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=H.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function ze(t,e){return e&&(H.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ee(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Ve(t){var e=W.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Ne(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Ne(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Ne(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Ne(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Ne(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Ne(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Ne(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Ne(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Ne(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function He(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Be(t){return ze([],Ee(t))}var je=$.extend({initialize:function(){this._model=Ve(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=ze(o,Ee(i)),o=ze(o,Ee(a)),o=ze(o,Ee(r))},getBeforeBody:function(){return Be(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return H.each(t,(function(t){var r={before:[],lines:[],after:[]};ze(r.before,Ee(i.beforeLabel.call(n,t,e))),ze(r.lines,i.label.call(n,t,e)),ze(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return Be(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=ze(r,Ee(n)),r=ze(r,Ee(i)),r=ze(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=Ve(c),m=h._active,p=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},y={width:f.width,height:f.height},x={x:f.caretX,y:f.caretY};if(m.length){g.opacity=1;var _=[],w=[];x=Ye[c.position].call(h,m,h._eventPosition);var k=[];for(e=0,n=m.length;e<n;++e)k.push((i=m[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(k=k.filter((function(t){return c.filter(t,p)}))),c.itemSort&&(k=k.sort((function(t,e){return c.itemSort(t,e,p)}))),H.each(k,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),w.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(k,p),g.beforeBody=h.getBeforeBody(k,p),g.body=h.getBody(k,p),g.afterBody=h.getAfterBody(k,p),g.footer=h.getFooter(k,p),g.x=x.x,g.y=x.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=w,g.dataPoints=k,y=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=H.fontString(u,e._titleFontStyle,e._titleFontFamily),H.each(e.title,f),n.font=H.fontString(d,e._bodyFontStyle,e._bodyFontFamily),H.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,H.each(r,(function(t){H.each(t.before,f),H.each(t.lines,f),H.each(t.after,f)})),c=0,n.font=H.fontString(h,e._footerFontStyle,e._footerFontFamily),H.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,y,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,y),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=y.width,g.height=y.height,g.caretX=x.x,g.caretY=x.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,m=e.width,p=e.height;if("center"===c)s=g+p/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+m)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+m-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+p)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=We(e.rtl,e.x,e.width);for(t.x=He(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=H.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,m=e.displayColors,p=0,v=m?He(e,"left"):0,b=We(e.rtl,e.x,e.width),y=function(e){n.fillText(e,b.x(t.x+p),t.y+h/2),t.y+=h+c},x=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=H.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=He(e,x),n.fillStyle=e.bodyFontColor,H.each(e.beforeBody,y),p=m&&"right"!==x?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,H.each(i.before,y),l=0,d=(o=i.lines).length;l<d;++l){if(m){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}y(o[l])}H.each(i.after,y)}p=0,H.each(e.afterBody,y),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=We(e.rtl,e.x,e.width);for(t.x=He(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=H.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,H.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),H.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!H.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ue=Ye,Ge=je;Ge.positioners=Ue;var qe=H.valueOrDefault;function Ze(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=qe(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?H.merge(e[t][a],[Re.getScaleDefaults(r),o]):H.merge(e[t][a],o)}else H._merger(t,e,n,i)}})}function $e(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ze(a,r):"scale"===t?e[t]=H.merge(a,[Re.getScaleDefaults(r.type),r]):H._merger(t,e,n,i)}})}function Xe(t){var e=t.options;H.each(t.scales,(function(e){me.removeBox(t,e)})),e=$e(W.global,W[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ke(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(H.findIndex(t,a)>=0);return i}function Je(t){return"top"===t||"bottom"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}W._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};H.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=$e(W.global,W[t.type],t.options||{}),t}(e);var i=Ie.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=H.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),H.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return H.canvas.clear(this),this},stop:function(){return J.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(H.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:H.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",H.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;H.each(e.xAxes,(function(t,n){t.id||(t.id=Ke(e.xAxes,"x-axis-",n))})),H.each(e.yAxes,(function(t,n){t.id||(t.id=Ke(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),H.each(i,(function(e){var i=e.options,r=i.id,o=qe(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Re.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),H.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Re.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=Jt[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;H.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Xe(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&H.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Qe("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(me.update(this,this.width,this.height),t._layers=[],H.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=qe(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),H.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new K({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=H.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});J.addAnimation(e,o,i,a)}else e.draw(),r(new K({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),H.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Qe("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return re.modes.single(this,t)},getElementsAtEvent:function(t){return re.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return re.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=re.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return re.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),H.canvas.clear(n),Ie.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete tn.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ge({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};H.each(t.options.events,(function(i){Ie.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Ie.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,H.each(e,(function(e,n){Ie.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),H.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!H.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),tn.instances={};var en=tn;tn.Controller=tn,tn.types={},H.configMerge=$e,H.scaleMerge=Ze;function nn(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function an(t){this.options=t||{}}H.extend(an.prototype,{formats:nn,parse:nn,format:nn,add:nn,diff:nn,startOf:nn,endOf:nn,_create:function(t){return t}}),an.override=function(t){H.extend(an.prototype,t)};var rn={_date:an},on={formatters:{values:function(t){return H.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=H.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=H.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(H.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},sn=H.isArray,ln=H.isNullOrUndef,un=H.valueOrDefault,dn=H.valueAtIndexOrDefault;function hn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function cn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,m,p,v=n.length,b=[],y=[],x=[];for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,ln(s)||sn(s)){if(sn(s))for(r=0,o=s.length;r<o;++r)g=s[r],ln(g)||sn(g)||(c=H.measureText(t,d.data,d.gc,c,g),f+=h)}else c=H.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),y.push(f),x.push(h/2)}function _(t){return{width:b[t]||0,height:y[t]||0,offset:x[t]||0}}return function(t,e){H.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),m=b.indexOf(Math.max.apply(null,b)),p=y.indexOf(Math.max.apply(null,y)),{first:_(0),last:_(v-1),widest:_(m),highest:_(p)}}function fn(t){return t.drawTicks?t.tickMarkLength:0}function gn(t){var e,n;return t.display?(e=H.options._parseFont(t),n=H.options.toPadding(t.padding),e.lineHeight+n.height):0}function mn(t,e){return H.extend(H.options._parseFont({fontFamily:un(e.fontFamily,t.fontFamily),fontSize:un(e.fontSize,t.fontSize),fontStyle:un(e.fontStyle,t.fontStyle),lineHeight:un(e.lineHeight,t.lineHeight)}),{color:H.options.resolve([e.fontColor,t.fontColor,W.global.defaultFontColor])})}function pn(t){var e=mn(t,t.minor);return{minor:e,major:t.major.enabled?mn(t,t.major):e}}function vn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function bn(t,e,n,i){var a,r,o,s,l=un(n,0),u=Math.min(un(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}W._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:on.formatters.values,minor:{},major:{}}});var yn=$.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){H.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=H.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){H.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){H.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){H.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){H.callback(this.options.beforeDataLimits,[this])},determineDataLimits:H.noop,afterDataLimits:function(){H.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){H.callback(this.options.beforeBuildTicks,[this])},buildTicks:H.noop,afterBuildTicks:function(t){var e=this;return sn(t)&&t.length?H.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=H.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){H.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){H.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){H.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-fn(l.gridLines)-u.padding-gn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=H.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=fn(o)+gn(r)),u?s&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,m=h.highest,p=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,y=H.toRadians(t.labelRotation),x=Math.cos(y),_=Math.sin(y),w=_*g.width+x*(m.height-(b?m.offset:0))+(b?0:p);e.height=Math.min(t.maxHeight,e.height+w+v);var k,M,S=t.getPixelForTick(0)-t.left,D=t.right-t.getPixelForTick(t.getTicks().length-1);b?(k=l?x*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):x*f.width+_*f.offset):(k=c.width/2,M=f.width/2),t.paddingLeft=Math.max((k-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-D)*t.width/(t.width-D),0)+3}else{var C=a.mirror?0:g.width+v+p;e.width=Math.min(t.maxWidth,e.width+C),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ln(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=cn(t.ctx,pn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return sn(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:H.noop,getPixelForValue:H.noop,getValueForPixel:H.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),vn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=H.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)bn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,bn(t,i,H.isNullOrUndef(a)?0:d-a,d),bn(t,i,h,H.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this.options.ticks,e=H.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,m,p,v,b=this,y=b.chart,x=b.options,_=x.gridLines,w=x.position,k=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,D=S.length+(k?1:0),C=fn(_),P=[],T=_.drawBorder?dn(_.lineWidth,0,0):0,O=T/2,A=H._alignPixel,F=function(t){return A(y,t,T)};for("top"===w?(e=F(b.bottom),s=b.bottom-C,u=e-O,h=F(t.top)+O,f=t.bottom):"bottom"===w?(e=F(b.top),h=t.top,f=F(t.bottom)-O,s=e+O,u=b.top+C):"left"===w?(e=F(b.right),o=b.right-C,l=e-O,d=F(t.left)+O,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-O,o=e+O,l=b.left+C),n=0;n<D;++n)i=S[n]||{},ln(i.label)&&n<S.length||(n===b.zeroLineIndex&&x.offset===k?(g=_.zeroLineWidth,m=_.zeroLineColor,p=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=dn(_.lineWidth,n,1),m=dn(_.color,n,"rgba(0,0,0,0.1)"),p=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=hn(b,i._index||n,k))&&(r=A(y,a,g),M?o=l=d=c=r:s=u=h=f=r,P.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:m,borderDash:p,borderDashOffset:v})));return P.ticksLength=D,P.borderValue=e,P},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,m=f.position,p=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,y=pn(g),x=g.padding,_=fn(f.gridLines),w=-H.toRadians(c.labelRotation),k=[];for("top"===m?(r=c.bottom-_-x,o=w?"left":"center"):"bottom"===m?(r=c.top+_+x,o=w?"right":"center"):"left"===m?(a=c.right-(p?0:_)-x,o=p?"left":"right"):(a=c.left+(p?0:_)+x,o=p?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,ln(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?y.major:y.minor).lineHeight,d=sn(i)?i.length:1,v?(a=s,h="top"===m?((w?1:.5)-d)*u:(w?0:.5)*u):(r=s,h=(1-d)*u/2),k.push({x:a,y:r,rotation:w,label:i,font:l,textOffset:h,textAlign:o}));return k},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=H._alignPixel,h=n.drawBorder?dn(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,m,p,v=h,b=dn(n.lineWidth,c.ticksLength-1,1),y=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,m=p=y):(m=d(u,e.top,v)-v/2,p=d(u,e.bottom,b)+b/2,f=g=y),l.lineWidth=h,l.strokeStyle=dn(n.color,0),l.beginPath(),l.moveTo(f,m),l.lineTo(g,p),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,sn(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=un(i.fontColor,W.global.defaultFontColor),s=H.options._parseFont(i),l=H.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});yn.prototype._draw=yn.prototype.draw;var xn=yn,_n=H.isNullOrUndef,wn=xn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=H.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};wn._defaults=kn;var Mn=H.noop,Sn=H.isNullOrUndef;var Dn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=H.sign(t.min),i=H.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:H.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,m=H.niceNum((g-f)/u/l)*l;if(m<1e-14&&Sn(d)&&Sn(h))return[f,g];(r=Math.ceil(g/m)-Math.floor(f/m))>u&&(m=H.niceNum(r*m/u/l)*l),s||Sn(c)?n=Math.pow(10,H._decimalPlaces(m)):(n=Math.pow(10,c),m=Math.ceil(m*n)/n),i=Math.floor(f/m)*m,a=Math.ceil(g/m)*m,s&&(!Sn(d)&&H.almostWhole(d/m,m/1e3)&&(i=d),!Sn(h)&&H.almostWhole(h/m,m/1e3)&&(a=h)),r=(a-i)/m,r=H.almostEquals(r,Math.round(r),m/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var p=1;p<r;++p)o.push(Math.round((i+p*m)*n)/n);return o.push(Sn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=H.max(a),t.min=H.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),xn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;xn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Cn={position:"left",ticks:{callback:on.formatters.linear}};function Pn(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function Tn(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var On=Dn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?Pn(a,u,e,n):Tn(a,e,n);H.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,H.min(i)),a.max=Math.max(a.max,H.max(i))})),a.min=H.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=H.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=H.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),An=Cn;On._defaults=An;var Fn=H.valueOrDefault,In=H.math.log10;var Ln={position:"left",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return H.isFinite(t)&&t>=0?t:e}var Nn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var m=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(m[a]=m[a]||0,m[a]+=n.max)}}H.each(f,(function(t){if(t.length>0){var e=H.min(t),n=H.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=H.isFinite(o.min)?o.min:null,o.max=H.isFinite(o.max)?o.max:null,o.minNotZero=H.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=Rn(e.min,t.min),t.max=Rn(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(In(t.min))-1),t.max=Math.pow(10,Math.floor(In(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(In(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(In(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(In(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(In(e.min)))),o=Math.floor(In(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(In(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(In(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=Fn(t.max,r);return a.push(u),a}(i,t);t.max=H.max(a),t.min=H.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),xn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(In(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,W.global.defaultFontSize)/t._length),t._startValue=In(e),t._valueOffset=n,t._valueRange=(In(t.max)-In(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(In(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Wn=Ln;Nn._defaults=Wn;var Yn=H.valueOrDefault,zn=H.valueAtIndexOrDefault,En=H.options.resolve,Vn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Yn(e.fontSize,W.global.defaultFontSize)+2*e.backdropPaddingY:0}function Bn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(H.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Gn(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function qn(t){return H.isNumber(t)?t:0}var Zn=Dn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;H.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);H.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Dn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=H.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=H.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=H.isArray(u)?{w:H.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=H.toDegrees(h)%360,f=Bn(c,i.x,n.w,0,180),g=Bn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=qn(a),r=qn(r),o=qn(o),s=qn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(H.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Yn(s.lineWidth,o.lineWidth),u=Yn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=H.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=zn(i.fontColor,s,W.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=H.toDegrees(h);e.textAlign=jn(c),Gn(c,t._pointLabelSizes[s],u),Un(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&H.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=zn(e.color,i-1),u=zn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(En([s.borderDash,o.borderDash,[]])),a.lineDashOffset=En([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=H.options._parseFont(n),s=Yn(n.fontColor,W.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",H.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:H.noop}),$n=Vn;Zn._defaults=$n;var Xn=H._deprecated,Kn=H.options.resolve,Jn=H.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return H.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return H.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function si(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),H.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),H.isFinite(o)||(o=n.parse(o))),o)}function li(t,e){if(H.isNullOrUndef(e))return null;var n=t.options.time,i=si(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function ui(t,e,n,i){var a,r,o,s=ni.length;for(a=ni.indexOf(t);a<s-1;++a)if(o=(r=ei[ni[a]]).steps?r.steps:ti,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ni[a];return ni[s-1]}function di(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var hi=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Xn("time scale",n.format,"time.format","time.parser"),Xn("time scale",n.min,"time.min","ticks.min"),Xn("time scale",n.max,"time.max","ticks.max"),H.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=ti,f=Qn,g=[],m=[],p=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)p.push(li(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,H.isObject(a[0]))for(m[t]=[],e=0,i=a.length;e<i;++e)r=li(s,a[e]),g.push(r),m[t][e]=r;else m[t]=p.slice(0),o||(g=g.concat(p),o=!0);else m[t]=[];p.length&&(c=Math.min(c,p[0]),f=Math.max(f,p[p.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ii):g.sort(ii),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=li(s,ai(d))||c,f=li(s,ri(d))||f,c=c===ti?+u.startOf(Date.now(),h):c,f=f===Qn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:m,labels:p}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||ui(s.minUnit,e,n,i),u=Kn([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=li(i,ai(o))||a,r=li(i,ri(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?ui(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;e<n;++e)if(ei[ni[e]].common)return ni[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=oi(t,"time",e[0],"pos"),s=1===e.length?1-r:(oi(t,"time",e[1],"pos")-r)/2,o=oi(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-oi(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),di(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return H.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(si(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(si(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,m=Kn([g.callback,g.userCallback,h.callback,h.userCallback]);return m?m(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=oi(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=li(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=oi(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=H.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=Jn(e.fontSize,W.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,di(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),ci={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};hi._defaults=ci;var fi={category:wn,linear:On,logarithmic:Nn,radialLinear:Zn,time:hi},gi=e((function(e,n){e.exports=function(){var n,i;function a(){return n.apply(null,arguments)}function r(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function l(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function d(t,e){var n,i=[];for(n=0;n<t.length;++n)i.push(e(t[n],n));return i}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function c(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function f(t,e,n,i){return Ie(t,e,n,i,!0).utc()}function g(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function m(t){if(null==t._isValid){var e=g(t),n=i.call(e.parsedDateParts,(function(t){return null!=t})),a=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(a=a&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return a;t._isValid=a}return t._isValid}function p(t){var e=f(NaN);return null!=t?c(g(e),t):g(e).userInvalidated=!0,e}i=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,i=0;i<n;i++)if(i in e&&t.call(this,e[i],i,e))return!0;return!1};var v=a.momentProperties=[];function b(t,e){var n,i,a;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=g(e)),s(e._locale)||(t._locale=e._locale),v.length>0)for(n=0;n<v.length;n++)s(a=e[i=v[n]])||(t[i]=a);return t}var y=!1;function x(t){b(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===y&&(y=!0,a.updateOffset(this),y=!1)}function _(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function w(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function k(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=w(e)),n}function M(t,e,n){var i,a=Math.min(t.length,e.length),r=Math.abs(t.length-e.length),o=0;for(i=0;i<a;i++)(n&&t[i]!==e[i]||!n&&k(t[i])!==k(e[i]))&&o++;return o+r}function S(t){!1===a.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function D(t,e){var n=!0;return c((function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,t),n){for(var i,r=[],o=0;o<arguments.length;o++){if(i="","object"==typeof arguments[o]){for(var s in i+="\n["+o+"] ",arguments[0])i+=s+": "+arguments[0][s]+", ";i=i.slice(0,-2)}else i=arguments[o];r.push(i)}S(t+"\nArguments: "+Array.prototype.slice.call(r).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var C,P={};function T(t,e){null!=a.deprecationHandler&&a.deprecationHandler(t,e),P[t]||(S(e),P[t]=!0)}function O(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function A(t,e){var n,i=c({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(i[n]={},c(i[n],t[n]),c(i[n],e[n])):null!=e[n]?i[n]=e[n]:delete i[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(i[n]=c({},i[n]));return i}function F(t){null!=t&&this.set(t)}a.suppressDeprecationWarnings=!1,a.deprecationHandler=null,C=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var I={};function L(t,e){var n=t.toLowerCase();I[n]=I[n+"s"]=I[e]=t}function R(t){return"string"==typeof t?I[t]||I[t.toLowerCase()]:void 0}function N(t){var e,n,i={};for(n in t)h(t,n)&&(e=R(n))&&(i[e]=t[n]);return i}var W={};function Y(t,e){W[t]=e}function z(t,e,n){var i=""+Math.abs(t),a=e-i.length;return(t>=0?n?"+":"":"-")+Math.pow(10,Math.max(0,a)).toString().substr(1)+i}var E=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,V=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,H={},B={};function j(t,e,n,i){var a=i;"string"==typeof i&&(a=function(){return this[i]()}),t&&(B[t]=a),e&&(B[e[0]]=function(){return z(a.apply(this,arguments),e[1],e[2])}),n&&(B[n]=function(){return this.localeData().ordinal(a.apply(this,arguments),t)})}function U(t,e){return t.isValid()?(e=G(e,t.localeData()),H[e]=H[e]||function(t){var e,n,i,a=t.match(E);for(e=0,n=a.length;e<n;e++)B[a[e]]?a[e]=B[a[e]]:a[e]=(i=a[e]).match(/\[[\s\S]/)?i.replace(/^\[|\]$/g,""):i.replace(/\\/g,"");return function(e){var i,r="";for(i=0;i<n;i++)r+=O(a[i])?a[i].call(e,t):a[i];return r}}(e),H[e](t)):t.localeData().invalidDate()}function G(t,e){var n=5;function i(t){return e.longDateFormat(t)||t}for(V.lastIndex=0;n>=0&&V.test(t);)t=t.replace(V,i),V.lastIndex=0,n-=1;return t}var q=/\d/,Z=/\d\d/,$=/\d{3}/,X=/\d{4}/,K=/[+-]?\d{6}/,J=/\d\d?/,Q=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,it=/[+-]?\d{1,6}/,at=/\d+/,rt=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,lt=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function dt(t,e,n){ut[t]=O(e)?e:function(t,i){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ct(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,i,a){return e||n||i||a}))))}function ct(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var ft={};function gt(t,e){var n,i=e;for("string"==typeof t&&(t=[t]),l(e)&&(i=function(t,n){n[e]=k(t)}),n=0;n<t.length;n++)ft[t[n]]=i}function mt(t,e){gt(t,(function(t,n,i,a){i._w=i._w||{},e(t,i._w,i,a)}))}function pt(t,e,n){null!=e&&h(ft,t)&&ft[t](e,n._a,n,t)}var vt=0,bt=1,yt=2,xt=3,_t=4,wt=5,kt=6,Mt=7,St=8;function Dt(t){return Ct(t)?366:365}function Ct(t){return t%4==0&&t%100!=0||t%400==0}j("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),j(0,["YY",2],0,(function(){return this.year()%100})),j(0,["YYYY",4],0,"year"),j(0,["YYYYY",5],0,"year"),j(0,["YYYYYY",6,!0],0,"year"),L("year","y"),Y("year",1),dt("Y",rt),dt("YY",J,Z),dt("YYYY",nt,X),dt("YYYYY",it,K),dt("YYYYYY",it,K),gt(["YYYYY","YYYYYY"],vt),gt("YYYY",(function(t,e){e[vt]=2===t.length?a.parseTwoDigitYear(t):k(t)})),gt("YY",(function(t,e){e[vt]=a.parseTwoDigitYear(t)})),gt("Y",(function(t,e){e[vt]=parseInt(t,10)})),a.parseTwoDigitYear=function(t){return k(t)+(k(t)>68?1900:2e3)};var Pt,Tt=Ot("FullYear",!0);function Ot(t,e){return function(n){return null!=n?(Ft(this,t,n),a.updateOffset(this,e),this):At(this,t)}}function At(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function Ft(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&Ct(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),It(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function It(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=function(t,e){return(t%e+e)%e}(e,12);return t+=(e-n)/12,1===n?Ct(t)?29:28:31-n%7%2}Pt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},j("M",["MM",2],"Mo",(function(){return this.month()+1})),j("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),j("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),Y("month",8),dt("M",J),dt("MM",J,Z),dt("MMM",(function(t,e){return e.monthsShortRegex(t)})),dt("MMMM",(function(t,e){return e.monthsRegex(t)})),gt(["M","MM"],(function(t,e){e[bt]=k(t)-1})),gt(["MMM","MMMM"],(function(t,e,n,i){var a=n._locale.monthsParse(t,i,n._strict);null!=a?e[bt]=a:g(n).invalidMonth=t}));var Lt=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Rt="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Nt="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Wt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],i=0;i<12;++i)r=f([2e3,i]),this._shortMonthsParse[i]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[i]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null}function Yt(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=k(e);else if(!l(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),It(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function zt(t){return null!=t?(Yt(this,t),a.updateOffset(this,!0),this):At(this,"Month")}var Et=lt,Vt=lt;function Ht(){function t(t,e){return e.length-t.length}var e,n,i=[],a=[],r=[];for(e=0;e<12;e++)n=f([2e3,e]),i.push(this.monthsShort(n,"")),a.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(i.sort(t),a.sort(t),r.sort(t),e=0;e<12;e++)i[e]=ct(i[e]),a[e]=ct(a[e]);for(e=0;e<24;e++)r[e]=ct(r[e]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+i.join("|")+")","i")}function Bt(t,e,n,i,a,r,o){var s;return t<100&&t>=0?(s=new Date(t+400,e,n,i,a,r,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,i,a,r,o),s}function jt(t){var e;if(t<100&&t>=0){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Ut(t,e,n){var i=7+e-n;return-(7+jt(t,0,i).getUTCDay()-e)%7+i-1}function Gt(t,e,n,i,a){var r,o,s=1+7*(e-1)+(7+n-i)%7+Ut(t,i,a);return s<=0?o=Dt(r=t-1)+s:s>Dt(t)?(r=t+1,o=s-Dt(t)):(r=t,o=s),{year:r,dayOfYear:o}}function qt(t,e,n){var i,a,r=Ut(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?i=o+Zt(a=t.year()-1,e,n):o>Zt(t.year(),e,n)?(i=o-Zt(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function Zt(t,e,n){var i=Ut(t,e,n),a=Ut(t+1,e,n);return(Dt(t)-i+a)/7}function $t(t,e){return t.slice(e,7).concat(t.slice(0,e))}j("w",["ww",2],"wo","week"),j("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),Y("week",5),Y("isoWeek",5),dt("w",J),dt("ww",J,Z),dt("W",J),dt("WW",J,Z),mt(["w","ww","W","WW"],(function(t,e,n,i){e[i.substr(0,1)]=k(t)})),j("d",0,"do","day"),j("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),j("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),j("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),j("e",0,0,"weekday"),j("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),Y("day",11),Y("weekday",11),Y("isoWeekday",11),dt("d",J),dt("e",J),dt("E",J),dt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),dt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),dt("dddd",(function(t,e){return e.weekdaysRegex(t)})),mt(["dd","ddd","dddd"],(function(t,e,n,i){var a=n._locale.weekdaysParse(t,i,n._strict);null!=a?e.d=a:g(n).invalidWeekday=t})),mt(["d","e","E"],(function(t,e,n,i){e[i]=k(t)}));var Xt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Kt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Jt="Su_Mo_Tu_We_Th_Fr_Sa".split("_");function Qt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=f([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null}var te=lt,ee=lt,ne=lt;function ie(){function t(t,e){return e.length-t.length}var e,n,i,a,r,o=[],s=[],l=[],u=[];for(e=0;e<7;e++)n=f([2e3,1]).day(e),i=this.weekdaysMin(n,""),a=this.weekdaysShort(n,""),r=this.weekdays(n,""),o.push(i),s.push(a),l.push(r),u.push(i),u.push(a),u.push(r);for(o.sort(t),s.sort(t),l.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ct(s[e]),l[e]=ct(l[e]),u[e]=ct(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function ae(){return this.hours()%12||12}function re(t,e){j(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function oe(t,e){return e._meridiemParse}j("H",["HH",2],0,"hour"),j("h",["hh",2],0,ae),j("k",["kk",2],0,(function(){return this.hours()||24})),j("hmm",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)})),j("hmmss",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)+z(this.seconds(),2)})),j("Hmm",0,0,(function(){return""+this.hours()+z(this.minutes(),2)})),j("Hmmss",0,0,(function(){return""+this.hours()+z(this.minutes(),2)+z(this.seconds(),2)})),re("a",!0),re("A",!1),L("hour","h"),Y("hour",13),dt("a",oe),dt("A",oe),dt("H",J),dt("h",J),dt("k",J),dt("HH",J,Z),dt("hh",J,Z),dt("kk",J,Z),dt("hmm",Q),dt("hmmss",tt),dt("Hmm",Q),dt("Hmmss",tt),gt(["H","HH"],xt),gt(["k","kk"],(function(t,e,n){var i=k(t);e[xt]=24===i?0:i})),gt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),gt(["h","hh"],(function(t,e,n){e[xt]=k(t),g(n).bigHour=!0})),gt("hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i)),g(n).bigHour=!0})),gt("hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a)),g(n).bigHour=!0})),gt("Hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i))})),gt("Hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a))}));var se,le=Ot("Hours",!0),ue={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Rt,monthsShort:Nt,week:{dow:0,doy:6},weekdays:Xt,weekdaysMin:Jt,weekdaysShort:Kt,meridiemParse:/[ap]\.?m?\.?/i},de={},he={};function ce(t){return t?t.toLowerCase().replace("_","-"):t}function fe(n){var i=null;if(!de[n]&&e&&e.exports)try{i=se._abbr,t(),ge(i)}catch(t){}return de[n]}function ge(t,e){var n;return t&&((n=s(e)?pe(t):me(t,e))?se=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),se._abbr}function me(t,e){if(null!==e){var n,i=ue;if(e.abbr=t,null!=de[t])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=de[t]._config;else if(null!=e.parentLocale)if(null!=de[e.parentLocale])i=de[e.parentLocale]._config;else{if(null==(n=fe(e.parentLocale)))return he[e.parentLocale]||(he[e.parentLocale]=[]),he[e.parentLocale].push({name:t,config:e}),null;i=n._config}return de[t]=new F(A(i,e)),he[t]&&he[t].forEach((function(t){me(t.name,t.config)})),ge(t),de[t]}return delete de[t],null}function pe(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return se;if(!r(t)){if(e=fe(t))return e;t=[t]}return function(t){for(var e,n,i,a,r=0;r<t.length;){for(e=(a=ce(t[r]).split("-")).length,n=(n=ce(t[r+1]))?n.split("-"):null;e>0;){if(i=fe(a.slice(0,e).join("-")))return i;if(n&&n.length>=e&&M(a,n,!0)>=e-1)break;e--}r++}return se}(t)}function ve(t){var e,n=t._a;return n&&-2===g(t).overflow&&(e=n[bt]<0||n[bt]>11?bt:n[yt]<1||n[yt]>It(n[vt],n[bt])?yt:n[xt]<0||n[xt]>24||24===n[xt]&&(0!==n[_t]||0!==n[wt]||0!==n[kt])?xt:n[_t]<0||n[_t]>59?_t:n[wt]<0||n[wt]>59?wt:n[kt]<0||n[kt]>999?kt:-1,g(t)._overflowDayOfYear&&(e<vt||e>yt)&&(e=yt),g(t)._overflowWeeks&&-1===e&&(e=Mt),g(t)._overflowWeekday&&-1===e&&(e=St),g(t).overflow=e),t}function be(t,e,n){return null!=t?t:null!=e?e:n}function ye(t){var e,n,i,r,o,s=[];if(!t._d){for(i=function(t){var e=new Date(a.now());return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}(t),t._w&&null==t._a[yt]&&null==t._a[bt]&&function(t){var e,n,i,a,r,o,s,l;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)r=1,o=4,n=be(e.GG,t._a[vt],qt(Le(),1,4).year),i=be(e.W,1),((a=be(e.E,1))<1||a>7)&&(l=!0);else{r=t._locale._week.dow,o=t._locale._week.doy;var u=qt(Le(),r,o);n=be(e.gg,t._a[vt],u.year),i=be(e.w,u.week),null!=e.d?((a=e.d)<0||a>6)&&(l=!0):null!=e.e?(a=e.e+r,(e.e<0||e.e>6)&&(l=!0)):a=r}i<1||i>Zt(n,r,o)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(s=Gt(n,i,a,r,o),t._a[vt]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=be(t._a[vt],i[vt]),(t._dayOfYear>Dt(o)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),n=jt(o,0,t._dayOfYear),t._a[bt]=n.getUTCMonth(),t._a[yt]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=i[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[xt]&&0===t._a[_t]&&0===t._a[wt]&&0===t._a[kt]&&(t._nextDay=!0,t._a[xt]=0),t._d=(t._useUTC?jt:Bt).apply(null,s),r=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[xt]=24),t._w&&void 0!==t._w.d&&t._w.d!==r&&(g(t).weekdayMismatch=!0)}}var xe=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_e=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,we=/Z|[+-]\d\d(?::?\d\d)?/,ke=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Me=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Se=/^\/?Date\((\-?\d+)/i;function De(t){var e,n,i,a,r,o,s=t._i,l=xe.exec(s)||_e.exec(s);if(l){for(g(t).iso=!0,e=0,n=ke.length;e<n;e++)if(ke[e][1].exec(l[1])){a=ke[e][0],i=!1!==ke[e][2];break}if(null==a)return void(t._isValid=!1);if(l[3]){for(e=0,n=Me.length;e<n;e++)if(Me[e][1].exec(l[3])){r=(l[2]||" ")+Me[e][0];break}if(null==r)return void(t._isValid=!1)}if(!i&&null!=r)return void(t._isValid=!1);if(l[4]){if(!we.exec(l[4]))return void(t._isValid=!1);o="Z"}t._f=a+(r||"")+(o||""),Ae(t)}else t._isValid=!1}var Ce=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function Pe(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}var Te={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Oe(t){var e,n,i,a,r,o,s,l=Ce.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(l){var u=(e=l[4],n=l[3],i=l[2],a=l[5],r=l[6],o=l[7],s=[Pe(e),Nt.indexOf(n),parseInt(i,10),parseInt(a,10),parseInt(r,10)],o&&s.push(parseInt(o,10)),s);if(!function(t,e,n){return!t||Kt.indexOf(t)===new Date(e[0],e[1],e[2]).getDay()||(g(n).weekdayMismatch=!0,n._isValid=!1,!1)}(l[1],u,t))return;t._a=u,t._tzm=function(t,e,n){if(t)return Te[t];if(e)return 0;var i=parseInt(n,10),a=i%100;return(i-a)/100*60+a}(l[8],l[9],l[10]),t._d=jt.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),g(t).rfc2822=!0}else t._isValid=!1}function Ae(t){if(t._f!==a.ISO_8601)if(t._f!==a.RFC_2822){t._a=[],g(t).empty=!0;var e,n,i,r,o,s=""+t._i,l=s.length,u=0;for(i=G(t._f,t._locale).match(E)||[],e=0;e<i.length;e++)r=i[e],(n=(s.match(ht(r,t))||[])[0])&&((o=s.substr(0,s.indexOf(n))).length>0&&g(t).unusedInput.push(o),s=s.slice(s.indexOf(n)+n.length),u+=n.length),B[r]?(n?g(t).empty=!1:g(t).unusedTokens.push(r),pt(r,n,t)):t._strict&&!n&&g(t).unusedTokens.push(r);g(t).charsLeftOver=l-u,s.length>0&&g(t).unusedInput.push(s),t._a[xt]<=12&&!0===g(t).bigHour&&t._a[xt]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[xt]=function(t,e,n){var i;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?((i=t.isPM(n))&&e<12&&(e+=12),i||12!==e||(e=0),e):e}(t._locale,t._a[xt],t._meridiem),ye(t),ve(t)}else Oe(t);else De(t)}function Fe(t){var e=t._i,n=t._f;return t._locale=t._locale||pe(t._l),null===e||void 0===n&&""===e?p({nullInput:!0}):("string"==typeof e&&(t._i=e=t._locale.preparse(e)),_(e)?new x(ve(e)):(u(e)?t._d=e:r(n)?function(t){var e,n,i,a,r;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(a=0;a<t._f.length;a++)r=0,e=b({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[a],Ae(e),m(e)&&(r+=g(e).charsLeftOver,r+=10*g(e).unusedTokens.length,g(e).score=r,(null==i||r<i)&&(i=r,n=e));c(t,n||e)}(t):n?Ae(t):function(t){var e=t._i;s(e)?t._d=new Date(a.now()):u(e)?t._d=new Date(e.valueOf()):"string"==typeof e?function(t){var e=Se.exec(t._i);null===e?(De(t),!1===t._isValid&&(delete t._isValid,Oe(t),!1===t._isValid&&(delete t._isValid,a.createFromInputFallback(t)))):t._d=new Date(+e[1])}(t):r(e)?(t._a=d(e.slice(0),(function(t){return parseInt(t,10)})),ye(t)):o(e)?function(t){if(!t._d){var e=N(t._i);t._a=d([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),ye(t)}}(t):l(e)?t._d=new Date(e):a.createFromInputFallback(t)}(t),m(t)||(t._d=null),t))}function Ie(t,e,n,i,a){var s,l={};return!0!==n&&!1!==n||(i=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||r(t)&&0===t.length)&&(t=void 0),l._isAMomentObject=!0,l._useUTC=l._isUTC=a,l._l=n,l._i=t,l._f=e,l._strict=i,(s=new x(ve(Fe(l))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function Le(t,e,n,i){return Ie(t,e,n,i,!1)}a.createFromInputFallback=D("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),a.ISO_8601=function(){},a.RFC_2822=function(){};var Re=D("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:p()})),Ne=D("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t>this?this:t:p()}));function We(t,e){var n,i;if(1===e.length&&r(e[0])&&(e=e[0]),!e.length)return Le();for(n=e[0],i=1;i<e.length;++i)e[i].isValid()&&!e[i][t](n)||(n=e[i]);return n}var Ye=["year","quarter","month","week","day","hour","minute","second","millisecond"];function ze(t){var e=N(t),n=e.year||0,i=e.quarter||0,a=e.month||0,r=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,l=e.minute||0,u=e.second||0,d=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===Pt.call(Ye,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,i=0;i<Ye.length;++i)if(t[Ye[i]]){if(n)return!1;parseFloat(t[Ye[i]])!==k(t[Ye[i]])&&(n=!0)}return!0}(e),this._milliseconds=+d+1e3*u+6e4*l+1e3*s*60*60,this._days=+o+7*r,this._months=+a+3*i+12*n,this._data={},this._locale=pe(),this._bubble()}function Ee(t){return t instanceof ze}function Ve(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function He(t,e){j(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+z(~~(t/60),2)+e+z(~~t%60,2)}))}He("Z",":"),He("ZZ",""),dt("Z",st),dt("ZZ",st),gt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=je(st,t)}));var Be=/([\+\-]|\d\d)/gi;function je(t,e){var n=(e||"").match(t);if(null===n)return null;var i=((n[n.length-1]||[])+"").match(Be)||["-",0,0],a=60*i[1]+k(i[2]);return 0===a?0:"+"===i[0]?a:-a}function Ue(t,e){var n,i;return e._isUTC?(n=e.clone(),i=(_(t)||u(t)?t.valueOf():Le(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+i),a.updateOffset(n,!1),n):Le(t).local()}function Ge(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function qe(){return!!this.isValid()&&this._isUTC&&0===this._offset}a.updateOffset=function(){};var Ze=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,$e=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Xe(t,e){var n,i,a,r,o,s,u=t,d=null;return Ee(t)?u={ms:t._milliseconds,d:t._days,M:t._months}:l(t)?(u={},e?u[e]=t:u.milliseconds=t):(d=Ze.exec(t))?(n="-"===d[1]?-1:1,u={y:0,d:k(d[yt])*n,h:k(d[xt])*n,m:k(d[_t])*n,s:k(d[wt])*n,ms:k(Ve(1e3*d[kt]))*n}):(d=$e.exec(t))?(n="-"===d[1]?-1:1,u={y:Ke(d[2],n),M:Ke(d[3],n),w:Ke(d[4],n),d:Ke(d[5],n),h:Ke(d[6],n),m:Ke(d[7],n),s:Ke(d[8],n)}):null==u?u={}:"object"==typeof u&&("from"in u||"to"in u)&&(r=Le(u.from),o=Le(u.to),a=r.isValid()&&o.isValid()?(o=Ue(o,r),r.isBefore(o)?s=Je(r,o):((s=Je(o,r)).milliseconds=-s.milliseconds,s.months=-s.months),s):{milliseconds:0,months:0},(u={}).ms=a.milliseconds,u.M=a.months),i=new ze(u),Ee(t)&&h(t,"_locale")&&(i._locale=t._locale),i}function Ke(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Je(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function Qe(t,e){return function(n,i){var a;return null===i||isNaN(+i)||(T(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),a=n,n=i,i=a),tn(this,Xe(n="string"==typeof n?+n:n,i),t),this}}function tn(t,e,n,i){var r=e._milliseconds,o=Ve(e._days),s=Ve(e._months);t.isValid()&&(i=null==i||i,s&&Yt(t,At(t,"Month")+s*n),o&&Ft(t,"Date",At(t,"Date")+o*n),r&&t._d.setTime(t._d.valueOf()+r*n),i&&a.updateOffset(t,o||s))}Xe.fn=ze.prototype,Xe.invalid=function(){return Xe(NaN)};var en=Qe(1,"add"),nn=Qe(-1,"subtract");function an(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),i=t.clone().add(n,"months");return-(n+(e-i<0?(e-i)/(i-t.clone().add(n-1,"months")):(e-i)/(t.clone().add(n+1,"months")-i)))||0}function rn(t){var e;return void 0===t?this._locale._abbr:(null!=(e=pe(t))&&(this._locale=e),this)}a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var on=D("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function sn(){return this._locale}var ln=1e3,un=60*ln,dn=60*un,hn=3506328*dn;function cn(t,e){return(t%e+e)%e}function fn(t,e,n){return t<100&&t>=0?new Date(t+400,e,n)-hn:new Date(t,e,n).valueOf()}function gn(t,e,n){return t<100&&t>=0?Date.UTC(t+400,e,n)-hn:Date.UTC(t,e,n)}function mn(t,e){j(0,[t,t.length],0,e)}function pn(t,e,n,i,a){var r;return null==t?qt(this,i,a).year:(e>(r=Zt(t,i,a))&&(e=r),vn.call(this,t,e,n,i,a))}function vn(t,e,n,i,a){var r=Gt(t,e,n,i,a),o=jt(r.year,0,r.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}j(0,["gg",2],0,(function(){return this.weekYear()%100})),j(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),mn("gggg","weekYear"),mn("ggggg","weekYear"),mn("GGGG","isoWeekYear"),mn("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),Y("weekYear",1),Y("isoWeekYear",1),dt("G",rt),dt("g",rt),dt("GG",J,Z),dt("gg",J,Z),dt("GGGG",nt,X),dt("gggg",nt,X),dt("GGGGG",it,K),dt("ggggg",it,K),mt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,i){e[i.substr(0,2)]=k(t)})),mt(["gg","GG"],(function(t,e,n,i){e[i]=a.parseTwoDigitYear(t)})),j("Q",0,"Qo","quarter"),L("quarter","Q"),Y("quarter",7),dt("Q",q),gt("Q",(function(t,e){e[bt]=3*(k(t)-1)})),j("D",["DD",2],"Do","date"),L("date","D"),Y("date",9),dt("D",J),dt("DD",J,Z),dt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),gt(["D","DD"],yt),gt("Do",(function(t,e){e[yt]=k(t.match(J)[0])}));var bn=Ot("Date",!0);j("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),Y("dayOfYear",4),dt("DDD",et),dt("DDDD",$),gt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=k(t)})),j("m",["mm",2],0,"minute"),L("minute","m"),Y("minute",14),dt("m",J),dt("mm",J,Z),gt(["m","mm"],_t);var yn=Ot("Minutes",!1);j("s",["ss",2],0,"second"),L("second","s"),Y("second",15),dt("s",J),dt("ss",J,Z),gt(["s","ss"],wt);var xn,_n=Ot("Seconds",!1);for(j("S",0,0,(function(){return~~(this.millisecond()/100)})),j(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),j(0,["SSS",3],0,"millisecond"),j(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),j(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),j(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),j(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),j(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),j(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),Y("millisecond",16),dt("S",et,q),dt("SS",et,Z),dt("SSS",et,$),xn="SSSS";xn.length<=9;xn+="S")dt(xn,at);function wn(t,e){e[kt]=k(1e3*("0."+t))}for(xn="S";xn.length<=9;xn+="S")gt(xn,wn);var kn=Ot("Milliseconds",!1);j("z",0,0,"zoneAbbr"),j("zz",0,0,"zoneName");var Mn=x.prototype;function Sn(t){return t}Mn.add=en,Mn.calendar=function(t,e){var n=t||Le(),i=Ue(n,this).startOf("day"),r=a.calendarFormat(this,i)||"sameElse",o=e&&(O(e[r])?e[r].call(this,n):e[r]);return this.format(o||this.localeData().calendar(r,this,Le(n)))},Mn.clone=function(){return new x(this)},Mn.diff=function(t,e,n){var i,a,r;if(!this.isValid())return NaN;if(!(i=Ue(t,this)).isValid())return NaN;switch(a=6e4*(i.utcOffset()-this.utcOffset()),e=R(e)){case"year":r=an(this,i)/12;break;case"month":r=an(this,i);break;case"quarter":r=an(this,i)/3;break;case"second":r=(this-i)/1e3;break;case"minute":r=(this-i)/6e4;break;case"hour":r=(this-i)/36e5;break;case"day":r=(this-i-a)/864e5;break;case"week":r=(this-i-a)/6048e5;break;default:r=this-i}return n?r:w(r)},Mn.endOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=dn-cn(e+(this._isUTC?0:this.utcOffset()*un),dn)-1;break;case"minute":e=this._d.valueOf(),e+=un-cn(e,un)-1;break;case"second":e=this._d.valueOf(),e+=ln-cn(e,ln)-1}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.format=function(t){t||(t=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var e=U(this,t);return this.localeData().postformat(e)},Mn.from=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.fromNow=function(t){return this.from(Le(),t)},Mn.to=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.toNow=function(t){return this.to(Le(),t)},Mn.get=function(t){return O(this[t=R(t)])?this[t]():this},Mn.invalidAt=function(){return g(this).overflow},Mn.isAfter=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},Mn.isBefore=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},Mn.isBetween=function(t,e,n,i){var a=_(t)?t:Le(t),r=_(e)?e:Le(e);return!!(this.isValid()&&a.isValid()&&r.isValid())&&("("===(i=i||"()")[0]?this.isAfter(a,n):!this.isBefore(a,n))&&(")"===i[1]?this.isBefore(r,n):!this.isAfter(r,n))},Mn.isSame=function(t,e){var n,i=_(t)?t:Le(t);return!(!this.isValid()||!i.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()===i.valueOf():(n=i.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},Mn.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},Mn.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},Mn.isValid=function(){return m(this)},Mn.lang=on,Mn.locale=rn,Mn.localeData=sn,Mn.max=Ne,Mn.min=Re,Mn.parsingFlags=function(){return c({},g(this))},Mn.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:W[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=N(t)),i=0;i<n.length;i++)this[n[i].unit](t[n[i].unit]);else if(O(this[t=R(t)]))return this[t](e);return this},Mn.startOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=cn(e+(this._isUTC?0:this.utcOffset()*un),dn);break;case"minute":e=this._d.valueOf(),e-=cn(e,un);break;case"second":e=this._d.valueOf(),e-=cn(e,ln)}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.subtract=nn,Mn.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},Mn.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},Mn.toDate=function(){return new Date(this.valueOf())},Mn.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||n.year()>9999?U(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):O(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",U(n,"Z")):U(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},Mn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',i=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",a=e+'[")]';return this.format(n+i+"-MM-DD[T]HH:mm:ss.SSS"+a)},Mn.toJSON=function(){return this.isValid()?this.toISOString():null},Mn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Mn.unix=function(){return Math.floor(this.valueOf()/1e3)},Mn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Mn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Mn.year=Tt,Mn.isLeapYear=function(){return Ct(this.year())},Mn.weekYear=function(t){return pn.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Mn.isoWeekYear=function(t){return pn.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Mn.quarter=Mn.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Mn.month=zt,Mn.daysInMonth=function(){return It(this.year(),this.month())},Mn.week=Mn.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},Mn.isoWeek=Mn.isoWeeks=function(t){var e=qt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},Mn.weeksInYear=function(){var t=this.localeData()._week;return Zt(this.year(),t.dow,t.doy)},Mn.isoWeeksInYear=function(){return Zt(this.year(),1,4)},Mn.date=bn,Mn.day=Mn.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=function(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}(t,this.localeData()),this.add(t-e,"d")):e},Mn.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},Mn.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=function(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7},Mn.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},Mn.hour=Mn.hours=le,Mn.minute=Mn.minutes=yn,Mn.second=Mn.seconds=_n,Mn.millisecond=Mn.milliseconds=kn,Mn.utcOffset=function(t,e,n){var i,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=je(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(i=Ge(this)),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),r!==t&&(!e||this._changeInProgress?tn(this,Xe(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Ge(this)},Mn.utc=function(t){return this.utcOffset(0,t)},Mn.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ge(this),"m")),this},Mn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=je(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Mn.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Le(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Mn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Mn.isUtc=qe,Mn.isUTC=qe,Mn.zoneAbbr=function(){return this._isUTC?"UTC":""},Mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Mn.dates=D("dates accessor is deprecated. Use date instead.",bn),Mn.months=D("months accessor is deprecated. Use month instead",zt),Mn.years=D("years accessor is deprecated. Use year instead",Tt),Mn.zone=D("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),Mn.isDSTShifted=D("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(b(t,this),(t=Fe(t))._a){var e=t._isUTC?f(t._a):Le(t._a);this._isDSTShifted=this.isValid()&&M(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}));var Dn=F.prototype;function Cn(t,e,n,i){var a=pe(),r=f().set(i,e);return a[n](r,t)}function Pn(t,e,n){if(l(t)&&(e=t,t=void 0),t=t||"",null!=e)return Cn(t,e,n,"month");var i,a=[];for(i=0;i<12;i++)a[i]=Cn(t,i,n,"month");return a}function Tn(t,e,n,i){"boolean"==typeof t?(l(e)&&(n=e,e=void 0),e=e||""):(n=e=t,t=!1,l(e)&&(n=e,e=void 0),e=e||"");var a,r=pe(),o=t?r._week.dow:0;if(null!=n)return Cn(e,(n+o)%7,i,"day");var s=[];for(a=0;a<7;a++)s[a]=Cn(e,(a+o)%7,i,"day");return s}Dn.calendar=function(t,e,n){var i=this._calendar[t]||this._calendar.sameElse;return O(i)?i.call(e,n):i},Dn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},Dn.invalidDate=function(){return this._invalidDate},Dn.ordinal=function(t){return this._ordinal.replace("%d",t)},Dn.preparse=Sn,Dn.postformat=Sn,Dn.relativeTime=function(t,e,n,i){var a=this._relativeTime[n];return O(a)?a(t,e,n,i):a.replace(/%d/i,t)},Dn.pastFuture=function(t,e){var n=this._relativeTime[t>0?"future":"past"];return O(n)?n(e):n.replace(/%s/i,e)},Dn.set=function(t){var e,n;for(n in t)O(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Dn.months=function(t,e){return t?r(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Lt).test(e)?"format":"standalone"][t.month()]:r(this._months)?this._months:this._months.standalone},Dn.monthsShort=function(t,e){return t?r(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Lt.test(e)?"format":"standalone"][t.month()]:r(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Dn.monthsParse=function(t,e,n){var i,a,r;if(this._monthsParseExact)return Wt.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),i=0;i<12;i++){if(a=f([2e3,i]),n&&!this._longMonthsParse[i]&&(this._longMonthsParse[i]=new RegExp("^"+this.months(a,"").replace(".","")+"$","i"),this._shortMonthsParse[i]=new RegExp("^"+this.monthsShort(a,"").replace(".","")+"$","i")),n||this._monthsParse[i]||(r="^"+this.months(a,"")+"|^"+this.monthsShort(a,""),this._monthsParse[i]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[i].test(t))return i;if(n&&"MMM"===e&&this._shortMonthsParse[i].test(t))return i;if(!n&&this._monthsParse[i].test(t))return i}},Dn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Vt),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Dn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Et),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Dn.week=function(t){return qt(t,this._week.dow,this._week.doy).week},Dn.firstDayOfYear=function(){return this._week.doy},Dn.firstDayOfWeek=function(){return this._week.dow},Dn.weekdays=function(t,e){var n=r(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?$t(n,this._week.dow):t?n[t.day()]:n},Dn.weekdaysMin=function(t){return!0===t?$t(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},Dn.weekdaysShort=function(t){return!0===t?$t(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},Dn.weekdaysParse=function(t,e,n){var i,a,r;if(this._weekdaysParseExact)return Qt.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=f([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].test(t))return i;if(!n&&this._weekdaysParse[i].test(t))return i}},Dn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=te),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Dn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ee),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Dn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=ne),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Dn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},Dn.meridiem=function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},ge("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===k(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),a.lang=D("moment.lang is deprecated. Use moment.locale instead.",ge),a.langData=D("moment.langData is deprecated. Use moment.localeData instead.",pe);var On=Math.abs;function An(t,e,n,i){var a=Xe(e,n);return t._milliseconds+=i*a._milliseconds,t._days+=i*a._days,t._months+=i*a._months,t._bubble()}function Fn(t){return t<0?Math.floor(t):Math.ceil(t)}function In(t){return 4800*t/146097}function Ln(t){return 146097*t/4800}function Rn(t){return function(){return this.as(t)}}var Nn=Rn("ms"),Wn=Rn("s"),Yn=Rn("m"),zn=Rn("h"),En=Rn("d"),Vn=Rn("w"),Hn=Rn("M"),Bn=Rn("Q"),jn=Rn("y");function Un(t){return function(){return this.isValid()?this._data[t]:NaN}}var Gn=Un("milliseconds"),qn=Un("seconds"),Zn=Un("minutes"),$n=Un("hours"),Xn=Un("days"),Kn=Un("months"),Jn=Un("years"),Qn=Math.round,ti={ss:44,s:45,m:45,h:22,d:26,M:11};function ei(t,e,n,i,a){return a.relativeTime(e||1,!!n,t,i)}var ni=Math.abs;function ii(t){return(t>0)-(t<0)||+t}function ai(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=ni(this._milliseconds)/1e3,i=ni(this._days),a=ni(this._months);t=w(n/60),e=w(t/60),n%=60,t%=60;var r=w(a/12),o=a%=12,s=i,l=e,u=t,d=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var c=h<0?"-":"",f=ii(this._months)!==ii(h)?"-":"",g=ii(this._days)!==ii(h)?"-":"",m=ii(this._milliseconds)!==ii(h)?"-":"";return c+"P"+(r?f+r+"Y":"")+(o?f+o+"M":"")+(s?g+s+"D":"")+(l||u||d?"T":"")+(l?m+l+"H":"")+(u?m+u+"M":"")+(d?m+d+"S":"")}var ri=ze.prototype;return ri.isValid=function(){return this._isValid},ri.abs=function(){var t=this._data;return this._milliseconds=On(this._milliseconds),this._days=On(this._days),this._months=On(this._months),t.milliseconds=On(t.milliseconds),t.seconds=On(t.seconds),t.minutes=On(t.minutes),t.hours=On(t.hours),t.months=On(t.months),t.years=On(t.years),this},ri.add=function(t,e){return An(this,t,e,1)},ri.subtract=function(t,e){return An(this,t,e,-1)},ri.as=function(t){if(!this.isValid())return NaN;var e,n,i=this._milliseconds;if("month"===(t=R(t))||"quarter"===t||"year"===t)switch(e=this._days+i/864e5,n=this._months+In(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(Ln(this._months)),t){case"week":return e/7+i/6048e5;case"day":return e+i/864e5;case"hour":return 24*e+i/36e5;case"minute":return 1440*e+i/6e4;case"second":return 86400*e+i/1e3;case"millisecond":return Math.floor(864e5*e)+i;default:throw new Error("Unknown unit "+t)}},ri.asMilliseconds=Nn,ri.asSeconds=Wn,ri.asMinutes=Yn,ri.asHours=zn,ri.asDays=En,ri.asWeeks=Vn,ri.asMonths=Hn,ri.asQuarters=Bn,ri.asYears=jn,ri.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},ri._bubble=function(){var t,e,n,i,a,r=this._milliseconds,o=this._days,s=this._months,l=this._data;return r>=0&&o>=0&&s>=0||r<=0&&o<=0&&s<=0||(r+=864e5*Fn(Ln(s)+o),o=0,s=0),l.milliseconds=r%1e3,t=w(r/1e3),l.seconds=t%60,e=w(t/60),l.minutes=e%60,n=w(e/60),l.hours=n%24,o+=w(n/24),a=w(In(o)),s+=a,o-=Fn(Ln(a)),i=w(s/12),s%=12,l.days=o,l.months=s,l.years=i,this},ri.clone=function(){return Xe(this)},ri.get=function(t){return t=R(t),this.isValid()?this[t+"s"]():NaN},ri.milliseconds=Gn,ri.seconds=qn,ri.minutes=Zn,ri.hours=$n,ri.days=Xn,ri.weeks=function(){return w(this.days()/7)},ri.months=Kn,ri.years=Jn,ri.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e=this.localeData(),n=function(t,e,n){var i=Xe(t).abs(),a=Qn(i.as("s")),r=Qn(i.as("m")),o=Qn(i.as("h")),s=Qn(i.as("d")),l=Qn(i.as("M")),u=Qn(i.as("y")),d=a<=ti.ss&&["s",a]||a<ti.s&&["ss",a]||r<=1&&["m"]||r<ti.m&&["mm",r]||o<=1&&["h"]||o<ti.h&&["hh",o]||s<=1&&["d"]||s<ti.d&&["dd",s]||l<=1&&["M"]||l<ti.M&&["MM",l]||u<=1&&["y"]||["yy",u];return d[2]=e,d[3]=+t>0,d[4]=n,ei.apply(null,d)}(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)},ri.toISOString=ai,ri.toString=ai,ri.toJSON=ai,ri.locale=rn,ri.localeData=sn,ri.toIsoString=D("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ai),ri.lang=on,j("X",0,0,"unix"),j("x",0,0,"valueOf"),dt("x",rt),dt("X",/[+-]?\d+(\.\d{1,3})?/),gt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),gt("x",(function(t,e,n){n._d=new Date(k(t))})),a.version="2.24.0",n=Le,a.fn=Mn,a.min=function(){return We("isBefore",[].slice.call(arguments,0))},a.max=function(){return We("isAfter",[].slice.call(arguments,0))},a.now=function(){return Date.now?Date.now():+new Date},a.utc=f,a.unix=function(t){return Le(1e3*t)},a.months=function(t,e){return Pn(t,e,"months")},a.isDate=u,a.locale=ge,a.invalid=p,a.duration=Xe,a.isMoment=_,a.weekdays=function(t,e,n){return Tn(t,e,n,"weekdays")},a.parseZone=function(){return Le.apply(null,arguments).parseZone()},a.localeData=pe,a.isDuration=Ee,a.monthsShort=function(t,e){return Pn(t,e,"monthsShort")},a.weekdaysMin=function(t,e,n){return Tn(t,e,n,"weekdaysMin")},a.defineLocale=me,a.updateLocale=function(t,e){if(null!=e){var n,i,a=ue;null!=(i=fe(t))&&(a=i._config),e=A(a,e),(n=new F(e)).parentLocale=de[t],de[t]=n,ge(t)}else null!=de[t]&&(null!=de[t].parentLocale?de[t]=de[t].parentLocale:null!=de[t]&&delete de[t]);return de[t]},a.locales=function(){return C(de)},a.weekdaysShort=function(t,e,n){return Tn(t,e,n,"weekdaysShort")},a.normalizeUnits=R,a.relativeTimeRounding=function(t){return void 0===t?Qn:"function"==typeof t&&(Qn=t,!0)},a.relativeTimeThreshold=function(t,e){return void 0!==ti[t]&&(void 0===e?ti[t]:(ti[t]=e,"s"===t&&(ti.ss=e-1),!0))},a.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},a.prototype=Mn,a.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},a}()})),mi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};rn._date.override("function"==typeof gi?{_id:"moment",formats:function(){return mi},parse:function(t,e){return"string"==typeof t&&"string"==typeof e?t=gi(t,e):t instanceof gi||(t=gi(t)),t.isValid()?t.valueOf():null},format:function(t,e){return gi(t).format(e)},add:function(t,e,n){return gi(t).add(e,n).valueOf()},diff:function(t,e,n){return gi(t).diff(gi(e),n)},startOf:function(t,e,n){return t=gi(t),"isoWeek"===e?t.isoWeekday(n).valueOf():t.startOf(e).valueOf()},endOf:function(t,e){return gi(t).endOf(e).valueOf()},_create:function(t){return gi(t)}}:{}),W._set("global",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return H.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function vi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function bi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(H.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function yi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function xi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),pi[n](t))}function _i(t){return t&&!t.skip}function wi(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)H.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)H.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,m=i.spanGaps,p=[],v=[],b=0,y=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=_i(u),c=_i(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=p.push(u),y=v.push(d)):b&&y&&(m?(h&&p.push(u),c&&v.push(d)):(wi(t,p,v,b,y),b=y=0,p=[],v=[]));wi(t,p,v,b,y),t.closePath(),t.fillStyle=a,t.fill()}var Mi={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof wt.Line&&(r={visible:t.isDatasetVisible(i),fill:vi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=yi(l,i,s),r.boundary=bi(r),r.mapper=xi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||W.global.defaultColor,o&&s&&r.length&&(H.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,s,i._loop),H.canvas.unclipArea(u)))}},Si=H.rtl.getRtlAdapter,Di=H.noop,Ci=H.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}W._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Ti=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Di,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Di,beforeSetDimensions:Di,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Di,beforeBuildLabels:Di,buildLabels:function(){var t=this,e=t.options.labels||{},n=H.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Di,beforeFit:Di,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=H.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],m=n.padding,p=0,v=0;H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(m+=p+n.padding,f.push(p),g.push(v),p=0,v=0),p=Math.max(p,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),m+=p,f.push(p),g.push(v),l.width+=m}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Di,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=W.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=Si(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=H.options._parseFont(n),m=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var p=Pi(n,m),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},y=t.isHorizontal();d=y?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},H.rtl.overrideTextDirection(t.ctx,e.textDirection);var x=m+n.padding;H.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=p+m/2+f,_=d.x,w=d.y;h.setWidth(t.minSize.width),y?i>0&&_+g+n.padding>t.left+t.minSize.width&&(w=d.y+=x,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&w+x>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,w=d.y=t.top+b(o,s[d.line]));var k=h.x(_);!function(t,e,i){if(!(isNaN(p)||p<=0)){c.save();var o=Ci(i.lineWidth,r.borderWidth);if(c.fillStyle=Ci(i.fillStyle,a),c.lineCap=Ci(i.lineCap,r.borderCapStyle),c.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Ci(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=p*Math.SQRT2/2,l=h.xPlus(t,p/2),u=e+m/2;H.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,p),e,p,m),0!==o&&c.strokeRect(h.leftForLtr(t,p),e,p,m);c.restore()}}(k,w,e),v[i].left=h.leftForLtr(k,v[i].width),v[i].top=w,function(t,e,n,i){var a=m/2,r=h.xPlus(t,p+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(k,w,e,f),y?d.x+=g+n.padding:d.y+=x})),H.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Oi(t,e){var n=new Ti({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.legend=n}var Ai={id:"legend",_element:Ti,beforeInit:function(t){var e=t.options.legend;e&&Oi(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(H.mergeIf(e,W.global.legend),n?(me.configure(t,n,e),n.options=e):Oi(t,e)):n&&(me.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Fi=H.noop;W._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Fi,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Fi,beforeSetDimensions:Fi,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Fi,beforeBuildLabels:Fi,buildLabels:Fi,afterBuildLabels:Fi,beforeFit:Fi,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(H.isArray(n.text)?n.text.length:1)*H.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Fi,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=H.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=H.valueOrDefault(n.fontColor,W.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(H.isArray(g))for(var m=0,p=0;p<g.length;++p)e.fillText(g[p],0,m,i),m+=s;else e.fillText(g,0,0,i);e.restore()}}});function Li(t,e){var n=new Ii({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.titleBlock=n}var Ri={},Ni=Mi,Wi=Ai,Yi={id:"title",_element:Ii,beforeInit:function(t){var e=t.options.title;e&&Li(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(H.mergeIf(e,W.global.title),n?(me.configure(t,n,e),n.options=e):Li(t,e)):n&&(me.removeBox(t,n),delete t.titleBlock)}};for(var zi in Ri.filler=Ni,Ri.legend=Wi,Ri.title=Yi,en.helpers=H,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=H._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}H.where=function(t,e){if(H.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return H.each(t,(function(t){e(t)&&n.push(t)})),n},H.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},H.findNextWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},H.findPreviousWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},H.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},H.almostEquals=function(t,e,n){return Math.abs(t-e)<n},H.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},H.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},H.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},H.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},H.toRadians=function(t){return t*(Math.PI/180)},H.toDegrees=function(t){return t*(180/Math.PI)},H._decimalPlaces=function(t){if(H.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},H.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},H.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},H.aliasPixel=function(t){return t%2==0?0:.5},H._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},H.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},H.EPSILON=Number.EPSILON||1e-14,H.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(H.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},H.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},H.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},H.niceNum=function(t,e){var n=Math.floor(H.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},H.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},H.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(H.getStyle(r,"padding-left")),u=parseFloat(H.getStyle(r,"padding-top")),d=parseFloat(H.getStyle(r,"padding-right")),h=parseFloat(H.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},H.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},H.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},H._calculatePadding=function(t,e,n){return(e=H.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},H._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},H.getMaximumWidth=function(t){var e=H._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-H._calculatePadding(e,"padding-left",n)-H._calculatePadding(e,"padding-right",n),a=H.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},H.getMaximumHeight=function(t){var e=H._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-H._calculatePadding(e,"padding-top",n)-H._calculatePadding(e,"padding-bottom",n),a=H.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},H.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},H.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},H.fontString=function(t,e,n){return e+" "+t+"px "+n},H.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==H.isArray(u))h=H.measureText(t,a,r,h,u);else if(H.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||H.isArray(d)||(h=H.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},H.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},H.numberOfLabelLines=function(t){var e=1;return H.each(t,(function(t){H.isArray(t)&&t.length>e&&(e=t.length)})),e},H.color=k?function(t){return t instanceof CanvasGradient&&(t=W.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},H.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:H.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=K,en.animationService=J,en.controllers=Jt,en.DatasetController=it,en.defaults=W,en.Element=$,en.elements=wt,en.Interaction=re,en.layouts=me,en.platform=Ie,en.plugins=Le,en.Scale=xn,en.scaleService=Re,en.Ticks=on,en.Tooltip=Ge,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Ri)Ri.hasOwnProperty(zi)&&en.plugins.register(Ri[zi]);en.platform.initialize();var Ei=en;return"undefined"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Ri.legend._element,en.Title=Ri.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Dn,en.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei})); diff --git a/lib/web/chartjs/Chart.css b/lib/web/chartjs/Chart.css deleted file mode 100644 index 5e749593eeb65..0000000000000 --- a/lib/web/chartjs/Chart.css +++ /dev/null @@ -1,47 +0,0 @@ -/* - * DOM element rendering detection - * https://davidwalsh.name/detect-node-insertion - */ -@keyframes chartjs-render-animation { - from { opacity: 0.99; } - to { opacity: 1; } -} - -.chartjs-render-monitor { - animation: chartjs-render-animation 0.001s; -} - -/* - * DOM element resizing detection - * https://github.com/marcj/css-element-queries - */ -.chartjs-size-monitor, -.chartjs-size-monitor-expand, -.chartjs-size-monitor-shrink { - position: absolute; - direction: ltr; - left: 0; - top: 0; - right: 0; - bottom: 0; - overflow: hidden; - pointer-events: none; - visibility: hidden; - z-index: -1; -} - -.chartjs-size-monitor-expand > div { - position: absolute; - width: 1000000px; - height: 1000000px; - left: 0; - top: 0; -} - -.chartjs-size-monitor-shrink > div { - position: absolute; - width: 200%; - height: 200%; - left: 0; - top: 0; -} diff --git a/lib/web/chartjs/Chart.js b/lib/web/chartjs/Chart.js deleted file mode 100644 index e8d937cf82c54..0000000000000 --- a/lib/web/chartjs/Chart.js +++ /dev/null @@ -1,16151 +0,0 @@ -/*! - * Chart.js v2.9.3 - * https://www.chartjs.org - * (c) 2019 Chart.js Contributors - * Released under the MIT License - */ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) : -typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) : -(global = global || self, global.Chart = factory(global.moment)); -}(this, (function (moment) { 'use strict'; - -moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment; - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -function getCjsExportFromNamespace (n) { - return n && n['default'] || n; -} - -var colorName = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - -var conversions = createCommonjsModule(function (module) { -/* MIT license */ - - -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) - -var reverseKeywords = {}; -for (var key in colorName) { - if (colorName.hasOwnProperty(key)) { - reverseKeywords[colorName[key]] = key; - } -} - -var convert = module.exports = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} -}; - -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } - - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } - - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } - - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); - } -} - -convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; - - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } - - h = Math.min(h * 60, 360); - - if (h < 0) { - h += 360; - } - - l = (min + max) / 2; - - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); - } - - return [h, s * 100, l * 100]; -}; - -convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; - - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; - - if (diff === 0) { - h = s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); - - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - } - - return [ - h * 360, - s * 100, - v * 100 - ]; -}; - -convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); - - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); - - return [h, w * 100, b * 100]; -}; - -convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; - - return [c * 100, m * 100, y * 100, k * 100]; -}; - -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ -function comparativeDistance(x, y) { - return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) - ); -} - -convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } - - var currentClosestDistance = Infinity; - var currentClosestKeyword; - - for (var keyword in colorName) { - if (colorName.hasOwnProperty(keyword)) { - var value = colorName[keyword]; - - // Compute comparative distance - var distance = comparativeDistance(rgb, value); - - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } - } - } - - return currentClosestKeyword; -}; - -convert.keyword.rgb = function (keyword) { - return colorName[keyword]; -}; - -convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - - return [x * 100, y * 100, z * 100]; -}; - -convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; - - if (s === 0) { - val = l * 255; - return [val, val, val]; - } - - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } - - t1 = 2 * l - t2; - - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } - if (t3 > 1) { - t3--; - } - - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; - } - - rgb[i] = val * 255; - } - - return rgb; -}; - -convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; - - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); - - return [h, sv * 100, v * 100]; -}; - -convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; - - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); - v *= 255; - - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -}; - -convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; - - l = (2 - s) * v; - lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; - - return [h, sl * 100, l * 100]; -}; - -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } - - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; - - if ((i & 0x01) !== 0) { - f = 1 - f; - } - - n = wh + f * (v - wh); // linear interpolation - - var r; - var g; - var b; - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } - - return [r * 255, g * 255, b * 255]; -}; - -convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; - - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; - - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - - // assume sRGB - r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r * 12.92; - - g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g * 12.92; - - b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b * 12.92; - - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; - - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; - - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; - - x *= 95.047; - y *= 100; - z *= 108.883; - - return [x, y, z]; -}; - -convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; - - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; - - if (h < 0) { - h += 360; - } - - c = Math.sqrt(a * a + b * b); - - return [l, c, h]; -}; - -convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; - - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); - - return [l, a, b]; -}; - -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization - - value = Math.round(value / 50); - - if (value === 0) { - return 30; - } - - var ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); - - if (value === 2) { - ansi += 60; - } - - return ansi; -}; - -convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; - -convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - - // we use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; - } - - if (r > 248) { - return 231; - } - - return Math.round(((r - 8) / 247) * 24) + 232; - } - - var ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); - - return ansi; -}; - -convert.ansi16.rgb = function (args) { - var color = args % 10; - - // handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; - } - - color = color / 10.5 * 255; - - return [color, color, color]; - } - - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; - - return [r, g, b]; -}; - -convert.ansi256.rgb = function (args) { - // handle greyscale - if (args >= 232) { - var c = (args - 232) * 10 + 8; - return [c, c, c]; - } - - args -= 16; - - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; - - return [r, g, b]; -}; - -convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; - } - - var colorString = match[0]; - - if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { - return char + char; - }).join(''); - } - - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; - - return [r, g, b]; -}; - -convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; - - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } - - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma + 4; - } - - hue /= 6; - hue %= 1; - - return [hue * 360, chroma * 100, grayscale * 100]; -}; - -convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; - - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } - - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } - - return [hsl[0], c * 100, f * 100]; -}; - -convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; - - var c = s * v; - var f = 0; - - if (c < 1.0) { - f = (v - c) / (1 - c); - } - - return [hsv[0], c * 100, f * 100]; -}; - -convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } - - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; - - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; - } - - mg = (1.0 - c) * g; - - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; - -convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var v = c + g * (1.0 - c); - var f = 0; - - if (v > 0.0) { - f = c / v; - } - - return [hcg[0], f * 100, v * 100]; -}; - -convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; - - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); - } - - return [hcg[0], s * 100, l * 100]; -}; - -convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; -}; - -convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; - - if (c < 1) { - g = (v - c) / (1 - c); - } - - return [hwb[0], c * 100, g * 100]; -}; - -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; - -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; - -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; - -convert.gray.hsl = convert.gray.hsv = function (args) { - return [0, 0, args[0]]; -}; - -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; - -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; - -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; - -convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; -}; -}); -var conversions_1 = conversions.rgb; -var conversions_2 = conversions.hsl; -var conversions_3 = conversions.hsv; -var conversions_4 = conversions.hwb; -var conversions_5 = conversions.cmyk; -var conversions_6 = conversions.xyz; -var conversions_7 = conversions.lab; -var conversions_8 = conversions.lch; -var conversions_9 = conversions.hex; -var conversions_10 = conversions.keyword; -var conversions_11 = conversions.ansi16; -var conversions_12 = conversions.ansi256; -var conversions_13 = conversions.hcg; -var conversions_14 = conversions.apple; -var conversions_15 = conversions.gray; - -/* - this function routes a model to all other models. - - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). - - conversions that are not possible simply are not included. -*/ - -function buildGraph() { - var graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); - - for (var len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; - } - - return graph; -} - -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop - - graph[fromModel].distance = 0; - - while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); - - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; - - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } - - return graph; -} - -function link(from, to) { - return function (args) { - return to(from(args)); - }; -} - -function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; - - var cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; - } - - fn.conversion = path; - return fn; -} - -var route = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; - - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; - - if (node.parent === null) { - // no possible conversion, or this node is the source model. - continue; - } - - conversion[toModel] = wrapConversion(toModel, graph); - } - - return conversion; -}; - -var convert = {}; - -var models = Object.keys(conversions); - -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - return fn(args); - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - var result = fn(args); - - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } - - return result; - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -models.forEach(function (fromModel) { - convert[fromModel] = {}; - - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - - var routes = route(fromModel); - var routeModels = Object.keys(routes); - - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; - - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); - -var colorConvert = convert; - -var colorName$1 = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - -/* MIT license */ - - -var colorString = { - getRgba: getRgba, - getHsla: getHsla, - getRgb: getRgb, - getHsl: getHsl, - getHwb: getHwb, - getAlpha: getAlpha, - - hexString: hexString, - rgbString: rgbString, - rgbaString: rgbaString, - percentString: percentString, - percentaString: percentaString, - hslString: hslString, - hslaString: hslaString, - hwbString: hwbString, - keyword: keyword -}; - -function getRgba(string) { - if (!string) { - return; - } - var abbr = /^#([a-fA-F0-9]{3,4})$/i, - hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, - rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - keyword = /(\w+)/; - - var rgb = [0, 0, 0], - a = 1, - match = string.match(abbr), - hexAlpha = ""; - if (match) { - match = match[1]; - hexAlpha = match[3]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i] + match[i], 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(hex)) { - hexAlpha = match[2]; - match = match[1]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(rgba)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i + 1]); - } - a = parseFloat(match[4]); - } - else if (match = string.match(per)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); - } - a = parseFloat(match[4]); - } - else if (match = string.match(keyword)) { - if (match[1] == "transparent") { - return [0, 0, 0, 0]; - } - rgb = colorName$1[match[1]]; - if (!rgb) { - return; - } - } - - for (var i = 0; i < rgb.length; i++) { - rgb[i] = scale(rgb[i], 0, 255); - } - if (!a && a != 0) { - a = 1; - } - else { - a = scale(a, 0, 1); - } - rgb[3] = a; - return rgb; -} - -function getHsla(string) { - if (!string) { - return; - } - var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hsl); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - s = scale(parseFloat(match[2]), 0, 100), - l = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, s, l, a]; - } -} - -function getHwb(string) { - if (!string) { - return; - } - var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hwb); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - w = scale(parseFloat(match[2]), 0, 100), - b = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, w, b, a]; - } -} - -function getRgb(string) { - var rgba = getRgba(string); - return rgba && rgba.slice(0, 3); -} - -function getHsl(string) { - var hsla = getHsla(string); - return hsla && hsla.slice(0, 3); -} - -function getAlpha(string) { - var vals = getRgba(string); - if (vals) { - return vals[3]; - } - else if (vals = getHsla(string)) { - return vals[3]; - } - else if (vals = getHwb(string)) { - return vals[3]; - } -} - -// generators -function hexString(rgba, a) { - var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; - return "#" + hexDouble(rgba[0]) - + hexDouble(rgba[1]) - + hexDouble(rgba[2]) - + ( - (a >= 0 && a < 1) - ? hexDouble(Math.round(a * 255)) - : "" - ); -} - -function rgbString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return rgbaString(rgba, alpha); - } - return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; -} - -function rgbaString(rgba, alpha) { - if (alpha === undefined) { - alpha = (rgba[3] !== undefined ? rgba[3] : 1); - } - return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] - + ", " + alpha + ")"; -} - -function percentString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return percentaString(rgba, alpha); - } - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - - return "rgb(" + r + "%, " + g + "%, " + b + "%)"; -} - -function percentaString(rgba, alpha) { - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; -} - -function hslString(hsla, alpha) { - if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { - return hslaString(hsla, alpha); - } - return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; -} - -function hslaString(hsla, alpha) { - if (alpha === undefined) { - alpha = (hsla[3] !== undefined ? hsla[3] : 1); - } - return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " - + alpha + ")"; -} - -// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax -// (hwb have alpha optional & 1 is default value) -function hwbString(hwb, alpha) { - if (alpha === undefined) { - alpha = (hwb[3] !== undefined ? hwb[3] : 1); - } - return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" - + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; -} - -function keyword(rgb) { - return reverseNames[rgb.slice(0, 3)]; -} - -// helpers -function scale(num, min, max) { - return Math.min(Math.max(min, num), max); -} - -function hexDouble(num) { - var str = num.toString(16).toUpperCase(); - return (str.length < 2) ? "0" + str : str; -} - - -//create a list of reverse color names -var reverseNames = {}; -for (var name in colorName$1) { - reverseNames[colorName$1[name]] = name; -} - -/* MIT license */ - - - -var Color = function (obj) { - if (obj instanceof Color) { - return obj; - } - if (!(this instanceof Color)) { - return new Color(obj); - } - - this.valid = false; - this.values = { - rgb: [0, 0, 0], - hsl: [0, 0, 0], - hsv: [0, 0, 0], - hwb: [0, 0, 0], - cmyk: [0, 0, 0, 0], - alpha: 1 - }; - - // parse Color() argument - var vals; - if (typeof obj === 'string') { - vals = colorString.getRgba(obj); - if (vals) { - this.setValues('rgb', vals); - } else if (vals = colorString.getHsla(obj)) { - this.setValues('hsl', vals); - } else if (vals = colorString.getHwb(obj)) { - this.setValues('hwb', vals); - } - } else if (typeof obj === 'object') { - vals = obj; - if (vals.r !== undefined || vals.red !== undefined) { - this.setValues('rgb', vals); - } else if (vals.l !== undefined || vals.lightness !== undefined) { - this.setValues('hsl', vals); - } else if (vals.v !== undefined || vals.value !== undefined) { - this.setValues('hsv', vals); - } else if (vals.w !== undefined || vals.whiteness !== undefined) { - this.setValues('hwb', vals); - } else if (vals.c !== undefined || vals.cyan !== undefined) { - this.setValues('cmyk', vals); - } - } -}; - -Color.prototype = { - isValid: function () { - return this.valid; - }, - rgb: function () { - return this.setSpace('rgb', arguments); - }, - hsl: function () { - return this.setSpace('hsl', arguments); - }, - hsv: function () { - return this.setSpace('hsv', arguments); - }, - hwb: function () { - return this.setSpace('hwb', arguments); - }, - cmyk: function () { - return this.setSpace('cmyk', arguments); - }, - - rgbArray: function () { - return this.values.rgb; - }, - hslArray: function () { - return this.values.hsl; - }, - hsvArray: function () { - return this.values.hsv; - }, - hwbArray: function () { - var values = this.values; - if (values.alpha !== 1) { - return values.hwb.concat([values.alpha]); - } - return values.hwb; - }, - cmykArray: function () { - return this.values.cmyk; - }, - rgbaArray: function () { - var values = this.values; - return values.rgb.concat([values.alpha]); - }, - hslaArray: function () { - var values = this.values; - return values.hsl.concat([values.alpha]); - }, - alpha: function (val) { - if (val === undefined) { - return this.values.alpha; - } - this.setValues('alpha', val); - return this; - }, - - red: function (val) { - return this.setChannel('rgb', 0, val); - }, - green: function (val) { - return this.setChannel('rgb', 1, val); - }, - blue: function (val) { - return this.setChannel('rgb', 2, val); - }, - hue: function (val) { - if (val) { - val %= 360; - val = val < 0 ? 360 + val : val; - } - return this.setChannel('hsl', 0, val); - }, - saturation: function (val) { - return this.setChannel('hsl', 1, val); - }, - lightness: function (val) { - return this.setChannel('hsl', 2, val); - }, - saturationv: function (val) { - return this.setChannel('hsv', 1, val); - }, - whiteness: function (val) { - return this.setChannel('hwb', 1, val); - }, - blackness: function (val) { - return this.setChannel('hwb', 2, val); - }, - value: function (val) { - return this.setChannel('hsv', 2, val); - }, - cyan: function (val) { - return this.setChannel('cmyk', 0, val); - }, - magenta: function (val) { - return this.setChannel('cmyk', 1, val); - }, - yellow: function (val) { - return this.setChannel('cmyk', 2, val); - }, - black: function (val) { - return this.setChannel('cmyk', 3, val); - }, - - hexString: function () { - return colorString.hexString(this.values.rgb); - }, - rgbString: function () { - return colorString.rgbString(this.values.rgb, this.values.alpha); - }, - rgbaString: function () { - return colorString.rgbaString(this.values.rgb, this.values.alpha); - }, - percentString: function () { - return colorString.percentString(this.values.rgb, this.values.alpha); - }, - hslString: function () { - return colorString.hslString(this.values.hsl, this.values.alpha); - }, - hslaString: function () { - return colorString.hslaString(this.values.hsl, this.values.alpha); - }, - hwbString: function () { - return colorString.hwbString(this.values.hwb, this.values.alpha); - }, - keyword: function () { - return colorString.keyword(this.values.rgb, this.values.alpha); - }, - - rgbNumber: function () { - var rgb = this.values.rgb; - return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; - }, - - luminosity: function () { - // http://www.w3.org/TR/WCAG20/#relativeluminancedef - var rgb = this.values.rgb; - var lum = []; - for (var i = 0; i < rgb.length; i++) { - var chan = rgb[i] / 255; - lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); - } - return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; - }, - - contrast: function (color2) { - // http://www.w3.org/TR/WCAG20/#contrast-ratiodef - var lum1 = this.luminosity(); - var lum2 = color2.luminosity(); - if (lum1 > lum2) { - return (lum1 + 0.05) / (lum2 + 0.05); - } - return (lum2 + 0.05) / (lum1 + 0.05); - }, - - level: function (color2) { - var contrastRatio = this.contrast(color2); - if (contrastRatio >= 7.1) { - return 'AAA'; - } - - return (contrastRatio >= 4.5) ? 'AA' : ''; - }, - - dark: function () { - // YIQ equation from http://24ways.org/2010/calculating-color-contrast - var rgb = this.values.rgb; - var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; - return yiq < 128; - }, - - light: function () { - return !this.dark(); - }, - - negate: function () { - var rgb = []; - for (var i = 0; i < 3; i++) { - rgb[i] = 255 - this.values.rgb[i]; - } - this.setValues('rgb', rgb); - return this; - }, - - lighten: function (ratio) { - var hsl = this.values.hsl; - hsl[2] += hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - darken: function (ratio) { - var hsl = this.values.hsl; - hsl[2] -= hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - saturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] += hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - desaturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] -= hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - whiten: function (ratio) { - var hwb = this.values.hwb; - hwb[1] += hwb[1] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - blacken: function (ratio) { - var hwb = this.values.hwb; - hwb[2] += hwb[2] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - greyscale: function () { - var rgb = this.values.rgb; - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; - this.setValues('rgb', [val, val, val]); - return this; - }, - - clearer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha - (alpha * ratio)); - return this; - }, - - opaquer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha + (alpha * ratio)); - return this; - }, - - rotate: function (degrees) { - var hsl = this.values.hsl; - var hue = (hsl[0] + degrees) % 360; - hsl[0] = hue < 0 ? 360 + hue : hue; - this.setValues('hsl', hsl); - return this; - }, - - /** - * Ported from sass implementation in C - * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 - */ - mix: function (mixinColor, weight) { - var color1 = this; - var color2 = mixinColor; - var p = weight === undefined ? 0.5 : weight; - - var w = 2 * p - 1; - var a = color1.alpha() - color2.alpha(); - - var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - return this - .rgb( - w1 * color1.red() + w2 * color2.red(), - w1 * color1.green() + w2 * color2.green(), - w1 * color1.blue() + w2 * color2.blue() - ) - .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); - }, - - toJSON: function () { - return this.rgb(); - }, - - clone: function () { - // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, - // making the final build way to big to embed in Chart.js. So let's do it manually, - // assuming that values to clone are 1 dimension arrays containing only numbers, - // except 'alpha' which is a number. - var result = new Color(); - var source = this.values; - var target = result.values; - var value, type; - - for (var prop in source) { - if (source.hasOwnProperty(prop)) { - value = source[prop]; - type = ({}).toString.call(value); - if (type === '[object Array]') { - target[prop] = value.slice(0); - } else if (type === '[object Number]') { - target[prop] = value; - } else { - console.error('unexpected color value:', value); - } - } - } - - return result; - } -}; - -Color.prototype.spaces = { - rgb: ['red', 'green', 'blue'], - hsl: ['hue', 'saturation', 'lightness'], - hsv: ['hue', 'saturation', 'value'], - hwb: ['hue', 'whiteness', 'blackness'], - cmyk: ['cyan', 'magenta', 'yellow', 'black'] -}; - -Color.prototype.maxes = { - rgb: [255, 255, 255], - hsl: [360, 100, 100], - hsv: [360, 100, 100], - hwb: [360, 100, 100], - cmyk: [100, 100, 100, 100] -}; - -Color.prototype.getValues = function (space) { - var values = this.values; - var vals = {}; - - for (var i = 0; i < space.length; i++) { - vals[space.charAt(i)] = values[space][i]; - } - - if (values.alpha !== 1) { - vals.a = values.alpha; - } - - // {r: 255, g: 255, b: 255, a: 0.4} - return vals; -}; - -Color.prototype.setValues = function (space, vals) { - var values = this.values; - var spaces = this.spaces; - var maxes = this.maxes; - var alpha = 1; - var i; - - this.valid = true; - - if (space === 'alpha') { - alpha = vals; - } else if (vals.length) { - // [10, 10, 10] - values[space] = vals.slice(0, space.length); - alpha = vals[space.length]; - } else if (vals[space.charAt(0)] !== undefined) { - // {r: 10, g: 10, b: 10} - for (i = 0; i < space.length; i++) { - values[space][i] = vals[space.charAt(i)]; - } - - alpha = vals.a; - } else if (vals[spaces[space][0]] !== undefined) { - // {red: 10, green: 10, blue: 10} - var chans = spaces[space]; - - for (i = 0; i < space.length; i++) { - values[space][i] = vals[chans[i]]; - } - - alpha = vals.alpha; - } - - values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); - - if (space === 'alpha') { - return false; - } - - var capped; - - // cap values of the space prior converting all values - for (i = 0; i < space.length; i++) { - capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); - values[space][i] = Math.round(capped); - } - - // convert to all the other color spaces - for (var sname in spaces) { - if (sname !== space) { - values[sname] = colorConvert[space][sname](values[space]); - } - } - - return true; -}; - -Color.prototype.setSpace = function (space, args) { - var vals = args[0]; - - if (vals === undefined) { - // color.rgb() - return this.getValues(space); - } - - // color.rgb(10, 10, 10) - if (typeof vals === 'number') { - vals = Array.prototype.slice.call(args); - } - - this.setValues(space, vals); - return this; -}; - -Color.prototype.setChannel = function (space, index, val) { - var svalues = this.values[space]; - if (val === undefined) { - // color.red() - return svalues[index]; - } else if (val === svalues[index]) { - // color.red(color.red()) - return this; - } - - // color.red(100) - svalues[index] = val; - this.setValues(space, svalues); - - return this; -}; - -if (typeof window !== 'undefined') { - window.Color = Color; -} - -var chartjsColor = Color; - -/** - * @namespace Chart.helpers - */ -var helpers = { - /** - * An empty function that can be used, for example, for optional callback. - */ - noop: function() {}, - - /** - * Returns a unique id, sequentially generated from a global variable. - * @returns {number} - * @function - */ - uid: (function() { - var id = 0; - return function() { - return id++; - }; - }()), - - /** - * Returns true if `value` is neither null nor undefined, else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isNullOrUndef: function(value) { - return value === null || typeof value === 'undefined'; - }, - - /** - * Returns true if `value` is an array (including typed arrays), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @function - */ - isArray: function(value) { - if (Array.isArray && Array.isArray(value)) { - return true; - } - var type = Object.prototype.toString.call(value); - if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { - return true; - } - return false; - }, - - /** - * Returns true if `value` is an object (excluding null), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isObject: function(value) { - return value !== null && Object.prototype.toString.call(value) === '[object Object]'; - }, - - /** - * Returns true if `value` is a finite number, else returns false - * @param {*} value - The value to test. - * @returns {boolean} - */ - isFinite: function(value) { - return (typeof value === 'number' || value instanceof Number) && isFinite(value); - }, - - /** - * Returns `value` if defined, else returns `defaultValue`. - * @param {*} value - The value to return if defined. - * @param {*} defaultValue - The value to return if `value` is undefined. - * @returns {*} - */ - valueOrDefault: function(value, defaultValue) { - return typeof value === 'undefined' ? defaultValue : value; - }, - - /** - * Returns value at the given `index` in array if defined, else returns `defaultValue`. - * @param {Array} value - The array to lookup for value at `index`. - * @param {number} index - The index in `value` to lookup for value. - * @param {*} defaultValue - The value to return if `value[index]` is undefined. - * @returns {*} - */ - valueAtIndexOrDefault: function(value, index, defaultValue) { - return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); - }, - - /** - * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the - * value returned by `fn`. If `fn` is not a function, this method returns undefined. - * @param {function} fn - The function to call. - * @param {Array|undefined|null} args - The arguments with which `fn` should be called. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @returns {*} - */ - callback: function(fn, args, thisArg) { - if (fn && typeof fn.call === 'function') { - return fn.apply(thisArg, args); - } - }, - - /** - * Note(SB) for performance sake, this method should only be used when loopable type - * is unknown or in none intensive code (not called often and small loopable). Else - * it's preferable to use a regular for() loop and save extra function calls. - * @param {object|Array} loopable - The object or array to be iterated. - * @param {function} fn - The function to call for each item. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @param {boolean} [reverse] - If true, iterates backward on the loopable. - */ - each: function(loopable, fn, thisArg, reverse) { - var i, len, keys; - if (helpers.isArray(loopable)) { - len = loopable.length; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - fn.call(thisArg, loopable[i], i); - } - } else { - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[i], i); - } - } - } else if (helpers.isObject(loopable)) { - keys = Object.keys(loopable); - len = keys.length; - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[keys[i]], keys[i]); - } - } - }, - - /** - * Returns true if the `a0` and `a1` arrays have the same content, else returns false. - * @see https://stackoverflow.com/a/14853974 - * @param {Array} a0 - The array to compare - * @param {Array} a1 - The array to compare - * @returns {boolean} - */ - arrayEquals: function(a0, a1) { - var i, ilen, v0, v1; - - if (!a0 || !a1 || a0.length !== a1.length) { - return false; - } - - for (i = 0, ilen = a0.length; i < ilen; ++i) { - v0 = a0[i]; - v1 = a1[i]; - - if (v0 instanceof Array && v1 instanceof Array) { - if (!helpers.arrayEquals(v0, v1)) { - return false; - } - } else if (v0 !== v1) { - // NOTE: two different object instances will never be equal: {x:20} != {x:20} - return false; - } - } - - return true; - }, - - /** - * Returns a deep copy of `source` without keeping references on objects and arrays. - * @param {*} source - The value to clone. - * @returns {*} - */ - clone: function(source) { - if (helpers.isArray(source)) { - return source.map(helpers.clone); - } - - if (helpers.isObject(source)) { - var target = {}; - var keys = Object.keys(source); - var klen = keys.length; - var k = 0; - - for (; k < klen; ++k) { - target[keys[k]] = helpers.clone(source[keys[k]]); - } - - return target; - } - - return source; - }, - - /** - * The default merger when Chart.helpers.merge is called without merger option. - * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. - * @private - */ - _merger: function(key, target, source, options) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.merge(tval, sval, options); - } else { - target[key] = helpers.clone(sval); - } - }, - - /** - * Merges source[key] in target[key] only if target[key] is undefined. - * @private - */ - _mergerIf: function(key, target, source) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.mergeIf(tval, sval); - } else if (!target.hasOwnProperty(key)) { - target[key] = helpers.clone(sval); - } - }, - - /** - * Recursively deep copies `source` properties into `target` with the given `options`. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @param {object} [options] - Merging options: - * @param {function} [options.merger] - The merge method (key, target, source, options) - * @returns {object} The `target` object. - */ - merge: function(target, source, options) { - var sources = helpers.isArray(source) ? source : [source]; - var ilen = sources.length; - var merge, i, keys, klen, k; - - if (!helpers.isObject(target)) { - return target; - } - - options = options || {}; - merge = options.merger || helpers._merger; - - for (i = 0; i < ilen; ++i) { - source = sources[i]; - if (!helpers.isObject(source)) { - continue; - } - - keys = Object.keys(source); - for (k = 0, klen = keys.length; k < klen; ++k) { - merge(keys[k], target, source, options); - } - } - - return target; - }, - - /** - * Recursively deep copies `source` properties into `target` *only* if not defined in target. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @returns {object} The `target` object. - */ - mergeIf: function(target, source) { - return helpers.merge(target, source, {merger: helpers._mergerIf}); - }, - - /** - * Applies the contents of two or more objects together into the first object. - * @param {object} target - The target object in which all objects are merged into. - * @param {object} arg1 - Object containing additional properties to merge in target. - * @param {object} argN - Additional objects containing properties to merge in target. - * @returns {object} The `target` object. - */ - extend: Object.assign || function(target) { - return helpers.merge(target, [].slice.call(arguments, 1), { - merger: function(key, dst, src) { - dst[key] = src[key]; - } - }); - }, - - /** - * Basic javascript inheritance based on the model created in Backbone.js - */ - inherits: function(extensions) { - var me = this; - var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { - return me.apply(this, arguments); - }; - - var Surrogate = function() { - this.constructor = ChartElement; - }; - - Surrogate.prototype = me.prototype; - ChartElement.prototype = new Surrogate(); - ChartElement.extend = helpers.inherits; - - if (extensions) { - helpers.extend(ChartElement.prototype, extensions); - } - - ChartElement.__super__ = me.prototype; - return ChartElement; - }, - - _deprecated: function(scope, value, previous, current) { - if (value !== undefined) { - console.warn(scope + ': "' + previous + - '" is deprecated. Please use "' + current + '" instead'); - } - } -}; - -var helpers_core = helpers; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.callback instead. - * @function Chart.helpers.callCallback - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -helpers.callCallback = helpers.callback; - -/** - * Provided for backward compatibility, use Array.prototype.indexOf instead. - * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ - * @function Chart.helpers.indexOf - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.indexOf = function(array, item, fromIndex) { - return Array.prototype.indexOf.call(array, item, fromIndex); -}; - -/** - * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. - * @function Chart.helpers.getValueOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueOrDefault = helpers.valueOrDefault; - -/** - * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. - * @function Chart.helpers.getValueAtIndexOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; - -/** - * Easing functions adapted from Robert Penner's easing equations. - * @namespace Chart.helpers.easingEffects - * @see http://www.robertpenner.com/easing/ - */ -var effects = { - linear: function(t) { - return t; - }, - - easeInQuad: function(t) { - return t * t; - }, - - easeOutQuad: function(t) { - return -t * (t - 2); - }, - - easeInOutQuad: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t; - } - return -0.5 * ((--t) * (t - 2) - 1); - }, - - easeInCubic: function(t) { - return t * t * t; - }, - - easeOutCubic: function(t) { - return (t = t - 1) * t * t + 1; - }, - - easeInOutCubic: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t; - } - return 0.5 * ((t -= 2) * t * t + 2); - }, - - easeInQuart: function(t) { - return t * t * t * t; - }, - - easeOutQuart: function(t) { - return -((t = t - 1) * t * t * t - 1); - }, - - easeInOutQuart: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t; - } - return -0.5 * ((t -= 2) * t * t * t - 2); - }, - - easeInQuint: function(t) { - return t * t * t * t * t; - }, - - easeOutQuint: function(t) { - return (t = t - 1) * t * t * t * t + 1; - }, - - easeInOutQuint: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t * t; - } - return 0.5 * ((t -= 2) * t * t * t * t + 2); - }, - - easeInSine: function(t) { - return -Math.cos(t * (Math.PI / 2)) + 1; - }, - - easeOutSine: function(t) { - return Math.sin(t * (Math.PI / 2)); - }, - - easeInOutSine: function(t) { - return -0.5 * (Math.cos(Math.PI * t) - 1); - }, - - easeInExpo: function(t) { - return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); - }, - - easeOutExpo: function(t) { - return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; - }, - - easeInOutExpo: function(t) { - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if ((t /= 0.5) < 1) { - return 0.5 * Math.pow(2, 10 * (t - 1)); - } - return 0.5 * (-Math.pow(2, -10 * --t) + 2); - }, - - easeInCirc: function(t) { - if (t >= 1) { - return t; - } - return -(Math.sqrt(1 - t * t) - 1); - }, - - easeOutCirc: function(t) { - return Math.sqrt(1 - (t = t - 1) * t); - }, - - easeInOutCirc: function(t) { - if ((t /= 0.5) < 1) { - return -0.5 * (Math.sqrt(1 - t * t) - 1); - } - return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - - easeInElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - }, - - easeOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; - }, - - easeInOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 0.5) === 2) { - return 1; - } - if (!p) { - p = 0.45; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - if (t < 1) { - return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function(t) { - var s = 1.70158; - return t * t * ((s + 1) * t - s); - }, - - easeOutBack: function(t) { - var s = 1.70158; - return (t = t - 1) * t * ((s + 1) * t + s) + 1; - }, - - easeInOutBack: function(t) { - var s = 1.70158; - if ((t /= 0.5) < 1) { - return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); - } - return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - - easeInBounce: function(t) { - return 1 - effects.easeOutBounce(1 - t); - }, - - easeOutBounce: function(t) { - if (t < (1 / 2.75)) { - return 7.5625 * t * t; - } - if (t < (2 / 2.75)) { - return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; - } - if (t < (2.5 / 2.75)) { - return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; - } - return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; - }, - - easeInOutBounce: function(t) { - if (t < 0.5) { - return effects.easeInBounce(t * 2) * 0.5; - } - return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; - } -}; - -var helpers_easing = { - effects: effects -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.easing.effects instead. - * @function Chart.helpers.easingEffects - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.easingEffects = effects; - -var PI = Math.PI; -var RAD_PER_DEG = PI / 180; -var DOUBLE_PI = PI * 2; -var HALF_PI = PI / 2; -var QUARTER_PI = PI / 4; -var TWO_THIRDS_PI = PI * 2 / 3; - -/** - * @namespace Chart.helpers.canvas - */ -var exports$1 = { - /** - * Clears the entire canvas associated to the given `chart`. - * @param {Chart} chart - The chart for which to clear the canvas. - */ - clear: function(chart) { - chart.ctx.clearRect(0, 0, chart.width, chart.height); - }, - - /** - * Creates a "path" for a rectangle with rounded corners at position (x, y) with a - * given size (width, height) and the same `radius` for all corners. - * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. - * @param {number} x - The x axis of the coordinate for the rectangle starting point. - * @param {number} y - The y axis of the coordinate for the rectangle starting point. - * @param {number} width - The rectangle's width. - * @param {number} height - The rectangle's height. - * @param {number} radius - The rounded amount (in pixels) for the four corners. - * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? - */ - roundedRect: function(ctx, x, y, width, height, radius) { - if (radius) { - var r = Math.min(radius, height / 2, width / 2); - var left = x + r; - var top = y + r; - var right = x + width - r; - var bottom = y + height - r; - - ctx.moveTo(x, top); - if (left < right && top < bottom) { - ctx.arc(left, top, r, -PI, -HALF_PI); - ctx.arc(right, top, r, -HALF_PI, 0); - ctx.arc(right, bottom, r, 0, HALF_PI); - ctx.arc(left, bottom, r, HALF_PI, PI); - } else if (left < right) { - ctx.moveTo(left, y); - ctx.arc(right, top, r, -HALF_PI, HALF_PI); - ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); - } else if (top < bottom) { - ctx.arc(left, top, r, -PI, 0); - ctx.arc(left, bottom, r, 0, PI); - } else { - ctx.arc(left, top, r, -PI, PI); - } - ctx.closePath(); - ctx.moveTo(x, y); - } else { - ctx.rect(x, y, width, height); - } - }, - - drawPoint: function(ctx, style, radius, x, y, rotation) { - var type, xOffset, yOffset, size, cornerRadius; - var rad = (rotation || 0) * RAD_PER_DEG; - - if (style && typeof style === 'object') { - type = style.toString(); - if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.save(); - ctx.translate(x, y); - ctx.rotate(rad); - ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); - ctx.restore(); - return; - } - } - - if (isNaN(radius) || radius <= 0) { - return; - } - - ctx.beginPath(); - - switch (style) { - // Default includes circle - default: - ctx.arc(x, y, radius, 0, DOUBLE_PI); - ctx.closePath(); - break; - case 'triangle': - ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - ctx.closePath(); - break; - case 'rectRounded': - // NOTE: the rounded rect implementation changed to use `arc` instead of - // `quadraticCurveTo` since it generates better results when rect is - // almost a circle. 0.516 (instead of 0.5) produces results with visually - // closer proportion to the previous impl and it is inscribed in the - // circle with `radius`. For more details, see the following PRs: - // https://github.com/chartjs/Chart.js/issues/5597 - // https://github.com/chartjs/Chart.js/issues/5858 - cornerRadius = radius * 0.516; - size = radius - cornerRadius; - xOffset = Math.cos(rad + QUARTER_PI) * size; - yOffset = Math.sin(rad + QUARTER_PI) * size; - ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); - ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); - ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); - ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); - ctx.closePath(); - break; - case 'rect': - if (!rotation) { - size = Math.SQRT1_2 * radius; - ctx.rect(x - size, y - size, 2 * size, 2 * size); - break; - } - rad += QUARTER_PI; - /* falls through */ - case 'rectRot': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + yOffset, y - xOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.lineTo(x - yOffset, y + xOffset); - ctx.closePath(); - break; - case 'crossRot': - rad += QUARTER_PI; - /* falls through */ - case 'cross': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'star': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - rad += QUARTER_PI; - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'line': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - break; - case 'dash': - ctx.moveTo(x, y); - ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); - break; - } - - ctx.fill(); - ctx.stroke(); - }, - - /** - * Returns true if the point is inside the rectangle - * @param {object} point - The point to test - * @param {object} area - The rectangle - * @returns {boolean} - * @private - */ - _isPointInArea: function(point, area) { - var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. - - return point.x > area.left - epsilon && point.x < area.right + epsilon && - point.y > area.top - epsilon && point.y < area.bottom + epsilon; - }, - - clipArea: function(ctx, area) { - ctx.save(); - ctx.beginPath(); - ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); - ctx.clip(); - }, - - unclipArea: function(ctx) { - ctx.restore(); - }, - - lineTo: function(ctx, previous, target, flip) { - var stepped = target.steppedLine; - if (stepped) { - if (stepped === 'middle') { - var midpoint = (previous.x + target.x) / 2.0; - ctx.lineTo(midpoint, flip ? target.y : previous.y); - ctx.lineTo(midpoint, flip ? previous.y : target.y); - } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { - ctx.lineTo(previous.x, target.y); - } else { - ctx.lineTo(target.x, previous.y); - } - ctx.lineTo(target.x, target.y); - return; - } - - if (!target.tension) { - ctx.lineTo(target.x, target.y); - return; - } - - ctx.bezierCurveTo( - flip ? previous.controlPointPreviousX : previous.controlPointNextX, - flip ? previous.controlPointPreviousY : previous.controlPointNextY, - flip ? target.controlPointNextX : target.controlPointPreviousX, - flip ? target.controlPointNextY : target.controlPointPreviousY, - target.x, - target.y); - } -}; - -var helpers_canvas = exports$1; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. - * @namespace Chart.helpers.clear - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.clear = exports$1.clear; - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. - * @namespace Chart.helpers.drawRoundedRectangle - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.drawRoundedRectangle = function(ctx) { - ctx.beginPath(); - exports$1.roundedRect.apply(exports$1, arguments); -}; - -var defaults = { - /** - * @private - */ - _set: function(scope, values) { - return helpers_core.merge(this[scope] || (this[scope] = {}), values); - } -}; - -// TODO(v3): remove 'global' from namespace. all default are global and -// there's inconsistency around which options are under 'global' -defaults._set('global', { - defaultColor: 'rgba(0,0,0,0.1)', - defaultFontColor: '#666', - defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - defaultFontSize: 12, - defaultFontStyle: 'normal', - defaultLineHeight: 1.2, - showLines: true -}); - -var core_defaults = defaults; - -var valueOrDefault = helpers_core.valueOrDefault; - -/** - * Converts the given font object into a CSS font string. - * @param {object} font - A font object. - * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font - * @private - */ -function toFontString(font) { - if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { - return null; - } - - return (font.style ? font.style + ' ' : '') - + (font.weight ? font.weight + ' ' : '') - + font.size + 'px ' - + font.family; -} - -/** - * @alias Chart.helpers.options - * @namespace - */ -var helpers_options = { - /** - * Converts the given line height `value` in pixels for a specific font `size`. - * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). - * @param {number} size - The font size (in pixels) used to resolve relative `value`. - * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - * @since 2.7.0 - */ - toLineHeight: function(value, size) { - var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); - if (!matches || matches[1] === 'normal') { - return size * 1.2; - } - - value = +matches[2]; - - switch (matches[3]) { - case 'px': - return value; - case '%': - value /= 100; - break; - } - - return size * value; - }, - - /** - * Converts the given value into a padding object with pre-computed width/height. - * @param {number|object} value - If a number, set the value to all TRBL component, - * else, if and object, use defined properties and sets undefined ones to 0. - * @returns {object} The padding values (top, right, bottom, left, width, height) - * @since 2.7.0 - */ - toPadding: function(value) { - var t, r, b, l; - - if (helpers_core.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - top: t, - right: r, - bottom: b, - left: l, - height: t + b, - width: l + r - }; - }, - - /** - * Parses font options and returns the font object. - * @param {object} options - A object that contains font options to be parsed. - * @return {object} The font object. - * @todo Support font.* options and renamed to toFont(). - * @private - */ - _parseFont: function(options) { - var globalDefaults = core_defaults.global; - var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); - var font = { - family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), - lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), - size: size, - style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), - weight: null, - string: '' - }; - - font.string = toFontString(font); - return font; - }, - - /** - * Evaluates the given `inputs` sequentially and returns the first defined value. - * @param {Array} inputs - An array of values, falling back to the last value. - * @param {object} [context] - If defined and the current value is a function, the value - * is called with `context` as first argument and the result becomes the new input. - * @param {number} [index] - If defined and the current value is an array, the value - * at `index` become the new input. - * @param {object} [info] - object to return information about resolution in - * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. - * @since 2.7.0 - */ - resolve: function(inputs, context, index, info) { - var cacheable = true; - var i, ilen, value; - - for (i = 0, ilen = inputs.length; i < ilen; ++i) { - value = inputs[i]; - if (value === undefined) { - continue; - } - if (context !== undefined && typeof value === 'function') { - value = value(context); - cacheable = false; - } - if (index !== undefined && helpers_core.isArray(value)) { - value = value[index]; - cacheable = false; - } - if (value !== undefined) { - if (info && !cacheable) { - info.cacheable = false; - } - return value; - } - } - } -}; - -/** - * @alias Chart.helpers.math - * @namespace - */ -var exports$2 = { - /** - * Returns an array of factors sorted from 1 to sqrt(value) - * @private - */ - _factorize: function(value) { - var result = []; - var sqrt = Math.sqrt(value); - var i; - - for (i = 1; i < sqrt; i++) { - if (value % i === 0) { - result.push(i); - result.push(value / i); - } - } - if (sqrt === (sqrt | 0)) { // if value is a square number - result.push(sqrt); - } - - result.sort(function(a, b) { - return a - b; - }).pop(); - return result; - }, - - log10: Math.log10 || function(x) { - var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. - // Check for whole powers of 10, - // which due to floating point rounding error should be corrected. - var powerOf10 = Math.round(exponent); - var isPowerOf10 = x === Math.pow(10, powerOf10); - - return isPowerOf10 ? powerOf10 : exponent; - } -}; - -var helpers_math = exports$2; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.math.log10 instead. - * @namespace Chart.helpers.log10 - * @deprecated since version 2.9.0 - * @todo remove at version 3 - * @private - */ -helpers_core.log10 = exports$2.log10; - -var getRtlAdapter = function(rectX, width) { - return { - x: function(x) { - return rectX + rectX + width - x; - }, - setWidth: function(w) { - width = w; - }, - textAlign: function(align) { - if (align === 'center') { - return align; - } - return align === 'right' ? 'left' : 'right'; - }, - xPlus: function(x, value) { - return x - value; - }, - leftForLtr: function(x, itemWidth) { - return x - itemWidth; - }, - }; -}; - -var getLtrAdapter = function() { - return { - x: function(x) { - return x; - }, - setWidth: function(w) { // eslint-disable-line no-unused-vars - }, - textAlign: function(align) { - return align; - }, - xPlus: function(x, value) { - return x + value; - }, - leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars - return x; - }, - }; -}; - -var getAdapter = function(rtl, rectX, width) { - return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter(); -}; - -var overrideTextDirection = function(ctx, direction) { - var style, original; - if (direction === 'ltr' || direction === 'rtl') { - style = ctx.canvas.style; - original = [ - style.getPropertyValue('direction'), - style.getPropertyPriority('direction'), - ]; - - style.setProperty('direction', direction, 'important'); - ctx.prevTextDirection = original; - } -}; - -var restoreTextDirection = function(ctx) { - var original = ctx.prevTextDirection; - if (original !== undefined) { - delete ctx.prevTextDirection; - ctx.canvas.style.setProperty('direction', original[0], original[1]); - } -}; - -var helpers_rtl = { - getRtlAdapter: getAdapter, - overrideTextDirection: overrideTextDirection, - restoreTextDirection: restoreTextDirection, -}; - -var helpers$1 = helpers_core; -var easing = helpers_easing; -var canvas = helpers_canvas; -var options = helpers_options; -var math = helpers_math; -var rtl = helpers_rtl; -helpers$1.easing = easing; -helpers$1.canvas = canvas; -helpers$1.options = options; -helpers$1.math = math; -helpers$1.rtl = rtl; - -function interpolate(start, view, model, ease) { - var keys = Object.keys(model); - var i, ilen, key, actual, origin, target, type, c0, c1; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - - target = model[key]; - - // if a value is added to the model after pivot() has been called, the view - // doesn't contain it, so let's initialize the view to the target value. - if (!view.hasOwnProperty(key)) { - view[key] = target; - } - - actual = view[key]; - - if (actual === target || key[0] === '_') { - continue; - } - - if (!start.hasOwnProperty(key)) { - start[key] = actual; - } - - origin = start[key]; - - type = typeof target; - - if (type === typeof origin) { - if (type === 'string') { - c0 = chartjsColor(origin); - if (c0.valid) { - c1 = chartjsColor(target); - if (c1.valid) { - view[key] = c1.mix(c0, ease).rgbString(); - continue; - } - } - } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { - view[key] = origin + (target - origin) * ease; - continue; - } - } - - view[key] = target; - } -} - -var Element = function(configuration) { - helpers$1.extend(this, configuration); - this.initialize.apply(this, arguments); -}; - -helpers$1.extend(Element.prototype, { - _type: undefined, - - initialize: function() { - this.hidden = false; - }, - - pivot: function() { - var me = this; - if (!me._view) { - me._view = helpers$1.extend({}, me._model); - } - me._start = {}; - return me; - }, - - transition: function(ease) { - var me = this; - var model = me._model; - var start = me._start; - var view = me._view; - - // No animation -> No Transition - if (!model || ease === 1) { - me._view = helpers$1.extend({}, model); - me._start = null; - return me; - } - - if (!view) { - view = me._view = {}; - } - - if (!start) { - start = me._start = {}; - } - - interpolate(start, view, model, ease); - - return me; - }, - - tooltipPosition: function() { - return { - x: this._model.x, - y: this._model.y - }; - }, - - hasValue: function() { - return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); - } -}); - -Element.extend = helpers$1.inherits; - -var core_element = Element; - -var exports$3 = core_element.extend({ - chart: null, // the animation associated chart instance - currentStep: 0, // the current animation step - numSteps: 60, // default number of steps - easing: '', // the easing to use for this animation - render: null, // render function used by the animation service - - onAnimationProgress: null, // user specified callback to fire on each step of the animation - onAnimationComplete: null, // user specified callback to fire when the animation finishes -}); - -var core_animation = exports$3; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.Animation instead - * @prop Chart.Animation#animationObject - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$3.prototype, 'animationObject', { - get: function() { - return this; - } -}); - -/** - * Provided for backward compatibility, use Chart.Animation#chart instead - * @prop Chart.Animation#chartInstance - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$3.prototype, 'chartInstance', { - get: function() { - return this.chart; - }, - set: function(value) { - this.chart = value; - } -}); - -core_defaults._set('global', { - animation: { - duration: 1000, - easing: 'easeOutQuart', - onProgress: helpers$1.noop, - onComplete: helpers$1.noop - } -}); - -var core_animations = { - animations: [], - request: null, - - /** - * @param {Chart} chart - The chart to animate. - * @param {Chart.Animation} animation - The animation that we will animate. - * @param {number} duration - The animation duration in ms. - * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions - */ - addAnimation: function(chart, animation, duration, lazy) { - var animations = this.animations; - var i, ilen; - - animation.chart = chart; - animation.startTime = Date.now(); - animation.duration = duration; - - if (!lazy) { - chart.animating = true; - } - - for (i = 0, ilen = animations.length; i < ilen; ++i) { - if (animations[i].chart === chart) { - animations[i] = animation; - return; - } - } - - animations.push(animation); - - // If there are no animations queued, manually kickstart a digest, for lack of a better word - if (animations.length === 1) { - this.requestAnimationFrame(); - } - }, - - cancelAnimation: function(chart) { - var index = helpers$1.findIndex(this.animations, function(animation) { - return animation.chart === chart; - }); - - if (index !== -1) { - this.animations.splice(index, 1); - chart.animating = false; - } - }, - - requestAnimationFrame: function() { - var me = this; - if (me.request === null) { - // Skip animation frame requests until the active one is executed. - // This can happen when processing mouse events, e.g. 'mousemove' - // and 'mouseout' events will trigger multiple renders. - me.request = helpers$1.requestAnimFrame.call(window, function() { - me.request = null; - me.startDigest(); - }); - } - }, - - /** - * @private - */ - startDigest: function() { - var me = this; - - me.advance(); - - // Do we have more stuff to animate? - if (me.animations.length > 0) { - me.requestAnimationFrame(); - } - }, - - /** - * @private - */ - advance: function() { - var animations = this.animations; - var animation, chart, numSteps, nextStep; - var i = 0; - - // 1 animation per chart, so we are looping charts here - while (i < animations.length) { - animation = animations[i]; - chart = animation.chart; - numSteps = animation.numSteps; - - // Make sure that currentStep starts at 1 - // https://github.com/chartjs/Chart.js/issues/6104 - nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; - animation.currentStep = Math.min(nextStep, numSteps); - - helpers$1.callback(animation.render, [chart, animation], chart); - helpers$1.callback(animation.onAnimationProgress, [animation], chart); - - if (animation.currentStep >= numSteps) { - helpers$1.callback(animation.onAnimationComplete, [animation], chart); - chart.animating = false; - animations.splice(i, 1); - } else { - ++i; - } - } - } -}; - -var resolve = helpers$1.options.resolve; - -var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; - -/** - * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', - * 'unshift') and notify the listener AFTER the array has been altered. Listeners are - * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. - */ -function listenArrayEvents(array, listener) { - if (array._chartjs) { - array._chartjs.listeners.push(listener); - return; - } - - Object.defineProperty(array, '_chartjs', { - configurable: true, - enumerable: false, - value: { - listeners: [listener] - } - }); - - arrayEvents.forEach(function(key) { - var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); - var base = array[key]; - - Object.defineProperty(array, key, { - configurable: true, - enumerable: false, - value: function() { - var args = Array.prototype.slice.call(arguments); - var res = base.apply(this, args); - - helpers$1.each(array._chartjs.listeners, function(object) { - if (typeof object[method] === 'function') { - object[method].apply(object, args); - } - }); - - return res; - } - }); - }); -} - -/** - * Removes the given array event listener and cleanup extra attached properties (such as - * the _chartjs stub and overridden methods) if array doesn't have any more listeners. - */ -function unlistenArrayEvents(array, listener) { - var stub = array._chartjs; - if (!stub) { - return; - } - - var listeners = stub.listeners; - var index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - - if (listeners.length > 0) { - return; - } - - arrayEvents.forEach(function(key) { - delete array[key]; - }); - - delete array._chartjs; -} - -// Base class for all dataset controllers (line, bar, etc) -var DatasetController = function(chart, datasetIndex) { - this.initialize(chart, datasetIndex); -}; - -helpers$1.extend(DatasetController.prototype, { - - /** - * Element type used to generate a meta dataset (e.g. Chart.element.Line). - * @type {Chart.core.element} - */ - datasetElementType: null, - - /** - * Element type used to generate a meta data (e.g. Chart.element.Point). - * @type {Chart.core.element} - */ - dataElementType: null, - - /** - * Dataset element option keys to be resolved in _resolveDatasetElementOptions. - * A derived controller may override this to resolve controller-specific options. - * The keys defined here are for backward compatibility for legend styles. - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderCapStyle', - 'borderColor', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'borderWidth' - ], - - /** - * Data element option keys to be resolved in _resolveDataElementOptions. - * A derived controller may override this to resolve controller-specific options. - * The keys defined here are for backward compatibility for legend styles. - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'pointStyle' - ], - - initialize: function(chart, datasetIndex) { - var me = this; - me.chart = chart; - me.index = datasetIndex; - me.linkScales(); - me.addElements(); - me._type = me.getMeta().type; - }, - - updateIndex: function(datasetIndex) { - this.index = datasetIndex; - }, - - linkScales: function() { - var me = this; - var meta = me.getMeta(); - var chart = me.chart; - var scales = chart.scales; - var dataset = me.getDataset(); - var scalesOpts = chart.options.scales; - - if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) { - meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id; - } - if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) { - meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id; - } - }, - - getDataset: function() { - return this.chart.data.datasets[this.index]; - }, - - getMeta: function() { - return this.chart.getDatasetMeta(this.index); - }, - - getScaleForId: function(scaleID) { - return this.chart.scales[scaleID]; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().yAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getValueScale: function() { - return this.getScaleForId(this._getValueScaleId()); - }, - - /** - * @private - */ - _getIndexScale: function() { - return this.getScaleForId(this._getIndexScaleId()); - }, - - reset: function() { - this._update(true); - }, - - /** - * @private - */ - destroy: function() { - if (this._data) { - unlistenArrayEvents(this._data, this); - } - }, - - createMetaDataset: function() { - var me = this; - var type = me.datasetElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index - }); - }, - - createMetaData: function(index) { - var me = this; - var type = me.dataElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index, - _index: index - }); - }, - - addElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data || []; - var metaData = meta.data; - var i, ilen; - - for (i = 0, ilen = data.length; i < ilen; ++i) { - metaData[i] = metaData[i] || me.createMetaData(i); - } - - meta.dataset = meta.dataset || me.createMetaDataset(); - }, - - addElementAndReset: function(index) { - var element = this.createMetaData(index); - this.getMeta().data.splice(index, 0, element); - this.updateElement(element, index, true); - }, - - buildOrUpdateElements: function() { - var me = this; - var dataset = me.getDataset(); - var data = dataset.data || (dataset.data = []); - - // In order to correctly handle data addition/deletion animation (an thus simulate - // real-time charts), we need to monitor these data modifications and synchronize - // the internal meta data accordingly. - if (me._data !== data) { - if (me._data) { - // This case happens when the user replaced the data array instance. - unlistenArrayEvents(me._data, me); - } - - if (data && Object.isExtensible(data)) { - listenArrayEvents(data, me); - } - me._data = data; - } - - // Re-sync meta data in case the user replaced the data array or if we missed - // any updates and so make sure that we handle number of datapoints changing. - me.resyncElements(); - }, - - /** - * Returns the merged user-supplied and default dataset-level options - * @private - */ - _configure: function() { - var me = this; - me._config = helpers$1.merge({}, [ - me.chart.options.datasets[me._type], - me.getDataset(), - ], { - merger: function(key, target, source) { - if (key !== '_meta' && key !== 'data') { - helpers$1._merger(key, target, source); - } - } - }); - }, - - _update: function(reset) { - var me = this; - me._configure(); - me._cachedDataOpts = null; - me.update(reset); - }, - - update: helpers$1.noop, - - transition: function(easingValue) { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - for (; i < ilen; ++i) { - elements[i].transition(easingValue); - } - - if (meta.dataset) { - meta.dataset.transition(easingValue); - } - }, - - draw: function() { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - if (meta.dataset) { - meta.dataset.draw(); - } - - for (; i < ilen; ++i) { - elements[i].draw(); - } - }, - - /** - * Returns a set of predefined style properties that should be used to represent the dataset - * or the data if the index is specified - * @param {number} index - data index - * @return {IStyleInterface} style object - */ - getStyle: function(index) { - var me = this; - var meta = me.getMeta(); - var dataset = meta.dataset; - var style; - - me._configure(); - if (dataset && index === undefined) { - style = me._resolveDatasetElementOptions(dataset || {}); - } else { - index = index || 0; - style = me._resolveDataElementOptions(meta.data[index] || {}, index); - } - - if (style.fill === false || style.fill === null) { - style.backgroundColor = style.borderColor; - } - - return style; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function(element, hover) { - var me = this; - var chart = me.chart; - var datasetOpts = me._config; - var custom = element.custom || {}; - var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; - var elementOptions = me._datasetElementOptions; - var values = {}; - var i, ilen, key, readKey; - - // Scriptable options - var context = { - chart: chart, - dataset: me.getDataset(), - datasetIndex: me.index, - hover: hover - }; - - for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { - key = elementOptions[i]; - readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key; - values[key] = resolve([ - custom[readKey], - datasetOpts[readKey], - options[readKey] - ], context); - } - - return values; - }, - - /** - * @private - */ - _resolveDataElementOptions: function(element, index) { - var me = this; - var custom = element && element.custom; - var cached = me._cachedDataOpts; - if (cached && !custom) { - return cached; - } - var chart = me.chart; - var datasetOpts = me._config; - var options = chart.options.elements[me.dataElementType.prototype._type] || {}; - var elementOptions = me._dataElementOptions; - var values = {}; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: me.getDataset(), - datasetIndex: me.index - }; - - // `resolve` sets cacheable to `false` if any option is indexed or scripted - var info = {cacheable: !custom}; - - var keys, i, ilen, key; - - custom = custom || {}; - - if (helpers$1.isArray(elementOptions)) { - for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { - key = elementOptions[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index, info); - } - } else { - keys = Object.keys(elementOptions); - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[elementOptions[key]], - datasetOpts[key], - options[key] - ], context, index, info); - } - } - - if (info.cacheable) { - me._cachedDataOpts = Object.freeze(values); - } - - return values; - }, - - removeHoverStyle: function(element) { - helpers$1.merge(element._model, element.$previousStyle || {}); - delete element.$previousStyle; - }, - - setHoverStyle: function(element) { - var dataset = this.chart.data.datasets[element._datasetIndex]; - var index = element._index; - var custom = element.custom || {}; - var model = element._model; - var getHoverColor = helpers$1.getHoverColor; - - element.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth - }; - - model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); - model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); - model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); - }, - - /** - * @private - */ - _removeDatasetHoverStyle: function() { - var element = this.getMeta().dataset; - - if (element) { - this.removeHoverStyle(element); - } - }, - - /** - * @private - */ - _setDatasetHoverStyle: function() { - var element = this.getMeta().dataset; - var prev = {}; - var i, ilen, key, keys, hoverOptions, model; - - if (!element) { - return; - } - - model = element._model; - hoverOptions = this._resolveDatasetElementOptions(element, true); - - keys = Object.keys(hoverOptions); - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - prev[key] = model[key]; - model[key] = hoverOptions[key]; - } - - element.$previousStyle = prev; - }, - - /** - * @private - */ - resyncElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data; - var numMeta = meta.data.length; - var numData = data.length; - - if (numData < numMeta) { - meta.data.splice(numData, numMeta - numData); - } else if (numData > numMeta) { - me.insertElements(numMeta, numData - numMeta); - } - }, - - /** - * @private - */ - insertElements: function(start, count) { - for (var i = 0; i < count; ++i) { - this.addElementAndReset(start + i); - } - }, - - /** - * @private - */ - onDataPush: function() { - var count = arguments.length; - this.insertElements(this.getDataset().data.length - count, count); - }, - - /** - * @private - */ - onDataPop: function() { - this.getMeta().data.pop(); - }, - - /** - * @private - */ - onDataShift: function() { - this.getMeta().data.shift(); - }, - - /** - * @private - */ - onDataSplice: function(start, count) { - this.getMeta().data.splice(start, count); - this.insertElements(start, arguments.length - 2); - }, - - /** - * @private - */ - onDataUnshift: function() { - this.insertElements(0, arguments.length); - } -}); - -DatasetController.extend = helpers$1.inherits; - -var core_datasetController = DatasetController; - -var TAU = Math.PI * 2; - -core_defaults._set('global', { - elements: { - arc: { - backgroundColor: core_defaults.global.defaultColor, - borderColor: '#fff', - borderWidth: 2, - borderAlign: 'center' - } - } -}); - -function clipArc(ctx, arc) { - var startAngle = arc.startAngle; - var endAngle = arc.endAngle; - var pixelMargin = arc.pixelMargin; - var angleMargin = pixelMargin / arc.outerRadius; - var x = arc.x; - var y = arc.y; - - // Draw an inner border by cliping the arc and drawing a double-width border - // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders - ctx.beginPath(); - ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin); - if (arc.innerRadius > pixelMargin) { - angleMargin = pixelMargin / arc.innerRadius; - ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true); - } else { - ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2); - } - ctx.closePath(); - ctx.clip(); -} - -function drawFullCircleBorders(ctx, vm, arc, inner) { - var endAngle = arc.endAngle; - var i; - - if (inner) { - arc.endAngle = arc.startAngle + TAU; - clipArc(ctx, arc); - arc.endAngle = endAngle; - if (arc.endAngle === arc.startAngle && arc.fullCircles) { - arc.endAngle += TAU; - arc.fullCircles--; - } - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.stroke(); - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.stroke(); - } -} - -function drawBorder(ctx, vm, arc) { - var inner = vm.borderAlign === 'inner'; - - if (inner) { - ctx.lineWidth = vm.borderWidth * 2; - ctx.lineJoin = 'round'; - } else { - ctx.lineWidth = vm.borderWidth; - ctx.lineJoin = 'bevel'; - } - - if (arc.fullCircles) { - drawFullCircleBorders(ctx, vm, arc, inner); - } - - if (inner) { - clipArc(ctx, arc); - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - ctx.stroke(); -} - -var element_arc = core_element.extend({ - _type: 'arc', - - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); - } - return false; - }, - - inRange: function(chartX, chartY) { - var vm = this._view; - - if (vm) { - var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); - var angle = pointRelativePosition.angle; - var distance = pointRelativePosition.distance; - - // Sanitise angle range - var startAngle = vm.startAngle; - var endAngle = vm.endAngle; - while (endAngle < startAngle) { - endAngle += TAU; - } - while (angle > endAngle) { - angle -= TAU; - } - while (angle < startAngle) { - angle += TAU; - } - - // Check if within the range of the open/close angle - var betweenAngles = (angle >= startAngle && angle <= endAngle); - var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); - - return (betweenAngles && withinRadius); - } - return false; - }, - - getCenterPoint: function() { - var vm = this._view; - var halfAngle = (vm.startAngle + vm.endAngle) / 2; - var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; - return { - x: vm.x + Math.cos(halfAngle) * halfRadius, - y: vm.y + Math.sin(halfAngle) * halfRadius - }; - }, - - getArea: function() { - var vm = this._view; - return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); - }, - - tooltipPosition: function() { - var vm = this._view; - var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); - var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; - - return { - x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), - y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; - var arc = { - x: vm.x, - y: vm.y, - innerRadius: vm.innerRadius, - outerRadius: Math.max(vm.outerRadius - pixelMargin, 0), - pixelMargin: pixelMargin, - startAngle: vm.startAngle, - endAngle: vm.endAngle, - fullCircles: Math.floor(vm.circumference / TAU) - }; - var i; - - ctx.save(); - - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - - if (arc.fullCircles) { - arc.endAngle = arc.startAngle + TAU; - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - for (i = 0; i < arc.fullCircles; ++i) { - ctx.fill(); - } - arc.endAngle = arc.startAngle + vm.circumference % TAU; - } - - ctx.beginPath(); - ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); - ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); - ctx.closePath(); - ctx.fill(); - - if (vm.borderWidth) { - drawBorder(ctx, vm, arc); - } - - ctx.restore(); - } -}); - -var valueOrDefault$1 = helpers$1.valueOrDefault; - -var defaultColor = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - line: { - tension: 0.4, - backgroundColor: defaultColor, - borderWidth: 3, - borderColor: defaultColor, - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - capBezierPoints: true, - fill: true, // do we fill in the area between the line and its base axis - } - } -}); - -var element_line = core_element.extend({ - _type: 'line', - - draw: function() { - var me = this; - var vm = me._view; - var ctx = me._chart.ctx; - var spanGaps = vm.spanGaps; - var points = me._children.slice(); // clone array - var globalDefaults = core_defaults.global; - var globalOptionLineElements = globalDefaults.elements.line; - var lastDrawnIndex = -1; - var closePath = me._loop; - var index, previous, currentVM; - - if (!points.length) { - return; - } - - if (me._loop) { - for (index = 0; index < points.length; ++index) { - previous = helpers$1.previousItem(points, index); - // If the line has an open path, shift the point array - if (!points[index]._view.skip && previous._view.skip) { - points = points.slice(index).concat(points.slice(0, index)); - closePath = spanGaps; - break; - } - } - // If the line has a close path, add the first point again - if (closePath) { - points.push(points[0]); - } - } - - ctx.save(); - - // Stroke Line Options - ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; - - // IE 9 and 10 do not support line dash - if (ctx.setLineDash) { - ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); - } - - ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); - ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; - ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); - ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; - - // Stroke Line - ctx.beginPath(); - - // First point moves to it's starting position no matter what - currentVM = points[0]._view; - if (!currentVM.skip) { - ctx.moveTo(currentVM.x, currentVM.y); - lastDrawnIndex = 0; - } - - for (index = 1; index < points.length; ++index) { - currentVM = points[index]._view; - previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex]; - - if (!currentVM.skip) { - if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { - // There was a gap and this is the first point after the gap - ctx.moveTo(currentVM.x, currentVM.y); - } else { - // Line to next point - helpers$1.canvas.lineTo(ctx, previous._view, currentVM); - } - lastDrawnIndex = index; - } - } - - if (closePath) { - ctx.closePath(); - } - - ctx.stroke(); - ctx.restore(); - } -}); - -var valueOrDefault$2 = helpers$1.valueOrDefault; - -var defaultColor$1 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - point: { - radius: 3, - pointStyle: 'circle', - backgroundColor: defaultColor$1, - borderColor: defaultColor$1, - borderWidth: 1, - // Hover - hitRadius: 1, - hoverRadius: 4, - hoverBorderWidth: 1 - } - } -}); - -function xRange(mouseX) { - var vm = this._view; - return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; -} - -function yRange(mouseY) { - var vm = this._view; - return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; -} - -var element_point = core_element.extend({ - _type: 'point', - - inRange: function(mouseX, mouseY) { - var vm = this._view; - return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; - }, - - inLabelRange: xRange, - inXRange: xRange, - inYRange: yRange, - - getCenterPoint: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - }, - - getArea: function() { - return Math.PI * Math.pow(this._view.radius, 2); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y, - padding: vm.radius + vm.borderWidth - }; - }, - - draw: function(chartArea) { - var vm = this._view; - var ctx = this._chart.ctx; - var pointStyle = vm.pointStyle; - var rotation = vm.rotation; - var radius = vm.radius; - var x = vm.x; - var y = vm.y; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow - - if (vm.skip) { - return; - } - - // Clipping for Points. - if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { - ctx.strokeStyle = vm.borderColor || defaultColor; - ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); - ctx.fillStyle = vm.backgroundColor || defaultColor; - helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); - } - } -}); - -var defaultColor$2 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - rectangle: { - backgroundColor: defaultColor$2, - borderColor: defaultColor$2, - borderSkipped: 'bottom', - borderWidth: 0 - } - } -}); - -function isVertical(vm) { - return vm && vm.width !== undefined; -} - -/** - * Helper function to get the bounds of the bar regardless of the orientation - * @param bar {Chart.Element.Rectangle} the bar - * @return {Bounds} bounds of the bar - * @private - */ -function getBarBounds(vm) { - var x1, x2, y1, y2, half; - - if (isVertical(vm)) { - half = vm.width / 2; - x1 = vm.x - half; - x2 = vm.x + half; - y1 = Math.min(vm.y, vm.base); - y2 = Math.max(vm.y, vm.base); - } else { - half = vm.height / 2; - x1 = Math.min(vm.x, vm.base); - x2 = Math.max(vm.x, vm.base); - y1 = vm.y - half; - y2 = vm.y + half; - } - - return { - left: x1, - top: y1, - right: x2, - bottom: y2 - }; -} - -function swap(orig, v1, v2) { - return orig === v1 ? v2 : orig === v2 ? v1 : orig; -} - -function parseBorderSkipped(vm) { - var edge = vm.borderSkipped; - var res = {}; - - if (!edge) { - return res; - } - - if (vm.horizontal) { - if (vm.base > vm.x) { - edge = swap(edge, 'left', 'right'); - } - } else if (vm.base < vm.y) { - edge = swap(edge, 'bottom', 'top'); - } - - res[edge] = true; - return res; -} - -function parseBorderWidth(vm, maxW, maxH) { - var value = vm.borderWidth; - var skip = parseBorderSkipped(vm); - var t, r, b, l; - - if (helpers$1.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, - r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, - b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, - l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l - }; -} - -function boundingRects(vm) { - var bounds = getBarBounds(vm); - var width = bounds.right - bounds.left; - var height = bounds.bottom - bounds.top; - var border = parseBorderWidth(vm, width / 2, height / 2); - - return { - outer: { - x: bounds.left, - y: bounds.top, - w: width, - h: height - }, - inner: { - x: bounds.left + border.l, - y: bounds.top + border.t, - w: width - border.l - border.r, - h: height - border.t - border.b - } - }; -} - -function inRange(vm, x, y) { - var skipX = x === null; - var skipY = y === null; - var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); - - return bounds - && (skipX || x >= bounds.left && x <= bounds.right) - && (skipY || y >= bounds.top && y <= bounds.bottom); -} - -var element_rectangle = core_element.extend({ - _type: 'rectangle', - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var rects = boundingRects(vm); - var outer = rects.outer; - var inner = rects.inner; - - ctx.fillStyle = vm.backgroundColor; - ctx.fillRect(outer.x, outer.y, outer.w, outer.h); - - if (outer.w === inner.w && outer.h === inner.h) { - return; - } - - ctx.save(); - ctx.beginPath(); - ctx.rect(outer.x, outer.y, outer.w, outer.h); - ctx.clip(); - ctx.fillStyle = vm.borderColor; - ctx.rect(inner.x, inner.y, inner.w, inner.h); - ctx.fill('evenodd'); - ctx.restore(); - }, - - height: function() { - var vm = this._view; - return vm.base - vm.y; - }, - - inRange: function(mouseX, mouseY) { - return inRange(this._view, mouseX, mouseY); - }, - - inLabelRange: function(mouseX, mouseY) { - var vm = this._view; - return isVertical(vm) - ? inRange(vm, mouseX, null) - : inRange(vm, null, mouseY); - }, - - inXRange: function(mouseX) { - return inRange(this._view, mouseX, null); - }, - - inYRange: function(mouseY) { - return inRange(this._view, null, mouseY); - }, - - getCenterPoint: function() { - var vm = this._view; - var x, y; - if (isVertical(vm)) { - x = vm.x; - y = (vm.y + vm.base) / 2; - } else { - x = (vm.x + vm.base) / 2; - y = vm.y; - } - - return {x: x, y: y}; - }, - - getArea: function() { - var vm = this._view; - - return isVertical(vm) - ? vm.width * Math.abs(vm.y - vm.base) - : vm.height * Math.abs(vm.x - vm.base); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - } -}); - -var elements = {}; -var Arc = element_arc; -var Line = element_line; -var Point = element_point; -var Rectangle = element_rectangle; -elements.Arc = Arc; -elements.Line = Line; -elements.Point = Point; -elements.Rectangle = Rectangle; - -var deprecated = helpers$1._deprecated; -var valueOrDefault$3 = helpers$1.valueOrDefault; - -core_defaults._set('bar', { - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - offset: true, - gridLines: { - offsetGridLines: true - } - }], - - yAxes: [{ - type: 'linear' - }] - } -}); - -core_defaults._set('global', { - datasets: { - bar: { - categoryPercentage: 0.8, - barPercentage: 0.9 - } - } -}); - -/** - * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. - * @private - */ -function computeMinSampleSize(scale, pixels) { - var min = scale._length; - var prev, curr, i, ilen; - - for (i = 1, ilen = pixels.length; i < ilen; ++i) { - min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); - } - - for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) { - curr = scale.getPixelForTick(i); - min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min; - prev = curr; - } - - return min; -} - -/** - * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, - * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This - * mode currently always generates bars equally sized (until we introduce scriptable options?). - * @private - */ -function computeFitCategoryTraits(index, ruler, options) { - var thickness = options.barThickness; - var count = ruler.stackCount; - var curr = ruler.pixels[index]; - var min = helpers$1.isNullOrUndef(thickness) - ? computeMinSampleSize(ruler.scale, ruler.pixels) - : -1; - var size, ratio; - - if (helpers$1.isNullOrUndef(thickness)) { - size = min * options.categoryPercentage; - ratio = options.barPercentage; - } else { - // When bar thickness is enforced, category and bar percentages are ignored. - // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') - // and deprecate barPercentage since this value is ignored when thickness is absolute. - size = thickness * count; - ratio = 1; - } - - return { - chunk: size / count, - ratio: ratio, - start: curr - (size / 2) - }; -} - -/** - * Computes an "optimal" category that globally arranges bars side by side (no gap when - * percentage options are 1), based on the previous and following categories. This mode - * generates bars with different widths when data are not evenly spaced. - * @private - */ -function computeFlexCategoryTraits(index, ruler, options) { - var pixels = ruler.pixels; - var curr = pixels[index]; - var prev = index > 0 ? pixels[index - 1] : null; - var next = index < pixels.length - 1 ? pixels[index + 1] : null; - var percent = options.categoryPercentage; - var start, size; - - if (prev === null) { - // first data: its size is double based on the next point or, - // if it's also the last data, we use the scale size. - prev = curr - (next === null ? ruler.end - ruler.start : next - curr); - } - - if (next === null) { - // last data: its size is also double based on the previous point. - next = curr + curr - prev; - } - - start = curr - (curr - Math.min(prev, next)) / 2 * percent; - size = Math.abs(next - prev) / 2 * percent; - - return { - chunk: size / ruler.stackCount, - ratio: options.barPercentage, - start: start - }; -} - -var controller_bar = core_datasetController.extend({ - - dataElementType: elements.Rectangle, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderSkipped', - 'borderWidth', - 'barPercentage', - 'barThickness', - 'categoryPercentage', - 'maxBarThickness', - 'minBarLength' - ], - - initialize: function() { - var me = this; - var meta, scaleOpts; - - core_datasetController.prototype.initialize.apply(me, arguments); - - meta = me.getMeta(); - meta.stack = me.getDataset().stack; - meta.bar = true; - - scaleOpts = me._getIndexScale().options; - deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage'); - deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness'); - deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage'); - deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength'); - deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness'); - }, - - update: function(reset) { - var me = this; - var rects = me.getMeta().data; - var i, ilen; - - me._ruler = me.getRuler(); - - for (i = 0, ilen = rects.length; i < ilen; ++i) { - me.updateElement(rects[i], i, reset); - } - }, - - updateElement: function(rectangle, index, reset) { - var me = this; - var meta = me.getMeta(); - var dataset = me.getDataset(); - var options = me._resolveDataElementOptions(rectangle, index); - - rectangle._xScale = me.getScaleForId(meta.xAxisID); - rectangle._yScale = me.getScaleForId(meta.yAxisID); - rectangle._datasetIndex = me.index; - rectangle._index = index; - rectangle._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderSkipped: options.borderSkipped, - borderWidth: options.borderWidth, - datasetLabel: dataset.label, - label: me.chart.data.labels[index] - }; - - if (helpers$1.isArray(dataset.data[index])) { - rectangle._model.borderSkipped = null; - } - - me._updateElementGeometry(rectangle, index, reset, options); - - rectangle.pivot(); - }, - - /** - * @private - */ - _updateElementGeometry: function(rectangle, index, reset, options) { - var me = this; - var model = rectangle._model; - var vscale = me._getValueScale(); - var base = vscale.getBasePixel(); - var horizontal = vscale.isHorizontal(); - var ruler = me._ruler || me.getRuler(); - var vpixels = me.calculateBarValuePixels(me.index, index, options); - var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options); - - model.horizontal = horizontal; - model.base = reset ? base : vpixels.base; - model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; - model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; - model.height = horizontal ? ipixels.size : undefined; - model.width = horizontal ? undefined : ipixels.size; - }, - - /** - * Returns the stacks based on groups and bar visibility. - * @param {number} [last] - The dataset index - * @returns {string[]} The list of stack IDs - * @private - */ - _getStacks: function(last) { - var me = this; - var scale = me._getIndexScale(); - var metasets = scale._getMatchingVisibleMetas(me._type); - var stacked = scale.options.stacked; - var ilen = metasets.length; - var stacks = []; - var i, meta; - - for (i = 0; i < ilen; ++i) { - meta = metasets[i]; - // stacked | meta.stack - // | found | not found | undefined - // false | x | x | x - // true | | x | - // undefined | | x | x - if (stacked === false || stacks.indexOf(meta.stack) === -1 || - (stacked === undefined && meta.stack === undefined)) { - stacks.push(meta.stack); - } - if (meta.index === last) { - break; - } - } - - return stacks; - }, - - /** - * Returns the effective number of stacks based on groups and bar visibility. - * @private - */ - getStackCount: function() { - return this._getStacks().length; - }, - - /** - * Returns the stack index for the given dataset based on groups and bar visibility. - * @param {number} [datasetIndex] - The dataset index - * @param {string} [name] - The stack name to find - * @returns {number} The stack index - * @private - */ - getStackIndex: function(datasetIndex, name) { - var stacks = this._getStacks(datasetIndex); - var index = (name !== undefined) - ? stacks.indexOf(name) - : -1; // indexOf returns -1 if element is not present - - return (index === -1) - ? stacks.length - 1 - : index; - }, - - /** - * @private - */ - getRuler: function() { - var me = this; - var scale = me._getIndexScale(); - var pixels = []; - var i, ilen; - - for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { - pixels.push(scale.getPixelForValue(null, i, me.index)); - } - - return { - pixels: pixels, - start: scale._startPixel, - end: scale._endPixel, - stackCount: me.getStackCount(), - scale: scale - }; - }, - - /** - * Note: pixel values are not clamped to the scale area. - * @private - */ - calculateBarValuePixels: function(datasetIndex, index, options) { - var me = this; - var chart = me.chart; - var scale = me._getValueScale(); - var isHorizontal = scale.isHorizontal(); - var datasets = chart.data.datasets; - var metasets = scale._getMatchingVisibleMetas(me._type); - var value = scale._parseValue(datasets[datasetIndex].data[index]); - var minBarLength = options.minBarLength; - var stacked = scale.options.stacked; - var stack = me.getMeta().stack; - var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; - var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; - var ilen = metasets.length; - var i, imeta, ivalue, base, head, size, stackLength; - - if (stacked || (stacked === undefined && stack !== undefined)) { - for (i = 0; i < ilen; ++i) { - imeta = metasets[i]; - - if (imeta.index === datasetIndex) { - break; - } - - if (imeta.stack === stack) { - stackLength = scale._parseValue(datasets[imeta.index].data[index]); - ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; - - if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { - start += ivalue; - } - } - } - } - - base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + length); - size = head - base; - - if (minBarLength !== undefined && Math.abs(size) < minBarLength) { - size = minBarLength; - if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { - head = base - minBarLength; - } else { - head = base + minBarLength; - } - } - - return { - size: size, - base: base, - head: head, - center: head + size / 2 - }; - }, - - /** - * @private - */ - calculateBarIndexPixels: function(datasetIndex, index, ruler, options) { - var me = this; - var range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options) - : computeFitCategoryTraits(index, ruler, options); - - var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); - var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - var size = Math.min( - valueOrDefault$3(options.maxBarThickness, Infinity), - range.chunk * range.ratio); - - return { - base: center - size / 2, - head: center + size / 2, - center: center, - size: size - }; - }, - - draw: function() { - var me = this; - var chart = me.chart; - var scale = me._getValueScale(); - var rects = me.getMeta().data; - var dataset = me.getDataset(); - var ilen = rects.length; - var i = 0; - - helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); - - for (; i < ilen; ++i) { - var val = scale._parseValue(dataset.data[i]); - if (!isNaN(val.min) && !isNaN(val.max)) { - rects[i].draw(); - } - } - - helpers$1.canvas.unclipArea(chart.ctx); - }, - - /** - * @private - */ - _resolveDataElementOptions: function() { - var me = this; - var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments)); - var indexOpts = me._getIndexScale().options; - var valueOpts = me._getValueScale().options; - - values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage); - values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness); - values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage); - values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness); - values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength); - - return values; - } - -}); - -var valueOrDefault$4 = helpers$1.valueOrDefault; -var resolve$1 = helpers$1.options.resolve; - -core_defaults._set('bubble', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - type: 'linear', // bubble should probably use a linear scale by default - position: 'bottom', - id: 'x-axis-0' // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: 'linear', - position: 'left', - id: 'y-axis-0' - }] - }, - - tooltips: { - callbacks: { - title: function() { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(item, data) { - var datasetLabel = data.datasets[item.datasetIndex].label || ''; - var dataPoint = data.datasets[item.datasetIndex].data[item.index]; - return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; - } - } - } -}); - -var controller_bubble = core_datasetController.extend({ - /** - * @protected - */ - dataElementType: elements.Point, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - 'hoverRadius', - 'hitRadius', - 'pointStyle', - 'rotation' - ], - - /** - * @protected - */ - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var points = meta.data; - - // Update Points - helpers$1.each(points, function(point, index) { - me.updateElement(point, index, reset); - }); - }, - - /** - * @protected - */ - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var xScale = me.getScaleForId(meta.xAxisID); - var yScale = me.getScaleForId(meta.yAxisID); - var options = me._resolveDataElementOptions(point, index); - var data = me.getDataset().data[index]; - var dsIndex = me.index; - - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); - var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); - - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = dsIndex; - point._index = index; - point._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - hitRadius: options.hitRadius, - pointStyle: options.pointStyle, - rotation: options.rotation, - radius: reset ? 0 : options.radius, - skip: custom.skip || isNaN(x) || isNaN(y), - x: x, - y: y, - }; - - point.pivot(); - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); - model.radius = options.radius + options.hoverRadius; - }, - - /** - * @private - */ - _resolveDataElementOptions: function(point, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var custom = point.custom || {}; - var data = dataset.data[index] || {}; - var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments); - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - // In case values were cached (and thus frozen), we need to clone the values - if (me._cachedDataOpts === values) { - values = helpers$1.extend({}, values); - } - - // Custom radius resolution - values.radius = resolve$1([ - custom.radius, - data.r, - me._config.radius, - chart.options.elements.point.radius - ], context, index); - - return values; - } -}); - -var valueOrDefault$5 = helpers$1.valueOrDefault; - -var PI$1 = Math.PI; -var DOUBLE_PI$1 = PI$1 * 2; -var HALF_PI$1 = PI$1 / 2; - -core_defaults._set('doughnut', { - animation: { - // Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - // Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false - }, - hover: { - mode: 'single' - }, - legendCallback: function(chart) { - var list = document.createElement('ul'); - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - if (datasets.length) { - for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; - if (labels[i]) { - listItem.appendChild(document.createTextNode(labels[i])); - } - } - } - - return list.outerHTML; - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var style = meta.controller.getStyle(i); - - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - // toggle visibility of index if exists - if (meta.data[index]) { - meta.data[index].hidden = !meta.data[index].hidden; - } - } - - chart.update(); - } - }, - - // The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, - - // The rotation of the chart, where the first data arc begins. - rotation: -HALF_PI$1, - - // The total circumference of the chart. - circumference: DOUBLE_PI$1, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - var dataLabel = data.labels[tooltipItem.index]; - var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - - if (helpers$1.isArray(dataLabel)) { - // show value on first line of multiline label - // need to clone because we are changing the value - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - - return dataLabel; - } - } - } -}); - -var controller_doughnut = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ], - - // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly - getRingIndex: function(datasetIndex) { - var ringIndex = 0; - - for (var j = 0; j < datasetIndex; ++j) { - if (this.chart.isDatasetVisible(j)) { - ++ringIndex; - } - } - - return ringIndex; - }, - - update: function(reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var ratioX = 1; - var ratioY = 1; - var offsetX = 0; - var offsetY = 0; - var meta = me.getMeta(); - var arcs = meta.data; - var cutout = opts.cutoutPercentage / 100 || 0; - var circumference = opts.circumference; - var chartWeight = me._getRingWeight(me.index); - var maxWidth, maxHeight, i, ilen; - - // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc - if (circumference < DOUBLE_PI$1) { - var startAngle = opts.rotation % DOUBLE_PI$1; - startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0; - var endAngle = startAngle + circumference; - var startX = Math.cos(startAngle); - var startY = Math.sin(startAngle); - var endX = Math.cos(endAngle); - var endY = Math.sin(endAngle); - var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1; - var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1; - var contains180 = startAngle === -PI$1 || endAngle >= PI$1; - var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1; - var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout); - var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout); - var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout); - var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout); - ratioX = (maxX - minX) / 2; - ratioY = (maxY - minY) / 2; - offsetX = -(maxX + minX) / 2; - offsetY = -(maxY + minY) / 2; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); - } - - chart.borderWidth = me.getMaxBorderWidth(); - maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX; - maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY; - chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); - chart.innerRadius = Math.max(chart.outerRadius * cutout, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); - chart.offsetX = offsetX * chart.outerRadius; - chart.offsetY = offsetY * chart.outerRadius; - - meta.total = me.calculateTotal(); - - me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); - me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - me.updateElement(arcs[i], i, reset); - } - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var animationOpts = opts.animation; - var centerX = (chartArea.left + chartArea.right) / 2; - var centerY = (chartArea.top + chartArea.bottom) / 2; - var startAngle = opts.rotation; // non reset case handled later - var endAngle = opts.rotation; // non reset case handled later - var dataset = me.getDataset(); - var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1); - var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; - var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX + chart.offsetX, - y: centerY + chart.offsetY, - startAngle: startAngle, - endAngle: endAngle, - circumference: circumference, - outerRadius: outerRadius, - innerRadius: innerRadius, - label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) - } - }); - - var model = arc._model; - - // Set correct angles if not resetting - if (!reset || !animationOpts.animateRotate) { - if (index === 0) { - model.startAngle = opts.rotation; - } else { - model.startAngle = me.getMeta().data[index - 1]._model.endAngle; - } - - model.endAngle = model.startAngle + model.circumference; - } - - arc.pivot(); - }, - - calculateTotal: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var total = 0; - var value; - - helpers$1.each(meta.data, function(element, index) { - value = dataset.data[index]; - if (!isNaN(value) && !element.hidden) { - total += Math.abs(value); - } - }); - - /* if (total === 0) { - total = NaN; - }*/ - - return total; - }, - - calculateCircumference: function(value) { - var total = this.getMeta().total; - if (total > 0 && !isNaN(value)) { - return DOUBLE_PI$1 * (Math.abs(value) / total); - } - return 0; - }, - - // gets the max border or hover width to properly scale pie charts - getMaxBorderWidth: function(arcs) { - var me = this; - var max = 0; - var chart = me.chart; - var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; - - if (!arcs) { - // Find the outmost visible dataset - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - arcs = meta.data; - if (i !== me.index) { - controller = meta.controller; - } - break; - } - } - } - - if (!arcs) { - return 0; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arc = arcs[i]; - if (controller) { - controller._configure(); - options = controller._resolveDataElementOptions(arc, i); - } else { - options = arc._options; - } - if (options.borderAlign !== 'inner') { - borderWidth = options.borderWidth; - hoverWidth = options.hoverBorderWidth; - - max = borderWidth > max ? borderWidth : max; - max = hoverWidth > max ? hoverWidth : max; - } - } - return max; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly - * @private - */ - _getRingWeightOffset: function(datasetIndex) { - var ringWeightOffset = 0; - - for (var i = 0; i < datasetIndex; ++i) { - if (this.chart.isDatasetVisible(i)) { - ringWeightOffset += this._getRingWeight(i); - } - } - - return ringWeightOffset; - }, - - /** - * @private - */ - _getRingWeight: function(dataSetIndex) { - return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0); - }, - - /** - * Returns the sum of all visibile data set weights. This value can be 0. - * @private - */ - _getVisibleDatasetWeightTotal: function() { - return this._getRingWeightOffset(this.chart.data.datasets.length); - } -}); - -core_defaults._set('horizontalBar', { - hover: { - mode: 'index', - axis: 'y' - }, - - scales: { - xAxes: [{ - type: 'linear', - position: 'bottom' - }], - - yAxes: [{ - type: 'category', - position: 'left', - offset: true, - gridLines: { - offsetGridLines: true - } - }] - }, - - elements: { - rectangle: { - borderSkipped: 'left' - } - }, - - tooltips: { - mode: 'index', - axis: 'y' - } -}); - -core_defaults._set('global', { - datasets: { - horizontalBar: { - categoryPercentage: 0.8, - barPercentage: 0.9 - } - } -}); - -var controller_horizontalBar = controller_bar.extend({ - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().yAxisID; - } -}); - -var valueOrDefault$6 = helpers$1.valueOrDefault; -var resolve$2 = helpers$1.options.resolve; -var isPointInArea = helpers$1.canvas._isPointInArea; - -core_defaults._set('line', { - showLines: true, - spanGaps: false, - - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - id: 'x-axis-0' - }], - yAxes: [{ - type: 'linear', - id: 'y-axis-0' - }] - } -}); - -function scaleClip(scale, halfBorderWidth) { - var tickOpts = scale && scale.options.ticks || {}; - var reverse = tickOpts.reverse; - var min = tickOpts.min === undefined ? halfBorderWidth : 0; - var max = tickOpts.max === undefined ? halfBorderWidth : 0; - return { - start: reverse ? max : min, - end: reverse ? min : max - }; -} - -function defaultClip(xScale, yScale, borderWidth) { - var halfBorderWidth = borderWidth / 2; - var x = scaleClip(xScale, halfBorderWidth); - var y = scaleClip(yScale, halfBorderWidth); - - return { - top: y.end, - right: x.end, - bottom: y.start, - left: x.start - }; -} - -function toClip(value) { - var t, r, b, l; - - if (helpers$1.isObject(value)) { - t = value.top; - r = value.right; - b = value.bottom; - l = value.left; - } else { - t = r = b = l = value; - } - - return { - top: t, - right: r, - bottom: b, - left: l - }; -} - - -var controller_line = core_datasetController.extend({ - - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - /** - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderCapStyle', - 'borderColor', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'borderWidth', - 'cubicInterpolationMode', - 'fill' - ], - - /** - * @private - */ - _dataElementOptions: { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var options = me.chart.options; - var config = me._config; - var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines); - var i, ilen; - - me._xScale = me.getScaleForId(meta.xAxisID); - me._yScale = me.getScaleForId(meta.yAxisID); - - // Update Line - if (showLine) { - // Compatibility: If the properties are defined with only the old name, use those values - if (config.tension !== undefined && config.lineTension === undefined) { - config.lineTension = config.tension; - } - - // Utility - line._scale = me._yScale; - line._datasetIndex = me.index; - // Data - line._children = points; - // Model - line._model = me._resolveDatasetElementOptions(line); - - line.pivot(); - } - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - if (showLine && line._model.tension !== 0) { - me.updateBezierControlPoints(); - } - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var dataset = me.getDataset(); - var datasetIndex = me.index; - var value = dataset.data[index]; - var xScale = me._xScale; - var yScale = me._yScale; - var lineModel = meta.dataset._model; - var x, y; - - var options = me._resolveDataElementOptions(point, index); - - x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); - y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); - - // Utility - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = datasetIndex; - point._index = index; - - // Desired view properties - point._model = { - x: x, - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), - steppedLine: lineModel ? lineModel.steppedLine : false, - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function(element) { - var me = this; - var config = me._config; - var custom = element.custom || {}; - var options = me.chart.options; - var lineOptions = options.elements.line; - var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); - - // The default behavior of lines is to break at null values, according - // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 - // This option gives lines the ability to span gaps - values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps); - values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension); - values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]); - values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth))); - - return values; - }, - - calculatePointY: function(value, index, datasetIndex) { - var me = this; - var chart = me.chart; - var yScale = me._yScale; - var sumPos = 0; - var sumNeg = 0; - var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen; - - if (yScale.options.stacked) { - rightValue = +yScale.getRightValue(value); - metasets = chart._getSortedVisibleDatasetMetas(); - ilen = metasets.length; - - for (i = 0; i < ilen; ++i) { - dsMeta = metasets[i]; - if (dsMeta.index === datasetIndex) { - break; - } - - ds = chart.data.datasets[dsMeta.index]; - if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) { - stackedRightValue = +yScale.getRightValue(ds.data[index]); - if (stackedRightValue < 0) { - sumNeg += stackedRightValue || 0; - } else { - sumPos += stackedRightValue || 0; - } - } - } - - if (rightValue < 0) { - return yScale.getPixelForValue(sumNeg + rightValue); - } - return yScale.getPixelForValue(sumPos + rightValue); - } - return yScale.getPixelForValue(value); - }, - - updateBezierControlPoints: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var lineModel = meta.dataset._model; - var area = chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - // Only consider points that are drawn in case the spanGaps option is used - if (lineModel.spanGaps) { - points = points.filter(function(pt) { - return !pt._model.skip; - }); - } - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - if (lineModel.cubicInterpolationMode === 'monotone') { - helpers$1.splineCurveMonotone(points); - } else { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i)._model, - model, - helpers$1.nextItem(points, i)._model, - lineModel.tension - ); - model.controlPointPreviousX = controlPoints.previous.x; - model.controlPointPreviousY = controlPoints.previous.y; - model.controlPointNextX = controlPoints.next.x; - model.controlPointNextY = controlPoints.next.y; - } - } - - if (chart.options.elements.line.capBezierPoints) { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - if (isPointInArea(model, area)) { - if (i > 0 && isPointInArea(points[i - 1]._model, area)) { - model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); - model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); - } - if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { - model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); - model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); - } - } - } - } - }, - - draw: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var points = meta.data || []; - var area = chart.chartArea; - var canvas = chart.canvas; - var i = 0; - var ilen = points.length; - var clip; - - if (me._showLine) { - clip = meta.dataset._model.clip; - - helpers$1.canvas.clipArea(chart.ctx, { - left: clip.left === false ? 0 : area.left - clip.left, - right: clip.right === false ? canvas.width : area.right + clip.right, - top: clip.top === false ? 0 : area.top - clip.top, - bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom - }); - - meta.dataset.draw(); - - helpers$1.canvas.unclipArea(chart.ctx); - } - - // Draw the points - for (; i < ilen; ++i) { - points[i].draw(area); - } - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$6(options.hoverRadius, options.radius); - }, -}); - -var resolve$3 = helpers$1.options.resolve; - -core_defaults._set('polarArea', { - scale: { - type: 'radialLinear', - angleLines: { - display: false - }, - gridLines: { - circular: true - }, - pointLabels: { - display: false - }, - ticks: { - beginAtZero: true - } - }, - - // Boolean - Whether to animate the rotation of the chart - animation: { - animateRotate: true, - animateScale: true - }, - - startAngle: -0.5 * Math.PI, - legendCallback: function(chart) { - var list = document.createElement('ul'); - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - if (datasets.length) { - for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i]; - if (labels[i]) { - listItem.appendChild(document.createTextNode(labels[i])); - } - } - } - - return list.outerHTML; - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var style = meta.controller.getStyle(i); - - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - meta.data[index].hidden = !meta.data[index].hidden; - } - - chart.update(); - } - }, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(item, data) { - return data.labels[item.index] + ': ' + item.yLabel; - } - } - } -}); - -var controller_polarArea = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _dataElementOptions: [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ], - - /** - * @private - */ - _getIndexScaleId: function() { - return this.chart.scale.id; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.chart.scale.id; - }, - - update: function(reset) { - var me = this; - var dataset = me.getDataset(); - var meta = me.getMeta(); - var start = me.chart.options.startAngle || 0; - var starts = me._starts = []; - var angles = me._angles = []; - var arcs = meta.data; - var i, ilen, angle; - - me._updateRadius(); - - meta.count = me.countVisibleElements(); - - for (i = 0, ilen = dataset.data.length; i < ilen; i++) { - starts[i] = start; - angle = me._computeAngle(i); - angles[i] = angle; - start += angle; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); - me.updateElement(arcs[i], i, reset); - } - }, - - /** - * @private - */ - _updateRadius: function() { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - - chart.outerRadius = Math.max(minSize / 2, 0); - chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); - - me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); - me.innerRadius = me.outerRadius - chart.radiusLength; - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var opts = chart.options; - var animationOpts = opts.animation; - var scale = chart.scale; - var labels = chart.data.labels; - - var centerX = scale.xCenter; - var centerY = scale.yCenter; - - // var negHalfPI = -0.5 * Math.PI; - var datasetStartAngle = opts.startAngle; - var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var startAngle = me._starts[index]; - var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); - - var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - _scale: scale, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius: reset ? resetRadius : distance, - startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, - endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, - label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) - } - }); - - arc.pivot(); - }, - - countVisibleElements: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var count = 0; - - helpers$1.each(meta.data, function(element, index) { - if (!isNaN(dataset.data[index]) && !element.hidden) { - count++; - } - }); - - return count; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - var valueOrDefault = helpers$1.valueOrDefault; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * @private - */ - _computeAngle: function(index) { - var me = this; - var count = this.getMeta().count; - var dataset = me.getDataset(); - var meta = me.getMeta(); - - if (isNaN(dataset.data[index]) || meta.data[index].hidden) { - return 0; - } - - // Scriptable options - var context = { - chart: me.chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - return resolve$3([ - me.chart.options.elements.arc.angle, - (2 * Math.PI) / count - ], context, index); - } -}); - -core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); -core_defaults._set('pie', { - cutoutPercentage: 0 -}); - -// Pie charts are Doughnut chart with different defaults -var controller_pie = controller_doughnut; - -var valueOrDefault$7 = helpers$1.valueOrDefault; - -core_defaults._set('radar', { - spanGaps: false, - scale: { - type: 'radialLinear' - }, - elements: { - line: { - fill: 'start', - tension: 0 // no bezier in radar - } - } -}); - -var controller_radar = core_datasetController.extend({ - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - linkScales: helpers$1.noop, - - /** - * @private - */ - _datasetElementOptions: [ - 'backgroundColor', - 'borderWidth', - 'borderColor', - 'borderCapStyle', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'fill' - ], - - /** - * @private - */ - _dataElementOptions: { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.chart.scale.id; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.chart.scale.id; - }, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var scale = me.chart.scale; - var config = me._config; - var i, ilen; - - // Compatibility: If the properties are defined with only the old name, use those values - if (config.tension !== undefined && config.lineTension === undefined) { - config.lineTension = config.tension; - } - - // Utility - line._scale = scale; - line._datasetIndex = me.index; - // Data - line._children = points; - line._loop = true; - // Model - line._model = me._resolveDatasetElementOptions(line); - - line.pivot(); - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - // Update bezier control points - me.updateBezierControlPoints(); - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var custom = point.custom || {}; - var dataset = me.getDataset(); - var scale = me.chart.scale; - var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); - var options = me._resolveDataElementOptions(point, index); - var lineModel = me.getMeta().dataset._model; - var x = reset ? scale.xCenter : pointPosition.x; - var y = reset ? scale.yCenter : pointPosition.y; - - // Utility - point._scale = scale; - point._options = options; - point._datasetIndex = me.index; - point._index = index; - - // Desired view properties - point._model = { - x: x, // value not used in dataset scale, but we want a consistent API between scales - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0), - - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolveDatasetElementOptions: function() { - var me = this; - var config = me._config; - var options = me.chart.options; - var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); - - values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps); - values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension); - - return values; - }, - - updateBezierControlPoints: function() { - var me = this; - var meta = me.getMeta(); - var area = me.chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - // Only consider points that are drawn in case the spanGaps option is used - if (meta.dataset._model.spanGaps) { - points = points.filter(function(pt) { - return !pt._model.skip; - }); - } - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i, true)._model, - model, - helpers$1.nextItem(points, i, true)._model, - model.tension - ); - - // Prevent the bezier going outside of the bounds of the graph - model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); - model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); - model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); - model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); - } - }, - - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$7(options.hoverRadius, options.radius); - } -}); - -core_defaults._set('scatter', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - id: 'x-axis-1', // need an ID so datasets can reference the scale - type: 'linear', // scatter should not use a category axis - position: 'bottom' - }], - yAxes: [{ - id: 'y-axis-1', - type: 'linear', - position: 'left' - }] - }, - - tooltips: { - callbacks: { - title: function() { - return ''; // doesn't make sense for scatter since data are formatted as a point - }, - label: function(item) { - return '(' + item.xLabel + ', ' + item.yLabel + ')'; - } - } - } -}); - -core_defaults._set('global', { - datasets: { - scatter: { - showLine: false - } - } -}); - -// Scatter charts use line controllers -var controller_scatter = controller_line; - -// NOTE export a map in which the key represents the controller type, not -// the class, and so must be CamelCase in order to be correctly retrieved -// by the controller in core.controller.js (`controllers[meta.type]`). - -var controllers = { - bar: controller_bar, - bubble: controller_bubble, - doughnut: controller_doughnut, - horizontalBar: controller_horizontalBar, - line: controller_line, - polarArea: controller_polarArea, - pie: controller_pie, - radar: controller_radar, - scatter: controller_scatter -}; - -/** - * Helper function to get relative position for an event - * @param {Event|IEvent} event - The event to get the position for - * @param {Chart} chart - The chart - * @returns {object} the event position - */ -function getRelativePosition(e, chart) { - if (e.native) { - return { - x: e.x, - y: e.y - }; - } - - return helpers$1.getRelativePosition(e, chart); -} - -/** - * Helper function to traverse all of the visible elements in the chart - * @param {Chart} chart - the chart - * @param {function} handler - the callback to execute for each visible item - */ -function parseVisibleItems(chart, handler) { - var metasets = chart._getSortedVisibleDatasetMetas(); - var metadata, i, j, ilen, jlen, element; - - for (i = 0, ilen = metasets.length; i < ilen; ++i) { - metadata = metasets[i].data; - for (j = 0, jlen = metadata.length; j < jlen; ++j) { - element = metadata[j]; - if (!element._view.skip) { - handler(element); - } - } - } -} - -/** - * Helper function to get the items that intersect the event position - * @param {ChartElement[]} items - elements to filter - * @param {object} position - the point to be nearest to - * @return {ChartElement[]} the nearest items - */ -function getIntersectItems(chart, position) { - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - } - }); - - return elements; -} - -/** - * Helper function to get the items nearest to the event position considering all visible items in teh chart - * @param {Chart} chart - the chart to look at elements from - * @param {object} position - the point to be nearest to - * @param {boolean} intersect - if true, only consider items that intersect the position - * @param {function} distanceMetric - function to provide the distance between points - * @return {ChartElement[]} the nearest items - */ -function getNearestItems(chart, position, intersect, distanceMetric) { - var minDistance = Number.POSITIVE_INFINITY; - var nearestItems = []; - - parseVisibleItems(chart, function(element) { - if (intersect && !element.inRange(position.x, position.y)) { - return; - } - - var center = element.getCenterPoint(); - var distance = distanceMetric(position, center); - if (distance < minDistance) { - nearestItems = [element]; - minDistance = distance; - } else if (distance === minDistance) { - // Can have multiple items at the same distance in which case we sort by size - nearestItems.push(element); - } - }); - - return nearestItems; -} - -/** - * Get a distance metric function for two points based on the - * axis mode setting - * @param {string} axis - the axis mode. x|y|xy - */ -function getDistanceMetricForAxis(axis) { - var useX = axis.indexOf('x') !== -1; - var useY = axis.indexOf('y') !== -1; - - return function(pt1, pt2) { - var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; - var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; - return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); - }; -} - -function indexMode(chart, e, options) { - var position = getRelativePosition(e, chart); - // Default axis for index mode is 'x' to match old behaviour - options.axis = options.axis || 'x'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - var elements = []; - - if (!items.length) { - return []; - } - - chart._getSortedVisibleDatasetMetas().forEach(function(meta) { - var element = meta.data[items[0]._index]; - - // don't count items that are skipped (null data) - if (element && !element._view.skip) { - elements.push(element); - } - }); - - return elements; -} - -/** - * @interface IInteractionOptions - */ -/** - * If true, only consider items that intersect the point - * @name IInterfaceOptions#boolean - * @type Boolean - */ - -/** - * Contains interaction related functions - * @namespace Chart.Interaction - */ -var core_interaction = { - // Helper function for different modes - modes: { - single: function(chart, e) { - var position = getRelativePosition(e, chart); - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - return elements; - } - }); - - return elements.slice(0, 1); - }, - - /** - * @function Chart.Interaction.modes.label - * @deprecated since version 2.4.0 - * @todo remove at version 3 - * @private - */ - label: indexMode, - - /** - * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item - * @function Chart.Interaction.modes.index - * @since v2.4.0 - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - index: indexMode, - - /** - * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect is false, we find the nearest item and return the items in that dataset - * @function Chart.Interaction.modes.dataset - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - dataset: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - - if (items.length > 0) { - items = chart.getDatasetMeta(items[0]._datasetIndex).data; - } - - return items; - }, - - /** - * @function Chart.Interaction.modes.x-axis - * @deprecated since version 2.4.0. Use index mode and intersect == true - * @todo remove at version 3 - * @private - */ - 'x-axis': function(chart, e) { - return indexMode(chart, e, {intersect: false}); - }, - - /** - * Point mode returns all elements that hit test based on the event position - * of the event - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - point: function(chart, e) { - var position = getRelativePosition(e, chart); - return getIntersectItems(chart, position); - }, - - /** - * nearest mode returns the element closest to the point - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - nearest: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - return getNearestItems(chart, position, options.intersect, distanceMetric); - }, - - /** - * x mode returns the elements that hit-test at the current x coordinate - * @function Chart.Interaction.modes.x - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - x: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inXRange(position.x)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - }, - - /** - * y mode returns the elements that hit-test at the current y coordinate - * @function Chart.Interaction.modes.y - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - y: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inYRange(position.y)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - } - } -}; - -var extend = helpers$1.extend; - -function filterByPosition(array, position) { - return helpers$1.where(array, function(v) { - return v.pos === position; - }); -} - -function sortByWeight(array, reverse) { - return array.sort(function(a, b) { - var v0 = reverse ? b : a; - var v1 = reverse ? a : b; - return v0.weight === v1.weight ? - v0.index - v1.index : - v0.weight - v1.weight; - }); -} - -function wrapBoxes(boxes) { - var layoutBoxes = []; - var i, ilen, box; - - for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) { - box = boxes[i]; - layoutBoxes.push({ - index: i, - box: box, - pos: box.position, - horizontal: box.isHorizontal(), - weight: box.weight - }); - } - return layoutBoxes; -} - -function setLayoutDims(layouts, params) { - var i, ilen, layout; - for (i = 0, ilen = layouts.length; i < ilen; ++i) { - layout = layouts[i]; - // store width used instead of chartArea.w in fitBoxes - layout.width = layout.horizontal - ? layout.box.fullWidth && params.availableWidth - : params.vBoxMaxWidth; - // store height used instead of chartArea.h in fitBoxes - layout.height = layout.horizontal && params.hBoxMaxHeight; - } -} - -function buildLayoutBoxes(boxes) { - var layoutBoxes = wrapBoxes(boxes); - var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true); - var right = sortByWeight(filterByPosition(layoutBoxes, 'right')); - var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true); - var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom')); - - return { - leftAndTop: left.concat(top), - rightAndBottom: right.concat(bottom), - chartArea: filterByPosition(layoutBoxes, 'chartArea'), - vertical: left.concat(right), - horizontal: top.concat(bottom) - }; -} - -function getCombinedMax(maxPadding, chartArea, a, b) { - return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]); -} - -function updateDims(chartArea, params, layout) { - var box = layout.box; - var maxPadding = chartArea.maxPadding; - var newWidth, newHeight; - - if (layout.size) { - // this layout was already counted for, lets first reduce old size - chartArea[layout.pos] -= layout.size; - } - layout.size = layout.horizontal ? box.height : box.width; - chartArea[layout.pos] += layout.size; - - if (box.getPadding) { - var boxPadding = box.getPadding(); - maxPadding.top = Math.max(maxPadding.top, boxPadding.top); - maxPadding.left = Math.max(maxPadding.left, boxPadding.left); - maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom); - maxPadding.right = Math.max(maxPadding.right, boxPadding.right); - } - - newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'); - newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'); - - if (newWidth !== chartArea.w || newHeight !== chartArea.h) { - chartArea.w = newWidth; - chartArea.h = newHeight; - - // return true if chart area changed in layout's direction - return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h; - } -} - -function handleMaxPadding(chartArea) { - var maxPadding = chartArea.maxPadding; - - function updatePos(pos) { - var change = Math.max(maxPadding[pos] - chartArea[pos], 0); - chartArea[pos] += change; - return change; - } - chartArea.y += updatePos('top'); - chartArea.x += updatePos('left'); - updatePos('right'); - updatePos('bottom'); -} - -function getMargins(horizontal, chartArea) { - var maxPadding = chartArea.maxPadding; - - function marginForPositions(positions) { - var margin = {left: 0, top: 0, right: 0, bottom: 0}; - positions.forEach(function(pos) { - margin[pos] = Math.max(chartArea[pos], maxPadding[pos]); - }); - return margin; - } - - return horizontal - ? marginForPositions(['left', 'right']) - : marginForPositions(['top', 'bottom']); -} - -function fitBoxes(boxes, chartArea, params) { - var refitBoxes = []; - var i, ilen, layout, box, refit, changed; - - for (i = 0, ilen = boxes.length; i < ilen; ++i) { - layout = boxes[i]; - box = layout.box; - - box.update( - layout.width || chartArea.w, - layout.height || chartArea.h, - getMargins(layout.horizontal, chartArea) - ); - if (updateDims(chartArea, params, layout)) { - changed = true; - if (refitBoxes.length) { - // Dimensions changed and there were non full width boxes before this - // -> we have to refit those - refit = true; - } - } - if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case - refitBoxes.push(layout); - } - } - - return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed; -} - -function placeBoxes(boxes, chartArea, params) { - var userPadding = params.padding; - var x = chartArea.x; - var y = chartArea.y; - var i, ilen, layout, box; - - for (i = 0, ilen = boxes.length; i < ilen; ++i) { - layout = boxes[i]; - box = layout.box; - if (layout.horizontal) { - box.left = box.fullWidth ? userPadding.left : chartArea.left; - box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w; - box.top = y; - box.bottom = y + box.height; - box.width = box.right - box.left; - y = box.bottom; - } else { - box.left = x; - box.right = x + box.width; - box.top = chartArea.top; - box.bottom = chartArea.top + chartArea.h; - box.height = box.bottom - box.top; - x = box.right; - } - } - - chartArea.x = x; - chartArea.y = y; -} - -core_defaults._set('global', { - layout: { - padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - } -}); - -/** - * @interface ILayoutItem - * @prop {string} position - The position of the item in the chart layout. Possible values are - * 'left', 'top', 'right', 'bottom', and 'chartArea' - * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area - * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down - * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) - * @prop {function} update - Takes two parameters: width and height. Returns size of item - * @prop {function} getPadding - Returns an object with padding on the edges - * @prop {number} width - Width of item. Must be valid after update() - * @prop {number} height - Height of item. Must be valid after update() - * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update - * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update - * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update - * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update - */ - -// The layout service is very self explanatory. It's responsible for the layout within a chart. -// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need -// It is this service's responsibility of carrying out that layout. -var core_layouts = { - defaults: {}, - - /** - * Register a box to a chart. - * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. - * @param {Chart} chart - the chart to use - * @param {ILayoutItem} item - the item to add to be layed out - */ - addBox: function(chart, item) { - if (!chart.boxes) { - chart.boxes = []; - } - - // initialize item with default values - item.fullWidth = item.fullWidth || false; - item.position = item.position || 'top'; - item.weight = item.weight || 0; - item._layers = item._layers || function() { - return [{ - z: 0, - draw: function() { - item.draw.apply(item, arguments); - } - }]; - }; - - chart.boxes.push(item); - }, - - /** - * Remove a layoutItem from a chart - * @param {Chart} chart - the chart to remove the box from - * @param {ILayoutItem} layoutItem - the item to remove from the layout - */ - removeBox: function(chart, layoutItem) { - var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; - if (index !== -1) { - chart.boxes.splice(index, 1); - } - }, - - /** - * Sets (or updates) options on the given `item`. - * @param {Chart} chart - the chart in which the item lives (or will be added to) - * @param {ILayoutItem} item - the item to configure with the given options - * @param {object} options - the new item options. - */ - configure: function(chart, item, options) { - var props = ['fullWidth', 'position', 'weight']; - var ilen = props.length; - var i = 0; - var prop; - - for (; i < ilen; ++i) { - prop = props[i]; - if (options.hasOwnProperty(prop)) { - item[prop] = options[prop]; - } - } - }, - - /** - * Fits boxes of the given chart into the given size by having each box measure itself - * then running a fitting algorithm - * @param {Chart} chart - the chart - * @param {number} width - the width to fit into - * @param {number} height - the height to fit into - */ - update: function(chart, width, height) { - if (!chart) { - return; - } - - var layoutOptions = chart.options.layout || {}; - var padding = helpers$1.options.toPadding(layoutOptions.padding); - - var availableWidth = width - padding.width; - var availableHeight = height - padding.height; - var boxes = buildLayoutBoxes(chart.boxes); - var verticalBoxes = boxes.vertical; - var horizontalBoxes = boxes.horizontal; - - // Essentially we now have any number of boxes on each of the 4 sides. - // Our canvas looks like the following. - // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and - // B1 is the bottom axis - // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays - // These locations are single-box locations only, when trying to register a chartArea location that is already taken, - // an error will be thrown. - // - // |----------------------------------------------------| - // | T1 (Full Width) | - // |----------------------------------------------------| - // | | | T2 | | - // | |----|-------------------------------------|----| - // | | | C1 | | C2 | | - // | | |----| |----| | - // | | | | | - // | L1 | L2 | ChartArea (C0) | R1 | - // | | | | | - // | | |----| |----| | - // | | | C3 | | C4 | | - // | |----|-------------------------------------|----| - // | | | B1 | | - // |----------------------------------------------------| - // | B2 (Full Width) | - // |----------------------------------------------------| - // - - var params = Object.freeze({ - outerWidth: width, - outerHeight: height, - padding: padding, - availableWidth: availableWidth, - vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length, - hBoxMaxHeight: availableHeight / 2 - }); - var chartArea = extend({ - maxPadding: extend({}, padding), - w: availableWidth, - h: availableHeight, - x: padding.left, - y: padding.top - }, padding); - - setLayoutDims(verticalBoxes.concat(horizontalBoxes), params); - - // First fit vertical boxes - fitBoxes(verticalBoxes, chartArea, params); - - // Then fit horizontal boxes - if (fitBoxes(horizontalBoxes, chartArea, params)) { - // if the area changed, re-fit vertical boxes - fitBoxes(verticalBoxes, chartArea, params); - } - - handleMaxPadding(chartArea); - - // Finally place the boxes to correct coordinates - placeBoxes(boxes.leftAndTop, chartArea, params); - - // Move to opposite side of chart - chartArea.x += chartArea.w; - chartArea.y += chartArea.h; - - placeBoxes(boxes.rightAndBottom, chartArea, params); - - chart.chartArea = { - left: chartArea.left, - top: chartArea.top, - right: chartArea.left + chartArea.w, - bottom: chartArea.top + chartArea.h - }; - - // Finally update boxes in chartArea (radial scale for example) - helpers$1.each(boxes.chartArea, function(layout) { - var box = layout.box; - extend(box, chart.chartArea); - box.update(chartArea.w, chartArea.h); - }); - } -}; - -/** - * Platform fallback implementation (minimal). - * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 - */ - -var platform_basic = { - acquireContext: function(item) { - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - return item && item.getContext('2d') || null; - } -}; - -var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; - -var platform_dom$1 = /*#__PURE__*/Object.freeze({ -__proto__: null, -'default': platform_dom -}); - -var stylesheet = getCjsExportFromNamespace(platform_dom$1); - -var EXPANDO_KEY = '$chartjs'; -var CSS_PREFIX = 'chartjs-'; -var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; -var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; -var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; -var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; - -/** - * DOM event types -> Chart.js event types. - * Note: only events with different types are mapped. - * @see https://developer.mozilla.org/en-US/docs/Web/Events - */ -var EVENT_TYPES = { - touchstart: 'mousedown', - touchmove: 'mousemove', - touchend: 'mouseup', - pointerenter: 'mouseenter', - pointerdown: 'mousedown', - pointermove: 'mousemove', - pointerup: 'mouseup', - pointerleave: 'mouseout', - pointerout: 'mouseout' -}; - -/** - * The "used" size is the final value of a dimension property after all calculations have - * been performed. This method uses the computed style of `element` but returns undefined - * if the computed style is not expressed in pixels. That can happen in some cases where - * `element` has a size relative to its parent and this last one is not yet displayed, - * for example because of `display: none` on a parent node. - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value - * @returns {number} Size in pixels or undefined if unknown. - */ -function readUsedSize(element, property) { - var value = helpers$1.getStyle(element, property); - var matches = value && value.match(/^(\d+)(\.\d+)?px$/); - return matches ? Number(matches[1]) : undefined; -} - -/** - * Initializes the canvas style and render size without modifying the canvas display size, - * since responsiveness is handled by the controller.resize() method. The config is used - * to determine the aspect ratio to apply in case no explicit height has been specified. - */ -function initCanvas(canvas, config) { - var style = canvas.style; - - // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it - // returns null or '' if no explicit value has been set to the canvas attribute. - var renderHeight = canvas.getAttribute('height'); - var renderWidth = canvas.getAttribute('width'); - - // Chart.js modifies some canvas values that we want to restore on destroy - canvas[EXPANDO_KEY] = { - initial: { - height: renderHeight, - width: renderWidth, - style: { - display: style.display, - height: style.height, - width: style.width - } - } - }; - - // Force canvas to display as block to avoid extra space caused by inline - // elements, which would interfere with the responsive resize process. - // https://github.com/chartjs/Chart.js/issues/2538 - style.display = style.display || 'block'; - - if (renderWidth === null || renderWidth === '') { - var displayWidth = readUsedSize(canvas, 'width'); - if (displayWidth !== undefined) { - canvas.width = displayWidth; - } - } - - if (renderHeight === null || renderHeight === '') { - if (canvas.style.height === '') { - // If no explicit render height and style height, let's apply the aspect ratio, - // which one can be specified by the user but also by charts as default option - // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. - canvas.height = canvas.width / (config.options.aspectRatio || 2); - } else { - var displayHeight = readUsedSize(canvas, 'height'); - if (displayWidth !== undefined) { - canvas.height = displayHeight; - } - } - } - - return canvas; -} - -/** - * Detects support for options object argument in addEventListener. - * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support - * @private - */ -var supportsEventListenerOptions = (function() { - var supports = false; - try { - var options = Object.defineProperty({}, 'passive', { - // eslint-disable-next-line getter-return - get: function() { - supports = true; - } - }); - window.addEventListener('e', null, options); - } catch (e) { - // continue regardless of error - } - return supports; -}()); - -// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. -// https://github.com/chartjs/Chart.js/issues/4287 -var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; - -function addListener(node, type, listener) { - node.addEventListener(type, listener, eventListenerOptions); -} - -function removeListener(node, type, listener) { - node.removeEventListener(type, listener, eventListenerOptions); -} - -function createEvent(type, chart, x, y, nativeEvent) { - return { - type: type, - chart: chart, - native: nativeEvent || null, - x: x !== undefined ? x : null, - y: y !== undefined ? y : null, - }; -} - -function fromNativeEvent(event, chart) { - var type = EVENT_TYPES[event.type] || event.type; - var pos = helpers$1.getRelativePosition(event, chart); - return createEvent(type, chart, pos.x, pos.y, event); -} - -function throttled(fn, thisArg) { - var ticking = false; - var args = []; - - return function() { - args = Array.prototype.slice.call(arguments); - thisArg = thisArg || this; - - if (!ticking) { - ticking = true; - helpers$1.requestAnimFrame.call(window, function() { - ticking = false; - fn.apply(thisArg, args); - }); - } - }; -} - -function createDiv(cls) { - var el = document.createElement('div'); - el.className = cls || ''; - return el; -} - -// Implementation based on https://github.com/marcj/css-element-queries -function createResizer(handler) { - var maxSize = 1000000; - - // NOTE(SB) Don't use innerHTML because it could be considered unsafe. - // https://github.com/chartjs/Chart.js/issues/5902 - var resizer = createDiv(CSS_SIZE_MONITOR); - var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); - var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); - - expand.appendChild(createDiv()); - shrink.appendChild(createDiv()); - - resizer.appendChild(expand); - resizer.appendChild(shrink); - resizer._reset = function() { - expand.scrollLeft = maxSize; - expand.scrollTop = maxSize; - shrink.scrollLeft = maxSize; - shrink.scrollTop = maxSize; - }; - - var onScroll = function() { - resizer._reset(); - handler(); - }; - - addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); - addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); - - return resizer; -} - -// https://davidwalsh.name/detect-node-insertion -function watchForRender(node, handler) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - var proxy = expando.renderProxy = function(e) { - if (e.animationName === CSS_RENDER_ANIMATION) { - handler(); - } - }; - - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - addListener(node, type, proxy); - }); - - // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class - // is removed then added back immediately (same animation frame?). Accessing the - // `offsetParent` property will force a reflow and re-evaluate the CSS animation. - // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics - // https://github.com/chartjs/Chart.js/issues/4737 - expando.reflow = !!node.offsetParent; - - node.classList.add(CSS_RENDER_MONITOR); -} - -function unwatchForRender(node) { - var expando = node[EXPANDO_KEY] || {}; - var proxy = expando.renderProxy; - - if (proxy) { - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - removeListener(node, type, proxy); - }); - - delete expando.renderProxy; - } - - node.classList.remove(CSS_RENDER_MONITOR); -} - -function addResizeListener(node, listener, chart) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - - // Let's keep track of this added resizer and thus avoid DOM query when removing it. - var resizer = expando.resizer = createResizer(throttled(function() { - if (expando.resizer) { - var container = chart.options.maintainAspectRatio && node.parentNode; - var w = container ? container.clientWidth : 0; - listener(createEvent('resize', chart)); - if (container && container.clientWidth < w && chart.canvas) { - // If the container size shrank during chart resize, let's assume - // scrollbar appeared. So we resize again with the scrollbar visible - - // effectively making chart smaller and the scrollbar hidden again. - // Because we are inside `throttled`, and currently `ticking`, scroll - // events are ignored during this whole 2 resize process. - // If we assumed wrong and something else happened, we are resizing - // twice in a frame (potential performance issue) - listener(createEvent('resize', chart)); - } - } - })); - - // The resizer needs to be attached to the node parent, so we first need to be - // sure that `node` is attached to the DOM before injecting the resizer element. - watchForRender(node, function() { - if (expando.resizer) { - var container = node.parentNode; - if (container && container !== resizer.parentNode) { - container.insertBefore(resizer, container.firstChild); - } - - // The container size might have changed, let's reset the resizer state. - resizer._reset(); - } - }); -} - -function removeResizeListener(node) { - var expando = node[EXPANDO_KEY] || {}; - var resizer = expando.resizer; - - delete expando.resizer; - unwatchForRender(node); - - if (resizer && resizer.parentNode) { - resizer.parentNode.removeChild(resizer); - } -} - -/** - * Injects CSS styles inline if the styles are not already present. - * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>. - * @param {string} css - the CSS to be injected. - */ -function injectCSS(rootNode, css) { - // https://stackoverflow.com/q/3922139 - var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {}); - if (!expando.containsStyles) { - expando.containsStyles = true; - css = '/* Chart.js */\n' + css; - var style = document.createElement('style'); - style.setAttribute('type', 'text/css'); - style.appendChild(document.createTextNode(css)); - rootNode.appendChild(style); - } -} - -var platform_dom$2 = { - /** - * When `true`, prevents the automatic injection of the stylesheet required to - * correctly detect when the chart is added to the DOM and then resized. This - * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) - * to be manually imported to make this library compatible with any CSP. - * See https://github.com/chartjs/Chart.js/issues/5208 - */ - disableCSSInjection: false, - - /** - * This property holds whether this platform is enabled for the current environment. - * Currently used by platform.js to select the proper implementation. - * @private - */ - _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', - - /** - * Initializes resources that depend on platform options. - * @param {HTMLCanvasElement} canvas - The Canvas element. - * @private - */ - _ensureLoaded: function(canvas) { - if (!this.disableCSSInjection) { - // If the canvas is in a shadow DOM, then the styles must also be inserted - // into the same shadow DOM. - // https://github.com/chartjs/Chart.js/issues/5763 - var root = canvas.getRootNode ? canvas.getRootNode() : document; - var targetNode = root.host ? root : document.head; - injectCSS(targetNode, stylesheet); - } - }, - - acquireContext: function(item, config) { - if (typeof item === 'string') { - item = document.getElementById(item); - } else if (item.length) { - // Support for array based queries (such as jQuery) - item = item[0]; - } - - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - // To prevent canvas fingerprinting, some add-ons undefine the getContext - // method, for example: https://github.com/kkapsner/CanvasBlocker - // https://github.com/chartjs/Chart.js/issues/2807 - var context = item && item.getContext && item.getContext('2d'); - - // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is - // inside an iframe or when running in a protected environment. We could guess the - // types from their toString() value but let's keep things flexible and assume it's - // a sufficient condition if the item has a context2D which has item as `canvas`. - // https://github.com/chartjs/Chart.js/issues/3887 - // https://github.com/chartjs/Chart.js/issues/4102 - // https://github.com/chartjs/Chart.js/issues/4152 - if (context && context.canvas === item) { - // Load platform resources on first chart creation, to make it possible to - // import the library before setting platform options. - this._ensureLoaded(item); - initCanvas(item, config); - return context; - } - - return null; - }, - - releaseContext: function(context) { - var canvas = context.canvas; - if (!canvas[EXPANDO_KEY]) { - return; - } - - var initial = canvas[EXPANDO_KEY].initial; - ['height', 'width'].forEach(function(prop) { - var value = initial[prop]; - if (helpers$1.isNullOrUndef(value)) { - canvas.removeAttribute(prop); - } else { - canvas.setAttribute(prop, value); - } - }); - - helpers$1.each(initial.style || {}, function(value, key) { - canvas.style[key] = value; - }); - - // The canvas render size might have been changed (and thus the state stack discarded), - // we can't use save() and restore() to restore the initial state. So make sure that at - // least the canvas context is reset to the default state by setting the canvas width. - // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html - // eslint-disable-next-line no-self-assign - canvas.width = canvas.width; - - delete canvas[EXPANDO_KEY]; - }, - - addEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - addResizeListener(canvas, listener, chart); - return; - } - - var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); - var proxies = expando.proxies || (expando.proxies = {}); - var proxy = proxies[chart.id + '_' + type] = function(event) { - listener(fromNativeEvent(event, chart)); - }; - - addListener(canvas, type, proxy); - }, - - removeEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - removeResizeListener(canvas); - return; - } - - var expando = listener[EXPANDO_KEY] || {}; - var proxies = expando.proxies || {}; - var proxy = proxies[chart.id + '_' + type]; - if (!proxy) { - return; - } - - removeListener(canvas, type, proxy); - } -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use EventTarget.addEventListener instead. - * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener - * @function Chart.helpers.addEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.addEvent = addListener; - -/** - * Provided for backward compatibility, use EventTarget.removeEventListener instead. - * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener - * @function Chart.helpers.removeEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.removeEvent = removeListener; - -// @TODO Make possible to select another platform at build time. -var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; - -/** - * @namespace Chart.platform - * @see https://chartjs.gitbooks.io/proposals/content/Platform.html - * @since 2.4.0 - */ -var platform = helpers$1.extend({ - /** - * @since 2.7.0 - */ - initialize: function() {}, - - /** - * Called at chart construction time, returns a context2d instance implementing - * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. - * @param {*} item - The native item from which to acquire context (platform specific) - * @param {object} options - The chart options - * @returns {CanvasRenderingContext2D} context2d instance - */ - acquireContext: function() {}, - - /** - * Called at chart destruction time, releases any resources associated to the context - * previously returned by the acquireContext() method. - * @param {CanvasRenderingContext2D} context - The context2d instance - * @returns {boolean} true if the method succeeded, else false - */ - releaseContext: function() {}, - - /** - * Registers the specified listener on the given chart. - * @param {Chart} chart - Chart from which to listen for event - * @param {string} type - The ({@link IEvent}) type to listen for - * @param {function} listener - Receives a notification (an object that implements - * the {@link IEvent} interface) when an event of the specified type occurs. - */ - addEventListener: function() {}, - - /** - * Removes the specified listener previously registered with addEventListener. - * @param {Chart} chart - Chart from which to remove the listener - * @param {string} type - The ({@link IEvent}) type to remove - * @param {function} listener - The listener function to remove from the event target. - */ - removeEventListener: function() {} - -}, implementation); - -core_defaults._set('global', { - plugins: {} -}); - -/** - * The plugin service singleton - * @namespace Chart.plugins - * @since 2.1.0 - */ -var core_plugins = { - /** - * Globally registered plugins. - * @private - */ - _plugins: [], - - /** - * This identifier is used to invalidate the descriptors cache attached to each chart - * when a global plugin is registered or unregistered. In this case, the cache ID is - * incremented and descriptors are regenerated during following API calls. - * @private - */ - _cacheId: 0, - - /** - * Registers the given plugin(s) if not already registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - register: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - if (p.indexOf(plugin) === -1) { - p.push(plugin); - } - }); - - this._cacheId++; - }, - - /** - * Unregisters the given plugin(s) only if registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - unregister: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - var idx = p.indexOf(plugin); - if (idx !== -1) { - p.splice(idx, 1); - } - }); - - this._cacheId++; - }, - - /** - * Remove all registered plugins. - * @since 2.1.5 - */ - clear: function() { - this._plugins = []; - this._cacheId++; - }, - - /** - * Returns the number of registered plugins? - * @returns {number} - * @since 2.1.5 - */ - count: function() { - return this._plugins.length; - }, - - /** - * Returns all registered plugin instances. - * @returns {IPlugin[]} array of plugin objects. - * @since 2.1.5 - */ - getAll: function() { - return this._plugins; - }, - - /** - * Calls enabled plugins for `chart` on the specified hook and with the given args. - * This method immediately returns as soon as a plugin explicitly returns false. The - * returned value can be used, for instance, to interrupt the current action. - * @param {Chart} chart - The chart instance for which plugins should be called. - * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). - * @param {Array} [args] - Extra arguments to apply to the hook call. - * @returns {boolean} false if any of the plugins return false, else returns true. - */ - notify: function(chart, hook, args) { - var descriptors = this.descriptors(chart); - var ilen = descriptors.length; - var i, descriptor, plugin, params, method; - - for (i = 0; i < ilen; ++i) { - descriptor = descriptors[i]; - plugin = descriptor.plugin; - method = plugin[hook]; - if (typeof method === 'function') { - params = [chart].concat(args || []); - params.push(descriptor.options); - if (method.apply(plugin, params) === false) { - return false; - } - } - } - - return true; - }, - - /** - * Returns descriptors of enabled plugins for the given chart. - * @returns {object[]} [{ plugin, options }] - * @private - */ - descriptors: function(chart) { - var cache = chart.$plugins || (chart.$plugins = {}); - if (cache.id === this._cacheId) { - return cache.descriptors; - } - - var plugins = []; - var descriptors = []; - var config = (chart && chart.config) || {}; - var options = (config.options && config.options.plugins) || {}; - - this._plugins.concat(config.plugins || []).forEach(function(plugin) { - var idx = plugins.indexOf(plugin); - if (idx !== -1) { - return; - } - - var id = plugin.id; - var opts = options[id]; - if (opts === false) { - return; - } - - if (opts === true) { - opts = helpers$1.clone(core_defaults.global.plugins[id]); - } - - plugins.push(plugin); - descriptors.push({ - plugin: plugin, - options: opts || {} - }); - }); - - cache.descriptors = descriptors; - cache.id = this._cacheId; - return descriptors; - }, - - /** - * Invalidates cache for the given chart: descriptors hold a reference on plugin option, - * but in some cases, this reference can be changed by the user when updating options. - * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - * @private - */ - _invalidate: function(chart) { - delete chart.$plugins; - } -}; - -var core_scaleService = { - // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then - // use the new chart options to grab the correct scale - constructors: {}, - // Use a registration function so that we can move to an ES6 map when we no longer need to support - // old browsers - - // Scale config defaults - defaults: {}, - registerScaleType: function(type, scaleConstructor, scaleDefaults) { - this.constructors[type] = scaleConstructor; - this.defaults[type] = helpers$1.clone(scaleDefaults); - }, - getScaleConstructor: function(type) { - return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; - }, - getScaleDefaults: function(type) { - // Return the scale defaults merged with the global settings so that we always use the latest ones - return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; - }, - updateScaleDefaults: function(type, additions) { - var me = this; - if (me.defaults.hasOwnProperty(type)) { - me.defaults[type] = helpers$1.extend(me.defaults[type], additions); - } - }, - addScalesToLayout: function(chart) { - // Adds each scale to the chart.boxes array to be sized accordingly - helpers$1.each(chart.scales, function(scale) { - // Set ILayoutItem parameters for backwards compatibility - scale.fullWidth = scale.options.fullWidth; - scale.position = scale.options.position; - scale.weight = scale.options.weight; - core_layouts.addBox(chart, scale); - }); - } -}; - -var valueOrDefault$8 = helpers$1.valueOrDefault; -var getRtlHelper = helpers$1.rtl.getRtlAdapter; - -core_defaults._set('global', { - tooltips: { - enabled: true, - custom: null, - mode: 'nearest', - position: 'average', - intersect: true, - backgroundColor: 'rgba(0,0,0,0.8)', - titleFontStyle: 'bold', - titleSpacing: 2, - titleMarginBottom: 6, - titleFontColor: '#fff', - titleAlign: 'left', - bodySpacing: 2, - bodyFontColor: '#fff', - bodyAlign: 'left', - footerFontStyle: 'bold', - footerSpacing: 2, - footerMarginTop: 6, - footerFontColor: '#fff', - footerAlign: 'left', - yPadding: 6, - xPadding: 6, - caretPadding: 2, - caretSize: 5, - cornerRadius: 6, - multiKeyBackground: '#fff', - displayColors: true, - borderColor: 'rgba(0,0,0,0)', - borderWidth: 0, - callbacks: { - // Args are: (tooltipItems, data) - beforeTitle: helpers$1.noop, - title: function(tooltipItems, data) { - var title = ''; - var labels = data.labels; - var labelCount = labels ? labels.length : 0; - - if (tooltipItems.length > 0) { - var item = tooltipItems[0]; - if (item.label) { - title = item.label; - } else if (item.xLabel) { - title = item.xLabel; - } else if (labelCount > 0 && item.index < labelCount) { - title = labels[item.index]; - } - } - - return title; - }, - afterTitle: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeBody: helpers$1.noop, - - // Args are: (tooltipItem, data) - beforeLabel: helpers$1.noop, - label: function(tooltipItem, data) { - var label = data.datasets[tooltipItem.datasetIndex].label || ''; - - if (label) { - label += ': '; - } - if (!helpers$1.isNullOrUndef(tooltipItem.value)) { - label += tooltipItem.value; - } else { - label += tooltipItem.yLabel; - } - return label; - }, - labelColor: function(tooltipItem, chart) { - var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); - var activeElement = meta.data[tooltipItem.index]; - var view = activeElement._view; - return { - borderColor: view.borderColor, - backgroundColor: view.backgroundColor - }; - }, - labelTextColor: function() { - return this._options.bodyFontColor; - }, - afterLabel: helpers$1.noop, - - // Args are: (tooltipItems, data) - afterBody: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeFooter: helpers$1.noop, - footer: helpers$1.noop, - afterFooter: helpers$1.noop - } - } -}); - -var positioners = { - /** - * Average mode places the tooltip at the average position of the elements shown - * @function Chart.Tooltip.positioners.average - * @param elements {ChartElement[]} the elements being displayed in the tooltip - * @returns {object} tooltip position - */ - average: function(elements) { - if (!elements.length) { - return false; - } - - var i, len; - var x = 0; - var y = 0; - var count = 0; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var pos = el.tooltipPosition(); - x += pos.x; - y += pos.y; - ++count; - } - } - - return { - x: x / count, - y: y / count - }; - }, - - /** - * Gets the tooltip position nearest of the item nearest to the event position - * @function Chart.Tooltip.positioners.nearest - * @param elements {Chart.Element[]} the tooltip elements - * @param eventPosition {object} the position of the event in canvas coordinates - * @returns {object} the tooltip position - */ - nearest: function(elements, eventPosition) { - var x = eventPosition.x; - var y = eventPosition.y; - var minDistance = Number.POSITIVE_INFINITY; - var i, len, nearestElement; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var center = el.getCenterPoint(); - var d = helpers$1.distanceBetweenPoints(eventPosition, center); - - if (d < minDistance) { - minDistance = d; - nearestElement = el; - } - } - } - - if (nearestElement) { - var tp = nearestElement.tooltipPosition(); - x = tp.x; - y = tp.y; - } - - return { - x: x, - y: y - }; - } -}; - -// Helper to push or concat based on if the 2nd parameter is an array or not -function pushOrConcat(base, toPush) { - if (toPush) { - if (helpers$1.isArray(toPush)) { - // base = base.concat(toPush); - Array.prototype.push.apply(base, toPush); - } else { - base.push(toPush); - } - } - - return base; -} - -/** - * Returns array of strings split by newline - * @param {string} value - The value to split by newline. - * @returns {string[]} value if newline present - Returned from String split() method - * @function - */ -function splitNewlines(str) { - if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { - return str.split('\n'); - } - return str; -} - - -/** - * Private helper to create a tooltip item model - * @param element - the chart element (point, arc, bar) to create the tooltip item for - * @return new tooltip item - */ -function createTooltipItem(element) { - var xScale = element._xScale; - var yScale = element._yScale || element._scale; // handle radar || polarArea charts - var index = element._index; - var datasetIndex = element._datasetIndex; - var controller = element._chart.getDatasetMeta(datasetIndex).controller; - var indexScale = controller._getIndexScale(); - var valueScale = controller._getValueScale(); - - return { - xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', - yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', - label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', - value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', - index: index, - datasetIndex: datasetIndex, - x: element._model.x, - y: element._model.y - }; -} - -/** - * Helper to get the reset model for the tooltip - * @param tooltipOpts {object} the tooltip options - */ -function getBaseModel(tooltipOpts) { - var globalDefaults = core_defaults.global; - - return { - // Positioning - xPadding: tooltipOpts.xPadding, - yPadding: tooltipOpts.yPadding, - xAlign: tooltipOpts.xAlign, - yAlign: tooltipOpts.yAlign, - - // Drawing direction and text direction - rtl: tooltipOpts.rtl, - textDirection: tooltipOpts.textDirection, - - // Body - bodyFontColor: tooltipOpts.bodyFontColor, - _bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), - _bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), - _bodyAlign: tooltipOpts.bodyAlign, - bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), - bodySpacing: tooltipOpts.bodySpacing, - - // Title - titleFontColor: tooltipOpts.titleFontColor, - _titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), - _titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), - titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), - _titleAlign: tooltipOpts.titleAlign, - titleSpacing: tooltipOpts.titleSpacing, - titleMarginBottom: tooltipOpts.titleMarginBottom, - - // Footer - footerFontColor: tooltipOpts.footerFontColor, - _footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), - _footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), - footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), - _footerAlign: tooltipOpts.footerAlign, - footerSpacing: tooltipOpts.footerSpacing, - footerMarginTop: tooltipOpts.footerMarginTop, - - // Appearance - caretSize: tooltipOpts.caretSize, - cornerRadius: tooltipOpts.cornerRadius, - backgroundColor: tooltipOpts.backgroundColor, - opacity: 0, - legendColorBackground: tooltipOpts.multiKeyBackground, - displayColors: tooltipOpts.displayColors, - borderColor: tooltipOpts.borderColor, - borderWidth: tooltipOpts.borderWidth - }; -} - -/** - * Get the size of the tooltip - */ -function getTooltipSize(tooltip, model) { - var ctx = tooltip._chart.ctx; - - var height = model.yPadding * 2; // Tooltip Padding - var width = 0; - - // Count of all lines in the body - var body = model.body; - var combinedBodyLength = body.reduce(function(count, bodyItem) { - return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; - }, 0); - combinedBodyLength += model.beforeBody.length + model.afterBody.length; - - var titleLineCount = model.title.length; - var footerLineCount = model.footer.length; - var titleFontSize = model.titleFontSize; - var bodyFontSize = model.bodyFontSize; - var footerFontSize = model.footerFontSize; - - height += titleLineCount * titleFontSize; // Title Lines - height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing - height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin - height += combinedBodyLength * bodyFontSize; // Body Lines - height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing - height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin - height += footerLineCount * (footerFontSize); // Footer Lines - height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing - - // Title width - var widthPadding = 0; - var maxLineWidth = function(line) { - width = Math.max(width, ctx.measureText(line).width + widthPadding); - }; - - ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); - helpers$1.each(model.title, maxLineWidth); - - // Body width - ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); - helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); - - // Body lines may include some extra width due to the color box - widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; - helpers$1.each(body, function(bodyItem) { - helpers$1.each(bodyItem.before, maxLineWidth); - helpers$1.each(bodyItem.lines, maxLineWidth); - helpers$1.each(bodyItem.after, maxLineWidth); - }); - - // Reset back to 0 - widthPadding = 0; - - // Footer width - ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); - helpers$1.each(model.footer, maxLineWidth); - - // Add padding - width += 2 * model.xPadding; - - return { - width: width, - height: height - }; -} - -/** - * Helper to get the alignment of a tooltip given the size - */ -function determineAlignment(tooltip, size) { - var model = tooltip._model; - var chart = tooltip._chart; - var chartArea = tooltip._chart.chartArea; - var xAlign = 'center'; - var yAlign = 'center'; - - if (model.y < size.height) { - yAlign = 'top'; - } else if (model.y > (chart.height - size.height)) { - yAlign = 'bottom'; - } - - var lf, rf; // functions to determine left, right alignment - var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart - var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges - var midX = (chartArea.left + chartArea.right) / 2; - var midY = (chartArea.top + chartArea.bottom) / 2; - - if (yAlign === 'center') { - lf = function(x) { - return x <= midX; - }; - rf = function(x) { - return x > midX; - }; - } else { - lf = function(x) { - return x <= (size.width / 2); - }; - rf = function(x) { - return x >= (chart.width - (size.width / 2)); - }; - } - - olf = function(x) { - return x + size.width + model.caretSize + model.caretPadding > chart.width; - }; - orf = function(x) { - return x - size.width - model.caretSize - model.caretPadding < 0; - }; - yf = function(y) { - return y <= midY ? 'top' : 'bottom'; - }; - - if (lf(model.x)) { - xAlign = 'left'; - - // Is tooltip too wide and goes over the right side of the chart.? - if (olf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } else if (rf(model.x)) { - xAlign = 'right'; - - // Is tooltip too wide and goes outside left edge of canvas? - if (orf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } - - var opts = tooltip._options; - return { - xAlign: opts.xAlign ? opts.xAlign : xAlign, - yAlign: opts.yAlign ? opts.yAlign : yAlign - }; -} - -/** - * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment - */ -function getBackgroundPoint(vm, size, alignment, chart) { - // Background Position - var x = vm.x; - var y = vm.y; - - var caretSize = vm.caretSize; - var caretPadding = vm.caretPadding; - var cornerRadius = vm.cornerRadius; - var xAlign = alignment.xAlign; - var yAlign = alignment.yAlign; - var paddingAndSize = caretSize + caretPadding; - var radiusAndPadding = cornerRadius + caretPadding; - - if (xAlign === 'right') { - x -= size.width; - } else if (xAlign === 'center') { - x -= (size.width / 2); - if (x + size.width > chart.width) { - x = chart.width - size.width; - } - if (x < 0) { - x = 0; - } - } - - if (yAlign === 'top') { - y += paddingAndSize; - } else if (yAlign === 'bottom') { - y -= size.height + paddingAndSize; - } else { - y -= (size.height / 2); - } - - if (yAlign === 'center') { - if (xAlign === 'left') { - x += paddingAndSize; - } else if (xAlign === 'right') { - x -= paddingAndSize; - } - } else if (xAlign === 'left') { - x -= radiusAndPadding; - } else if (xAlign === 'right') { - x += radiusAndPadding; - } - - return { - x: x, - y: y - }; -} - -function getAlignedX(vm, align) { - return align === 'center' - ? vm.x + vm.width / 2 - : align === 'right' - ? vm.x + vm.width - vm.xPadding - : vm.x + vm.xPadding; -} - -/** - * Helper to build before and after body lines - */ -function getBeforeAfterBodyLines(callback) { - return pushOrConcat([], splitNewlines(callback)); -} - -var exports$4 = core_element.extend({ - initialize: function() { - this._model = getBaseModel(this._options); - this._lastActive = []; - }, - - // Get the title - // Args are: (tooltipItem, data) - getTitle: function() { - var me = this; - var opts = me._options; - var callbacks = opts.callbacks; - - var beforeTitle = callbacks.beforeTitle.apply(me, arguments); - var title = callbacks.title.apply(me, arguments); - var afterTitle = callbacks.afterTitle.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeTitle)); - lines = pushOrConcat(lines, splitNewlines(title)); - lines = pushOrConcat(lines, splitNewlines(afterTitle)); - - return lines; - }, - - // Args are: (tooltipItem, data) - getBeforeBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); - }, - - // Args are: (tooltipItem, data) - getBody: function(tooltipItems, data) { - var me = this; - var callbacks = me._options.callbacks; - var bodyItems = []; - - helpers$1.each(tooltipItems, function(tooltipItem) { - var bodyItem = { - before: [], - lines: [], - after: [] - }; - pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); - pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); - pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); - - bodyItems.push(bodyItem); - }); - - return bodyItems; - }, - - // Args are: (tooltipItem, data) - getAfterBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); - }, - - // Get the footer and beforeFooter and afterFooter lines - // Args are: (tooltipItem, data) - getFooter: function() { - var me = this; - var callbacks = me._options.callbacks; - - var beforeFooter = callbacks.beforeFooter.apply(me, arguments); - var footer = callbacks.footer.apply(me, arguments); - var afterFooter = callbacks.afterFooter.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeFooter)); - lines = pushOrConcat(lines, splitNewlines(footer)); - lines = pushOrConcat(lines, splitNewlines(afterFooter)); - - return lines; - }, - - update: function(changed) { - var me = this; - var opts = me._options; - - // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition - // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time - // which breaks any animations. - var existingModel = me._model; - var model = me._model = getBaseModel(opts); - var active = me._active; - - var data = me._data; - - // In the case where active.length === 0 we need to keep these at existing values for good animations - var alignment = { - xAlign: existingModel.xAlign, - yAlign: existingModel.yAlign - }; - var backgroundPoint = { - x: existingModel.x, - y: existingModel.y - }; - var tooltipSize = { - width: existingModel.width, - height: existingModel.height - }; - var tooltipPosition = { - x: existingModel.caretX, - y: existingModel.caretY - }; - - var i, len; - - if (active.length) { - model.opacity = 1; - - var labelColors = []; - var labelTextColors = []; - tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); - - var tooltipItems = []; - for (i = 0, len = active.length; i < len; ++i) { - tooltipItems.push(createTooltipItem(active[i])); - } - - // If the user provided a filter function, use it to modify the tooltip items - if (opts.filter) { - tooltipItems = tooltipItems.filter(function(a) { - return opts.filter(a, data); - }); - } - - // If the user provided a sorting function, use it to modify the tooltip items - if (opts.itemSort) { - tooltipItems = tooltipItems.sort(function(a, b) { - return opts.itemSort(a, b, data); - }); - } - - // Determine colors for boxes - helpers$1.each(tooltipItems, function(tooltipItem) { - labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); - labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); - }); - - - // Build the Text Lines - model.title = me.getTitle(tooltipItems, data); - model.beforeBody = me.getBeforeBody(tooltipItems, data); - model.body = me.getBody(tooltipItems, data); - model.afterBody = me.getAfterBody(tooltipItems, data); - model.footer = me.getFooter(tooltipItems, data); - - // Initial positioning and colors - model.x = tooltipPosition.x; - model.y = tooltipPosition.y; - model.caretPadding = opts.caretPadding; - model.labelColors = labelColors; - model.labelTextColors = labelTextColors; - - // data points - model.dataPoints = tooltipItems; - - // We need to determine alignment of the tooltip - tooltipSize = getTooltipSize(this, model); - alignment = determineAlignment(this, tooltipSize); - // Final Size and Position - backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); - } else { - model.opacity = 0; - } - - model.xAlign = alignment.xAlign; - model.yAlign = alignment.yAlign; - model.x = backgroundPoint.x; - model.y = backgroundPoint.y; - model.width = tooltipSize.width; - model.height = tooltipSize.height; - - // Point where the caret on the tooltip points to - model.caretX = tooltipPosition.x; - model.caretY = tooltipPosition.y; - - me._model = model; - - if (changed && opts.custom) { - opts.custom.call(me, model); - } - - return me; - }, - - drawCaret: function(tooltipPoint, size) { - var ctx = this._chart.ctx; - var vm = this._view; - var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); - - ctx.lineTo(caretPosition.x1, caretPosition.y1); - ctx.lineTo(caretPosition.x2, caretPosition.y2); - ctx.lineTo(caretPosition.x3, caretPosition.y3); - }, - getCaretPosition: function(tooltipPoint, size, vm) { - var x1, x2, x3, y1, y2, y3; - var caretSize = vm.caretSize; - var cornerRadius = vm.cornerRadius; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var ptX = tooltipPoint.x; - var ptY = tooltipPoint.y; - var width = size.width; - var height = size.height; - - if (yAlign === 'center') { - y2 = ptY + (height / 2); - - if (xAlign === 'left') { - x1 = ptX; - x2 = x1 - caretSize; - x3 = x1; - - y1 = y2 + caretSize; - y3 = y2 - caretSize; - } else { - x1 = ptX + width; - x2 = x1 + caretSize; - x3 = x1; - - y1 = y2 - caretSize; - y3 = y2 + caretSize; - } - } else { - if (xAlign === 'left') { - x2 = ptX + cornerRadius + (caretSize); - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else if (xAlign === 'right') { - x2 = ptX + width - cornerRadius - caretSize; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else { - x2 = vm.caretX; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } - if (yAlign === 'top') { - y1 = ptY; - y2 = y1 - caretSize; - y3 = y1; - } else { - y1 = ptY + height; - y2 = y1 + caretSize; - y3 = y1; - // invert drawing order - var tmp = x3; - x3 = x1; - x1 = tmp; - } - } - return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; - }, - - drawTitle: function(pt, vm, ctx) { - var title = vm.title; - var length = title.length; - var titleFontSize, titleSpacing, i; - - if (length) { - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - pt.x = getAlignedX(vm, vm._titleAlign); - - ctx.textAlign = rtlHelper.textAlign(vm._titleAlign); - ctx.textBaseline = 'middle'; - - titleFontSize = vm.titleFontSize; - titleSpacing = vm.titleSpacing; - - ctx.fillStyle = vm.titleFontColor; - ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); - - for (i = 0; i < length; ++i) { - ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2); - pt.y += titleFontSize + titleSpacing; // Line Height and spacing - - if (i + 1 === length) { - pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing - } - } - } - }, - - drawBody: function(pt, vm, ctx) { - var bodyFontSize = vm.bodyFontSize; - var bodySpacing = vm.bodySpacing; - var bodyAlign = vm._bodyAlign; - var body = vm.body; - var drawColorBoxes = vm.displayColors; - var xLinePadding = 0; - var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; - - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - var fillLineOfText = function(line) { - ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2); - pt.y += bodyFontSize + bodySpacing; - }; - - var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen; - var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); - - ctx.textAlign = bodyAlign; - ctx.textBaseline = 'middle'; - ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); - - pt.x = getAlignedX(vm, bodyAlignForCalculation); - - // Before body lines - ctx.fillStyle = vm.bodyFontColor; - helpers$1.each(vm.beforeBody, fillLineOfText); - - xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right' - ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) - : 0; - - // Draw body lines now - for (i = 0, ilen = body.length; i < ilen; ++i) { - bodyItem = body[i]; - textColor = vm.labelTextColors[i]; - labelColors = vm.labelColors[i]; - - ctx.fillStyle = textColor; - helpers$1.each(bodyItem.before, fillLineOfText); - - lines = bodyItem.lines; - for (j = 0, jlen = lines.length; j < jlen; ++j) { - // Draw Legend-like boxes if needed - if (drawColorBoxes) { - var rtlColorX = rtlHelper.x(colorX); - - // Fill a white rect so that colours merge nicely if the opacity is < 1 - ctx.fillStyle = vm.legendColorBackground; - ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); - - // Border - ctx.lineWidth = 1; - ctx.strokeStyle = labelColors.borderColor; - ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize); - - // Inner square - ctx.fillStyle = labelColors.backgroundColor; - ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); - ctx.fillStyle = textColor; - } - - fillLineOfText(lines[j]); - } - - helpers$1.each(bodyItem.after, fillLineOfText); - } - - // Reset back to 0 for after body - xLinePadding = 0; - - // After body lines - helpers$1.each(vm.afterBody, fillLineOfText); - pt.y -= bodySpacing; // Remove last body spacing - }, - - drawFooter: function(pt, vm, ctx) { - var footer = vm.footer; - var length = footer.length; - var footerFontSize, i; - - if (length) { - var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width); - - pt.x = getAlignedX(vm, vm._footerAlign); - pt.y += vm.footerMarginTop; - - ctx.textAlign = rtlHelper.textAlign(vm._footerAlign); - ctx.textBaseline = 'middle'; - - footerFontSize = vm.footerFontSize; - - ctx.fillStyle = vm.footerFontColor; - ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); - - for (i = 0; i < length; ++i) { - ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2); - pt.y += footerFontSize + vm.footerSpacing; - } - } - }, - - drawBackground: function(pt, vm, ctx, tooltipSize) { - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var x = pt.x; - var y = pt.y; - var width = tooltipSize.width; - var height = tooltipSize.height; - var radius = vm.cornerRadius; - - ctx.beginPath(); - ctx.moveTo(x + radius, y); - if (yAlign === 'top') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - if (yAlign === 'center' && xAlign === 'right') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - if (yAlign === 'bottom') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - if (yAlign === 'center' && xAlign === 'left') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - - ctx.fill(); - - if (vm.borderWidth > 0) { - ctx.stroke(); - } - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - - if (vm.opacity === 0) { - return; - } - - var tooltipSize = { - width: vm.width, - height: vm.height - }; - var pt = { - x: vm.x, - y: vm.y - }; - - // IE11/Edge does not like very small opacities, so snap to 0 - var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; - - // Truthy/falsey value for empty tooltip - var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; - - if (this._options.enabled && hasTooltipContent) { - ctx.save(); - ctx.globalAlpha = opacity; - - // Draw Background - this.drawBackground(pt, vm, ctx, tooltipSize); - - // Draw Title, Body, and Footer - pt.y += vm.yPadding; - - helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection); - - // Titles - this.drawTitle(pt, vm, ctx); - - // Body - this.drawBody(pt, vm, ctx); - - // Footer - this.drawFooter(pt, vm, ctx); - - helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection); - - ctx.restore(); - } - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - * @returns {boolean} true if the tooltip changed - */ - handleEvent: function(e) { - var me = this; - var options = me._options; - var changed = false; - - me._lastActive = me._lastActive || []; - - // Find Active Elements for tooltips - if (e.type === 'mouseout') { - me._active = []; - } else { - me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); - if (options.reverse) { - me._active.reverse(); - } - } - - // Remember Last Actives - changed = !helpers$1.arrayEquals(me._active, me._lastActive); - - // Only handle target event on tooltip change - if (changed) { - me._lastActive = me._active; - - if (options.enabled || options.custom) { - me._eventPosition = { - x: e.x, - y: e.y - }; - - me.update(true); - me.pivot(); - } - } - - return changed; - } -}); - -/** - * @namespace Chart.Tooltip.positioners - */ -var positioners_1 = positioners; - -var core_tooltip = exports$4; -core_tooltip.positioners = positioners_1; - -var valueOrDefault$9 = helpers$1.valueOrDefault; - -core_defaults._set('global', { - elements: {}, - events: [ - 'mousemove', - 'mouseout', - 'click', - 'touchstart', - 'touchmove' - ], - hover: { - onHover: null, - mode: 'nearest', - intersect: true, - animationDuration: 400 - }, - onClick: null, - maintainAspectRatio: true, - responsive: true, - responsiveAnimationDuration: 0 -}); - -/** - * Recursively merge the given config objects representing the `scales` option - * by incorporating scale defaults in `xAxes` and `yAxes` array items, then - * returns a deep copy of the result, thus doesn't alter inputs. - */ -function mergeScaleConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - if (key === 'xAxes' || key === 'yAxes') { - var slen = source[key].length; - var i, type, scale; - - if (!target[key]) { - target[key] = []; - } - - for (i = 0; i < slen; ++i) { - scale = source[key][i]; - type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear'); - - if (i >= target[key].length) { - target[key].push({}); - } - - if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { - // new/untyped scale or type changed: let's apply the new defaults - // then merge source scale to correctly overwrite the defaults. - helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); - } else { - // scales type are the same - helpers$1.merge(target[key][i], scale); - } - } - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -/** - * Recursively merge the given config objects as the root options by handling - * default scale options for the `scales` and `scale` properties, then returns - * a deep copy of the result, thus doesn't alter inputs. - */ -function mergeConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - var tval = target[key] || {}; - var sval = source[key]; - - if (key === 'scales') { - // scale config merging is complex. Add our own function here for that - target[key] = mergeScaleConfig(tval, sval); - } else if (key === 'scale') { - // used in polar area & radar charts since there is only one scale - target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -function initConfig(config) { - config = config || {}; - - // Do NOT use mergeConfig for the data object because this method merges arrays - // and so would change references to labels and datasets, preventing data updates. - var data = config.data = config.data || {}; - data.datasets = data.datasets || []; - data.labels = data.labels || []; - - config.options = mergeConfig( - core_defaults.global, - core_defaults[config.type], - config.options || {}); - - return config; -} - -function updateConfig(chart) { - var newOptions = chart.options; - - helpers$1.each(chart.scales, function(scale) { - core_layouts.removeBox(chart, scale); - }); - - newOptions = mergeConfig( - core_defaults.global, - core_defaults[chart.config.type], - newOptions); - - chart.options = chart.config.options = newOptions; - chart.ensureScalesHaveIDs(); - chart.buildOrUpdateScales(); - - // Tooltip - chart.tooltip._options = newOptions.tooltips; - chart.tooltip.initialize(); -} - -function nextAvailableScaleId(axesOpts, prefix, index) { - var id; - var hasId = function(obj) { - return obj.id === id; - }; - - do { - id = prefix + index++; - } while (helpers$1.findIndex(axesOpts, hasId) >= 0); - - return id; -} - -function positionIsHorizontal(position) { - return position === 'top' || position === 'bottom'; -} - -function compare2Level(l1, l2) { - return function(a, b) { - return a[l1] === b[l1] - ? a[l2] - b[l2] - : a[l1] - b[l1]; - }; -} - -var Chart = function(item, config) { - this.construct(item, config); - return this; -}; - -helpers$1.extend(Chart.prototype, /** @lends Chart */ { - /** - * @private - */ - construct: function(item, config) { - var me = this; - - config = initConfig(config); - - var context = platform.acquireContext(item, config); - var canvas = context && context.canvas; - var height = canvas && canvas.height; - var width = canvas && canvas.width; - - me.id = helpers$1.uid(); - me.ctx = context; - me.canvas = canvas; - me.config = config; - me.width = width; - me.height = height; - me.aspectRatio = height ? width / height : null; - me.options = config.options; - me._bufferedRender = false; - me._layers = []; - - /** - * Provided for backward compatibility, Chart and Chart.Controller have been merged, - * the "instance" still need to be defined since it might be called from plugins. - * @prop Chart#chart - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ - me.chart = me; - me.controller = me; // chart.chart.controller #inception - - // Add the chart instance to the global namespace - Chart.instances[me.id] = me; - - // Define alias to the config data: `chart.data === chart.config.data` - Object.defineProperty(me, 'data', { - get: function() { - return me.config.data; - }, - set: function(value) { - me.config.data = value; - } - }); - - if (!context || !canvas) { - // The given item is not a compatible context2d element, let's return before finalizing - // the chart initialization but after setting basic chart / controller properties that - // can help to figure out that the chart is not valid (e.g chart.canvas !== null); - // https://github.com/chartjs/Chart.js/issues/2807 - console.error("Failed to create chart: can't acquire context from the given item"); - return; - } - - me.initialize(); - me.update(); - }, - - /** - * @private - */ - initialize: function() { - var me = this; - - // Before init plugin notification - core_plugins.notify(me, 'beforeInit'); - - helpers$1.retinaScale(me, me.options.devicePixelRatio); - - me.bindEvents(); - - if (me.options.responsive) { - // Initial resize before chart draws (must be silent to preserve initial animations). - me.resize(true); - } - - me.initToolTip(); - - // After init plugin notification - core_plugins.notify(me, 'afterInit'); - - return me; - }, - - clear: function() { - helpers$1.canvas.clear(this); - return this; - }, - - stop: function() { - // Stops any current animation loop occurring - core_animations.cancelAnimation(this); - return this; - }, - - resize: function(silent) { - var me = this; - var options = me.options; - var canvas = me.canvas; - var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; - - // the canvas render width and height will be casted to integers so make sure that - // the canvas display style uses the same integer values to avoid blurring effect. - - // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed - var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); - var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); - - if (me.width === newWidth && me.height === newHeight) { - return; - } - - canvas.width = me.width = newWidth; - canvas.height = me.height = newHeight; - canvas.style.width = newWidth + 'px'; - canvas.style.height = newHeight + 'px'; - - helpers$1.retinaScale(me, options.devicePixelRatio); - - if (!silent) { - // Notify any plugins about the resize - var newSize = {width: newWidth, height: newHeight}; - core_plugins.notify(me, 'resize', [newSize]); - - // Notify of resize - if (options.onResize) { - options.onResize(me, newSize); - } - - me.stop(); - me.update({ - duration: options.responsiveAnimationDuration - }); - } - }, - - ensureScalesHaveIDs: function() { - var options = this.options; - var scalesOptions = options.scales || {}; - var scaleOptions = options.scale; - - helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { - if (!xAxisOptions.id) { - xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index); - } - }); - - helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { - if (!yAxisOptions.id) { - yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index); - } - }); - - if (scaleOptions) { - scaleOptions.id = scaleOptions.id || 'scale'; - } - }, - - /** - * Builds a map of scale ID to scale object for future lookup. - */ - buildOrUpdateScales: function() { - var me = this; - var options = me.options; - var scales = me.scales || {}; - var items = []; - var updated = Object.keys(scales).reduce(function(obj, id) { - obj[id] = false; - return obj; - }, {}); - - if (options.scales) { - items = items.concat( - (options.scales.xAxes || []).map(function(xAxisOptions) { - return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; - }), - (options.scales.yAxes || []).map(function(yAxisOptions) { - return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; - }) - ); - } - - if (options.scale) { - items.push({ - options: options.scale, - dtype: 'radialLinear', - isDefault: true, - dposition: 'chartArea' - }); - } - - helpers$1.each(items, function(item) { - var scaleOptions = item.options; - var id = scaleOptions.id; - var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype); - - if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { - scaleOptions.position = item.dposition; - } - - updated[id] = true; - var scale = null; - if (id in scales && scales[id].type === scaleType) { - scale = scales[id]; - scale.options = scaleOptions; - scale.ctx = me.ctx; - scale.chart = me; - } else { - var scaleClass = core_scaleService.getScaleConstructor(scaleType); - if (!scaleClass) { - return; - } - scale = new scaleClass({ - id: id, - type: scaleType, - options: scaleOptions, - ctx: me.ctx, - chart: me - }); - scales[scale.id] = scale; - } - - scale.mergeTicksOptions(); - - // TODO(SB): I think we should be able to remove this custom case (options.scale) - // and consider it as a regular scale part of the "scales"" map only! This would - // make the logic easier and remove some useless? custom code. - if (item.isDefault) { - me.scale = scale; - } - }); - // clear up discarded scales - helpers$1.each(updated, function(hasUpdated, id) { - if (!hasUpdated) { - delete scales[id]; - } - }); - - me.scales = scales; - - core_scaleService.addScalesToLayout(this); - }, - - buildOrUpdateControllers: function() { - var me = this; - var newControllers = []; - var datasets = me.data.datasets; - var i, ilen; - - for (i = 0, ilen = datasets.length; i < ilen; i++) { - var dataset = datasets[i]; - var meta = me.getDatasetMeta(i); - var type = dataset.type || me.config.type; - - if (meta.type && meta.type !== type) { - me.destroyDatasetMeta(i); - meta = me.getDatasetMeta(i); - } - meta.type = type; - meta.order = dataset.order || 0; - meta.index = i; - - if (meta.controller) { - meta.controller.updateIndex(i); - meta.controller.linkScales(); - } else { - var ControllerClass = controllers[meta.type]; - if (ControllerClass === undefined) { - throw new Error('"' + meta.type + '" is not a chart type.'); - } - - meta.controller = new ControllerClass(me, i); - newControllers.push(meta.controller); - } - } - - return newControllers; - }, - - /** - * Reset the elements of all datasets - * @private - */ - resetElements: function() { - var me = this; - helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { - me.getDatasetMeta(datasetIndex).controller.reset(); - }, me); - }, - - /** - * Resets the chart back to it's state before the initial animation - */ - reset: function() { - this.resetElements(); - this.tooltip.initialize(); - }, - - update: function(config) { - var me = this; - var i, ilen; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - updateConfig(me); - - // plugins options references might have change, let's invalidate the cache - // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - core_plugins._invalidate(me); - - if (core_plugins.notify(me, 'beforeUpdate') === false) { - return; - } - - // In case the entire data object changed - me.tooltip._data = me.data; - - // Make sure dataset controllers are updated and new controllers are reset - var newControllers = me.buildOrUpdateControllers(); - - // Make sure all dataset controllers have correct meta data counts - for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) { - me.getDatasetMeta(i).controller.buildOrUpdateElements(); - } - - me.updateLayout(); - - // Can only reset the new controllers after the scales have been updated - if (me.options.animation && me.options.animation.duration) { - helpers$1.each(newControllers, function(controller) { - controller.reset(); - }); - } - - me.updateDatasets(); - - // Need to reset tooltip in case it is displayed with elements that are removed - // after update. - me.tooltip.initialize(); - - // Last active contains items that were previously in the tooltip. - // When we reset the tooltip, we need to clear it - me.lastActive = []; - - // Do this before render so that any plugins that need final scale updates can use it - core_plugins.notify(me, 'afterUpdate'); - - me._layers.sort(compare2Level('z', '_idx')); - - if (me._bufferedRender) { - me._bufferedRequest = { - duration: config.duration, - easing: config.easing, - lazy: config.lazy - }; - } else { - me.render(config); - } - }, - - /** - * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` - * hook, in which case, plugins will not be called on `afterLayout`. - * @private - */ - updateLayout: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeLayout') === false) { - return; - } - - core_layouts.update(this, this.width, this.height); - - me._layers = []; - helpers$1.each(me.boxes, function(box) { - // _configure is called twice, once in core.scale.update and once here. - // Here the boxes are fully updated and at their final positions. - if (box._configure) { - box._configure(); - } - me._layers.push.apply(me._layers, box._layers()); - }, me); - - me._layers.forEach(function(item, index) { - item._idx = index; - }); - - /** - * Provided for backward compatibility, use `afterLayout` instead. - * @method IPlugin#afterScaleUpdate - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ - core_plugins.notify(me, 'afterScaleUpdate'); - core_plugins.notify(me, 'afterLayout'); - }, - - /** - * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` - * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. - * @private - */ - updateDatasets: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { - return; - } - - for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.updateDataset(i); - } - - core_plugins.notify(me, 'afterDatasetsUpdate'); - }, - - /** - * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` - * hook, in which case, plugins will not be called on `afterDatasetUpdate`. - * @private - */ - updateDataset: function(index) { - var me = this; - var meta = me.getDatasetMeta(index); - var args = { - meta: meta, - index: index - }; - - if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { - return; - } - - meta.controller._update(); - - core_plugins.notify(me, 'afterDatasetUpdate', [args]); - }, - - render: function(config) { - var me = this; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - var animationOptions = me.options.animation; - var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration); - var lazy = config.lazy; - - if (core_plugins.notify(me, 'beforeRender') === false) { - return; - } - - var onComplete = function(animation) { - core_plugins.notify(me, 'afterRender'); - helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); - }; - - if (animationOptions && duration) { - var animation = new core_animation({ - numSteps: duration / 16.66, // 60 fps - easing: config.easing || animationOptions.easing, - - render: function(chart, animationObject) { - var easingFunction = helpers$1.easing.effects[animationObject.easing]; - var currentStep = animationObject.currentStep; - var stepDecimal = currentStep / animationObject.numSteps; - - chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); - }, - - onAnimationProgress: animationOptions.onProgress, - onAnimationComplete: onComplete - }); - - core_animations.addAnimation(me, animation, duration, lazy); - } else { - me.draw(); - - // See https://github.com/chartjs/Chart.js/issues/3781 - onComplete(new core_animation({numSteps: 0, chart: me})); - } - - return me; - }, - - draw: function(easingValue) { - var me = this; - var i, layers; - - me.clear(); - - if (helpers$1.isNullOrUndef(easingValue)) { - easingValue = 1; - } - - me.transition(easingValue); - - if (me.width <= 0 || me.height <= 0) { - return; - } - - if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { - return; - } - - // Because of plugin hooks (before/afterDatasetsDraw), datasets can't - // currently be part of layers. Instead, we draw - // layers <= 0 before(default, backward compat), and the rest after - layers = me._layers; - for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { - layers[i].draw(me.chartArea); - } - - me.drawDatasets(easingValue); - - // Rest of layers - for (; i < layers.length; ++i) { - layers[i].draw(me.chartArea); - } - - me._drawTooltip(easingValue); - - core_plugins.notify(me, 'afterDraw', [easingValue]); - }, - - /** - * @private - */ - transition: function(easingValue) { - var me = this; - - for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { - if (me.isDatasetVisible(i)) { - me.getDatasetMeta(i).controller.transition(easingValue); - } - } - - me.tooltip.transition(easingValue); - }, - - /** - * @private - */ - _getSortedDatasetMetas: function(filterVisible) { - var me = this; - var datasets = me.data.datasets || []; - var result = []; - var i, ilen; - - for (i = 0, ilen = datasets.length; i < ilen; ++i) { - if (!filterVisible || me.isDatasetVisible(i)) { - result.push(me.getDatasetMeta(i)); - } - } - - result.sort(compare2Level('order', 'index')); - - return result; - }, - - /** - * @private - */ - _getSortedVisibleDatasetMetas: function() { - return this._getSortedDatasetMetas(true); - }, - - /** - * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` - * hook, in which case, plugins will not be called on `afterDatasetsDraw`. - * @private - */ - drawDatasets: function(easingValue) { - var me = this; - var metasets, i; - - if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { - return; - } - - metasets = me._getSortedVisibleDatasetMetas(); - for (i = metasets.length - 1; i >= 0; --i) { - me.drawDataset(metasets[i], easingValue); - } - - core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); - }, - - /** - * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` - * hook, in which case, plugins will not be called on `afterDatasetDraw`. - * @private - */ - drawDataset: function(meta, easingValue) { - var me = this; - var args = { - meta: meta, - index: meta.index, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { - return; - } - - meta.controller.draw(easingValue); - - core_plugins.notify(me, 'afterDatasetDraw', [args]); - }, - - /** - * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` - * hook, in which case, plugins will not be called on `afterTooltipDraw`. - * @private - */ - _drawTooltip: function(easingValue) { - var me = this; - var tooltip = me.tooltip; - var args = { - tooltip: tooltip, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { - return; - } - - tooltip.draw(); - - core_plugins.notify(me, 'afterTooltipDraw', [args]); - }, - - /** - * Get the single element that was clicked on - * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw - */ - getElementAtEvent: function(e) { - return core_interaction.modes.single(this, e); - }, - - getElementsAtEvent: function(e) { - return core_interaction.modes.label(this, e, {intersect: true}); - }, - - getElementsAtXAxis: function(e) { - return core_interaction.modes['x-axis'](this, e, {intersect: true}); - }, - - getElementsAtEventForMode: function(e, mode, options) { - var method = core_interaction.modes[mode]; - if (typeof method === 'function') { - return method(this, e, options); - } - - return []; - }, - - getDatasetAtEvent: function(e) { - return core_interaction.modes.dataset(this, e, {intersect: true}); - }, - - getDatasetMeta: function(datasetIndex) { - var me = this; - var dataset = me.data.datasets[datasetIndex]; - if (!dataset._meta) { - dataset._meta = {}; - } - - var meta = dataset._meta[me.id]; - if (!meta) { - meta = dataset._meta[me.id] = { - type: null, - data: [], - dataset: null, - controller: null, - hidden: null, // See isDatasetVisible() comment - xAxisID: null, - yAxisID: null, - order: dataset.order || 0, - index: datasetIndex - }; - } - - return meta; - }, - - getVisibleDatasetCount: function() { - var count = 0; - for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { - if (this.isDatasetVisible(i)) { - count++; - } - } - return count; - }, - - isDatasetVisible: function(datasetIndex) { - var meta = this.getDatasetMeta(datasetIndex); - - // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, - // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. - return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; - }, - - generateLegend: function() { - return this.options.legendCallback(this); - }, - - /** - * @private - */ - destroyDatasetMeta: function(datasetIndex) { - var id = this.id; - var dataset = this.data.datasets[datasetIndex]; - var meta = dataset._meta && dataset._meta[id]; - - if (meta) { - meta.controller.destroy(); - delete dataset._meta[id]; - } - }, - - destroy: function() { - var me = this; - var canvas = me.canvas; - var i, ilen; - - me.stop(); - - // dataset controllers need to cleanup associated data - for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.destroyDatasetMeta(i); - } - - if (canvas) { - me.unbindEvents(); - helpers$1.canvas.clear(me); - platform.releaseContext(me.ctx); - me.canvas = null; - me.ctx = null; - } - - core_plugins.notify(me, 'destroy'); - - delete Chart.instances[me.id]; - }, - - toBase64Image: function() { - return this.canvas.toDataURL.apply(this.canvas, arguments); - }, - - initToolTip: function() { - var me = this; - me.tooltip = new core_tooltip({ - _chart: me, - _chartInstance: me, // deprecated, backward compatibility - _data: me.data, - _options: me.options.tooltips - }, me); - }, - - /** - * @private - */ - bindEvents: function() { - var me = this; - var listeners = me._listeners = {}; - var listener = function() { - me.eventHandler.apply(me, arguments); - }; - - helpers$1.each(me.options.events, function(type) { - platform.addEventListener(me, type, listener); - listeners[type] = listener; - }); - - // Elements used to detect size change should not be injected for non responsive charts. - // See https://github.com/chartjs/Chart.js/issues/2210 - if (me.options.responsive) { - listener = function() { - me.resize(); - }; - - platform.addEventListener(me, 'resize', listener); - listeners.resize = listener; - } - }, - - /** - * @private - */ - unbindEvents: function() { - var me = this; - var listeners = me._listeners; - if (!listeners) { - return; - } - - delete me._listeners; - helpers$1.each(listeners, function(listener, type) { - platform.removeEventListener(me, type, listener); - }); - }, - - updateHoverStyle: function(elements, mode, enabled) { - var prefix = enabled ? 'set' : 'remove'; - var element, i, ilen; - - for (i = 0, ilen = elements.length; i < ilen; ++i) { - element = elements[i]; - if (element) { - this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element); - } - } - - if (mode === 'dataset') { - this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle'](); - } - }, - - /** - * @private - */ - eventHandler: function(e) { - var me = this; - var tooltip = me.tooltip; - - if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { - return; - } - - // Buffer any update calls so that renders do not occur - me._bufferedRender = true; - me._bufferedRequest = null; - - var changed = me.handleEvent(e); - // for smooth tooltip animations issue #4989 - // the tooltip should be the source of change - // Animation check workaround: - // tooltip._start will be null when tooltip isn't animating - if (tooltip) { - changed = tooltip._start - ? tooltip.handleEvent(e) - : changed | tooltip.handleEvent(e); - } - - core_plugins.notify(me, 'afterEvent', [e]); - - var bufferedRequest = me._bufferedRequest; - if (bufferedRequest) { - // If we have an update that was triggered, we need to do a normal render - me.render(bufferedRequest); - } else if (changed && !me.animating) { - // If entering, leaving, or changing elements, animate the change via pivot - me.stop(); - - // We only need to render at this point. Updating will cause scales to be - // recomputed generating flicker & using more memory than necessary. - me.render({ - duration: me.options.hover.animationDuration, - lazy: true - }); - } - - me._bufferedRender = false; - me._bufferedRequest = null; - - return me; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event the event to handle - * @return {boolean} true if the chart needs to re-render - */ - handleEvent: function(e) { - var me = this; - var options = me.options || {}; - var hoverOptions = options.hover; - var changed = false; - - me.lastActive = me.lastActive || []; - - // Find Active Elements for hover and tooltips - if (e.type === 'mouseout') { - me.active = []; - } else { - me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); - } - - // Invoke onHover hook - // Need to call with native event here to not break backwards compatibility - helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); - - if (e.type === 'mouseup' || e.type === 'click') { - if (options.onClick) { - // Use e.native here for backwards compatibility - options.onClick.call(me, e.native, me.active); - } - } - - // Remove styling for last active (even if it may still be active) - if (me.lastActive.length) { - me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); - } - - // Built in hover styling - if (me.active.length && hoverOptions.mode) { - me.updateHoverStyle(me.active, hoverOptions.mode, true); - } - - changed = !helpers$1.arrayEquals(me.active, me.lastActive); - - // Remember Last Actives - me.lastActive = me.active; - - return changed; - } -}); - -/** - * NOTE(SB) We actually don't use this container anymore but we need to keep it - * for backward compatibility. Though, it can still be useful for plugins that - * would need to work on multiple charts?! - */ -Chart.instances = {}; - -var core_controller = Chart; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart instead. - * @class Chart.Controller - * @deprecated since version 2.6 - * @todo remove at version 3 - * @private - */ -Chart.Controller = Chart; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -Chart.types = {}; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.configMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.configMerge = mergeConfig; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.scaleMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.scaleMerge = mergeScaleConfig; - -var core_helpers = function() { - - // -- Basic js utility methods - - helpers$1.where = function(collection, filterCallback) { - if (helpers$1.isArray(collection) && Array.prototype.filter) { - return collection.filter(filterCallback); - } - var filtered = []; - - helpers$1.each(collection, function(item) { - if (filterCallback(item)) { - filtered.push(item); - } - }); - - return filtered; - }; - helpers$1.findIndex = Array.prototype.findIndex ? - function(array, callback, scope) { - return array.findIndex(callback, scope); - } : - function(array, callback, scope) { - scope = scope === undefined ? array : scope; - for (var i = 0, ilen = array.length; i < ilen; ++i) { - if (callback.call(scope, array[i], i, array)) { - return i; - } - } - return -1; - }; - helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to start of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = -1; - } - for (var i = startIndex + 1; i < arrayToSearch.length; i++) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to end of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = arrayToSearch.length; - } - for (var i = startIndex - 1; i >= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - - // -- Math methods - helpers$1.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - helpers$1.almostEquals = function(x, y, epsilon) { - return Math.abs(x - y) < epsilon; - }; - helpers$1.almostWhole = function(x, epsilon) { - var rounded = Math.round(x); - return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x); - }; - helpers$1.max = function(array) { - return array.reduce(function(max, value) { - if (!isNaN(value)) { - return Math.max(max, value); - } - return max; - }, Number.NEGATIVE_INFINITY); - }; - helpers$1.min = function(array) { - return array.reduce(function(min, value) { - if (!isNaN(value)) { - return Math.min(min, value); - } - return min; - }, Number.POSITIVE_INFINITY); - }; - helpers$1.sign = Math.sign ? - function(x) { - return Math.sign(x); - } : - function(x) { - x = +x; // convert to a number - if (x === 0 || isNaN(x)) { - return x; - } - return x > 0 ? 1 : -1; - }; - helpers$1.toRadians = function(degrees) { - return degrees * (Math.PI / 180); - }; - helpers$1.toDegrees = function(radians) { - return radians * (180 / Math.PI); - }; - - /** - * Returns the number of decimal places - * i.e. the number of digits after the decimal point, of the value of this Number. - * @param {number} x - A number. - * @returns {number} The number of decimal places. - * @private - */ - helpers$1._decimalPlaces = function(x) { - if (!helpers$1.isFinite(x)) { - return; - } - var e = 1; - var p = 0; - while (Math.round(x * e) / e !== x) { - e *= 10; - p++; - } - return p; - }; - - // Gets the angle from vertical upright to the point about a centre. - helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { - var distanceFromXCenter = anglePoint.x - centrePoint.x; - var distanceFromYCenter = anglePoint.y - centrePoint.y; - var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); - - if (angle < (-0.5 * Math.PI)) { - angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }; - helpers$1.distanceBetweenPoints = function(pt1, pt2) { - return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); - }; - - /** - * Provided for backward compatibility, not available anymore - * @function Chart.helpers.aliasPixel - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ - helpers$1.aliasPixel = function(pixelWidth) { - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }; - - /** - * Returns the aligned pixel value to avoid anti-aliasing blur - * @param {Chart} chart - The chart instance. - * @param {number} pixel - A pixel value. - * @param {number} width - The width of the element. - * @returns {number} The aligned pixel value. - * @private - */ - helpers$1._alignPixel = function(chart, pixel, width) { - var devicePixelRatio = chart.currentDevicePixelRatio; - var halfWidth = width / 2; - return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; - }; - - helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { - // Props to Rob Spencer at scaled innovation for his post on splining between points - // http://scaledinnovation.com/analytics/splines/aboutSplines.html - - // This function must also respect "skipped" points - - var previous = firstPoint.skip ? middlePoint : firstPoint; - var current = middlePoint; - var next = afterPoint.skip ? middlePoint : afterPoint; - - var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); - var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); - - var s01 = d01 / (d01 + d12); - var s12 = d12 / (d01 + d12); - - // If all points are the same, s01 & s02 will be inf - s01 = isNaN(s01) ? 0 : s01; - s12 = isNaN(s12) ? 0 : s12; - - var fa = t * s01; // scaling factor for triangle Ta - var fb = t * s12; - - return { - previous: { - x: current.x - fa * (next.x - previous.x), - y: current.y - fa * (next.y - previous.y) - }, - next: { - x: current.x + fb * (next.x - previous.x), - y: current.y + fb * (next.y - previous.y) - } - }; - }; - helpers$1.EPSILON = Number.EPSILON || 1e-14; - helpers$1.splineCurveMonotone = function(points) { - // This function calculates Bézier control points in a similar way than |splineCurve|, - // but preserves monotonicity of the provided data and ensures no local extremums are added - // between the dataset discrete points due to the interpolation. - // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation - - var pointsWithTangents = (points || []).map(function(point) { - return { - model: point._model, - deltaK: 0, - mK: 0 - }; - }); - - // Calculate slopes (deltaK) and initialize tangents (mK) - var pointsLen = pointsWithTangents.length; - var i, pointBefore, pointCurrent, pointAfter; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointAfter && !pointAfter.model.skip) { - var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); - - // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 - pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; - } - - if (!pointBefore || pointBefore.model.skip) { - pointCurrent.mK = pointCurrent.deltaK; - } else if (!pointAfter || pointAfter.model.skip) { - pointCurrent.mK = pointBefore.deltaK; - } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { - pointCurrent.mK = 0; - } else { - pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; - } - } - - // Adjust tangents to ensure monotonic properties - var alphaK, betaK, tauK, squaredMagnitude; - for (i = 0; i < pointsLen - 1; ++i) { - pointCurrent = pointsWithTangents[i]; - pointAfter = pointsWithTangents[i + 1]; - if (pointCurrent.model.skip || pointAfter.model.skip) { - continue; - } - - if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { - pointCurrent.mK = pointAfter.mK = 0; - continue; - } - - alphaK = pointCurrent.mK / pointCurrent.deltaK; - betaK = pointAfter.mK / pointCurrent.deltaK; - squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); - if (squaredMagnitude <= 9) { - continue; - } - - tauK = 3 / Math.sqrt(squaredMagnitude); - pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; - pointAfter.mK = betaK * tauK * pointCurrent.deltaK; - } - - // Compute control points - var deltaX; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointBefore && !pointBefore.model.skip) { - deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; - pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; - pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; - } - if (pointAfter && !pointAfter.model.skip) { - deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; - pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; - pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; - } - } - }; - helpers$1.nextItem = function(collection, index, loop) { - if (loop) { - return index >= collection.length - 1 ? collection[0] : collection[index + 1]; - } - return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; - }; - helpers$1.previousItem = function(collection, index, loop) { - if (loop) { - return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; - } - return index <= 0 ? collection[0] : collection[index - 1]; - }; - // Implementation of the nice number algorithm used in determining where axis labels will go - helpers$1.niceNum = function(range, round) { - var exponent = Math.floor(helpers$1.log10(range)); - var fraction = range / Math.pow(10, exponent); - var niceFraction; - - if (round) { - if (fraction < 1.5) { - niceFraction = 1; - } else if (fraction < 3) { - niceFraction = 2; - } else if (fraction < 7) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } else if (fraction <= 1.0) { - niceFraction = 1; - } else if (fraction <= 2) { - niceFraction = 2; - } else if (fraction <= 5) { - niceFraction = 5; - } else { - niceFraction = 10; - } - - return niceFraction * Math.pow(10, exponent); - }; - // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - helpers$1.requestAnimFrame = (function() { - if (typeof window === 'undefined') { - return function(callback) { - callback(); - }; - } - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - }()); - // -- DOM methods - helpers$1.getRelativePosition = function(evt, chart) { - var mouseX, mouseY; - var e = evt.originalEvent || evt; - var canvas = evt.target || evt.srcElement; - var boundingRect = canvas.getBoundingClientRect(); - - var touches = e.touches; - if (touches && touches.length > 0) { - mouseX = touches[0].clientX; - mouseY = touches[0].clientY; - - } else { - mouseX = e.clientX; - mouseY = e.clientY; - } - - // Scale mouse coordinates into canvas coordinates - // by following the pattern laid out by 'jerryj' in the comments of - // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ - var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); - var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); - var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); - var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); - var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; - var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; - - // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However - // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here - mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); - mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); - - return { - x: mouseX, - y: mouseY - }; - - }; - - // Private helper function to convert max-width/max-height values that may be percentages into a number - function parseMaxStyle(styleValue, node, parentProperty) { - var valueInPixels; - if (typeof styleValue === 'string') { - valueInPixels = parseInt(styleValue, 10); - - if (styleValue.indexOf('%') !== -1) { - // percentage * size in dimension - valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; - } - } else { - valueInPixels = styleValue; - } - - return valueInPixels; - } - - /** - * Returns if the given value contains an effective constraint. - * @private - */ - function isConstrainedValue(value) { - return value !== undefined && value !== null && value !== 'none'; - } - - /** - * Returns the max width or height of the given DOM node in a cross-browser compatible fashion - * @param {HTMLElement} domNode - the node to check the constraint on - * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') - * @param {string} percentageProperty - property of parent to use when calculating width as a percentage - * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} - */ - function getConstraintDimension(domNode, maxStyle, percentageProperty) { - var view = document.defaultView; - var parentNode = helpers$1._getParentNode(domNode); - var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; - var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; - var hasCNode = isConstrainedValue(constrainedNode); - var hasCContainer = isConstrainedValue(constrainedContainer); - var infinity = Number.POSITIVE_INFINITY; - - if (hasCNode || hasCContainer) { - return Math.min( - hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, - hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); - } - - return 'none'; - } - // returns Number or undefined if no constraint - helpers$1.getConstraintWidth = function(domNode) { - return getConstraintDimension(domNode, 'max-width', 'clientWidth'); - }; - // returns Number or undefined if no constraint - helpers$1.getConstraintHeight = function(domNode) { - return getConstraintDimension(domNode, 'max-height', 'clientHeight'); - }; - /** - * @private - */ - helpers$1._calculatePadding = function(container, padding, parentDimension) { - padding = helpers$1.getStyle(container, padding); - - return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); - }; - /** - * @private - */ - helpers$1._getParentNode = function(domNode) { - var parent = domNode.parentNode; - if (parent && parent.toString() === '[object ShadowRoot]') { - parent = parent.host; - } - return parent; - }; - helpers$1.getMaximumWidth = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientWidth; - } - - var clientWidth = container.clientWidth; - var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); - var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); - - var w = clientWidth - paddingLeft - paddingRight; - var cw = helpers$1.getConstraintWidth(domNode); - return isNaN(cw) ? w : Math.min(w, cw); - }; - helpers$1.getMaximumHeight = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientHeight; - } - - var clientHeight = container.clientHeight; - var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); - var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); - - var h = clientHeight - paddingTop - paddingBottom; - var ch = helpers$1.getConstraintHeight(domNode); - return isNaN(ch) ? h : Math.min(h, ch); - }; - helpers$1.getStyle = function(el, property) { - return el.currentStyle ? - el.currentStyle[property] : - document.defaultView.getComputedStyle(el, null).getPropertyValue(property); - }; - helpers$1.retinaScale = function(chart, forceRatio) { - var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; - if (pixelRatio === 1) { - return; - } - - var canvas = chart.canvas; - var height = chart.height; - var width = chart.width; - - canvas.height = height * pixelRatio; - canvas.width = width * pixelRatio; - chart.ctx.scale(pixelRatio, pixelRatio); - - // If no style has been set on the canvas, the render size is used as display size, - // making the chart visually bigger, so let's enforce it to the "correct" values. - // See https://github.com/chartjs/Chart.js/issues/3575 - if (!canvas.style.height && !canvas.style.width) { - canvas.style.height = height + 'px'; - canvas.style.width = width + 'px'; - } - }; - // -- Canvas methods - helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { - return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; - }; - helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { - cache = cache || {}; - var data = cache.data = cache.data || {}; - var gc = cache.garbageCollect = cache.garbageCollect || []; - - if (cache.font !== font) { - data = cache.data = {}; - gc = cache.garbageCollect = []; - cache.font = font; - } - - ctx.font = font; - var longest = 0; - var ilen = arrayOfThings.length; - var i, j, jlen, thing, nestedThing; - for (i = 0; i < ilen; i++) { - thing = arrayOfThings[i]; - - // Undefined strings and arrays should not be measured - if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { - longest = helpers$1.measureText(ctx, data, gc, longest, thing); - } else if (helpers$1.isArray(thing)) { - // if it is an array lets measure each element - // to do maybe simplify this function a bit so we can do this more recursively? - for (j = 0, jlen = thing.length; j < jlen; j++) { - nestedThing = thing[j]; - // Undefined strings and arrays should not be measured - if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { - longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); - } - } - } - } - - var gcLen = gc.length / 2; - if (gcLen > arrayOfThings.length) { - for (i = 0; i < gcLen; i++) { - delete data[gc[i]]; - } - gc.splice(0, gcLen); - } - return longest; - }; - helpers$1.measureText = function(ctx, data, gc, longest, string) { - var textWidth = data[string]; - if (!textWidth) { - textWidth = data[string] = ctx.measureText(string).width; - gc.push(string); - } - if (textWidth > longest) { - longest = textWidth; - } - return longest; - }; - - /** - * @deprecated - */ - helpers$1.numberOfLabelLines = function(arrayOfThings) { - var numberOfLines = 1; - helpers$1.each(arrayOfThings, function(thing) { - if (helpers$1.isArray(thing)) { - if (thing.length > numberOfLines) { - numberOfLines = thing.length; - } - } - }); - return numberOfLines; - }; - - helpers$1.color = !chartjsColor ? - function(value) { - console.error('Color.js not found!'); - return value; - } : - function(value) { - /* global CanvasGradient */ - if (value instanceof CanvasGradient) { - value = core_defaults.global.defaultColor; - } - - return chartjsColor(value); - }; - - helpers$1.getHoverColor = function(colorValue) { - /* global CanvasPattern */ - return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? - colorValue : - helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); - }; -}; - -function abstract() { - throw new Error( - 'This method is not implemented: either no adapter can ' + - 'be found or an incomplete integration was provided.' - ); -} - -/** - * Date adapter (current used by the time scale) - * @namespace Chart._adapters._date - * @memberof Chart._adapters - * @private - */ - -/** - * Currently supported unit string values. - * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} - * @memberof Chart._adapters._date - * @name Unit - */ - -/** - * @class - */ -function DateAdapter(options) { - this.options = options || {}; -} - -helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { - /** - * Returns a map of time formats for the supported formatting units defined - * in Unit as well as 'datetime' representing a detailed date/time string. - * @returns {{string: string}} - */ - formats: abstract, - - /** - * Parses the given `value` and return the associated timestamp. - * @param {any} value - the value to parse (usually comes from the data) - * @param {string} [format] - the expected data format - * @returns {(number|null)} - * @function - */ - parse: abstract, - - /** - * Returns the formatted date in the specified `format` for a given `timestamp`. - * @param {number} timestamp - the timestamp to format - * @param {string} format - the date/time token - * @return {string} - * @function - */ - format: abstract, - - /** - * Adds the specified `amount` of `unit` to the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {number} amount - the amount to add - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - add: abstract, - - /** - * Returns the number of `unit` between the given timestamps. - * @param {number} max - the input timestamp (reference) - * @param {number} min - the timestamp to substract - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - diff: abstract, - - /** - * Returns start of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @param {number} [weekday] - the ISO day of the week with 1 being Monday - * and 7 being Sunday (only needed if param *unit* is `isoWeek`). - * @function - */ - startOf: abstract, - - /** - * Returns end of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @function - */ - endOf: abstract, - - // DEPRECATIONS - - /** - * Provided for backward compatibility for scale.getValueForPixel(), - * this method should be overridden only by the moment adapter. - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(value) { - return value; - } -}); - -DateAdapter.override = function(members) { - helpers$1.extend(DateAdapter.prototype, members); -}; - -var _date = DateAdapter; - -var core_adapters = { - _date: _date -}; - -/** - * Namespace to hold static tick generation functions - * @namespace Chart.Ticks - */ -var core_ticks = { - /** - * Namespace to hold formatters for different types of ticks - * @namespace Chart.Ticks.formatters - */ - formatters: { - /** - * Formatter for value labels - * @method Chart.Ticks.formatters.values - * @param value the value to display - * @return {string|string[]} the label to display - */ - values: function(value) { - return helpers$1.isArray(value) ? value : '' + value; - }, - - /** - * Formatter for linear numeric ticks - * @method Chart.Ticks.formatters.linear - * @param tickValue {number} the value to be formatted - * @param index {number} the position of the tickValue parameter in the ticks array - * @param ticks {number[]} the list of ticks being converted - * @return {string} string representation of the tickValue parameter - */ - linear: function(tickValue, index, ticks) { - // If we have lots of ticks, don't use the ones - var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; - - // If we have a number like 2.5 as the delta, figure out how many decimal places we need - if (Math.abs(delta) > 1) { - if (tickValue !== Math.floor(tickValue)) { - // not an integer - delta = tickValue - Math.floor(tickValue); - } - } - - var logDelta = helpers$1.log10(Math.abs(delta)); - var tickString = ''; - - if (tickValue !== 0) { - var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); - if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation - var logTick = helpers$1.log10(Math.abs(tickValue)); - var numExponential = Math.floor(logTick) - Math.floor(logDelta); - numExponential = Math.max(Math.min(numExponential, 20), 0); - tickString = tickValue.toExponential(numExponential); - } else { - var numDecimal = -1 * Math.floor(logDelta); - numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places - tickString = tickValue.toFixed(numDecimal); - } - } else { - tickString = '0'; // never show decimal places for 0 - } - - return tickString; - }, - - logarithmic: function(tickValue, index, ticks) { - var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); - - if (tickValue === 0) { - return '0'; - } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { - return tickValue.toExponential(); - } - return ''; - } - } -}; - -var isArray = helpers$1.isArray; -var isNullOrUndef = helpers$1.isNullOrUndef; -var valueOrDefault$a = helpers$1.valueOrDefault; -var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; - -core_defaults._set('scale', { - display: true, - position: 'left', - offset: false, - - // grid line settings - gridLines: { - display: true, - color: 'rgba(0,0,0,0.1)', - lineWidth: 1, - drawBorder: true, - drawOnChartArea: true, - drawTicks: true, - tickMarkLength: 10, - zeroLineWidth: 1, - zeroLineColor: 'rgba(0,0,0,0.25)', - zeroLineBorderDash: [], - zeroLineBorderDashOffset: 0.0, - offsetGridLines: false, - borderDash: [], - borderDashOffset: 0.0 - }, - - // scale label - scaleLabel: { - // display property - display: false, - - // actual label - labelString: '', - - // top/bottom padding - padding: { - top: 4, - bottom: 4 - } - }, - - // label settings - ticks: { - beginAtZero: false, - minRotation: 0, - maxRotation: 50, - mirror: false, - padding: 0, - reverse: false, - display: true, - autoSkip: true, - autoSkipPadding: 0, - labelOffset: 0, - // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. - callback: core_ticks.formatters.values, - minor: {}, - major: {} - } -}); - -/** Returns a new array containing numItems from arr */ -function sample(arr, numItems) { - var result = []; - var increment = arr.length / numItems; - var i = 0; - var len = arr.length; - - for (; i < len; i += increment) { - result.push(arr[Math.floor(i)]); - } - return result; -} - -function getPixelForGridLine(scale, index, offsetGridLines) { - var length = scale.getTicks().length; - var validIndex = Math.min(index, length - 1); - var lineValue = scale.getPixelForTick(validIndex); - var start = scale._startPixel; - var end = scale._endPixel; - var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. - var offset; - - if (offsetGridLines) { - if (length === 1) { - offset = Math.max(lineValue - start, end - lineValue); - } else if (index === 0) { - offset = (scale.getPixelForTick(1) - lineValue) / 2; - } else { - offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; - } - lineValue += validIndex < index ? offset : -offset; - - // Return undefined if the pixel is out of the range - if (lineValue < start - epsilon || lineValue > end + epsilon) { - return; - } - } - return lineValue; -} - -function garbageCollect(caches, length) { - helpers$1.each(caches, function(cache) { - var gc = cache.gc; - var gcLen = gc.length / 2; - var i; - if (gcLen > length) { - for (i = 0; i < gcLen; ++i) { - delete cache.data[gc[i]]; - } - gc.splice(0, gcLen); - } - }); -} - -/** - * Returns {width, height, offset} objects for the first, last, widest, highest tick - * labels where offset indicates the anchor point offset from the top in pixels. - */ -function computeLabelSizes(ctx, tickFonts, ticks, caches) { - var length = ticks.length; - var widths = []; - var heights = []; - var offsets = []; - var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest; - - for (i = 0; i < length; ++i) { - label = ticks[i].label; - tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor; - ctx.font = fontString = tickFont.string; - cache = caches[fontString] = caches[fontString] || {data: {}, gc: []}; - lineHeight = tickFont.lineHeight; - width = height = 0; - // Undefined labels and arrays should not be measured - if (!isNullOrUndef(label) && !isArray(label)) { - width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label); - height = lineHeight; - } else if (isArray(label)) { - // if it is an array let's measure each element - for (j = 0, jlen = label.length; j < jlen; ++j) { - nestedLabel = label[j]; - // Undefined labels and arrays should not be measured - if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { - width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel); - height += lineHeight; - } - } - } - widths.push(width); - heights.push(height); - offsets.push(lineHeight / 2); - } - garbageCollect(caches, length); - - widest = widths.indexOf(Math.max.apply(null, widths)); - highest = heights.indexOf(Math.max.apply(null, heights)); - - function valueAt(idx) { - return { - width: widths[idx] || 0, - height: heights[idx] || 0, - offset: offsets[idx] || 0 - }; - } - - return { - first: valueAt(0), - last: valueAt(length - 1), - widest: valueAt(widest), - highest: valueAt(highest) - }; -} - -function getTickMarkLength(options) { - return options.drawTicks ? options.tickMarkLength : 0; -} - -function getScaleLabelHeight(options) { - var font, padding; - - if (!options.display) { - return 0; - } - - font = helpers$1.options._parseFont(options); - padding = helpers$1.options.toPadding(options.padding); - - return font.lineHeight + padding.height; -} - -function parseFontOptions(options, nestedOpts) { - return helpers$1.extend(helpers$1.options._parseFont({ - fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily), - fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize), - fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle), - lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight) - }), { - color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor]) - }); -} - -function parseTickFontOptions(options) { - var minor = parseFontOptions(options, options.minor); - var major = options.major.enabled ? parseFontOptions(options, options.major) : minor; - - return {minor: minor, major: major}; -} - -function nonSkipped(ticksToFilter) { - var filtered = []; - var item, index, len; - for (index = 0, len = ticksToFilter.length; index < len; ++index) { - item = ticksToFilter[index]; - if (typeof item._index !== 'undefined') { - filtered.push(item); - } - } - return filtered; -} - -function getEvenSpacing(arr) { - var len = arr.length; - var i, diff; - - if (len < 2) { - return false; - } - - for (diff = arr[0], i = 1; i < len; ++i) { - if (arr[i] - arr[i - 1] !== diff) { - return false; - } - } - return diff; -} - -function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) { - var evenMajorSpacing = getEvenSpacing(majorIndices); - var spacing = (ticks.length - 1) / ticksLimit; - var factors, factor, i, ilen; - - // If the major ticks are evenly spaced apart, place the minor ticks - // so that they divide the major ticks into even chunks - if (!evenMajorSpacing) { - return Math.max(spacing, 1); - } - - factors = helpers$1.math._factorize(evenMajorSpacing); - for (i = 0, ilen = factors.length - 1; i < ilen; i++) { - factor = factors[i]; - if (factor > spacing) { - return factor; - } - } - return Math.max(spacing, 1); -} - -function getMajorIndices(ticks) { - var result = []; - var i, ilen; - for (i = 0, ilen = ticks.length; i < ilen; i++) { - if (ticks[i].major) { - result.push(i); - } - } - return result; -} - -function skipMajors(ticks, majorIndices, spacing) { - var count = 0; - var next = majorIndices[0]; - var i, tick; - - spacing = Math.ceil(spacing); - for (i = 0; i < ticks.length; i++) { - tick = ticks[i]; - if (i === next) { - tick._index = i; - count++; - next = majorIndices[count * spacing]; - } else { - delete tick.label; - } - } -} - -function skip(ticks, spacing, majorStart, majorEnd) { - var start = valueOrDefault$a(majorStart, 0); - var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length); - var count = 0; - var length, i, tick, next; - - spacing = Math.ceil(spacing); - if (majorEnd) { - length = majorEnd - majorStart; - spacing = length / Math.floor(length / spacing); - } - - next = start; - - while (next < 0) { - count++; - next = Math.round(start + count * spacing); - } - - for (i = Math.max(start, 0); i < end; i++) { - tick = ticks[i]; - if (i === next) { - tick._index = i; - count++; - next = Math.round(start + count * spacing); - } else { - delete tick.label; - } - } -} - -var Scale = core_element.extend({ - - zeroLineIndex: 0, - - /** - * Get the padding needed for the scale - * @method getPadding - * @private - * @returns {Padding} the necessary padding - */ - getPadding: function() { - var me = this; - return { - left: me.paddingLeft || 0, - top: me.paddingTop || 0, - right: me.paddingRight || 0, - bottom: me.paddingBottom || 0 - }; - }, - - /** - * Returns the scale tick objects ({label, major}) - * @since 2.7 - */ - getTicks: function() { - return this._ticks; - }, - - /** - * @private - */ - _getLabels: function() { - var data = this.chart.data; - return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || []; - }, - - // These methods are ordered by lifecyle. Utilities then follow. - // Any function defined here is inherited by all scale types. - // Any function can be extended by the scale type - - /** - * Provided for backward compatibility, not available anymore - * @function Chart.Scale.mergeTicksOptions - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ - mergeTicksOptions: function() { - // noop - }, - - beforeUpdate: function() { - helpers$1.callback(this.options.beforeUpdate, [this]); - }, - - /** - * @param {number} maxWidth - the max width in pixels - * @param {number} maxHeight - the max height in pixels - * @param {object} margins - the space between the edge of the other scales and edge of the chart - * This space comes from two sources: - * - padding - space that's required to show the labels at the edges of the scale - * - thickness of scales or legends in another orientation - */ - update: function(maxWidth, maxHeight, margins) { - var me = this; - var tickOpts = me.options.ticks; - var sampleSize = tickOpts.sampleSize; - var i, ilen, labels, ticks, samplingEnabled; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = helpers$1.extend({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }, margins); - - me._ticks = null; - me.ticks = null; - me._labelSizes = null; - me._maxLabelLines = 0; - me.longestLabelWidth = 0; - me.longestTextCache = me.longestTextCache || {}; - me._gridLineItems = null; - me._labelItems = null; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - - // Data min/max - me.beforeDataLimits(); - me.determineDataLimits(); - me.afterDataLimits(); - - // Ticks - `this.ticks` is now DEPRECATED! - // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member - // and must not be accessed directly from outside this class. `this.ticks` being - // around for long time and not marked as private, we can't change its structure - // without unexpected breaking changes. If you need to access the scale ticks, - // use scale.getTicks() instead. - - me.beforeBuildTicks(); - - // New implementations should return an array of objects but for BACKWARD COMPAT, - // we still support no return (`this.ticks` internally set by calling this method). - ticks = me.buildTicks() || []; - - // Allow modification of ticks in callback. - ticks = me.afterBuildTicks(ticks) || ticks; - - // Ensure ticks contains ticks in new tick format - if ((!ticks || !ticks.length) && me.ticks) { - ticks = []; - for (i = 0, ilen = me.ticks.length; i < ilen; ++i) { - ticks.push({ - value: me.ticks[i], - major: false - }); - } - } - - me._ticks = ticks; - - // Compute tick rotation and fit using a sampled subset of labels - // We generally don't need to compute the size of every single label for determining scale size - samplingEnabled = sampleSize < ticks.length; - labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks); - - // _configure is called twice, once here, once from core.controller.updateLayout. - // Here we haven't been positioned yet, but dimensions are correct. - // Variables set in _configure are needed for calculateTickRotation, and - // it's ok that coordinates are not correct there, only dimensions matter. - me._configure(); - - // Tick Rotation - me.beforeCalculateTickRotation(); - me.calculateTickRotation(); - me.afterCalculateTickRotation(); - - me.beforeFit(); - me.fit(); - me.afterFit(); - - // Auto-skip - me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks; - - if (samplingEnabled) { - // Generate labels using all non-skipped ticks - labels = me._convertTicksToLabels(me._ticksToDraw); - } - - me.ticks = labels; // BACKWARD COMPATIBILITY - - // IMPORTANT: after this point, we consider that `this.ticks` will NEVER change! - - me.afterUpdate(); - - // TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused - // make maxWidth and maxHeight private - return me.minSize; - }, - - /** - * @private - */ - _configure: function() { - var me = this; - var reversePixels = me.options.ticks.reverse; - var startPixel, endPixel; - - if (me.isHorizontal()) { - startPixel = me.left; - endPixel = me.right; - } else { - startPixel = me.top; - endPixel = me.bottom; - // by default vertical scales are from bottom to top, so pixels are reversed - reversePixels = !reversePixels; - } - me._startPixel = startPixel; - me._endPixel = endPixel; - me._reversePixels = reversePixels; - me._length = endPixel - startPixel; - }, - - afterUpdate: function() { - helpers$1.callback(this.options.afterUpdate, [this]); - }, - - // - - beforeSetDimensions: function() { - helpers$1.callback(this.options.beforeSetDimensions, [this]); - }, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - }, - afterSetDimensions: function() { - helpers$1.callback(this.options.afterSetDimensions, [this]); - }, - - // Data limits - beforeDataLimits: function() { - helpers$1.callback(this.options.beforeDataLimits, [this]); - }, - determineDataLimits: helpers$1.noop, - afterDataLimits: function() { - helpers$1.callback(this.options.afterDataLimits, [this]); - }, - - // - beforeBuildTicks: function() { - helpers$1.callback(this.options.beforeBuildTicks, [this]); - }, - buildTicks: helpers$1.noop, - afterBuildTicks: function(ticks) { - var me = this; - // ticks is empty for old axis implementations here - if (isArray(ticks) && ticks.length) { - return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); - } - // Support old implementations (that modified `this.ticks` directly in buildTicks) - me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; - return ticks; - }, - - beforeTickToLabelConversion: function() { - helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); - }, - convertTicksToLabels: function() { - var me = this; - // Convert ticks to strings - var tickOpts = me.options.ticks; - me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); - }, - afterTickToLabelConversion: function() { - helpers$1.callback(this.options.afterTickToLabelConversion, [this]); - }, - - // - - beforeCalculateTickRotation: function() { - helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); - }, - calculateTickRotation: function() { - var me = this; - var options = me.options; - var tickOpts = options.ticks; - var numTicks = me.getTicks().length; - var minRotation = tickOpts.minRotation || 0; - var maxRotation = tickOpts.maxRotation; - var labelRotation = minRotation; - var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal; - - if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) { - me.labelRotation = minRotation; - return; - } - - labelSizes = me._getLabelSizes(); - maxLabelWidth = labelSizes.widest.width; - maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset; - - // Estimate the width of each grid based on the canvas width, the maximum - // label width and the number of tick intervals - maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth); - tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1); - - // Allow 3 pixels x2 padding either side for label readability - if (maxLabelWidth + 6 > tickWidth) { - tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1)); - maxHeight = me.maxHeight - getTickMarkLength(options.gridLines) - - tickOpts.padding - getScaleLabelHeight(options.scaleLabel); - maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight); - labelRotation = helpers$1.toDegrees(Math.min( - Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)), - Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal) - )); - labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation)); - } - - me.labelRotation = labelRotation; - }, - afterCalculateTickRotation: function() { - helpers$1.callback(this.options.afterCalculateTickRotation, [this]); - }, - - // - - beforeFit: function() { - helpers$1.callback(this.options.beforeFit, [this]); - }, - fit: function() { - var me = this; - // Reset - var minSize = me.minSize = { - width: 0, - height: 0 - }; - - var chart = me.chart; - var opts = me.options; - var tickOpts = opts.ticks; - var scaleLabelOpts = opts.scaleLabel; - var gridLineOpts = opts.gridLines; - var display = me._isVisible(); - var isBottom = opts.position === 'bottom'; - var isHorizontal = me.isHorizontal(); - - // Width - if (isHorizontal) { - minSize.width = me.maxWidth; - } else if (display) { - minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); - } - - // height - if (!isHorizontal) { - minSize.height = me.maxHeight; // fill all the height - } else if (display) { - minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts); - } - - // Don't bother fitting the ticks if we are not showing the labels - if (tickOpts.display && display) { - var tickFonts = parseTickFontOptions(tickOpts); - var labelSizes = me._getLabelSizes(); - var firstLabelSize = labelSizes.first; - var lastLabelSize = labelSizes.last; - var widestLabelSize = labelSizes.widest; - var highestLabelSize = labelSizes.highest; - var lineSpace = tickFonts.minor.lineHeight * 0.4; - var tickPadding = tickOpts.padding; - - if (isHorizontal) { - // A horizontal axis is more constrained by the height. - var isRotated = me.labelRotation !== 0; - var angleRadians = helpers$1.toRadians(me.labelRotation); - var cosRotation = Math.cos(angleRadians); - var sinRotation = Math.sin(angleRadians); - - var labelHeight = sinRotation * widestLabelSize.width - + cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0)) - + (isRotated ? 0 : lineSpace); // padding - - minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); - - var offsetLeft = me.getPixelForTick(0) - me.left; - var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1); - var paddingLeft, paddingRight; - - // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned - // which means that the right padding is dominated by the font height - if (isRotated) { - paddingLeft = isBottom ? - cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset : - sinRotation * (firstLabelSize.height - firstLabelSize.offset); - paddingRight = isBottom ? - sinRotation * (lastLabelSize.height - lastLabelSize.offset) : - cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset; - } else { - paddingLeft = firstLabelSize.width / 2; - paddingRight = lastLabelSize.width / 2; - } - - // Adjust padding taking into account changes in offsets - // and add 3 px to move away from canvas edges - me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3; - me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3; - } else { - // A vertical axis is more constrained by the width. Labels are the - // dominant factor here, so get that length first and account for padding - var labelWidth = tickOpts.mirror ? 0 : - // use lineSpace for consistency with horizontal axis - // tickPadding is not implemented for horizontal - widestLabelSize.width + tickPadding + lineSpace; - - minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth); - - me.paddingTop = firstLabelSize.height / 2; - me.paddingBottom = lastLabelSize.height / 2; - } - } - - me.handleMargins(); - - if (isHorizontal) { - me.width = me._length = chart.width - me.margins.left - me.margins.right; - me.height = minSize.height; - } else { - me.width = minSize.width; - me.height = me._length = chart.height - me.margins.top - me.margins.bottom; - } - }, - - /** - * Handle margins and padding interactions - * @private - */ - handleMargins: function() { - var me = this; - if (me.margins) { - me.margins.left = Math.max(me.paddingLeft, me.margins.left); - me.margins.top = Math.max(me.paddingTop, me.margins.top); - me.margins.right = Math.max(me.paddingRight, me.margins.right); - me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom); - } - }, - - afterFit: function() { - helpers$1.callback(this.options.afterFit, [this]); - }, - - // Shared Methods - isHorizontal: function() { - var pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - }, - isFullWidth: function() { - return this.options.fullWidth; - }, - - // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not - getRightValue: function(rawValue) { - // Null and undefined values first - if (isNullOrUndef(rawValue)) { - return NaN; - } - // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values - if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { - return NaN; - } - - // If it is in fact an object, dive in one more level - if (rawValue) { - if (this.isHorizontal()) { - if (rawValue.x !== undefined) { - return this.getRightValue(rawValue.x); - } - } else if (rawValue.y !== undefined) { - return this.getRightValue(rawValue.y); - } - } - - // Value is good, return it - return rawValue; - }, - - _convertTicksToLabels: function(ticks) { - var me = this; - var labels, i, ilen; - - me.ticks = ticks.map(function(tick) { - return tick.value; - }); - - me.beforeTickToLabelConversion(); - - // New implementations should return the formatted tick labels but for BACKWARD - // COMPAT, we still support no return (`this.ticks` internally changed by calling - // this method and supposed to contain only string values). - labels = me.convertTicksToLabels(ticks) || me.ticks; - - me.afterTickToLabelConversion(); - - // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - ticks[i].label = labels[i]; - } - - return labels; - }, - - /** - * @private - */ - _getLabelSizes: function() { - var me = this; - var labelSizes = me._labelSizes; - - if (!labelSizes) { - me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache); - me.longestLabelWidth = labelSizes.widest.width; - } - - return labelSizes; - }, - - /** - * @private - */ - _parseValue: function(value) { - var start, end, min, max; - - if (isArray(value)) { - start = +this.getRightValue(value[0]); - end = +this.getRightValue(value[1]); - min = Math.min(start, end); - max = Math.max(start, end); - } else { - value = +this.getRightValue(value); - start = undefined; - end = value; - min = value; - max = value; - } - - return { - min: min, - max: max, - start: start, - end: end - }; - }, - - /** - * @private - */ - _getScaleLabel: function(rawValue) { - var v = this._parseValue(rawValue); - if (v.start !== undefined) { - return '[' + v.start + ', ' + v.end + ']'; - } - - return +this.getRightValue(rawValue); - }, - - /** - * Used to get the value to display in the tooltip for the data at the given index - * @param index - * @param datasetIndex - */ - getLabelForIndex: helpers$1.noop, - - /** - * Returns the location of the given data point. Value can either be an index or a numerical value - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param value - * @param index - * @param datasetIndex - */ - getPixelForValue: helpers$1.noop, - - /** - * Used to get the data value from a given pixel. This is the inverse of getPixelForValue - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param pixel - */ - getValueForPixel: helpers$1.noop, - - /** - * Returns the location of the tick at the given index - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForTick: function(index) { - var me = this; - var offset = me.options.offset; - var numTicks = me._ticks.length; - var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1); - - return index < 0 || index > numTicks - 1 - ? null - : me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0)); - }, - - /** - * Utility for getting the pixel location of a percentage of scale - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForDecimal: function(decimal) { - var me = this; - - if (me._reversePixels) { - decimal = 1 - decimal; - } - - return me._startPixel + decimal * me._length; - }, - - getDecimalForPixel: function(pixel) { - var decimal = (pixel - this._startPixel) / this._length; - return this._reversePixels ? 1 - decimal : decimal; - }, - - /** - * Returns the pixel for the minimum chart value - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getBasePixel: function() { - return this.getPixelForValue(this.getBaseValue()); - }, - - getBaseValue: function() { - var me = this; - var min = me.min; - var max = me.max; - - return me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0; - }, - - /** - * Returns a subset of ticks to be plotted to avoid overlapping labels. - * @private - */ - _autoSkip: function(ticks) { - var me = this; - var tickOpts = me.options.ticks; - var axisLength = me._length; - var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1; - var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; - var numMajorIndices = majorIndices.length; - var first = majorIndices[0]; - var last = majorIndices[numMajorIndices - 1]; - var i, ilen, spacing, avgMajorSpacing; - - // If there are too many major ticks to display them all - if (numMajorIndices > ticksLimit) { - skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit); - return nonSkipped(ticks); - } - - spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit); - - if (numMajorIndices > 0) { - for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) { - skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]); - } - avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null; - skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); - skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); - return nonSkipped(ticks); - } - skip(ticks, spacing); - return nonSkipped(ticks); - }, - - /** - * @private - */ - _tickSize: function() { - var me = this; - var optionTicks = me.options.ticks; - - // Calculate space needed by label in axis direction. - var rot = helpers$1.toRadians(me.labelRotation); - var cos = Math.abs(Math.cos(rot)); - var sin = Math.abs(Math.sin(rot)); - - var labelSizes = me._getLabelSizes(); - var padding = optionTicks.autoSkipPadding || 0; - var w = labelSizes ? labelSizes.widest.width + padding : 0; - var h = labelSizes ? labelSizes.highest.height + padding : 0; - - // Calculate space needed for 1 tick in axis direction. - return me.isHorizontal() - ? h * cos > w * sin ? w / cos : h / sin - : h * sin < w * cos ? h / cos : w / sin; - }, - - /** - * @private - */ - _isVisible: function() { - var me = this; - var chart = me.chart; - var display = me.options.display; - var i, ilen, meta; - - if (display !== 'auto') { - return !!display; - } - - // When 'auto', the scale is visible if at least one associated dataset is visible. - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - if (meta.xAxisID === me.id || meta.yAxisID === me.id) { - return true; - } - } - } - - return false; - }, - - /** - * @private - */ - _computeGridLineItems: function(chartArea) { - var me = this; - var chart = me.chart; - var options = me.options; - var gridLines = options.gridLines; - var position = options.position; - var offsetGridLines = gridLines.offsetGridLines; - var isHorizontal = me.isHorizontal(); - var ticks = me._ticksToDraw; - var ticksLength = ticks.length + (offsetGridLines ? 1 : 0); - - var tl = getTickMarkLength(gridLines); - var items = []; - var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; - var axisHalfWidth = axisWidth / 2; - var alignPixel = helpers$1._alignPixel; - var alignBorderValue = function(pixel) { - return alignPixel(chart, pixel, axisWidth); - }; - var borderValue, i, tick, lineValue, alignedLineValue; - var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset; - - if (position === 'top') { - borderValue = alignBorderValue(me.bottom); - ty1 = me.bottom - tl; - ty2 = borderValue - axisHalfWidth; - y1 = alignBorderValue(chartArea.top) + axisHalfWidth; - y2 = chartArea.bottom; - } else if (position === 'bottom') { - borderValue = alignBorderValue(me.top); - y1 = chartArea.top; - y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; - ty1 = borderValue + axisHalfWidth; - ty2 = me.top + tl; - } else if (position === 'left') { - borderValue = alignBorderValue(me.right); - tx1 = me.right - tl; - tx2 = borderValue - axisHalfWidth; - x1 = alignBorderValue(chartArea.left) + axisHalfWidth; - x2 = chartArea.right; - } else { - borderValue = alignBorderValue(me.left); - x1 = chartArea.left; - x2 = alignBorderValue(chartArea.right) - axisHalfWidth; - tx1 = borderValue + axisHalfWidth; - tx2 = me.left + tl; - } - - for (i = 0; i < ticksLength; ++i) { - tick = ticks[i] || {}; - - // autoskipper skipped this tick (#4635) - if (isNullOrUndef(tick.label) && i < ticks.length) { - continue; - } - - if (i === me.zeroLineIndex && options.offset === offsetGridLines) { - // Draw the first index specially - lineWidth = gridLines.zeroLineWidth; - lineColor = gridLines.zeroLineColor; - borderDash = gridLines.zeroLineBorderDash || []; - borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; - } else { - lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1); - lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)'); - borderDash = gridLines.borderDash || []; - borderDashOffset = gridLines.borderDashOffset || 0.0; - } - - lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines); - - // Skip if the pixel is out of the range - if (lineValue === undefined) { - continue; - } - - alignedLineValue = alignPixel(chart, lineValue, lineWidth); - - if (isHorizontal) { - tx1 = tx2 = x1 = x2 = alignedLineValue; - } else { - ty1 = ty2 = y1 = y2 = alignedLineValue; - } - - items.push({ - tx1: tx1, - ty1: ty1, - tx2: tx2, - ty2: ty2, - x1: x1, - y1: y1, - x2: x2, - y2: y2, - width: lineWidth, - color: lineColor, - borderDash: borderDash, - borderDashOffset: borderDashOffset, - }); - } - - items.ticksLength = ticksLength; - items.borderValue = borderValue; - - return items; - }, - - /** - * @private - */ - _computeLabelItems: function() { - var me = this; - var options = me.options; - var optionTicks = options.ticks; - var position = options.position; - var isMirrored = optionTicks.mirror; - var isHorizontal = me.isHorizontal(); - var ticks = me._ticksToDraw; - var fonts = parseTickFontOptions(optionTicks); - var tickPadding = optionTicks.padding; - var tl = getTickMarkLength(options.gridLines); - var rotation = -helpers$1.toRadians(me.labelRotation); - var items = []; - var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; - - if (position === 'top') { - y = me.bottom - tl - tickPadding; - textAlign = !rotation ? 'center' : 'left'; - } else if (position === 'bottom') { - y = me.top + tl + tickPadding; - textAlign = !rotation ? 'center' : 'right'; - } else if (position === 'left') { - x = me.right - (isMirrored ? 0 : tl) - tickPadding; - textAlign = isMirrored ? 'left' : 'right'; - } else { - x = me.left + (isMirrored ? 0 : tl) + tickPadding; - textAlign = isMirrored ? 'right' : 'left'; - } - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - tick = ticks[i]; - label = tick.label; - - // autoskipper skipped this tick (#4635) - if (isNullOrUndef(label)) { - continue; - } - - pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset; - font = tick.major ? fonts.major : fonts.minor; - lineHeight = font.lineHeight; - lineCount = isArray(label) ? label.length : 1; - - if (isHorizontal) { - x = pixel; - textOffset = position === 'top' - ? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight - : (!rotation ? 0.5 : 0) * lineHeight; - } else { - y = pixel; - textOffset = (1 - lineCount) * lineHeight / 2; - } - - items.push({ - x: x, - y: y, - rotation: rotation, - label: label, - font: font, - textOffset: textOffset, - textAlign: textAlign - }); - } - - return items; - }, - - /** - * @private - */ - _drawGrid: function(chartArea) { - var me = this; - var gridLines = me.options.gridLines; - - if (!gridLines.display) { - return; - } - - var ctx = me.ctx; - var chart = me.chart; - var alignPixel = helpers$1._alignPixel; - var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; - var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea)); - var width, color, i, ilen, item; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - width = item.width; - color = item.color; - - if (width && color) { - ctx.save(); - ctx.lineWidth = width; - ctx.strokeStyle = color; - if (ctx.setLineDash) { - ctx.setLineDash(item.borderDash); - ctx.lineDashOffset = item.borderDashOffset; - } - - ctx.beginPath(); - - if (gridLines.drawTicks) { - ctx.moveTo(item.tx1, item.ty1); - ctx.lineTo(item.tx2, item.ty2); - } - - if (gridLines.drawOnChartArea) { - ctx.moveTo(item.x1, item.y1); - ctx.lineTo(item.x2, item.y2); - } - - ctx.stroke(); - ctx.restore(); - } - } - - if (axisWidth) { - // Draw the line at the edge of the axis - var firstLineWidth = axisWidth; - var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1); - var borderValue = items.borderValue; - var x1, x2, y1, y2; - - if (me.isHorizontal()) { - x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; - x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; - y1 = y2 = borderValue; - } else { - y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; - y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; - x1 = x2 = borderValue; - } - - ctx.lineWidth = axisWidth; - ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - } - }, - - /** - * @private - */ - _drawLabels: function() { - var me = this; - var optionTicks = me.options.ticks; - - if (!optionTicks.display) { - return; - } - - var ctx = me.ctx; - var items = me._labelItems || (me._labelItems = me._computeLabelItems()); - var i, j, ilen, jlen, item, tickFont, label, y; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - tickFont = item.font; - - // Make sure we draw text in the correct color and font - ctx.save(); - ctx.translate(item.x, item.y); - ctx.rotate(item.rotation); - ctx.font = tickFont.string; - ctx.fillStyle = tickFont.color; - ctx.textBaseline = 'middle'; - ctx.textAlign = item.textAlign; - - label = item.label; - y = item.textOffset; - if (isArray(label)) { - for (j = 0, jlen = label.length; j < jlen; ++j) { - // We just make sure the multiline element is a string here.. - ctx.fillText('' + label[j], 0, y); - y += tickFont.lineHeight; - } - } else { - ctx.fillText(label, 0, y); - } - ctx.restore(); - } - }, - - /** - * @private - */ - _drawTitle: function() { - var me = this; - var ctx = me.ctx; - var options = me.options; - var scaleLabel = options.scaleLabel; - - if (!scaleLabel.display) { - return; - } - - var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor); - var scaleLabelFont = helpers$1.options._parseFont(scaleLabel); - var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); - var halfLineHeight = scaleLabelFont.lineHeight / 2; - var position = options.position; - var rotation = 0; - var scaleLabelX, scaleLabelY; - - if (me.isHorizontal()) { - scaleLabelX = me.left + me.width / 2; // midpoint of the width - scaleLabelY = position === 'bottom' - ? me.bottom - halfLineHeight - scaleLabelPadding.bottom - : me.top + halfLineHeight + scaleLabelPadding.top; - } else { - var isLeft = position === 'left'; - scaleLabelX = isLeft - ? me.left + halfLineHeight + scaleLabelPadding.top - : me.right - halfLineHeight - scaleLabelPadding.top; - scaleLabelY = me.top + me.height / 2; - rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; - } - - ctx.save(); - ctx.translate(scaleLabelX, scaleLabelY); - ctx.rotate(rotation); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = scaleLabelFontColor; // render in correct colour - ctx.font = scaleLabelFont.string; - ctx.fillText(scaleLabel.labelString, 0, 0); - ctx.restore(); - }, - - draw: function(chartArea) { - var me = this; - - if (!me._isVisible()) { - return; - } - - me._drawGrid(chartArea); - me._drawTitle(); - me._drawLabels(); - }, - - /** - * @private - */ - _layers: function() { - var me = this; - var opts = me.options; - var tz = opts.ticks && opts.ticks.z || 0; - var gz = opts.gridLines && opts.gridLines.z || 0; - - if (!me._isVisible() || tz === gz || me.draw !== me._draw) { - // backward compatibility: draw has been overridden by custom scale - return [{ - z: tz, - draw: function() { - me.draw.apply(me, arguments); - } - }]; - } - - return [{ - z: gz, - draw: function() { - me._drawGrid.apply(me, arguments); - me._drawTitle.apply(me, arguments); - } - }, { - z: tz, - draw: function() { - me._drawLabels.apply(me, arguments); - } - }]; - }, - - /** - * @private - */ - _getMatchingVisibleMetas: function(type) { - var me = this; - var isHorizontal = me.isHorizontal(); - return me.chart._getSortedVisibleDatasetMetas() - .filter(function(meta) { - return (!type || meta.type === type) - && (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id); - }); - } -}); - -Scale.prototype._draw = Scale.prototype.draw; - -var core_scale = Scale; - -var isNullOrUndef$1 = helpers$1.isNullOrUndef; - -var defaultConfig = { - position: 'bottom' -}; - -var scale_category = core_scale.extend({ - determineDataLimits: function() { - var me = this; - var labels = me._getLabels(); - var ticksOpts = me.options.ticks; - var min = ticksOpts.min; - var max = ticksOpts.max; - var minIndex = 0; - var maxIndex = labels.length - 1; - var findIndex; - - if (min !== undefined) { - // user specified min value - findIndex = labels.indexOf(min); - if (findIndex >= 0) { - minIndex = findIndex; - } - } - - if (max !== undefined) { - // user specified max value - findIndex = labels.indexOf(max); - if (findIndex >= 0) { - maxIndex = findIndex; - } - } - - me.minIndex = minIndex; - me.maxIndex = maxIndex; - me.min = labels[minIndex]; - me.max = labels[maxIndex]; - }, - - buildTicks: function() { - var me = this; - var labels = me._getLabels(); - var minIndex = me.minIndex; - var maxIndex = me.maxIndex; - - // If we are viewing some subset of labels, slice the original array - me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var chart = me.chart; - - if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { - return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); - } - - return me._getLabels()[index]; - }, - - _configure: function() { - var me = this; - var offset = me.options.offset; - var ticks = me.ticks; - - core_scale.prototype._configure.call(me); - - if (!me.isHorizontal()) { - // For backward compatibility, vertical category scale reverse is inverted. - me._reversePixels = !me._reversePixels; - } - - if (!ticks) { - return; - } - - me._startValue = me.minIndex - (offset ? 0.5 : 0); - me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1); - }, - - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: function(value, index, datasetIndex) { - var me = this; - var valueCategory, labels, idx; - - if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) { - value = me.chart.data.datasets[datasetIndex].data[index]; - } - - // If value is a data object, then index is the index in the data array, - // not the index of the scale. We need to change that. - if (!isNullOrUndef$1(value)) { - valueCategory = me.isHorizontal() ? value.x : value.y; - } - if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { - labels = me._getLabels(); - value = helpers$1.valueOrDefault(valueCategory, value); - idx = labels.indexOf(value); - index = idx !== -1 ? idx : index; - if (isNaN(index)) { - index = value; - } - } - return me.getPixelForDecimal((index - me._startValue) / me._valueRange); - }, - - getPixelForTick: function(index) { - var ticks = this.ticks; - return index < 0 || index > ticks.length - 1 - ? null - : this.getPixelForValue(ticks[index], index + this.minIndex); - }, - - getValueForPixel: function(pixel) { - var me = this; - var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange); - return Math.min(Math.max(value, 0), me.ticks.length - 1); - }, - - getBasePixel: function() { - return this.bottom; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults = defaultConfig; -scale_category._defaults = _defaults; - -var noop = helpers$1.noop; -var isNullOrUndef$2 = helpers$1.isNullOrUndef; - -/** - * Generate a set of linear ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks(generationOptions, dataRange) { - var ticks = []; - // To get a "nice" value for the tick spacing, we will use the appropriately named - // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks - // for details. - - var MIN_SPACING = 1e-14; - var stepSize = generationOptions.stepSize; - var unit = stepSize || 1; - var maxNumSpaces = generationOptions.maxTicks - 1; - var min = generationOptions.min; - var max = generationOptions.max; - var precision = generationOptions.precision; - var rmin = dataRange.min; - var rmax = dataRange.max; - var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; - var factor, niceMin, niceMax, numSpaces; - - // Beyond MIN_SPACING floating point numbers being to lose precision - // such that we can't do the math necessary to generate ticks - if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) { - return [rmin, rmax]; - } - - numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); - if (numSpaces > maxNumSpaces) { - // If the calculated num of spaces exceeds maxNumSpaces, recalculate it - spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; - } - - if (stepSize || isNullOrUndef$2(precision)) { - // If a precision is not specified, calculate factor based on spacing - factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); - } else { - // If the user specified a precision, round to that number of decimal places - factor = Math.pow(10, precision); - spacing = Math.ceil(spacing * factor) / factor; - } - - niceMin = Math.floor(rmin / spacing) * spacing; - niceMax = Math.ceil(rmax / spacing) * spacing; - - // If min, max and stepSize is set and they make an evenly spaced scale use it. - if (stepSize) { - // If very close to our whole number, use it. - if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { - niceMin = min; - } - if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { - niceMax = max; - } - } - - numSpaces = (niceMax - niceMin) / spacing; - // If very close to our rounded value, use it. - if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { - numSpaces = Math.round(numSpaces); - } else { - numSpaces = Math.ceil(numSpaces); - } - - niceMin = Math.round(niceMin * factor) / factor; - niceMax = Math.round(niceMax * factor) / factor; - ticks.push(isNullOrUndef$2(min) ? niceMin : min); - for (var j = 1; j < numSpaces; ++j) { - ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); - } - ticks.push(isNullOrUndef$2(max) ? niceMax : max); - - return ticks; -} - -var scale_linearbase = core_scale.extend({ - getRightValue: function(value) { - if (typeof value === 'string') { - return +value; - } - return core_scale.prototype.getRightValue.call(this, value); - }, - - handleTickRangeOptions: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, - // do nothing since that would make the chart weird. If the user really wants a weird chart - // axis, they can manually override it - if (tickOpts.beginAtZero) { - var minSign = helpers$1.sign(me.min); - var maxSign = helpers$1.sign(me.max); - - if (minSign < 0 && maxSign < 0) { - // move the top up to 0 - me.max = 0; - } else if (minSign > 0 && maxSign > 0) { - // move the bottom down to 0 - me.min = 0; - } - } - - var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; - var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; - - if (tickOpts.min !== undefined) { - me.min = tickOpts.min; - } else if (tickOpts.suggestedMin !== undefined) { - if (me.min === null) { - me.min = tickOpts.suggestedMin; - } else { - me.min = Math.min(me.min, tickOpts.suggestedMin); - } - } - - if (tickOpts.max !== undefined) { - me.max = tickOpts.max; - } else if (tickOpts.suggestedMax !== undefined) { - if (me.max === null) { - me.max = tickOpts.suggestedMax; - } else { - me.max = Math.max(me.max, tickOpts.suggestedMax); - } - } - - if (setMin !== setMax) { - // We set the min or the max but not both. - // So ensure that our range is good - // Inverted or 0 length range can happen when - // ticks.min is set, and no datasets are visible - if (me.min >= me.max) { - if (setMin) { - me.max = me.min + 1; - } else { - me.min = me.max - 1; - } - } - } - - if (me.min === me.max) { - me.max++; - - if (!tickOpts.beginAtZero) { - me.min--; - } - } - }, - - getTickLimit: function() { - var me = this; - var tickOpts = me.options.ticks; - var stepSize = tickOpts.stepSize; - var maxTicksLimit = tickOpts.maxTicksLimit; - var maxTicks; - - if (stepSize) { - maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; - } else { - maxTicks = me._computeTickLimit(); - maxTicksLimit = maxTicksLimit || 11; - } - - if (maxTicksLimit) { - maxTicks = Math.min(maxTicksLimit, maxTicks); - } - - return maxTicks; - }, - - _computeTickLimit: function() { - return Number.POSITIVE_INFINITY; - }, - - handleDirectionalChanges: noop, - - buildTicks: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // Figure out what the max number of ticks we can support it is based on the size of - // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 - // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on - // the graph. Make sure we always have at least 2 ticks - var maxTicks = me.getTickLimit(); - maxTicks = Math.max(2, maxTicks); - - var numericGeneratorOptions = { - maxTicks: maxTicks, - min: tickOpts.min, - max: tickOpts.max, - precision: tickOpts.precision, - stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) - }; - var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); - - me.handleDirectionalChanges(); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - ticks.reverse(); - - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - }, - - convertTicksToLabels: function() { - var me = this; - me.ticksAsNumbers = me.ticks.slice(); - me.zeroLineIndex = me.ticks.indexOf(0); - - core_scale.prototype.convertTicksToLabels.call(me); - }, - - _configure: function() { - var me = this; - var ticks = me.getTicks(); - var start = me.min; - var end = me.max; - var offset; - - core_scale.prototype._configure.call(me); - - if (me.options.offset && ticks.length) { - offset = (end - start) / Math.max(ticks.length - 1, 1) / 2; - start -= offset; - end += offset; - } - me._startValue = start; - me._endValue = end; - me._valueRange = end - start; - } -}); - -var defaultConfig$1 = { - position: 'left', - ticks: { - callback: core_ticks.formatters.linear - } -}; - -var DEFAULT_MIN = 0; -var DEFAULT_MAX = 1; - -function getOrCreateStack(stacks, stacked, meta) { - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - stacked === undefined && meta.stack === undefined ? meta.index : '', - meta.stack - ].join('.'); - - if (stacks[key] === undefined) { - stacks[key] = { - pos: [], - neg: [] - }; - } - - return stacks[key]; -} - -function stackData(scale, stacks, meta, data) { - var opts = scale.options; - var stacked = opts.stacked; - var stack = getOrCreateStack(stacks, stacked, meta); - var pos = stack.pos; - var neg = stack.neg; - var ilen = data.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = scale._parseValue(data[i]); - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { - continue; - } - - pos[i] = pos[i] || 0; - neg[i] = neg[i] || 0; - - if (opts.relativePoints) { - pos[i] = 100; - } else if (value.min < 0 || value.max < 0) { - neg[i] += value.min; - } else { - pos[i] += value.max; - } - } -} - -function updateMinMax(scale, meta, data) { - var ilen = data.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = scale._parseValue(data[i]); - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) { - continue; - } - - scale.min = Math.min(scale.min, value.min); - scale.max = Math.max(scale.max, value.max); - } -} - -var scale_linear = scale_linearbase.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var datasets = chart.data.datasets; - var metasets = me._getMatchingVisibleMetas(); - var hasStacks = opts.stacked; - var stacks = {}; - var ilen = metasets.length; - var i, meta, data, values; - - me.min = Number.POSITIVE_INFINITY; - me.max = Number.NEGATIVE_INFINITY; - - if (hasStacks === undefined) { - for (i = 0; !hasStacks && i < ilen; ++i) { - meta = metasets[i]; - hasStacks = meta.stack !== undefined; - } - } - - for (i = 0; i < ilen; ++i) { - meta = metasets[i]; - data = datasets[meta.index].data; - if (hasStacks) { - stackData(me, stacks, meta, data); - } else { - updateMinMax(me, meta, data); - } - } - - helpers$1.each(stacks, function(stackValues) { - values = stackValues.pos.concat(stackValues.neg); - me.min = Math.min(me.min, helpers$1.min(values)); - me.max = Math.max(me.max, helpers$1.max(values)); - }); - - me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; - me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - me.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - var me = this; - var tickFont; - - if (me.isHorizontal()) { - return Math.ceil(me.width / 40); - } - tickFont = helpers$1.options._parseFont(me.options.ticks); - return Math.ceil(me.height / tickFont.lineHeight); - }, - - // Called after the ticks are built. We need - handleDirectionalChanges: function() { - if (!this.isHorizontal()) { - // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); - } - }, - - getLabelForIndex: function(index, datasetIndex) { - return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); - }, - - // Utils - getPixelForValue: function(value) { - var me = this; - return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange); - }, - - getValueForPixel: function(pixel) { - return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange; - }, - - getPixelForTick: function(index) { - var ticks = this.ticksAsNumbers; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index]); - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$1 = defaultConfig$1; -scale_linear._defaults = _defaults$1; - -var valueOrDefault$b = helpers$1.valueOrDefault; -var log10 = helpers$1.math.log10; - -/** - * Generate a set of logarithmic ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks$1(generationOptions, dataRange) { - var ticks = []; - - var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); - - var endExp = Math.floor(log10(dataRange.max)); - var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); - var exp, significand; - - if (tickVal === 0) { - exp = Math.floor(log10(dataRange.minNotZero)); - significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); - - ticks.push(tickVal); - tickVal = significand * Math.pow(10, exp); - } else { - exp = Math.floor(log10(tickVal)); - significand = Math.floor(tickVal / Math.pow(10, exp)); - } - var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; - - do { - ticks.push(tickVal); - - ++significand; - if (significand === 10) { - significand = 1; - ++exp; - precision = exp >= 0 ? 1 : precision; - } - - tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; - } while (exp < endExp || (exp === endExp && significand < endSignificand)); - - var lastTick = valueOrDefault$b(generationOptions.max, tickVal); - ticks.push(lastTick); - - return ticks; -} - -var defaultConfig$2 = { - position: 'left', - - // label settings - ticks: { - callback: core_ticks.formatters.logarithmic - } -}; - -// TODO(v3): change this to positiveOrDefault -function nonNegativeOrDefault(value, defaultValue) { - return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; -} - -var scale_logarithmic = core_scale.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var datasets = chart.data.datasets; - var isHorizontal = me.isHorizontal(); - function IDMatches(meta) { - return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; - } - var datasetIndex, meta, value, data, i, ilen; - - // Calculate Range - me.min = Number.POSITIVE_INFINITY; - me.max = Number.NEGATIVE_INFINITY; - me.minNotZero = Number.POSITIVE_INFINITY; - - var hasStacks = opts.stacked; - if (hasStacks === undefined) { - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && - meta.stack !== undefined) { - hasStacks = true; - break; - } - } - } - - if (opts.stacked || hasStacks) { - var valuesPerStack = {}; - - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), - meta.stack - ].join('.'); - - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - if (valuesPerStack[key] === undefined) { - valuesPerStack[key] = []; - } - - data = datasets[datasetIndex].data; - for (i = 0, ilen = data.length; i < ilen; i++) { - var values = valuesPerStack[key]; - value = me._parseValue(data[i]); - // invalid, hidden and negative values are ignored - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { - continue; - } - values[i] = values[i] || 0; - values[i] += value.max; - } - } - } - - helpers$1.each(valuesPerStack, function(valuesForType) { - if (valuesForType.length > 0) { - var minVal = helpers$1.min(valuesForType); - var maxVal = helpers$1.max(valuesForType); - me.min = Math.min(me.min, minVal); - me.max = Math.max(me.max, maxVal); - } - }); - - } else { - for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) { - meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - data = datasets[datasetIndex].data; - for (i = 0, ilen = data.length; i < ilen; i++) { - value = me._parseValue(data[i]); - // invalid, hidden and negative values are ignored - if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) { - continue; - } - - me.min = Math.min(value.min, me.min); - me.max = Math.max(value.max, me.max); - - if (value.min !== 0) { - me.minNotZero = Math.min(value.min, me.minNotZero); - } - } - } - } - } - - me.min = helpers$1.isFinite(me.min) ? me.min : null; - me.max = helpers$1.isFinite(me.max) ? me.max : null; - me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null; - - // Common base implementation to handle ticks.min, ticks.max - this.handleTickRangeOptions(); - }, - - handleTickRangeOptions: function() { - var me = this; - var tickOpts = me.options.ticks; - var DEFAULT_MIN = 1; - var DEFAULT_MAX = 10; - - me.min = nonNegativeOrDefault(tickOpts.min, me.min); - me.max = nonNegativeOrDefault(tickOpts.max, me.max); - - if (me.min === me.max) { - if (me.min !== 0 && me.min !== null) { - me.min = Math.pow(10, Math.floor(log10(me.min)) - 1); - me.max = Math.pow(10, Math.floor(log10(me.max)) + 1); - } else { - me.min = DEFAULT_MIN; - me.max = DEFAULT_MAX; - } - } - if (me.min === null) { - me.min = Math.pow(10, Math.floor(log10(me.max)) - 1); - } - if (me.max === null) { - me.max = me.min !== 0 - ? Math.pow(10, Math.floor(log10(me.min)) + 1) - : DEFAULT_MAX; - } - if (me.minNotZero === null) { - if (me.min > 0) { - me.minNotZero = me.min; - } else if (me.max < 1) { - me.minNotZero = Math.pow(10, Math.floor(log10(me.max))); - } else { - me.minNotZero = DEFAULT_MIN; - } - } - }, - - buildTicks: function() { - var me = this; - var tickOpts = me.options.ticks; - var reverse = !me.isHorizontal(); - - var generationOptions = { - min: nonNegativeOrDefault(tickOpts.min), - max: nonNegativeOrDefault(tickOpts.max) - }; - var ticks = me.ticks = generateTicks$1(generationOptions, me); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - reverse = !reverse; - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - if (reverse) { - ticks.reverse(); - } - }, - - convertTicksToLabels: function() { - this.tickValues = this.ticks.slice(); - - core_scale.prototype.convertTicksToLabels.call(this); - }, - - // Get the correct tooltip label - getLabelForIndex: function(index, datasetIndex) { - return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); - }, - - getPixelForTick: function(index) { - var ticks = this.tickValues; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index]); - }, - - /** - * Returns the value of the first tick. - * @param {number} value - The minimum not zero value. - * @return {number} The first tick value. - * @private - */ - _getFirstTickValue: function(value) { - var exp = Math.floor(log10(value)); - var significand = Math.floor(value / Math.pow(10, exp)); - - return significand * Math.pow(10, exp); - }, - - _configure: function() { - var me = this; - var start = me.min; - var offset = 0; - - core_scale.prototype._configure.call(me); - - if (start === 0) { - start = me._getFirstTickValue(me.minNotZero); - offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length; - } - - me._startValue = log10(start); - me._valueOffset = offset; - me._valueRange = (log10(me.max) - log10(start)) / (1 - offset); - }, - - getPixelForValue: function(value) { - var me = this; - var decimal = 0; - - value = +me.getRightValue(value); - - if (value > me.min && value > 0) { - decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset; - } - return me.getPixelForDecimal(decimal); - }, - - getValueForPixel: function(pixel) { - var me = this; - var decimal = me.getDecimalForPixel(pixel); - return decimal === 0 && me.min === 0 - ? 0 - : Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange); - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$2 = defaultConfig$2; -scale_logarithmic._defaults = _defaults$2; - -var valueOrDefault$c = helpers$1.valueOrDefault; -var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; -var resolve$4 = helpers$1.options.resolve; - -var defaultConfig$3 = { - display: true, - - // Boolean - Whether to animate scaling the chart from the centre - animate: true, - position: 'chartArea', - - angleLines: { - display: true, - color: 'rgba(0,0,0,0.1)', - lineWidth: 1, - borderDash: [], - borderDashOffset: 0.0 - }, - - gridLines: { - circular: false - }, - - // label settings - ticks: { - // Boolean - Show a backdrop to the scale label - showLabelBackdrop: true, - - // String - The colour of the label backdrop - backdropColor: 'rgba(255,255,255,0.75)', - - // Number - The backdrop padding above & below the label in pixels - backdropPaddingY: 2, - - // Number - The backdrop padding to the side of the label in pixels - backdropPaddingX: 2, - - callback: core_ticks.formatters.linear - }, - - pointLabels: { - // Boolean - if true, show point labels - display: true, - - // Number - Point label font size in pixels - fontSize: 10, - - // Function - Used to convert point labels - callback: function(label) { - return label; - } - } -}; - -function getTickBackdropHeight(opts) { - var tickOpts = opts.ticks; - - if (tickOpts.display && opts.display) { - return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; - } - return 0; -} - -function measureLabelSize(ctx, lineHeight, label) { - if (helpers$1.isArray(label)) { - return { - w: helpers$1.longestText(ctx, ctx.font, label), - h: label.length * lineHeight - }; - } - - return { - w: ctx.measureText(label).width, - h: lineHeight - }; -} - -function determineLimits(angle, pos, size, min, max) { - if (angle === min || angle === max) { - return { - start: pos - (size / 2), - end: pos + (size / 2) - }; - } else if (angle < min || angle > max) { - return { - start: pos - size, - end: pos - }; - } - - return { - start: pos, - end: pos + size - }; -} - -/** - * Helper function to fit a radial linear scale with point labels - */ -function fitWithPointLabels(scale) { - - // Right, this is really confusing and there is a lot of maths going on here - // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - // - // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - // - // Solution: - // - // We assume the radius of the polygon is half the size of the canvas at first - // at each index we check if the text overlaps. - // - // Where it does, we store that angle and that index. - // - // After finding the largest index and angle we calculate how much we need to remove - // from the shape radius to move the point inwards by that x. - // - // We average the left and right distances to get the maximum shape radius that can fit in the box - // along with labels. - // - // Once we have that, we can find the centre point for the chart, by taking the x text protrusion - // on each side, removing that from the size, halving it and adding the left x protrusion width. - // - // This will mean we have a shape fitted to the canvas, as large as it can be with the labels - // and position it in the most space efficient manner - // - // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - - var plFont = helpers$1.options._parseFont(scale.options.pointLabels); - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var furthestLimits = { - l: 0, - r: scale.width, - t: 0, - b: scale.height - scale.paddingTop - }; - var furthestAngles = {}; - var i, textSize, pointPosition; - - scale.ctx.font = plFont.string; - scale._pointLabelSizes = []; - - var valueCount = scale.chart.data.labels.length; - for (i = 0; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); - textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]); - scale._pointLabelSizes[i] = textSize; - - // Add quarter circle to make degree 0 mean top of circle - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians) % 360; - var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); - var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); - - if (hLimits.start < furthestLimits.l) { - furthestLimits.l = hLimits.start; - furthestAngles.l = angleRadians; - } - - if (hLimits.end > furthestLimits.r) { - furthestLimits.r = hLimits.end; - furthestAngles.r = angleRadians; - } - - if (vLimits.start < furthestLimits.t) { - furthestLimits.t = vLimits.start; - furthestAngles.t = angleRadians; - } - - if (vLimits.end > furthestLimits.b) { - furthestLimits.b = vLimits.end; - furthestAngles.b = angleRadians; - } - } - - scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); -} - -function getTextAlignForAngle(angle) { - if (angle === 0 || angle === 180) { - return 'center'; - } else if (angle < 180) { - return 'left'; - } - - return 'right'; -} - -function fillText(ctx, text, position, lineHeight) { - var y = position.y + lineHeight / 2; - var i, ilen; - - if (helpers$1.isArray(text)) { - for (i = 0, ilen = text.length; i < ilen; ++i) { - ctx.fillText(text[i], position.x, y); - y += lineHeight; - } - } else { - ctx.fillText(text, position.x, y); - } -} - -function adjustPointPositionForLabelHeight(angle, textSize, position) { - if (angle === 90 || angle === 270) { - position.y -= (textSize.h / 2); - } else if (angle > 270 || angle < 90) { - position.y -= textSize.h; - } -} - -function drawPointLabels(scale) { - var ctx = scale.ctx; - var opts = scale.options; - var pointLabelOpts = opts.pointLabels; - var tickBackdropHeight = getTickBackdropHeight(opts); - var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); - var plFont = helpers$1.options._parseFont(pointLabelOpts); - - ctx.save(); - - ctx.font = plFont.string; - ctx.textBaseline = 'middle'; - - for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) { - // Extra pixels out for some label spacing - var extra = (i === 0 ? tickBackdropHeight / 2 : 0); - var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); - - // Keep this in loop since we may support array properties here - var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); - ctx.fillStyle = pointLabelFontColor; - - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians); - ctx.textAlign = getTextAlignForAngle(angle); - adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); - fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight); - } - ctx.restore(); -} - -function drawRadiusLine(scale, gridLineOpts, radius, index) { - var ctx = scale.ctx; - var circular = gridLineOpts.circular; - var valueCount = scale.chart.data.labels.length; - var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); - var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); - var pointPosition; - - if ((!circular && !valueCount) || !lineColor || !lineWidth) { - return; - } - - ctx.save(); - ctx.strokeStyle = lineColor; - ctx.lineWidth = lineWidth; - if (ctx.setLineDash) { - ctx.setLineDash(gridLineOpts.borderDash || []); - ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; - } - - ctx.beginPath(); - if (circular) { - // Draw circular arcs between the points - ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); - } else { - // Draw straight lines connecting each index - pointPosition = scale.getPointPosition(0, radius); - ctx.moveTo(pointPosition.x, pointPosition.y); - - for (var i = 1; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, radius); - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } - ctx.closePath(); - ctx.stroke(); - ctx.restore(); -} - -function numberOrZero(param) { - return helpers$1.isNumber(param) ? param : 0; -} - -var scale_radialLinear = scale_linearbase.extend({ - setDimensions: function() { - var me = this; - - // Set the unconstrained dimension before label rotation - me.width = me.maxWidth; - me.height = me.maxHeight; - me.paddingTop = getTickBackdropHeight(me.options) / 2; - me.xCenter = Math.floor(me.width / 2); - me.yCenter = Math.floor((me.height - me.paddingTop) / 2); - me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var min = Number.POSITIVE_INFINITY; - var max = Number.NEGATIVE_INFINITY; - - helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { - if (chart.isDatasetVisible(datasetIndex)) { - var meta = chart.getDatasetMeta(datasetIndex); - - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { - return; - } - - min = Math.min(value, min); - max = Math.max(value, max); - }); - } - }); - - me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); - me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - me.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); - }, - - convertTicksToLabels: function() { - var me = this; - - scale_linearbase.prototype.convertTicksToLabels.call(me); - - // Point labels - me.pointLabels = me.chart.data.labels.map(function() { - var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me); - return label || label === 0 ? label : ''; - }); - }, - - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - - fit: function() { - var me = this; - var opts = me.options; - - if (opts.display && opts.pointLabels.display) { - fitWithPointLabels(me); - } else { - me.setCenterPoint(0, 0, 0, 0); - } - }, - - /** - * Set radius reductions and determine new radius and center point - * @private - */ - setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { - var me = this; - var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); - var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); - var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); - var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); - - radiusReductionLeft = numberOrZero(radiusReductionLeft); - radiusReductionRight = numberOrZero(radiusReductionRight); - radiusReductionTop = numberOrZero(radiusReductionTop); - radiusReductionBottom = numberOrZero(radiusReductionBottom); - - me.drawingArea = Math.min( - Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), - Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); - me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); - }, - - setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { - var me = this; - var maxRight = me.width - rightMovement - me.drawingArea; - var maxLeft = leftMovement + me.drawingArea; - var maxTop = topMovement + me.drawingArea; - var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; - - me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); - me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); - }, - - getIndexAngle: function(index) { - var chart = this.chart; - var angleMultiplier = 360 / chart.data.labels.length; - var options = chart.options || {}; - var startAngle = options.startAngle || 0; - - // Start from the top instead of right, so remove a quarter of the circle - var angle = (index * angleMultiplier + startAngle) % 360; - - return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360; - }, - - getDistanceFromCenterForValue: function(value) { - var me = this; - - if (helpers$1.isNullOrUndef(value)) { - return NaN; - } - - // Take into account half font size + the yPadding of the top value - var scalingFactor = me.drawingArea / (me.max - me.min); - if (me.options.ticks.reverse) { - return (me.max - value) * scalingFactor; - } - return (value - me.min) * scalingFactor; - }, - - getPointPosition: function(index, distanceFromCenter) { - var me = this; - var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); - return { - x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, - y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter - }; - }, - - getPointPositionForValue: function(index, value) { - return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); - }, - - getBasePosition: function(index) { - var me = this; - var min = me.min; - var max = me.max; - - return me.getPointPositionForValue(index || 0, - me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0); - }, - - /** - * @private - */ - _drawGrid: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - var gridLineOpts = opts.gridLines; - var angleLineOpts = opts.angleLines; - var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth); - var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color); - var i, offset, position; - - if (opts.pointLabels.display) { - drawPointLabels(me); - } - - if (gridLineOpts.display) { - helpers$1.each(me.ticks, function(label, index) { - if (index !== 0) { - offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); - drawRadiusLine(me, gridLineOpts, offset, index); - } - }); - } - - if (angleLineOpts.display && lineWidth && lineColor) { - ctx.save(); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = lineColor; - if (ctx.setLineDash) { - ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); - ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); - } - - for (i = me.chart.data.labels.length - 1; i >= 0; i--) { - offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max); - position = me.getPointPosition(i, offset); - ctx.beginPath(); - ctx.moveTo(me.xCenter, me.yCenter); - ctx.lineTo(position.x, position.y); - ctx.stroke(); - } - - ctx.restore(); - } - }, - - /** - * @private - */ - _drawLabels: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - var tickOpts = opts.ticks; - - if (!tickOpts.display) { - return; - } - - var startAngle = me.getIndexAngle(0); - var tickFont = helpers$1.options._parseFont(tickOpts); - var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor); - var offset, width; - - ctx.save(); - ctx.font = tickFont.string; - ctx.translate(me.xCenter, me.yCenter); - ctx.rotate(startAngle); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - helpers$1.each(me.ticks, function(label, index) { - if (index === 0 && !tickOpts.reverse) { - return; - } - - offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); - - if (tickOpts.showLabelBackdrop) { - width = ctx.measureText(label).width; - ctx.fillStyle = tickOpts.backdropColor; - - ctx.fillRect( - -width / 2 - tickOpts.backdropPaddingX, - -offset - tickFont.size / 2 - tickOpts.backdropPaddingY, - width + tickOpts.backdropPaddingX * 2, - tickFont.size + tickOpts.backdropPaddingY * 2 - ); - } - - ctx.fillStyle = tickFontColor; - ctx.fillText(label, 0, -offset); - }); - - ctx.restore(); - }, - - /** - * @private - */ - _drawTitle: helpers$1.noop -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$3 = defaultConfig$3; -scale_radialLinear._defaults = _defaults$3; - -var deprecated$1 = helpers$1._deprecated; -var resolve$5 = helpers$1.options.resolve; -var valueOrDefault$d = helpers$1.valueOrDefault; - -// Integer constants are from the ES6 spec. -var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; -var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; - -var INTERVALS = { - millisecond: { - common: true, - size: 1, - steps: 1000 - }, - second: { - common: true, - size: 1000, - steps: 60 - }, - minute: { - common: true, - size: 60000, - steps: 60 - }, - hour: { - common: true, - size: 3600000, - steps: 24 - }, - day: { - common: true, - size: 86400000, - steps: 30 - }, - week: { - common: false, - size: 604800000, - steps: 4 - }, - month: { - common: true, - size: 2.628e9, - steps: 12 - }, - quarter: { - common: false, - size: 7.884e9, - steps: 4 - }, - year: { - common: true, - size: 3.154e10 - } -}; - -var UNITS = Object.keys(INTERVALS); - -function sorter(a, b) { - return a - b; -} - -function arrayUnique(items) { - var hash = {}; - var out = []; - var i, ilen, item; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - if (!hash[item]) { - hash[item] = true; - out.push(item); - } - } - - return out; -} - -function getMin(options) { - return helpers$1.valueOrDefault(options.time.min, options.ticks.min); -} - -function getMax(options) { - return helpers$1.valueOrDefault(options.time.max, options.ticks.max); -} - -/** - * Returns an array of {time, pos} objects used to interpolate a specific `time` or position - * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is - * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other - * extremity (left + width or top + height). Note that it would be more optimized to directly - * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need - * to create the lookup table. The table ALWAYS contains at least two items: min and max. - * - * @param {number[]} timestamps - timestamps sorted from lowest to highest. - * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min - * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. - * If 'series', timestamps will be positioned at the same distance from each other. In this - * case, only timestamps that break the time linearity are registered, meaning that in the - * best case, all timestamps are linear, the table contains only min and max. - */ -function buildLookupTable(timestamps, min, max, distribution) { - if (distribution === 'linear' || !timestamps.length) { - return [ - {time: min, pos: 0}, - {time: max, pos: 1} - ]; - } - - var table = []; - var items = [min]; - var i, ilen, prev, curr, next; - - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - curr = timestamps[i]; - if (curr > min && curr < max) { - items.push(curr); - } - } - - items.push(max); - - for (i = 0, ilen = items.length; i < ilen; ++i) { - next = items[i + 1]; - prev = items[i - 1]; - curr = items[i]; - - // only add points that breaks the scale linearity - if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { - table.push({time: curr, pos: i / (ilen - 1)}); - } - } - - return table; -} - -// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ -function lookup(table, key, value) { - var lo = 0; - var hi = table.length - 1; - var mid, i0, i1; - - while (lo >= 0 && lo <= hi) { - mid = (lo + hi) >> 1; - i0 = table[mid - 1] || null; - i1 = table[mid]; - - if (!i0) { - // given value is outside table (before first item) - return {lo: null, hi: i1}; - } else if (i1[key] < value) { - lo = mid + 1; - } else if (i0[key] > value) { - hi = mid - 1; - } else { - return {lo: i0, hi: i1}; - } - } - - // given value is outside table (after last item) - return {lo: i1, hi: null}; -} - -/** - * Linearly interpolates the given source `value` using the table items `skey` values and - * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') - * returns the position for a timestamp equal to 42. If value is out of bounds, values at - * index [0, 1] or [n - 1, n] are used for the interpolation. - */ -function interpolate$1(table, skey, sval, tkey) { - var range = lookup(table, skey, sval); - - // Note: the lookup table ALWAYS contains at least 2 items (min and max) - var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; - var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; - - var span = next[skey] - prev[skey]; - var ratio = span ? (sval - prev[skey]) / span : 0; - var offset = (next[tkey] - prev[tkey]) * ratio; - - return prev[tkey] + offset; -} - -function toTimestamp(scale, input) { - var adapter = scale._adapter; - var options = scale.options.time; - var parser = options.parser; - var format = parser || options.format; - var value = input; - - if (typeof parser === 'function') { - value = parser(value); - } - - // Only parse if its not a timestamp already - if (!helpers$1.isFinite(value)) { - value = typeof format === 'string' - ? adapter.parse(value, format) - : adapter.parse(value); - } - - if (value !== null) { - return +value; - } - - // Labels are in an incompatible format and no `parser` has been provided. - // The user might still use the deprecated `format` option for parsing. - if (!parser && typeof format === 'function') { - value = format(input); - - // `format` could return something else than a timestamp, if so, parse it - if (!helpers$1.isFinite(value)) { - value = adapter.parse(value); - } - } - - return value; -} - -function parse(scale, input) { - if (helpers$1.isNullOrUndef(input)) { - return null; - } - - var options = scale.options.time; - var value = toTimestamp(scale, scale.getRightValue(input)); - if (value === null) { - return value; - } - - if (options.round) { - value = +scale._adapter.startOf(value, options.round); - } - - return value; -} - -/** - * Figures out what unit results in an appropriate number of auto-generated ticks - */ -function determineUnitForAutoTicks(minUnit, min, max, capacity) { - var ilen = UNITS.length; - var i, interval, factor; - - for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { - interval = INTERVALS[UNITS[i]]; - factor = interval.steps ? interval.steps : MAX_INTEGER; - - if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { - return UNITS[i]; - } - } - - return UNITS[ilen - 1]; -} - -/** - * Figures out what unit to format a set of ticks with - */ -function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { - var i, unit; - - for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { - unit = UNITS[i]; - if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { - return unit; - } - } - - return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; -} - -function determineMajorUnit(unit) { - for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { - if (INTERVALS[UNITS[i]].common) { - return UNITS[i]; - } - } -} - -/** - * Generates a maximum of `capacity` timestamps between min and max, rounded to the - * `minor` unit using the given scale time `options`. - * Important: this method can return ticks outside the min and max range, it's the - * responsibility of the calling code to clamp values if needed. - */ -function generate(scale, min, max, capacity) { - var adapter = scale._adapter; - var options = scale.options; - var timeOpts = options.time; - var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); - var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]); - var weekday = minor === 'week' ? timeOpts.isoWeekday : false; - var first = min; - var ticks = []; - var time; - - // For 'week' unit, handle the first day of week option - if (weekday) { - first = +adapter.startOf(first, 'isoWeek', weekday); - } - - // Align first ticks on unit - first = +adapter.startOf(first, weekday ? 'day' : minor); - - // Prevent browser from freezing in case user options request millions of milliseconds - if (adapter.diff(max, min, minor) > 100000 * stepSize) { - throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor; - } - - for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) { - ticks.push(time); - } - - if (time === max || options.bounds === 'ticks') { - ticks.push(time); - } - - return ticks; -} - -/** - * Returns the start and end offsets from edges in the form of {start, end} - * where each value is a relative width to the scale and ranges between 0 and 1. - * They add extra margins on the both sides by scaling down the original scale. - * Offsets are added when the `offset` option is true. - */ -function computeOffsets(table, ticks, min, max, options) { - var start = 0; - var end = 0; - var first, last; - - if (options.offset && ticks.length) { - first = interpolate$1(table, 'time', ticks[0], 'pos'); - if (ticks.length === 1) { - start = 1 - first; - } else { - start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; - } - last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); - if (ticks.length === 1) { - end = last; - } else { - end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; - } - } - - return {start: start, end: end, factor: 1 / (start + 1 + end)}; -} - -function setMajorTicks(scale, ticks, map, majorUnit) { - var adapter = scale._adapter; - var first = +adapter.startOf(ticks[0].value, majorUnit); - var last = ticks[ticks.length - 1].value; - var major, index; - - for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { - index = map[major]; - if (index >= 0) { - ticks[index].major = true; - } - } - return ticks; -} - -function ticksFromTimestamps(scale, values, majorUnit) { - var ticks = []; - var map = {}; - var ilen = values.length; - var i, value; - - for (i = 0; i < ilen; ++i) { - value = values[i]; - map[value] = i; - - ticks.push({ - value: value, - major: false - }); - } - - // We set the major ticks separately from the above loop because calling startOf for every tick - // is expensive when there is a large number of ticks - return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); -} - -var defaultConfig$4 = { - position: 'bottom', - - /** - * Data distribution along the scale: - * - 'linear': data are spread according to their time (distances can vary), - * - 'series': data are spread at the same distance from each other. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - distribution: 'linear', - - /** - * Scale boundary strategy (bypassed by min/max time options) - * - `data`: make sure data are fully visible, ticks outside are removed - * - `ticks`: make sure ticks are fully visible, data outside are truncated - * @see https://github.com/chartjs/Chart.js/pull/4556 - * @since 2.7.0 - */ - bounds: 'data', - - adapters: {}, - time: { - parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment - unit: false, // false == automatic or override with week, month, year, etc. - round: false, // none, or override with week, month, year, etc. - displayFormat: false, // DEPRECATED - isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ - minUnit: 'millisecond', - displayFormats: {} - }, - ticks: { - autoSkip: false, - - /** - * Ticks generation input values: - * - 'auto': generates "optimal" ticks based on scale size and time options. - * - 'data': generates ticks from data (including labels from data {t|x|y} objects). - * - 'labels': generates ticks from user given `data.labels` values ONLY. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - source: 'auto', - - major: { - enabled: false - } - } -}; - -var scale_time = core_scale.extend({ - initialize: function() { - this.mergeTicksOptions(); - core_scale.prototype.initialize.call(this); - }, - - update: function() { - var me = this; - var options = me.options; - var time = options.time || (options.time = {}); - var adapter = me._adapter = new core_adapters._date(options.adapters.date); - - // DEPRECATIONS: output a message only one time per update - deprecated$1('time scale', time.format, 'time.format', 'time.parser'); - deprecated$1('time scale', time.min, 'time.min', 'ticks.min'); - deprecated$1('time scale', time.max, 'time.max', 'ticks.max'); - - // Backward compatibility: before introducing adapter, `displayFormats` was - // supposed to contain *all* unit/string pairs but this can't be resolved - // when loading the scale (adapters are loaded afterward), so let's populate - // missing formats on update - helpers$1.mergeIf(time.displayFormats, adapter.formats()); - - return core_scale.prototype.update.apply(me, arguments); - }, - - /** - * Allows data to be referenced via 't' attribute - */ - getRightValue: function(rawValue) { - if (rawValue && rawValue.t !== undefined) { - rawValue = rawValue.t; - } - return core_scale.prototype.getRightValue.call(this, rawValue); - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var adapter = me._adapter; - var options = me.options; - var unit = options.time.unit || 'day'; - var min = MAX_INTEGER; - var max = MIN_INTEGER; - var timestamps = []; - var datasets = []; - var labels = []; - var i, j, ilen, jlen, data, timestamp, labelsAdded; - var dataLabels = me._getLabels(); - - for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { - labels.push(parse(me, dataLabels[i])); - } - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - data = chart.data.datasets[i].data; - - // Let's consider that all data have the same format. - if (helpers$1.isObject(data[0])) { - datasets[i] = []; - - for (j = 0, jlen = data.length; j < jlen; ++j) { - timestamp = parse(me, data[j]); - timestamps.push(timestamp); - datasets[i][j] = timestamp; - } - } else { - datasets[i] = labels.slice(0); - if (!labelsAdded) { - timestamps = timestamps.concat(labels); - labelsAdded = true; - } - } - } else { - datasets[i] = []; - } - } - - if (labels.length) { - min = Math.min(min, labels[0]); - max = Math.max(max, labels[labels.length - 1]); - } - - if (timestamps.length) { - timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter); - min = Math.min(min, timestamps[0]); - max = Math.max(max, timestamps[timestamps.length - 1]); - } - - min = parse(me, getMin(options)) || min; - max = parse(me, getMax(options)) || max; - - // In case there is no valid min/max, set limits based on unit time option - min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; - max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; - - // Make sure that max is strictly higher than min (required by the lookup table) - me.min = Math.min(min, max); - me.max = Math.max(min + 1, max); - - // PRIVATE - me._table = []; - me._timestamps = { - data: timestamps, - datasets: datasets, - labels: labels - }; - }, - - buildTicks: function() { - var me = this; - var min = me.min; - var max = me.max; - var options = me.options; - var tickOpts = options.ticks; - var timeOpts = options.time; - var timestamps = me._timestamps; - var ticks = []; - var capacity = me.getLabelCapacity(min); - var source = tickOpts.source; - var distribution = options.distribution; - var i, ilen, timestamp; - - if (source === 'data' || (source === 'auto' && distribution === 'series')) { - timestamps = timestamps.data; - } else if (source === 'labels') { - timestamps = timestamps.labels; - } else { - timestamps = generate(me, min, max, capacity); - } - - if (options.bounds === 'ticks' && timestamps.length) { - min = timestamps[0]; - max = timestamps[timestamps.length - 1]; - } - - // Enforce limits with user min/max options - min = parse(me, getMin(options)) || min; - max = parse(me, getMax(options)) || max; - - // Remove ticks outside the min/max range - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - timestamp = timestamps[i]; - if (timestamp >= min && timestamp <= max) { - ticks.push(timestamp); - } - } - - me.min = min; - me.max = max; - - // PRIVATE - // determineUnitForFormatting relies on the number of ticks so we don't use it when - // autoSkip is enabled because we don't yet know what the final number of ticks will be - me._unit = timeOpts.unit || (tickOpts.autoSkip - ? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity) - : determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max)); - me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined - : determineMajorUnit(me._unit); - me._table = buildLookupTable(me._timestamps.data, min, max, distribution); - me._offsets = computeOffsets(me._table, ticks, min, max, options); - - if (tickOpts.reverse) { - ticks.reverse(); - } - - return ticksFromTimestamps(me, ticks, me._majorUnit); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var adapter = me._adapter; - var data = me.chart.data; - var timeOpts = me.options.time; - var label = data.labels && index < data.labels.length ? data.labels[index] : ''; - var value = data.datasets[datasetIndex].data[index]; - - if (helpers$1.isObject(value)) { - label = me.getRightValue(value); - } - if (timeOpts.tooltipFormat) { - return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); - } - if (typeof label === 'string') { - return label; - } - return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); - }, - - /** - * Function to format an individual tick mark - * @private - */ - tickFormatFunction: function(time, index, ticks, format) { - var me = this; - var adapter = me._adapter; - var options = me.options; - var formats = options.time.displayFormats; - var minorFormat = formats[me._unit]; - var majorUnit = me._majorUnit; - var majorFormat = formats[majorUnit]; - var tick = ticks[index]; - var tickOpts = options.ticks; - var major = majorUnit && majorFormat && tick && tick.major; - var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); - var nestedTickOpts = major ? tickOpts.major : tickOpts.minor; - var formatter = resolve$5([ - nestedTickOpts.callback, - nestedTickOpts.userCallback, - tickOpts.callback, - tickOpts.userCallback - ]); - - return formatter ? formatter(label, index, ticks) : label; - }, - - convertTicksToLabels: function(ticks) { - var labels = []; - var i, ilen; - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); - } - - return labels; - }, - - /** - * @private - */ - getPixelForOffset: function(time) { - var me = this; - var offsets = me._offsets; - var pos = interpolate$1(me._table, 'time', time, 'pos'); - return me.getPixelForDecimal((offsets.start + pos) * offsets.factor); - }, - - getPixelForValue: function(value, index, datasetIndex) { - var me = this; - var time = null; - - if (index !== undefined && datasetIndex !== undefined) { - time = me._timestamps.datasets[datasetIndex][index]; - } - - if (time === null) { - time = parse(me, value); - } - - if (time !== null) { - return me.getPixelForOffset(time); - } - }, - - getPixelForTick: function(index) { - var ticks = this.getTicks(); - return index >= 0 && index < ticks.length ? - this.getPixelForOffset(ticks[index].value) : - null; - }, - - getValueForPixel: function(pixel) { - var me = this; - var offsets = me._offsets; - var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end; - var time = interpolate$1(me._table, 'pos', pos, 'time'); - - // DEPRECATION, we should return time directly - return me._adapter._create(time); - }, - - /** - * @private - */ - _getLabelSize: function(label) { - var me = this; - var ticksOpts = me.options.ticks; - var tickLabelWidth = me.ctx.measureText(label).width; - var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); - var cosRotation = Math.cos(angle); - var sinRotation = Math.sin(angle); - var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize); - - return { - w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation), - h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation) - }; - }, - - /** - * Crude approximation of what the label width might be - * @private - */ - getLabelWidth: function(label) { - return this._getLabelSize(label).w; - }, - - /** - * @private - */ - getLabelCapacity: function(exampleTime) { - var me = this; - var timeOpts = me.options.time; - var displayFormats = timeOpts.displayFormats; - - // pick the longest format (milliseconds) for guestimation - var format = displayFormats[timeOpts.unit] || displayFormats.millisecond; - var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format); - var size = me._getLabelSize(exampleLabel); - var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h); - - if (me.options.offset) { - capacity--; - } - - return capacity > 0 ? capacity : 1; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$4 = defaultConfig$4; -scale_time._defaults = _defaults$4; - -var scales = { - category: scale_category, - linear: scale_linear, - logarithmic: scale_logarithmic, - radialLinear: scale_radialLinear, - time: scale_time -}; - -var FORMATS = { - datetime: 'MMM D, YYYY, h:mm:ss a', - millisecond: 'h:mm:ss.SSS a', - second: 'h:mm:ss a', - minute: 'h:mm a', - hour: 'hA', - day: 'MMM D', - week: 'll', - month: 'MMM YYYY', - quarter: '[Q]Q - YYYY', - year: 'YYYY' -}; - -core_adapters._date.override(typeof moment === 'function' ? { - _id: 'moment', // DEBUG ONLY - - formats: function() { - return FORMATS; - }, - - parse: function(value, format) { - if (typeof value === 'string' && typeof format === 'string') { - value = moment(value, format); - } else if (!(value instanceof moment)) { - value = moment(value); - } - return value.isValid() ? value.valueOf() : null; - }, - - format: function(time, format) { - return moment(time).format(format); - }, - - add: function(time, amount, unit) { - return moment(time).add(amount, unit).valueOf(); - }, - - diff: function(max, min, unit) { - return moment(max).diff(moment(min), unit); - }, - - startOf: function(time, unit, weekday) { - time = moment(time); - if (unit === 'isoWeek') { - return time.isoWeekday(weekday).valueOf(); - } - return time.startOf(unit).valueOf(); - }, - - endOf: function(time, unit) { - return moment(time).endOf(unit).valueOf(); - }, - - // DEPRECATIONS - - /** - * Provided for backward compatibility with scale.getValueForPixel(). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(time) { - return moment(time); - }, -} : {}); - -core_defaults._set('global', { - plugins: { - filler: { - propagate: true - } - } -}); - -var mappers = { - dataset: function(source) { - var index = source.fill; - var chart = source.chart; - var meta = chart.getDatasetMeta(index); - var visible = meta && chart.isDatasetVisible(index); - var points = (visible && meta.dataset._children) || []; - var length = points.length || 0; - - return !length ? null : function(point, i) { - return (i < length && points[i]._view) || null; - }; - }, - - boundary: function(source) { - var boundary = source.boundary; - var x = boundary ? boundary.x : null; - var y = boundary ? boundary.y : null; - - if (helpers$1.isArray(boundary)) { - return function(point, i) { - return boundary[i]; - }; - } - - return function(point) { - return { - x: x === null ? point.x : x, - y: y === null ? point.y : y, - }; - }; - } -}; - -// @todo if (fill[0] === '#') -function decodeFill(el, index, count) { - var model = el._model || {}; - var fill = model.fill; - var target; - - if (fill === undefined) { - fill = !!model.backgroundColor; - } - - if (fill === false || fill === null) { - return false; - } - - if (fill === true) { - return 'origin'; - } - - target = parseFloat(fill, 10); - if (isFinite(target) && Math.floor(target) === target) { - if (fill[0] === '-' || fill[0] === '+') { - target = index + target; - } - - if (target === index || target < 0 || target >= count) { - return false; - } - - return target; - } - - switch (fill) { - // compatibility - case 'bottom': - return 'start'; - case 'top': - return 'end'; - case 'zero': - return 'origin'; - // supported boundaries - case 'origin': - case 'start': - case 'end': - return fill; - // invalid fill values - default: - return false; - } -} - -function computeLinearBoundary(source) { - var model = source.el._model || {}; - var scale = source.el._scale || {}; - var fill = source.fill; - var target = null; - var horizontal; - - if (isFinite(fill)) { - return null; - } - - // Backward compatibility: until v3, we still need to support boundary values set on - // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and - // controllers might still use it (e.g. the Smith chart). - - if (fill === 'start') { - target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; - } else if (fill === 'end') { - target = model.scaleTop === undefined ? scale.top : model.scaleTop; - } else if (model.scaleZero !== undefined) { - target = model.scaleZero; - } else if (scale.getBasePixel) { - target = scale.getBasePixel(); - } - - if (target !== undefined && target !== null) { - if (target.x !== undefined && target.y !== undefined) { - return target; - } - - if (helpers$1.isFinite(target)) { - horizontal = scale.isHorizontal(); - return { - x: horizontal ? target : null, - y: horizontal ? null : target - }; - } - } - - return null; -} - -function computeCircularBoundary(source) { - var scale = source.el._scale; - var options = scale.options; - var length = scale.chart.data.labels.length; - var fill = source.fill; - var target = []; - var start, end, center, i, point; - - if (!length) { - return null; - } - - start = options.ticks.reverse ? scale.max : scale.min; - end = options.ticks.reverse ? scale.min : scale.max; - center = scale.getPointPositionForValue(0, start); - for (i = 0; i < length; ++i) { - point = fill === 'start' || fill === 'end' - ? scale.getPointPositionForValue(i, fill === 'start' ? start : end) - : scale.getBasePosition(i); - if (options.gridLines.circular) { - point.cx = center.x; - point.cy = center.y; - point.angle = scale.getIndexAngle(i) - Math.PI / 2; - } - target.push(point); - } - return target; -} - -function computeBoundary(source) { - var scale = source.el._scale || {}; - - if (scale.getPointPositionForValue) { - return computeCircularBoundary(source); - } - return computeLinearBoundary(source); -} - -function resolveTarget(sources, index, propagate) { - var source = sources[index]; - var fill = source.fill; - var visited = [index]; - var target; - - if (!propagate) { - return fill; - } - - while (fill !== false && visited.indexOf(fill) === -1) { - if (!isFinite(fill)) { - return fill; - } - - target = sources[fill]; - if (!target) { - return false; - } - - if (target.visible) { - return fill; - } - - visited.push(fill); - fill = target.fill; - } - - return false; -} - -function createMapper(source) { - var fill = source.fill; - var type = 'dataset'; - - if (fill === false) { - return null; - } - - if (!isFinite(fill)) { - type = 'boundary'; - } - - return mappers[type](source); -} - -function isDrawable(point) { - return point && !point.skip; -} - -function drawArea(ctx, curve0, curve1, len0, len1) { - var i, cx, cy, r; - - if (!len0 || !len1) { - return; - } - - // building first area curve (normal) - ctx.moveTo(curve0[0].x, curve0[0].y); - for (i = 1; i < len0; ++i) { - helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); - } - - if (curve1[0].angle !== undefined) { - cx = curve1[0].cx; - cy = curve1[0].cy; - r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2)); - for (i = len1 - 1; i > 0; --i) { - ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true); - } - return; - } - - // joining the two area curves - ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); - - // building opposite area curve (reverse) - for (i = len1 - 1; i > 0; --i) { - helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); - } -} - -function doFill(ctx, points, mapper, view, color, loop) { - var count = points.length; - var span = view.spanGaps; - var curve0 = []; - var curve1 = []; - var len0 = 0; - var len1 = 0; - var i, ilen, index, p0, p1, d0, d1, loopOffset; - - ctx.beginPath(); - - for (i = 0, ilen = count; i < ilen; ++i) { - index = i % count; - p0 = points[index]._view; - p1 = mapper(p0, index, view); - d0 = isDrawable(p0); - d1 = isDrawable(p1); - - if (loop && loopOffset === undefined && d0) { - loopOffset = i + 1; - ilen = count + loopOffset; - } - - if (d0 && d1) { - len0 = curve0.push(p0); - len1 = curve1.push(p1); - } else if (len0 && len1) { - if (!span) { - drawArea(ctx, curve0, curve1, len0, len1); - len0 = len1 = 0; - curve0 = []; - curve1 = []; - } else { - if (d0) { - curve0.push(p0); - } - if (d1) { - curve1.push(p1); - } - } - } - } - - drawArea(ctx, curve0, curve1, len0, len1); - - ctx.closePath(); - ctx.fillStyle = color; - ctx.fill(); -} - -var plugin_filler = { - id: 'filler', - - afterDatasetsUpdate: function(chart, options) { - var count = (chart.data.datasets || []).length; - var propagate = options.propagate; - var sources = []; - var meta, i, el, source; - - for (i = 0; i < count; ++i) { - meta = chart.getDatasetMeta(i); - el = meta.dataset; - source = null; - - if (el && el._model && el instanceof elements.Line) { - source = { - visible: chart.isDatasetVisible(i), - fill: decodeFill(el, i, count), - chart: chart, - el: el - }; - } - - meta.$filler = source; - sources.push(source); - } - - for (i = 0; i < count; ++i) { - source = sources[i]; - if (!source) { - continue; - } - - source.fill = resolveTarget(sources, i, propagate); - source.boundary = computeBoundary(source); - source.mapper = createMapper(source); - } - }, - - beforeDatasetsDraw: function(chart) { - var metasets = chart._getSortedVisibleDatasetMetas(); - var ctx = chart.ctx; - var meta, i, el, view, points, mapper, color; - - for (i = metasets.length - 1; i >= 0; --i) { - meta = metasets[i].$filler; - - if (!meta || !meta.visible) { - continue; - } - - el = meta.el; - view = el._view; - points = el._children || []; - mapper = meta.mapper; - color = view.backgroundColor || core_defaults.global.defaultColor; - - if (mapper && color && points.length) { - helpers$1.canvas.clipArea(ctx, chart.chartArea); - doFill(ctx, points, mapper, view, color, el._loop); - helpers$1.canvas.unclipArea(ctx); - } - } - } -}; - -var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter; -var noop$1 = helpers$1.noop; -var valueOrDefault$e = helpers$1.valueOrDefault; - -core_defaults._set('global', { - legend: { - display: true, - position: 'top', - align: 'center', - fullWidth: true, - reverse: false, - weight: 1000, - - // a callback that will handle - onClick: function(e, legendItem) { - var index = legendItem.datasetIndex; - var ci = this.chart; - var meta = ci.getDatasetMeta(index); - - // See controller.isDatasetVisible comment - meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; - - // We hid a dataset ... rerender the chart - ci.update(); - }, - - onHover: null, - onLeave: null, - - labels: { - boxWidth: 40, - padding: 10, - // Generates labels shown in the legend - // Valid properties to return: - // text : text to display - // fillStyle : fill of coloured box - // strokeStyle: stroke of coloured box - // hidden : if this legend item refers to a hidden item - // lineCap : cap style for line - // lineDash - // lineDashOffset : - // lineJoin : - // lineWidth : - generateLabels: function(chart) { - var datasets = chart.data.datasets; - var options = chart.options.legend || {}; - var usePointStyle = options.labels && options.labels.usePointStyle; - - return chart._getSortedDatasetMetas().map(function(meta) { - var style = meta.controller.getStyle(usePointStyle ? 0 : undefined); - - return { - text: datasets[meta.index].label, - fillStyle: style.backgroundColor, - hidden: !chart.isDatasetVisible(meta.index), - lineCap: style.borderCapStyle, - lineDash: style.borderDash, - lineDashOffset: style.borderDashOffset, - lineJoin: style.borderJoinStyle, - lineWidth: style.borderWidth, - strokeStyle: style.borderColor, - pointStyle: style.pointStyle, - rotation: style.rotation, - - // Below is extra data used for toggling the datasets - datasetIndex: meta.index - }; - }, this); - } - } - }, - - legendCallback: function(chart) { - var list = document.createElement('ul'); - var datasets = chart.data.datasets; - var i, ilen, listItem, listItemSpan; - - list.setAttribute('class', chart.id + '-legend'); - - for (i = 0, ilen = datasets.length; i < ilen; i++) { - listItem = list.appendChild(document.createElement('li')); - listItemSpan = listItem.appendChild(document.createElement('span')); - listItemSpan.style.backgroundColor = datasets[i].backgroundColor; - if (datasets[i].label) { - listItem.appendChild(document.createTextNode(datasets[i].label)); - } - } - - return list.outerHTML; - } -}); - -/** - * Helper function to get the box width based on the usePointStyle option - * @param {object} labelopts - the label options on the legend - * @param {number} fontSize - the label font size - * @return {number} width of the color box area - */ -function getBoxWidth(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? - fontSize : - labelOpts.boxWidth; -} - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Legend = core_element.extend({ - - initialize: function(config) { - var me = this; - helpers$1.extend(me, config); - - // Contains hit boxes for each dataset (in dataset order) - me.legendHitBoxes = []; - - /** - * @private - */ - me._hoveredItem = null; - - // Are we in doughnut mode which has a different data type - me.doughnutMode = false; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - // Any function defined here is inherited by all legend types. - // Any function can be extended by the legend type - - beforeUpdate: noop$1, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - }, - afterUpdate: noop$1, - - // - - beforeSetDimensions: noop$1, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$1, - - // - - beforeBuildLabels: noop$1, - buildLabels: function() { - var me = this; - var labelOpts = me.options.labels || {}; - var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; - - if (labelOpts.filter) { - legendItems = legendItems.filter(function(item) { - return labelOpts.filter(item, me.chart.data); - }); - } - - if (me.options.reverse) { - legendItems.reverse(); - } - - me.legendItems = legendItems; - }, - afterBuildLabels: noop$1, - - // - - beforeFit: noop$1, - fit: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var display = opts.display; - - var ctx = me.ctx; - - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - - // Reset hit boxes - var hitboxes = me.legendHitBoxes = []; - - var minSize = me.minSize; - var isHorizontal = me.isHorizontal(); - - if (isHorizontal) { - minSize.width = me.maxWidth; // fill all the width - minSize.height = display ? 10 : 0; - } else { - minSize.width = display ? 10 : 0; - minSize.height = me.maxHeight; // fill all the height - } - - // Increase sizes here - if (!display) { - me.width = minSize.width = me.height = minSize.height = 0; - return; - } - ctx.font = labelFont.string; - - if (isHorizontal) { - // Labels - - // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one - var lineWidths = me.lineWidths = [0]; - var totalHeight = 0; - - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { - totalHeight += fontSize + labelOpts.padding; - lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; - } - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: width, - height: fontSize - }; - - lineWidths[lineWidths.length - 1] += width + labelOpts.padding; - }); - - minSize.height += totalHeight; - - } else { - var vPadding = labelOpts.padding; - var columnWidths = me.columnWidths = []; - var columnHeights = me.columnHeights = []; - var totalWidth = labelOpts.padding; - var currentColWidth = 0; - var currentColHeight = 0; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - // If too tall, go to new column - if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) { - totalWidth += currentColWidth + labelOpts.padding; - columnWidths.push(currentColWidth); // previous column width - columnHeights.push(currentColHeight); - currentColWidth = 0; - currentColHeight = 0; - } - - // Get max width - currentColWidth = Math.max(currentColWidth, itemWidth); - currentColHeight += fontSize + vPadding; - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: itemWidth, - height: fontSize - }; - }); - - totalWidth += currentColWidth; - columnWidths.push(currentColWidth); - columnHeights.push(currentColHeight); - minSize.width += totalWidth; - } - - me.width = minSize.width; - me.height = minSize.height; - }, - afterFit: noop$1, - - // Shared Methods - isHorizontal: function() { - return this.options.position === 'top' || this.options.position === 'bottom'; - }, - - // Actually draw the legend on the canvas - draw: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; - var lineDefault = globalDefaults.elements.line; - var legendHeight = me.height; - var columnHeights = me.columnHeights; - var legendWidth = me.width; - var lineWidths = me.lineWidths; - - if (!opts.display) { - return; - } - - var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width); - var ctx = me.ctx; - var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor); - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - var cursor; - - // Canvas setup - ctx.textAlign = rtlHelper.textAlign('left'); - ctx.textBaseline = 'middle'; - ctx.lineWidth = 0.5; - ctx.strokeStyle = fontColor; // for strikethrough effect - ctx.fillStyle = fontColor; // render in correct colour - ctx.font = labelFont.string; - - var boxWidth = getBoxWidth(labelOpts, fontSize); - var hitboxes = me.legendHitBoxes; - - // current position - var drawLegendBox = function(x, y, legendItem) { - if (isNaN(boxWidth) || boxWidth <= 0) { - return; - } - - // Set the ctx for the box - ctx.save(); - - var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth); - ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor); - ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle); - ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset); - ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor); - - if (ctx.setLineDash) { - // IE 9 and 10 do not support line dash - ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash)); - } - - if (labelOpts && labelOpts.usePointStyle) { - // Recalculate x and y for drawPoint() because its expecting - // x and y to be center of figure (instead of top left) - var radius = boxWidth * Math.SQRT2 / 2; - var centerX = rtlHelper.xPlus(x, boxWidth / 2); - var centerY = y + fontSize / 2; - - // Draw pointStyle as legend symbol - helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); - } else { - // Draw box as legend symbol - ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); - if (lineWidth !== 0) { - ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); - } - } - - ctx.restore(); - }; - - var fillText = function(x, y, legendItem, textWidth) { - var halfFontSize = fontSize / 2; - var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); - var yMiddle = y + halfFontSize; - - ctx.fillText(legendItem.text, xLeft, yMiddle); - - if (legendItem.hidden) { - // Strikethrough the text if hidden - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.moveTo(xLeft, yMiddle); - ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle); - ctx.stroke(); - } - }; - - var alignmentOffset = function(dimension, blockSize) { - switch (opts.align) { - case 'start': - return labelOpts.padding; - case 'end': - return dimension - blockSize; - default: // center - return (dimension - blockSize + labelOpts.padding) / 2; - } - }; - - // Horizontal - var isHorizontal = me.isHorizontal(); - if (isHorizontal) { - cursor = { - x: me.left + alignmentOffset(legendWidth, lineWidths[0]), - y: me.top + labelOpts.padding, - line: 0 - }; - } else { - cursor = { - x: me.left + labelOpts.padding, - y: me.top + alignmentOffset(legendHeight, columnHeights[0]), - line: 0 - }; - } - - helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection); - - var itemHeight = fontSize + labelOpts.padding; - helpers$1.each(me.legendItems, function(legendItem, i) { - var textWidth = ctx.measureText(legendItem.text).width; - var width = boxWidth + (fontSize / 2) + textWidth; - var x = cursor.x; - var y = cursor.y; - - rtlHelper.setWidth(me.minSize.width); - - // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) - // instead of me.right and me.bottom because me.width and me.height - // may have been changed since me.minSize was calculated - if (isHorizontal) { - if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { - y = cursor.y += itemHeight; - cursor.line++; - x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]); - } - } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { - x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; - cursor.line++; - y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]); - } - - var realX = rtlHelper.x(x); - - drawLegendBox(realX, y, legendItem); - - hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width); - hitboxes[i].top = y; - - // Fill the actual label - fillText(realX, y, legendItem, textWidth); - - if (isHorizontal) { - cursor.x += width + labelOpts.padding; - } else { - cursor.y += itemHeight; - } - }); - - helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection); - }, - - /** - * @private - */ - _getLegendItemAt: function(x, y) { - var me = this; - var i, hitBox, lh; - - if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { - // See if we are touching one of the dataset boxes - lh = me.legendHitBoxes; - for (i = 0; i < lh.length; ++i) { - hitBox = lh[i]; - - if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { - // Touching an element - return me.legendItems[i]; - } - } - } - - return null; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - */ - handleEvent: function(e) { - var me = this; - var opts = me.options; - var type = e.type === 'mouseup' ? 'click' : e.type; - var hoveredItem; - - if (type === 'mousemove') { - if (!opts.onHover && !opts.onLeave) { - return; - } - } else if (type === 'click') { - if (!opts.onClick) { - return; - } - } else { - return; - } - - // Chart event already has relative position in it - hoveredItem = me._getLegendItemAt(e.x, e.y); - - if (type === 'click') { - if (hoveredItem && opts.onClick) { - // use e.native for backwards compatibility - opts.onClick.call(me, e.native, hoveredItem); - } - } else { - if (opts.onLeave && hoveredItem !== me._hoveredItem) { - if (me._hoveredItem) { - opts.onLeave.call(me, e.native, me._hoveredItem); - } - me._hoveredItem = hoveredItem; - } - - if (opts.onHover && hoveredItem) { - // use e.native for backwards compatibility - opts.onHover.call(me, e.native, hoveredItem); - } - } - } -}); - -function createNewLegendAndAttach(chart, legendOpts) { - var legend = new Legend({ - ctx: chart.ctx, - options: legendOpts, - chart: chart - }); - - core_layouts.configure(chart, legend, legendOpts); - core_layouts.addBox(chart, legend); - chart.legend = legend; -} - -var plugin_legend = { - id: 'legend', - - /** - * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making - * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Legend, - - beforeInit: function(chart) { - var legendOpts = chart.options.legend; - - if (legendOpts) { - createNewLegendAndAttach(chart, legendOpts); - } - }, - - beforeUpdate: function(chart) { - var legendOpts = chart.options.legend; - var legend = chart.legend; - - if (legendOpts) { - helpers$1.mergeIf(legendOpts, core_defaults.global.legend); - - if (legend) { - core_layouts.configure(chart, legend, legendOpts); - legend.options = legendOpts; - } else { - createNewLegendAndAttach(chart, legendOpts); - } - } else if (legend) { - core_layouts.removeBox(chart, legend); - delete chart.legend; - } - }, - - afterEvent: function(chart, e) { - var legend = chart.legend; - if (legend) { - legend.handleEvent(e); - } - } -}; - -var noop$2 = helpers$1.noop; - -core_defaults._set('global', { - title: { - display: false, - fontStyle: 'bold', - fullWidth: true, - padding: 10, - position: 'top', - text: '', - weight: 2000 // by default greater than legend (1000) to be above - } -}); - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Title = core_element.extend({ - initialize: function(config) { - var me = this; - helpers$1.extend(me, config); - - // Contains hit boxes for each dataset (in dataset order) - me.legendHitBoxes = []; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - - beforeUpdate: noop$2, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - - }, - afterUpdate: noop$2, - - // - - beforeSetDimensions: noop$2, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$2, - - // - - beforeBuildLabels: noop$2, - buildLabels: noop$2, - afterBuildLabels: noop$2, - - // - - beforeFit: noop$2, - fit: function() { - var me = this; - var opts = me.options; - var minSize = me.minSize = {}; - var isHorizontal = me.isHorizontal(); - var lineCount, textSize; - - if (!opts.display) { - me.width = minSize.width = me.height = minSize.height = 0; - return; - } - - lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; - textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2; - - me.width = minSize.width = isHorizontal ? me.maxWidth : textSize; - me.height = minSize.height = isHorizontal ? textSize : me.maxHeight; - }, - afterFit: noop$2, - - // Shared Methods - isHorizontal: function() { - var pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - }, - - // Actually draw the title block on the canvas - draw: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - - if (!opts.display) { - return; - } - - var fontOpts = helpers$1.options._parseFont(opts); - var lineHeight = fontOpts.lineHeight; - var offset = lineHeight / 2 + opts.padding; - var rotation = 0; - var top = me.top; - var left = me.left; - var bottom = me.bottom; - var right = me.right; - var maxWidth, titleX, titleY; - - ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour - ctx.font = fontOpts.string; - - // Horizontal - if (me.isHorizontal()) { - titleX = left + ((right - left) / 2); // midpoint of the width - titleY = top + offset; - maxWidth = right - left; - } else { - titleX = opts.position === 'left' ? left + offset : right - offset; - titleY = top + ((bottom - top) / 2); - maxWidth = bottom - top; - rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); - } - - ctx.save(); - ctx.translate(titleX, titleY); - ctx.rotate(rotation); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - var text = opts.text; - if (helpers$1.isArray(text)) { - var y = 0; - for (var i = 0; i < text.length; ++i) { - ctx.fillText(text[i], 0, y, maxWidth); - y += lineHeight; - } - } else { - ctx.fillText(text, 0, 0, maxWidth); - } - - ctx.restore(); - } -}); - -function createNewTitleBlockAndAttach(chart, titleOpts) { - var title = new Title({ - ctx: chart.ctx, - options: titleOpts, - chart: chart - }); - - core_layouts.configure(chart, title, titleOpts); - core_layouts.addBox(chart, title); - chart.titleBlock = title; -} - -var plugin_title = { - id: 'title', - - /** - * Backward compatibility: since 2.1.5, the title is registered as a plugin, making - * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Title, - - beforeInit: function(chart) { - var titleOpts = chart.options.title; - - if (titleOpts) { - createNewTitleBlockAndAttach(chart, titleOpts); - } - }, - - beforeUpdate: function(chart) { - var titleOpts = chart.options.title; - var titleBlock = chart.titleBlock; - - if (titleOpts) { - helpers$1.mergeIf(titleOpts, core_defaults.global.title); - - if (titleBlock) { - core_layouts.configure(chart, titleBlock, titleOpts); - titleBlock.options = titleOpts; - } else { - createNewTitleBlockAndAttach(chart, titleOpts); - } - } else if (titleBlock) { - core_layouts.removeBox(chart, titleBlock); - delete chart.titleBlock; - } - } -}; - -var plugins = {}; -var filler = plugin_filler; -var legend = plugin_legend; -var title = plugin_title; -plugins.filler = filler; -plugins.legend = legend; -plugins.title = title; - -/** - * @namespace Chart - */ - - -core_controller.helpers = helpers$1; - -// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! -core_helpers(); - -core_controller._adapters = core_adapters; -core_controller.Animation = core_animation; -core_controller.animationService = core_animations; -core_controller.controllers = controllers; -core_controller.DatasetController = core_datasetController; -core_controller.defaults = core_defaults; -core_controller.Element = core_element; -core_controller.elements = elements; -core_controller.Interaction = core_interaction; -core_controller.layouts = core_layouts; -core_controller.platform = platform; -core_controller.plugins = core_plugins; -core_controller.Scale = core_scale; -core_controller.scaleService = core_scaleService; -core_controller.Ticks = core_ticks; -core_controller.Tooltip = core_tooltip; - -// Register built-in scales - -core_controller.helpers.each(scales, function(scale, type) { - core_controller.scaleService.registerScaleType(type, scale, scale._defaults); -}); - -// Load to register built-in adapters (as side effects) - - -// Loading built-in plugins - -for (var k in plugins) { - if (plugins.hasOwnProperty(k)) { - core_controller.plugins.register(plugins[k]); - } -} - -core_controller.platform.initialize(); - -var src = core_controller; -if (typeof window !== 'undefined') { - window.Chart = core_controller; -} - -// DEPRECATIONS - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Chart - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -core_controller.Chart = core_controller; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Legend - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Legend = plugins.legend._element; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Title - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Title = plugins.title._element; - -/** - * Provided for backward compatibility, use Chart.plugins instead - * @namespace Chart.pluginService - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.pluginService = core_controller.plugins; - -/** - * Provided for backward compatibility, inheriting from Chart.PlugingBase has no - * effect, instead simply create/register plugins via plain JavaScript objects. - * @interface Chart.PluginBase - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ -core_controller.PluginBase = core_controller.Element.extend({}); - -/** - * Provided for backward compatibility, use Chart.helpers.canvas instead. - * @namespace Chart.canvasHelpers - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -core_controller.canvasHelpers = core_controller.helpers.canvas; - -/** - * Provided for backward compatibility, use Chart.layouts instead. - * @namespace Chart.layoutService - * @deprecated since version 2.7.3 - * @todo remove at version 3 - * @private - */ -core_controller.layoutService = core_controller.layouts; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.LinearScaleBase - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -core_controller.LinearScaleBase = scale_linearbase; - -/** - * Provided for backward compatibility, instead we should create a new Chart - * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ -core_controller.helpers.each( - [ - 'Bar', - 'Bubble', - 'Doughnut', - 'Line', - 'PolarArea', - 'Radar', - 'Scatter' - ], - function(klass) { - core_controller[klass] = function(ctx, cfg) { - return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { - type: klass.charAt(0).toLowerCase() + klass.slice(1) - })); - }; - } -); - -return src; - -}))); diff --git a/lib/web/chartjs/Chart.min.css b/lib/web/chartjs/Chart.min.css deleted file mode 100644 index 9dc5ac2e5faca..0000000000000 --- a/lib/web/chartjs/Chart.min.css +++ /dev/null @@ -1 +0,0 @@ -@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} \ No newline at end of file From 43402b06dd021ed725a2a1a574735e3d1e7c17c3 Mon Sep 17 00:00:00 2001 From: Vadim Malesh <51680850+engcom-Charlie@users.noreply.github.com> Date: Mon, 2 Mar 2020 13:33:34 +0200 Subject: [PATCH 243/734] Update Rows.php fix static --- .../Catalog/Model/Indexer/Product/Flat/Action/Rows.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php index 8cafc82bf77d6..6c7b69b884f8d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php @@ -5,14 +5,14 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; +use Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction; use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; /** * Class Rows reindex action for mass actions - * */ -class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction +class Rows extends AbstractAction { /** * @var Eraser From 7afe256432592f069e1b8d39e4e0abf804d709fc Mon Sep 17 00:00:00 2001 From: Vadim Malesh <51680850+engcom-Charlie@users.noreply.github.com> Date: Mon, 2 Mar 2020 13:37:27 +0200 Subject: [PATCH 244/734] Update RowsTest.php test coverage --- .../Indexer/Product/Flat/Action/RowsTest.php | 110 +++++++++++++----- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php index baf0a0c1a9c4b..1252245a259c2 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php @@ -3,63 +3,117 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Catalog\Model\Indexer\Product\Flat\Processor; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; + /** - * Class RowsTest + * Test for \Magento\Catalog\Model\Indexer\Product\Flat\Action\Rows. */ -class RowsTest extends \Magento\TestFramework\Indexer\TestCase +class RowsTest extends TestCase { /** - * @var \Magento\Catalog\Model\Product + * @var Processor */ - protected $_product; + private $processor; /** - * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor + * @var ProductRepositoryInterface */ - protected $_processor; + private $productRepository; + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @var CollectionFactory + */ + private $productCollectionFactory; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @inheritdoc + */ protected function setUp() { - $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\Indexer\Product\Flat\Processor::class - ); + $objectManager = Bootstrap::getObjectManager(); + $this->processor = $objectManager->get(Processor::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + $this->productCollectionFactory = $objectManager->get(CollectionFactory::class); + $this->layout = $objectManager->get(LayoutInterface::class); } /** + * Test update category products + * * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * + * @return void */ - public function testProductsUpdate() + public function testProductsUpdate(): void { - $this->_product->load(1); - - $this->_processor->reindexList([$this->_product->getId()]); - - $categoryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\CategoryFactory::class - ); - $listProduct = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Block\Product\ListProduct::class - ); + $product = $this->productRepository->getById(1); + $this->processor->reindexList([$product->getId()]); - $category = $categoryFactory->create()->load(2); + $category = $this->categoryRepository->get(2); + $listProduct = $this->layout->createBlock(ListProduct::class); $layer = $listProduct->getLayer(); $layer->setCurrentCategory($category); $productCollection = $layer->getProductCollection(); $this->assertCount(1, $productCollection); - /** @var $product \Magento\Catalog\Model\Product */ - foreach ($productCollection as $product) { - $this->assertEquals($this->_product->getName(), $product->getName()); - $this->assertEquals($this->_product->getShortDescription(), $product->getShortDescription()); + /** @var $productItem Product */ + foreach ($productCollection as $productItem) { + $this->assertEquals($product->getName(), $productItem->getName()); + $this->assertEquals($product->getShortDescription(), $productItem->getShortDescription()); } } + + /** + * Products update with different statuses + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_attribute_in_flat.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @return void + */ + public function testProductsDifferentStatusesUpdate(): void + { + $firstProduct = $this->productRepository->get('simple_with_custom_flat_attribute'); + $secondProduct = $this->productRepository->get('simple-1'); + + $this->processor->getIndexer()->setScheduled(true); + $this->productRepository->save($secondProduct->setStatus(Status::STATUS_DISABLED)); + $this->processor->reindexList([$firstProduct->getId(), $secondProduct->getId()], true); + $collection = $this->productCollectionFactory->create(); + + $this->assertCount(1, $collection); + $this->assertEquals($firstProduct->getId(), $collection->getFirstItem()->getId()); + } } From a2cceb2490814a065cbdf1e972143a123fce6971 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 2 Mar 2020 14:25:37 +0200 Subject: [PATCH 245/734] MC-29052: Magento\FunctionalTestingFramework.functional.AdminTaxReportGridTest fails randomly --- .../Mftf/Page/AdminSalesTaxReportPage.xml | 11 + .../Section/AdminOrderItemsOrderedSection.xml | 1 + .../AdminSelectProductTaxClassActionGroup.xml | 23 ++ .../AdminSelectTaxRateActionGroup.xml | 24 ++ ...dminUnassignProductTaxClassActionGroup.xml | 23 ++ .../Tax/Test/Mftf/Data/TaxRateData.xml | 11 + .../Tax/Test/Mftf/Metadata/tax_rate-meta.xml | 4 +- .../Section/AdminOrderFormTotalSection.xml | 14 ++ .../Mftf/Section/AdminTaxReportsSection.xml | 1 + .../Mftf/Section/AdminTaxRuleFormSection.xml | 1 + .../Test/AdminCheckingTaxReportGridTest.xml | 207 ++++++++++++++++++ .../Test/Mftf/Test/AdminTaxReportGridTest.xml | 4 +- 12 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml diff --git a/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml new file mode 100644 index 0000000000000..db60433830001 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSalesTaxReportPage" url="reports/report_sales/tax/" area="admin" module="Magento_Reports"> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml index a2c82de60a78e..94af3c79c8e02 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -20,6 +20,7 @@ <element name="itemTaxPercent" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-tax-percent" parameterized="true"/> <element name="itemDiscountAmount" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-discont .price" parameterized="true"/> <element name="itemTotal" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-total .price" parameterized="true"/> + <element name="itemTaxAmountByProductName" type="text" selector="//table[contains(@class,'edit-order-table')]//div[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'col-tax-amount')]//span" parameterized="true"/> <element name="productNameColumn" type="text" selector=".edit-order-table .col-product .product-title"/> <element name="productNameOptions" type="text" selector=".edit-order-table .col-product .item-options"/> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..c59c710a7500d --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.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="AdminSelectProductTaxClassActionGroup"> + <annotations> + <description>Select "Product Tax Class" in tax rule edit form.</description> + </annotations> + <arguments> + <argument name="taxClass" type="string" defaultValue="{{productTaxClass.class_name}}"/> + </arguments> + + <conditionalClick selector="{{AdminTaxRuleFormSection.additionalSettings}}" dependentSelector="{{AdminTaxRuleFormSection.additionalSettingsOpened}}" visible="false" stepKey="openAdditionalSettings"/> + <waitForElementVisible selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" stepKey="waitForVisibleTaxClass"/> + <conditionalClick selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" dependentSelector="{{AdminTaxRuleFormSection.productTaxClassSelected(taxClass)}}" visible="false" stepKey="assignProdTaxClass"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml new file mode 100644 index 0000000000000..6fc6ecc6dbcdf --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.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="AdminSelectTaxRateActionGroup"> + <annotations> + <description>Select "Tax Rate" in tax rule edit form.</description> + </annotations> + <arguments> + <argument name="taxRate" type="string" defaultValue="{{TaxRateTexas.code}}"/> + </arguments> + + <fillField selector="{{AdminTaxRuleFormSection.taxRateSearch}}" userInput="{{taxRate}}" stepKey="searchTaxRate"/> + <waitForPageLoad time="30" stepKey="waitForAjaxLoad"/> + <waitForElementVisible selector="{{AdminTaxRuleFormSection.taxRateOption(taxRate)}}" time="30" stepKey="waitForVisibleTaxRate" /> + <click selector="{{AdminTaxRuleFormSection.taxRateOption(taxRate)}}" stepKey="clickTaxRate"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..bda604ffa5da0 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.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="AdminUnassignProductTaxClassActionGroup"> + <annotations> + <description>Admin unassign "Product Tax Class" in tax rule edit form</description> + </annotations> + <arguments> + <argument name="taxClass" type="string" defaultValue="{{productTaxClass.class_name}}"/> + </arguments> + + <conditionalClick selector="{{AdminTaxRuleFormSection.additionalSettings}}" dependentSelector="{{AdminTaxRuleFormSection.additionalSettingsOpened}}" visible="false" stepKey="openAdditionalSettings"/> + <waitForElementVisible selector="{{AdminProductTaxClassSection.productTaxClass}}" stepKey="waitForAddProductTaxClassButton"/> + <conditionalClick selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" dependentSelector="{{AdminTaxRuleFormSection.productTaxClassSelected(taxClass)}}" visible="true" stepKey="unSelectTaxClass"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml index 20d05e1f572c2..80b158d116aa7 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml @@ -127,4 +127,15 @@ <data key="zip_is_range">0</data> <data key="rate">100.0000</data> </entity> + <entity name="TaxRateTexas" type="taxRate"> + <data key="code" unique="suffix">Tax Rate </data> + <data key="tax_region">Texas</data> + <data key="tax_country_id">US</data> + <data key="tax_country">United States</data> + <data key="tax_postcode">78729</data> + <data key="rate">7.25</data> + </entity> + <entity name="SecondTaxRateTexas" extends="TaxRateTexas"> + <data key="rate">0.125</data> + </entity> </entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml index 3f192920c5cc3..6236a6d6c627e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml @@ -17,11 +17,11 @@ <field key="zip_is_range">integer</field> <field key="zip_from">integer</field> <field key="zip_to">integer</field> - <field key="rate">integer</field> + <field key="rate">number</field> <field key="code">string</field> </object> </operation> <operation name="DeleteTaxRate" dataType="taxRate" type="delete" auth="adminOauth" url="/V1/taxRates/{id}" method="DELETE"> <contentType>application/json</contentType> </operation> -</operations> \ No newline at end of file +</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml new file mode 100644 index 0000000000000..9b033bb375eb2 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormTotalSection"> + <element name="totalTax" type="text" selector="//table[contains(@class, 'order-subtotal-table')]/tbody/tr/td/div[contains(text(), 'Tax')]/ancestor::tr/td/span[contains(@class, 'price')]"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml index 71bc4cbceff83..3cfa8206a089c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml @@ -11,6 +11,7 @@ <section name="AdminTaxReportsSection"> <element name="refreshStatistics" type="button" selector="//a[contains(text(),'here')]"/> <element name="fromDate" type="input" selector="#sales_report_from"/> + <element name="toDateInput" type="input" selector="#sales_report_to"/> <element name="toDate" type="input" selector="//*[@id='sales_report_to']/following-sibling::button"/> <element name="goTodayButton" type="input" selector="//button[contains(text(),'Go Today')]"/> <element name="showReportButton" type="button" selector="#filter_form_submit"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml index c77d3ad0d9444..a7e5826454ac6 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml @@ -19,6 +19,7 @@ <element name="deleteRule" type="button" selector="#delete" timeout="30"/> <element name="ok" type="button" selector="button.action-primary.action-accept" timeout="30"/> <element name="additionalSettings" type="button" selector="#details-summarybase_fieldset" timeout="30"/> + <element name="additionalSettingsOpened" type="button" selector="#details-summarybase_fieldset[aria-expanded=true]"/> <element name="customerTaxClassOption" type="checkbox" selector="//*[@id='tax_customer_class']/..//span[.='{{taxCustomerClass}}']" parameterized="true"/> <element name="productTaxClassOption" type="checkbox" selector="//*[@id='tax_product_class']/..//span[.='{{taxProductClass}}']" parameterized="true"/> <element name="customerTaxClassSelected" type="checkbox" selector="//*[@id='tax_customer_class']/..//span[.='{{taxCustomerClass}}' and preceding-sibling::input[contains(@class, 'mselect-checked')]]" parameterized="true"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml new file mode 100644 index 0000000000000..9b1a0ca2a9892 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -0,0 +1,207 @@ +<?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="AdminCheckingTaxReportGridTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Report Grid"/> + <title value="Checking Tax Report grid"/> + <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6230"/> + <useCaseId value="MAGETWO-91521"/> + <group value="Tax"/> + </annotations> + <before> + <!-- Create category and product --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createSecondProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create Tax Rule and Tax Rate --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <createData entity="SimpleTaxRule" stepKey="createSecondTaxRule"/> + <createData entity="TaxRateTexas" stepKey="createTaxRate"/> + <createData entity="SecondTaxRateTexas" stepKey="createSecondTaxRate"/> + + <!-- Create product tax class --> + <createData entity="productTaxClass" stepKey="createProductTaxClass"/> + <getData entity="productTaxClass" stepKey="productTaxClass"> + <requiredEntity createDataKey="createProductTaxClass"/> + </getData> + <createData entity="productTaxClass" stepKey="createSecondProductTaxClass"/> + <getData entity="productTaxClass" stepKey="productSecondTaxClass"> + <requiredEntity createDataKey="createSecondProductTaxClass"/> + </getData> + + <!-- Login to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Go to Tax Rule page, add Tax Rate, unassign Default Tax Rate --> + <amOnPage url="{{AdminEditTaxRulePage.url($createTaxRule.id$)}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRulePage"/> + <actionGroup ref="AdminSelectTaxRateActionGroup" stepKey="assignTaxRate"> + <argument name="taxRate" value="$createTaxRate.code$"/> + </actionGroup> + + <!-- Assign Product Tax Class and Unassign Default Product Tax Class --> + <actionGroup ref="AdminSelectProductTaxClassActionGroup" stepKey="assignProductTaxClass"> + <argument name="taxClass" value="$productTaxClass.class_name$"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductTaxClassActionGroup" stepKey="unSelectTaxRuleDefaultProductTax"> + <argument name="taxClass" value="{{taxableGoodsTaxClass.class_name}}"/> + </actionGroup> + + <!-- Save Tax Rule --> + <actionGroup ref="ClickSaveButtonActionGroup" stepKey="saveTaxRule"> + <argument name="message" value="You saved the tax rule."/> + </actionGroup> + + <!-- Go to Tax Rule page to create second Tax Rule, add Tax Rate, unassign Default Tax Rate --> + <amOnPage url="{{AdminEditTaxRulePage.url($createSecondTaxRule.id$)}}" stepKey="goToSecondTaxRulePage"/> + <waitForPageLoad stepKey="waitForSecondTaxRatePage"/> + <actionGroup ref="AdminSelectTaxRateActionGroup" stepKey="assignSecondTaxRate"> + <argument name="taxRate" value="$createSecondTaxRate.code$"/> + </actionGroup> + <!-- Assign Product Tax Class and Unassign Default Product Tax Class --> + <actionGroup ref="AdminSelectProductTaxClassActionGroup" stepKey="assignSecondProductTaxClass"> + <argument name="taxClass" value="$productSecondTaxClass.class_name$"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductTaxClassActionGroup" stepKey="unaSelectTaxRuleDefaultSecondProductTaxClass"> + <argument name="taxClass" value="{{taxableGoodsTaxClass.class_name}}"/> + </actionGroup> + + <!-- Save Tax Rule --> + <actionGroup ref="ClickSaveButtonActionGroup" stepKey="saveSecondTaxRule"> + <argument name="message" value="You saved the tax rule."/> + </actionGroup> + </before> + <after> + <!-- Delete product and category --> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!--Delete Tax Rule --> + <deleteData createDataKey="createTaxRule" stepKey="deleteRule"/> + <deleteData createDataKey="createSecondTaxRule" stepKey="deleteSecondRule"/> + + <!-- Delete Tax Rate --> + <deleteData createDataKey="createTaxRate" stepKey="deleteTaxRate"/> + <deleteData createDataKey="createSecondTaxRate" stepKey="deleteSecondTaxRate"/> + + <!-- Delete Product Tax Class --> + <deleteData createDataKey="createProductTaxClass" stepKey="deleteProductTaxClass"/> + <deleteData createDataKey="createSecondProductTaxClass" stepKey="deleteSecondProductTaxClass"/> + + <!-- Clear filter Product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterProduct"/> + + <!-- Delete Customer and clear filter --> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="{{Simple_US_Customer.email}}"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="A total of 1 record(s) were deleted."/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterCustomer"/> + + <!-- Logout Admin --> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!--Open Created product. In Tax Class select new created Product Tax class.--> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductForEdit"> + <argument name="productId" value="$createFirstProduct.id$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="$productTaxClass.class_name$" stepKey="selectTexClassForProduct"/> + <!-- Save the second product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!--Open Created Second Product. In Tax Class select new created Product Tax class.--> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openSecondProductForEdit"> + <argument name="productId" value="$createSecondProduct.id$"/> + </actionGroup> + + <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="$productSecondTaxClass.class_name$" stepKey="selectTexClassForSecondProduct"/> + + <!-- Save the second product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> + + <!--Create an order with these 2 products in that zip code.--> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrder"/> + <!--Check if order can be submitted without the required fields including email address--> + <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage"/> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitForAddProductButton"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder"> + <argument name="product" value="$createFirstProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitForAddProductButtonAfterOneProductIsAdded"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToOrder"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> + + <!--Fill customer group and customer email--> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> + + <!--Fill customer address information--> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + + <!-- Select shipping --> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> + <!--Submit Order and verify information--> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + + <!-- Grab tax amounts --> + <!-- need check selector --> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.itemTaxAmountByProductName($createFirstProduct.name$)}}" stepKey="amountOfTaxOnFirstProduct"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.itemTaxAmountByProductName($createSecondProduct.name$)}}" stepKey="amountOfTaxOnSecondProduct"/> + <grabTextFrom selector="{{AdminOrderFormTotalSection.totalTax}}" stepKey="amountOfTotalTax"/> + + <!--Create Invoice and Shipment for this Order.--> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreatingInvoice"/> + + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="clickSubmitInvoice"/> + + <actionGroup ref="goToShipmentIntoOrder" stepKey="seeShipmentOrderPage"/> + <!--Submit Shipment--> + <actionGroup ref="submitShipmentIntoOrder" stepKey="clickSubmitShipment"/> + <!--Go to "Reports" -> "Sales" -> "Tax"--> + <amOnPage url="{{AdminSalesTaxReportPage.url}}" stepKey="navigateToReportsTaxPage"/> + <waitForPageLoad stepKey="waitForReportsTaxPageLoad"/> + + <!--click "here" to refresh last day's statistics --> + <click selector="{{AdminTaxReportsSection.refreshStatistics}}" stepKey="clickRefreshStatistics"/> + <waitForPageLoad time="30" stepKey="waitForRefresh"/> + + <!--Select Dates--> + <generateDate date="+0 day" format="m/d/Y" stepKey="today"/> + <fillField selector="{{AdminTaxReportsSection.fromDate}}" userInput="{$today}" stepKey="fillDateFrom"/> + <fillField selector="{{AdminTaxReportsSection.toDateInput}}" userInput="{$today}" stepKey="fillDateTo"/> + <!--Click "Show report" in the upper right corner.--> + <click selector="{{AdminTaxReportsSection.showReportButton}}" stepKey="clickShowReportButton"/> + <waitForPageLoad time="60" stepKey="waitForReload"/> + <!--Tax Report Grid displays Tax amount in rows. "Total" and "Subtotal" is a sum of all tax amounts--> + <see selector="{{AdminTaxReportsSection.taxRuleAmount(TaxRateTexas.code)}}" userInput="$amountOfTaxOnFirstProduct" stepKey="assertSubtotalFirstField"/> + <see selector="{{AdminTaxReportsSection.taxRuleAmount(SecondTaxRateTexas.code)}}" userInput="$amountOfTaxOnSecondProduct" stepKey="assertSubtotalSecondField"/> + <see selector="{{AdminTaxReportsSection.taxRuleAmount('Subtotal')}}" userInput="$amountOfTotalTax" stepKey="assertSubtotalField"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml index 1611704c43334..1c23f455d0ad0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml @@ -12,13 +12,13 @@ <annotations> <features value="Tax"/> <stories value="MAGETWO-91521: Reports / Sales / Tax report show incorrect amount"/> - <title value="Checking Tax Report grid"/> + <title value="DEPRECATED Checking Tax Report grid"/> <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-94338"/> <group value="Tax"/> <skip> - <issueId value="MAGETWO-96193"/> + <issueId value="DEPRECATED">Use AdminCheckingTaxReportGridTest instead.</issueId> </skip> </annotations> <before> From 074aa5cdb0faace13b2a43c159bb2fd33ee05b87 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Mon, 2 Mar 2020 10:18:40 -0600 Subject: [PATCH 246/734] MQE-1057: Update metadata filenames convention --- .../{braintree_config-meta.xml => BraintreeConfigMeta.xml} | 0 .../Mftf/Metadata/{bundle_link-meta.xml => BundleLinkMeta.xml} | 0 .../Metadata/{bundle_option-meta.xml => BundleOptionMeta.xml} | 0 .../Metadata/{bundle_options-meta.xml => BundleOptionsMeta.xml} | 0 ...catalog_attribute_set-meta.xml => CatalogAttributeSetMeta.xml} | 0 ...atalog_configuration-meta.xml => CatalogConfigurationMeta.xml} | 0 .../Metadata/{catalog_price-meta.xml => CatalogPriceMeta.xml} | 0 ...recently_products-meta.xml => CatalogRecentlyProductsMeta.xml} | 0 ...catalog_special_price-meta.xml => CatalogSpecialPriceMeta.xml} | 0 .../{catalog_tier_price-meta.xml => CatalogTierPriceMeta.xml} | 0 .../Test/Mftf/Metadata/{category-meta.xml => CategoryMeta.xml} | 0 .../{custom_attribute-meta.xml => CustomAttributeMeta.xml} | 0 ...tension_attribute-meta.xml => EmptyExtensionAttributeMeta.xml} | 0 .../Metadata/{frontend_label-meta.xml => FrontendLabelMeta.xml} | 0 .../Metadata/{image_content-meta.xml => ImageContentMeta.xml} | 0 ...y_entry-meta.xml => ProductAttributeMediaGalleryEntryMeta.xml} | 0 .../{product_attribute-meta.xml => ProductAttributeMeta.xml} | 0 ...t_attribute_option-meta.xml => ProductAttributeOptionMeta.xml} | 0 ...product_attribute_set-meta.xml => ProductAttributeSetMeta.xml} | 0 ...nsion_attribute-meta.xml => ProductExtensionAttributeMeta.xml} | 0 ...n_attribute-meta.xml => ProductLinkExtensionAttributeMeta.xml} | 0 .../Mftf/Metadata/{product_link-meta.xml => ProductLinkMeta.xml} | 0 .../Metadata/{product_links-meta.xml => ProductLinksMeta.xml} | 0 .../Test/Mftf/Metadata/{product-meta.xml => ProductMeta.xml} | 0 .../Metadata/{product_option-meta.xml => ProductOptionMeta.xml} | 0 .../{product_option_value-meta.xml => ProductOptionValueMeta.xml} | 0 .../Test/Mftf/Metadata/{stock_item-meta.xml => StockItemMeta.xml} | 0 .../Mftf/Metadata/{store_label-meta.xml => StoreLabelMeta.xml} | 0 .../Metadata/{validation_rule-meta.xml => ValidationRuleMeta.xml} | 0 ..._item_options-meta.xml => CataloginventoryItemOptionsMeta.xml} | 0 .../Mftf/Metadata/{catalog-rule-meta.xml => CatalogRuleMeta.xml} | 0 .../Metadata/{catalog_search-meta.xml => CatalogSearchMeta.xml} | 0 .../Cms/Test/Mftf/Metadata/{block-meta.xml => BlockMeta.xml} | 0 .../Magento/Cms/Test/Mftf/Metadata/{cms-meta.xml => CmsMeta.xml} | 0 .../{allow_guest_checkout-meta.xml => AllowGuestCheckoutMeta.xml} | 0 ...locale_options_config-meta.xml => LocaleOptionsConfigMeta.xml} | 0 ...em_config-countries-meta.xml => SystemConfigCountriesMeta.xml} | 0 .../Metadata/{system_config-meta.xml => SystemConfigMeta.xml} | 0 ...eb_url_options_config-meta.xml => WebUrlOptionsConfigMeta.xml} | 0 ...uct_add_child-meta.xml => ConfigurableProductAddChildMeta.xml} | 0 ...roduct_options-meta.xml => ConfigurableProductOptionsMeta.xml} | 0 ...a.xml => ExtensionAttributeConfigurableProductOptionsMeta.xml} | 0 .../Mftf/Metadata/{valueIndex-meta.xml => ValueIndexMeta.xml} | 0 .../Test/Mftf/Metadata/{address-meta.xml => AddressMeta.xml} | 0 ...ount_sharing-meta.xml => CustomerConfigAccountSharingMeta.xml} | 0 ...eate_new_account-meta.xml => CustomerCreateNewAccountMeta.xml} | 0 ...sion_attribute-meta.xml => CustomerExtensionAttributeMeta.xml} | 0 .../Metadata/{customer_group-meta.xml => CustomerGroupMeta.xml} | 0 .../Test/Mftf/Metadata/{customer-meta.xml => CustomerMeta.xml} | 0 ...ttribute-meta.xml => CustomerNestedExtensionAttributeMeta.xml} | 0 .../Test/Mftf/Metadata/{region-meta.xml => RegionMeta.xml} | 0 .../{downloadable_link-meta.xml => DownloadableLinkMeta.xml} | 0 ...adable_link_sample-meta.xml => DownloadableLinkSampleMeta.xml} | 0 .../{link_file_content-meta.xml => LinkFileContentMeta.xml} | 0 .../{sample_file_content-meta.xml => SampleFileContentMeta.xml} | 0 .../Mftf/Metadata/{gift_options-meta.xml => GiftOptionsMeta.xml} | 0 .../Metadata/{msrp_settings-meta.xml => MsrpSettingsMeta.xml} | 0 .../Metadata/{payment_method-meta.xml => PaymentMethodMeta.xml} | 0 .../Metadata/{paypal_config-meta.xml => PaypalConfigMeta.xml} | 0 .../{persistent_config-meta.xml => PersistentConfigMeta.xml} | 0 .../{product_video_config-meta.xml => ProductVideoConfigMeta.xml} | 0 .../Metadata/{billing_address-meta.xml => BillingAddressMeta.xml} | 0 .../Test/Mftf/Metadata/{guest_cart-meta.xml => GuestCartMeta.xml} | 0 .../{shipping_address-meta.xml => ShippingAddressMeta.xml} | 0 .../Mftf/Metadata/{sales_config-meta.xml => SalesConfigMeta.xml} | 0 .../{sales_rule-condition-meta.xml => SalesRuleConditionMeta.xml} | 0 .../{sales_rule-coupon-meta.xml => SalesRuleCouponMeta.xml} | 0 .../{sales_rule-label-meta.xml => SalesRuleLabelMeta.xml} | 0 .../Test/Mftf/Metadata/{sales_rule-meta.xml => SalesRuleMeta.xml} | 0 .../Mftf/Metadata/{search_term-meta.xml => SearchTermMeta.xml} | 0 .../{shipping_methods-meta.xml => ShippingMethodsMeta.xml} | 0 .../Metadata/{shipping_origin-meta.xml => ShippingOriginMeta.xml} | 0 .../{product_website_link-meta.xml => ProductWebsiteLinkMeta.xml} | 0 .../Mftf/Metadata/{store_group-meta.xml => StoreGroupMeta.xml} | 0 .../Store/Test/Mftf/Metadata/{store-meta.xml => StoreMeta.xml} | 0 ...store_payment_methods-meta.xml => StorePaymentMethodsMeta.xml} | 0 ...ore_shipping_methods-meta.xml => StoreShippingMethodsMeta.xml} | 0 .../Test/Mftf/Metadata/{website-meta.xml => WebsiteMeta.xml} | 0 .../Test/Mftf/Metadata/{tax_class-meta.xml => TaxClassMeta.xml} | 0 .../Test/Mftf/Metadata/{tax_config-meta.xml => TaxConfigMeta.xml} | 0 .../Tax/Test/Mftf/Metadata/{tax_rate-meta.xml => TaxRateMeta.xml} | 0 .../Tax/Test/Mftf/Metadata/{tax_rule-meta.xml => TaxRuleMeta.xml} | 0 ..._type_config-meta.xml => ShippingMethodsUpsTypeConfigMeta.xml} | 0 .../Mftf/Metadata/{url_rewrite-meta.xml => UrlRewriteMeta.xml} | 0 .../User/Test/Mftf/Metadata/{user-meta.xml => UserMeta.xml} | 0 .../Test/Mftf/Metadata/{user_role-meta.xml => UserRoleMeta.xml} | 0 .../Mftf/Metadata/{weee_config-meta.xml => WeeeConfigMeta.xml} | 0 .../Test/Mftf/Metadata/{wishlist-meta.xml => WishlistMeta.xml} | 0 88 files changed, 0 insertions(+), 0 deletions(-) rename app/code/Magento/Braintree/Test/Mftf/Metadata/{braintree_config-meta.xml => BraintreeConfigMeta.xml} (100%) rename app/code/Magento/Bundle/Test/Mftf/Metadata/{bundle_link-meta.xml => BundleLinkMeta.xml} (100%) rename app/code/Magento/Bundle/Test/Mftf/Metadata/{bundle_option-meta.xml => BundleOptionMeta.xml} (100%) rename app/code/Magento/Bundle/Test/Mftf/Metadata/{bundle_options-meta.xml => BundleOptionsMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_attribute_set-meta.xml => CatalogAttributeSetMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_configuration-meta.xml => CatalogConfigurationMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_price-meta.xml => CatalogPriceMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_recently_products-meta.xml => CatalogRecentlyProductsMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_special_price-meta.xml => CatalogSpecialPriceMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{catalog_tier_price-meta.xml => CatalogTierPriceMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{category-meta.xml => CategoryMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{custom_attribute-meta.xml => CustomAttributeMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{empty_extension_attribute-meta.xml => EmptyExtensionAttributeMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{frontend_label-meta.xml => FrontendLabelMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{image_content-meta.xml => ImageContentMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_attribute_media_gallery_entry-meta.xml => ProductAttributeMediaGalleryEntryMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_attribute-meta.xml => ProductAttributeMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_attribute_option-meta.xml => ProductAttributeOptionMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_attribute_set-meta.xml => ProductAttributeSetMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_extension_attribute-meta.xml => ProductExtensionAttributeMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_link_extension_attribute-meta.xml => ProductLinkExtensionAttributeMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_link-meta.xml => ProductLinkMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_links-meta.xml => ProductLinksMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product-meta.xml => ProductMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_option-meta.xml => ProductOptionMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{product_option_value-meta.xml => ProductOptionValueMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{stock_item-meta.xml => StockItemMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{store_label-meta.xml => StoreLabelMeta.xml} (100%) rename app/code/Magento/Catalog/Test/Mftf/Metadata/{validation_rule-meta.xml => ValidationRuleMeta.xml} (100%) rename app/code/Magento/CatalogInventory/Test/Mftf/Metadata/{cataloginventory_item_options-meta.xml => CataloginventoryItemOptionsMeta.xml} (100%) rename app/code/Magento/CatalogRule/Test/Mftf/Metadata/{catalog-rule-meta.xml => CatalogRuleMeta.xml} (100%) rename app/code/Magento/CatalogSearch/Test/Mftf/Metadata/{catalog_search-meta.xml => CatalogSearchMeta.xml} (100%) rename app/code/Magento/Cms/Test/Mftf/Metadata/{block-meta.xml => BlockMeta.xml} (100%) rename app/code/Magento/Cms/Test/Mftf/Metadata/{cms-meta.xml => CmsMeta.xml} (100%) rename app/code/Magento/Config/Test/Mftf/Metadata/{allow_guest_checkout-meta.xml => AllowGuestCheckoutMeta.xml} (100%) rename app/code/Magento/Config/Test/Mftf/Metadata/{locale_options_config-meta.xml => LocaleOptionsConfigMeta.xml} (100%) rename app/code/Magento/Config/Test/Mftf/Metadata/{system_config-countries-meta.xml => SystemConfigCountriesMeta.xml} (100%) rename app/code/Magento/Config/Test/Mftf/Metadata/{system_config-meta.xml => SystemConfigMeta.xml} (100%) rename app/code/Magento/Config/Test/Mftf/Metadata/{web_url_options_config-meta.xml => WebUrlOptionsConfigMeta.xml} (100%) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/{configurable_product_add_child-meta.xml => ConfigurableProductAddChildMeta.xml} (100%) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/{configurable_product_options-meta.xml => ConfigurableProductOptionsMeta.xml} (100%) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/{extension_attribute_configurable_product_options-meta.xml => ExtensionAttributeConfigurableProductOptionsMeta.xml} (100%) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/{valueIndex-meta.xml => ValueIndexMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{address-meta.xml => AddressMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer_config_account_sharing-meta.xml => CustomerConfigAccountSharingMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer_create_new_account-meta.xml => CustomerCreateNewAccountMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer_extension_attribute-meta.xml => CustomerExtensionAttributeMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer_group-meta.xml => CustomerGroupMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer-meta.xml => CustomerMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{customer_nested_extension_attribute-meta.xml => CustomerNestedExtensionAttributeMeta.xml} (100%) rename app/code/Magento/Customer/Test/Mftf/Metadata/{region-meta.xml => RegionMeta.xml} (100%) rename app/code/Magento/Downloadable/Test/Mftf/Metadata/{downloadable_link-meta.xml => DownloadableLinkMeta.xml} (100%) rename app/code/Magento/Downloadable/Test/Mftf/Metadata/{downloadable_link_sample-meta.xml => DownloadableLinkSampleMeta.xml} (100%) rename app/code/Magento/Downloadable/Test/Mftf/Metadata/{link_file_content-meta.xml => LinkFileContentMeta.xml} (100%) rename app/code/Magento/Downloadable/Test/Mftf/Metadata/{sample_file_content-meta.xml => SampleFileContentMeta.xml} (100%) rename app/code/Magento/GiftMessage/Test/Mftf/Metadata/{gift_options-meta.xml => GiftOptionsMeta.xml} (100%) rename app/code/Magento/Msrp/Test/Mftf/Metadata/{msrp_settings-meta.xml => MsrpSettingsMeta.xml} (100%) rename app/code/Magento/Payment/Test/Mftf/Metadata/{payment_method-meta.xml => PaymentMethodMeta.xml} (100%) rename app/code/Magento/Paypal/Test/Mftf/Metadata/{paypal_config-meta.xml => PaypalConfigMeta.xml} (100%) rename app/code/Magento/Persistent/Test/Mftf/Metadata/{persistent_config-meta.xml => PersistentConfigMeta.xml} (100%) rename app/code/Magento/ProductVideo/Test/Mftf/Metadata/{product_video_config-meta.xml => ProductVideoConfigMeta.xml} (100%) rename app/code/Magento/Quote/Test/Mftf/Metadata/{billing_address-meta.xml => BillingAddressMeta.xml} (100%) rename app/code/Magento/Quote/Test/Mftf/Metadata/{guest_cart-meta.xml => GuestCartMeta.xml} (100%) rename app/code/Magento/Quote/Test/Mftf/Metadata/{shipping_address-meta.xml => ShippingAddressMeta.xml} (100%) rename app/code/Magento/Sales/Test/Mftf/Metadata/{sales_config-meta.xml => SalesConfigMeta.xml} (100%) rename app/code/Magento/SalesRule/Test/Mftf/Metadata/{sales_rule-condition-meta.xml => SalesRuleConditionMeta.xml} (100%) rename app/code/Magento/SalesRule/Test/Mftf/Metadata/{sales_rule-coupon-meta.xml => SalesRuleCouponMeta.xml} (100%) rename app/code/Magento/SalesRule/Test/Mftf/Metadata/{sales_rule-label-meta.xml => SalesRuleLabelMeta.xml} (100%) rename app/code/Magento/SalesRule/Test/Mftf/Metadata/{sales_rule-meta.xml => SalesRuleMeta.xml} (100%) rename app/code/Magento/Search/Test/Mftf/Metadata/{search_term-meta.xml => SearchTermMeta.xml} (100%) rename app/code/Magento/Shipping/Test/Mftf/Metadata/{shipping_methods-meta.xml => ShippingMethodsMeta.xml} (100%) rename app/code/Magento/Shipping/Test/Mftf/Metadata/{shipping_origin-meta.xml => ShippingOriginMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{product_website_link-meta.xml => ProductWebsiteLinkMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{store_group-meta.xml => StoreGroupMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{store-meta.xml => StoreMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{store_payment_methods-meta.xml => StorePaymentMethodsMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{store_shipping_methods-meta.xml => StoreShippingMethodsMeta.xml} (100%) rename app/code/Magento/Store/Test/Mftf/Metadata/{website-meta.xml => WebsiteMeta.xml} (100%) rename app/code/Magento/Tax/Test/Mftf/Metadata/{tax_class-meta.xml => TaxClassMeta.xml} (100%) rename app/code/Magento/Tax/Test/Mftf/Metadata/{tax_config-meta.xml => TaxConfigMeta.xml} (100%) rename app/code/Magento/Tax/Test/Mftf/Metadata/{tax_rate-meta.xml => TaxRateMeta.xml} (100%) rename app/code/Magento/Tax/Test/Mftf/Metadata/{tax_rule-meta.xml => TaxRuleMeta.xml} (100%) rename app/code/Magento/Ups/Test/Mftf/Metadata/{shipping_methods_ups_type_config-meta.xml => ShippingMethodsUpsTypeConfigMeta.xml} (100%) rename app/code/Magento/UrlRewrite/Test/Mftf/Metadata/{url_rewrite-meta.xml => UrlRewriteMeta.xml} (100%) rename app/code/Magento/User/Test/Mftf/Metadata/{user-meta.xml => UserMeta.xml} (100%) rename app/code/Magento/User/Test/Mftf/Metadata/{user_role-meta.xml => UserRoleMeta.xml} (100%) rename app/code/Magento/Weee/Test/Mftf/Metadata/{weee_config-meta.xml => WeeeConfigMeta.xml} (100%) rename app/code/Magento/Wishlist/Test/Mftf/Metadata/{wishlist-meta.xml => WishlistMeta.xml} (100%) diff --git a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml b/app/code/Magento/Braintree/Test/Mftf/Metadata/BraintreeConfigMeta.xml similarity index 100% rename from app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml rename to app/code/Magento/Braintree/Test/Mftf/Metadata/BraintreeConfigMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleLinkMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleLinkMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionsMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionsMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogAttributeSetMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogAttributeSetMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogConfigurationMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogConfigurationMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_recently_products-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogRecentlyProductsMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_recently_products-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogRecentlyProductsMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogSpecialPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogSpecialPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_tier_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogTierPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_tier_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogTierPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CategoryMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CategoryMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CustomAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CustomAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/EmptyExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/EmptyExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/FrontendLabelMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/FrontendLabelMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ImageContentMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ImageContentMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMediaGalleryEntryMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMediaGalleryEntryMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeOptionMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeOptionMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeSetMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeSetMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinksMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinksMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionValueMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionValueMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/StockItemMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/StockItemMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/StoreLabelMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/StoreLabelMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ValidationRuleMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ValidationRuleMeta.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/cataloginventory_item_options-meta.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/CataloginventoryItemOptionsMeta.xml similarity index 100% rename from app/code/Magento/CatalogInventory/Test/Mftf/Metadata/cataloginventory_item_options-meta.xml rename to app/code/Magento/CatalogInventory/Test/Mftf/Metadata/CataloginventoryItemOptionsMeta.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/CatalogRuleMeta.xml similarity index 100% rename from app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml rename to app/code/Magento/CatalogRule/Test/Mftf/Metadata/CatalogRuleMeta.xml diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/CatalogSearchMeta.xml similarity index 100% rename from app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml rename to app/code/Magento/CatalogSearch/Test/Mftf/Metadata/CatalogSearchMeta.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/BlockMeta.xml similarity index 100% rename from app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml rename to app/code/Magento/Cms/Test/Mftf/Metadata/BlockMeta.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/CmsMeta.xml similarity index 100% rename from app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml rename to app/code/Magento/Cms/Test/Mftf/Metadata/CmsMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/AllowGuestCheckoutMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/AllowGuestCheckoutMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/LocaleOptionsConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/LocaleOptionsConfigMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigCountriesMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigCountriesMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/web_url_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/WebUrlOptionsConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/web_url_options_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/WebUrlOptionsConfigMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductAddChildMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductAddChildMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductOptionsMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductOptionsMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ExtensionAttributeConfigurableProductOptionsMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ExtensionAttributeConfigurableProductOptionsMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ValueIndexMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ValueIndexMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/AddressMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/AddressMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerConfigAccountSharingMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerConfigAccountSharingMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_create_new_account-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerCreateNewAccountMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_create_new_account-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerCreateNewAccountMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerExtensionAttributeMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_group-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerGroupMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_group-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerGroupMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerNestedExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerNestedExtensionAttributeMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/RegionMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/RegionMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link_sample-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkSampleMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link_sample-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkSampleMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/LinkFileContentMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/LinkFileContentMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/SampleFileContentMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/SampleFileContentMeta.xml diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/GiftOptionsMeta.xml similarity index 100% rename from app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml rename to app/code/Magento/GiftMessage/Test/Mftf/Metadata/GiftOptionsMeta.xml diff --git a/app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_settings-meta.xml b/app/code/Magento/Msrp/Test/Mftf/Metadata/MsrpSettingsMeta.xml similarity index 100% rename from app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_settings-meta.xml rename to app/code/Magento/Msrp/Test/Mftf/Metadata/MsrpSettingsMeta.xml diff --git a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml b/app/code/Magento/Payment/Test/Mftf/Metadata/PaymentMethodMeta.xml similarity index 100% rename from app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml rename to app/code/Magento/Payment/Test/Mftf/Metadata/PaymentMethodMeta.xml diff --git a/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml b/app/code/Magento/Paypal/Test/Mftf/Metadata/PaypalConfigMeta.xml similarity index 100% rename from app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml rename to app/code/Magento/Paypal/Test/Mftf/Metadata/PaypalConfigMeta.xml diff --git a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml b/app/code/Magento/Persistent/Test/Mftf/Metadata/PersistentConfigMeta.xml similarity index 100% rename from app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml rename to app/code/Magento/Persistent/Test/Mftf/Metadata/PersistentConfigMeta.xml diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml b/app/code/Magento/ProductVideo/Test/Mftf/Metadata/ProductVideoConfigMeta.xml similarity index 100% rename from app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml rename to app/code/Magento/ProductVideo/Test/Mftf/Metadata/ProductVideoConfigMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/BillingAddressMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/BillingAddressMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/GuestCartMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/GuestCartMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/ShippingAddressMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/ShippingAddressMeta.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml b/app/code/Magento/Sales/Test/Mftf/Metadata/SalesConfigMeta.xml similarity index 100% rename from app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml rename to app/code/Magento/Sales/Test/Mftf/Metadata/SalesConfigMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleConditionMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleConditionMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleCouponMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleCouponMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleLabelMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleLabelMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleMeta.xml diff --git a/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml b/app/code/Magento/Search/Test/Mftf/Metadata/SearchTermMeta.xml similarity index 100% rename from app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml rename to app/code/Magento/Search/Test/Mftf/Metadata/SearchTermMeta.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingMethodsMeta.xml similarity index 100% rename from app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml rename to app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingMethodsMeta.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_origin-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingOriginMeta.xml similarity index 100% rename from app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_origin-meta.xml rename to app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingOriginMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/product_website_link-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/ProductWebsiteLinkMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/product_website_link-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/ProductWebsiteLinkMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreGroupMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StoreGroupMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StoreMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StorePaymentMethodsMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StorePaymentMethodsMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreShippingMethodsMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StoreShippingMethodsMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/WebsiteMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/WebsiteMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_class-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxClassMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_class-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxClassMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxConfigMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxConfigMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRateMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxRateMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRuleMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxRuleMeta.xml diff --git a/app/code/Magento/Ups/Test/Mftf/Metadata/shipping_methods_ups_type_config-meta.xml b/app/code/Magento/Ups/Test/Mftf/Metadata/ShippingMethodsUpsTypeConfigMeta.xml similarity index 100% rename from app/code/Magento/Ups/Test/Mftf/Metadata/shipping_methods_ups_type_config-meta.xml rename to app/code/Magento/Ups/Test/Mftf/Metadata/ShippingMethodsUpsTypeConfigMeta.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Metadata/url_rewrite-meta.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Metadata/UrlRewriteMeta.xml similarity index 100% rename from app/code/Magento/UrlRewrite/Test/Mftf/Metadata/url_rewrite-meta.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Metadata/UrlRewriteMeta.xml diff --git a/app/code/Magento/User/Test/Mftf/Metadata/user-meta.xml b/app/code/Magento/User/Test/Mftf/Metadata/UserMeta.xml similarity index 100% rename from app/code/Magento/User/Test/Mftf/Metadata/user-meta.xml rename to app/code/Magento/User/Test/Mftf/Metadata/UserMeta.xml diff --git a/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml b/app/code/Magento/User/Test/Mftf/Metadata/UserRoleMeta.xml similarity index 100% rename from app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml rename to app/code/Magento/User/Test/Mftf/Metadata/UserRoleMeta.xml diff --git a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml b/app/code/Magento/Weee/Test/Mftf/Metadata/WeeeConfigMeta.xml similarity index 100% rename from app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml rename to app/code/Magento/Weee/Test/Mftf/Metadata/WeeeConfigMeta.xml diff --git a/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml b/app/code/Magento/Wishlist/Test/Mftf/Metadata/WishlistMeta.xml similarity index 100% rename from app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml rename to app/code/Magento/Wishlist/Test/Mftf/Metadata/WishlistMeta.xml From 854761db86de5a2bb960fc66c0b9922dd792f423 Mon Sep 17 00:00:00 2001 From: Michael Bottens <michael.bottens@blueacorn.com> Date: Mon, 2 Mar 2020 11:45:45 -0500 Subject: [PATCH 247/734] #27124: Update wishlist image logic to match logic on wishlist page --- .../Wishlist/Block/Share/Email/Items.php | 40 +++++++++++++++++++ .../view/frontend/templates/email/items.phtml | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Block/Share/Email/Items.php b/app/code/Magento/Wishlist/Block/Share/Email/Items.php index d4e6587fd6519..0887951d180aa 100644 --- a/app/code/Magento/Wishlist/Block/Share/Email/Items.php +++ b/app/code/Magento/Wishlist/Block/Share/Email/Items.php @@ -11,17 +11,57 @@ */ namespace Magento\Wishlist\Block\Share\Email; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; +use Magento\Catalog\Model\Product\Image\UrlBuilder; +use Magento\Framework\View\ConfigInterface; +use Magento\Wishlist\Model\Item; + /** * @api * @since 100.0.2 */ class Items extends \Magento\Wishlist\Block\AbstractBlock { + /** @var ItemResolverInterface */ + private $itemResolver; + /** * @var string */ protected $_template = 'Magento_Wishlist::email/items.phtml'; + /** + * @param \Magento\Catalog\Block\Product\Context $context + * @param \Magento\Framework\App\Http\Context $httpContext + * @param ItemResolverInterface $itemResolver + * @param array $data + * @param ConfigInterface|null $config + * @param UrlBuilder|null $urlBuilder + */ + public function __construct( + \Magento\Catalog\Block\Product\Context $context, + \Magento\Framework\App\Http\Context $httpContext, + ItemResolverInterface $itemResolver, + array $data = [], + ConfigInterface $config = null, + UrlBuilder $urlBuilder = null + ) { + $this->itemResolver = $itemResolver; + parent::__construct($context, $httpContext, $data, $config, $urlBuilder); + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @param Item $item + * @return Product + */ + public function getProductForThumbnail(Item $item) : Product + { + return $this->itemResolver->getFinalProduct($item); + } + /** * Retrieve Product View URL * diff --git a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml index 449d4c43c5aa0..a7cfabc7da35f 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml @@ -18,7 +18,7 @@ <td class="col product"> <p> <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> - <?= /* @noEscape */ $block->getImage($_product, 'product_small_image')->toHtml() ?> + <?= /* @noEscape */ $block->getImage($block->getProductForThumbnail($item), 'product_small_image')->toHtml() ?> </a> </p> From 4d7ae4526b983975fe43374b72690537b274005d Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 2 Mar 2020 13:56:45 -0600 Subject: [PATCH 248/734] MQE-1962: Remove support of deprecated locations for tests --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index d5fb4ec0ed891..5775009bd433e 100644 --- a/composer.lock +++ b/composer.lock @@ -7385,12 +7385,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "f1820ff3cf8401f02137ddb1457b6a0d298e64d5" + "reference": "3a18c6bca1d7fd5a8a62033148968a4888ff8f21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/f1820ff3cf8401f02137ddb1457b6a0d298e64d5", - "reference": "f1820ff3cf8401f02137ddb1457b6a0d298e64d5", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/3a18c6bca1d7fd5a8a62033148968a4888ff8f21", + "reference": "3a18c6bca1d7fd5a8a62033148968a4888ff8f21", "shasum": "" }, "require": { @@ -7462,7 +7462,7 @@ "magento", "testing" ], - "time": "2020-02-27T19:58:02+00:00" + "time": "2020-03-02T19:48:04+00:00" }, { "name": "mikey179/vfsstream", From 1b3da7e499d8dfce5199850ffad4eae72a160944 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Thu, 27 Feb 2020 16:06:09 +0200 Subject: [PATCH 249/734] Fix unit and functional tests --- .../StorefrontCategorySidebarSection.xml | 2 +- ...inBackorderAllowedAddProductToCartTest.xml | 1 + .../StorefrontProductInfoMainSection.xml | 4 +- .../Mftf/Test/AdminCreateTextSwatchTest.xml | 9 +- .../view/base/web/js/swatch-renderer.js | 3 +- .../Block/Product/ProductList/ProductItem.php | 2 +- .../Test/Block/Product/ViewWithSwatches.php | 6 +- .../frontend/web/js/swatch-renderer.test.js | 268 +++++++++--------- 8 files changed, 148 insertions(+), 147 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index d629441782551..786131c93e5a4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -13,7 +13,7 @@ <element name="filterOptions" type="text" selector=".filter-options-content .items"/> <element name="filterOption" type="text" selector=".filter-options-content .item"/> <element name="optionQty" type="text" selector=".filter-options-content .item .count"/> - <element name="filterOptionByLabel" type="button" selector=" div.filter-options-item div[option-label='{{optionLabel}}']" parameterized="true"/> + <element name="filterOptionByLabel" type="button" selector=" div.filter-options-item div[data-option-label='{{optionLabel}}']" parameterized="true"/> <element name="removeFilter" type="button" selector="div.filter-current .remove"/> <element name="activeFilterOptions" type="text" selector=".filter-options-item.active .items"/> <element name="activeFilterOptionItemByPosition" type="text" selector=".filter-options-item.active .items li:nth-child({{itemPosition}}) a" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml index 9361637a0a935..f567239b5f262 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml @@ -31,6 +31,7 @@ <!-- Set Magento back to default configuration --> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersDisable.path}} {{CatalogInventoryItemOptionsBackordersDisable.value}}" stepKey="setConfigAllowBackordersFalse"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 5b714f01fd46f..3725143bd6d45 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -9,13 +9,13 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> - <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[option-label='{{opt}}']" parameterized="true"/> + <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[data-option-label='{{opt}}']" parameterized="true"/> <element name="nthSwatchOption" type="button" selector="div.swatch-option:nth-of-type({{var}})" parameterized="true"/> <element name="selectedSwatchValue" type="text" selector="//div[contains(@class, 'swatch-attribute') and contains(., '{{attr}}')]//span[contains(@class, 'swatch-attribute-selected-option')]" parameterized="true"/> <element name="swatchAttributeOptions" type="text" selector="div.swatch-attribute-options"/> <element name="nthSwatchOptionText" type="button" selector="div.swatch-option.text:nth-of-type({{n}})" parameterized="true"/> <element name="productSwatch" type="button" selector="//div[@class='swatch-option'][@aria-label='{{var1}}']" parameterized="true"/> - <element name="visualSwatchOption" type="button" selector=".swatch-option[option-tooltip-value='#{{visualSwatchOption}}']" parameterized="true"/> + <element name="visualSwatchOption" type="button" selector=".swatch-option[data-option-tooltip-value='#{{visualSwatchOption}}']" parameterized="true"/> <element name="swatchOptionTooltip" type="block" selector="div.swatch-option-tooltip"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 4685670fbfdd2..65e57e143c629 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -6,8 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateTextSwatchTest"> <annotations> <features value="Swatches"/> @@ -83,19 +82,19 @@ <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForProductPage"/> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="red" stepKey="seeRed"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('1')}}" userInput="option-label" stepKey="grabRedLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('1')}}" userInput="data-option-label" stepKey="grabRedLabel"/> <assertEquals stepKey="assertRedLabel"> <expectedResult type="string">Something red.</expectedResult> <actualResult type="string">{$grabRedLabel}</actualResult> </assertEquals> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="green" stepKey="seeGreen"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('2')}}" userInput="option-label" stepKey="grabGreenLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('2')}}" userInput="data-option-label" stepKey="grabGreenLabel"/> <assertEquals stepKey="assertGreenLabel"> <expectedResult type="string">Something green.</expectedResult> <actualResult type="string">{$grabGreenLabel}</actualResult> </assertEquals> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="blue" stepKey="seeBlue"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('3')}}" userInput="option-label" stepKey="grabBlueLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('3')}}" userInput="data-option-label" stepKey="grabBlueLabel"/> <assertEquals stepKey="assertBlueLabel"> <expectedResult type="string">Something blue.</expectedResult> <actualResult type="string">{$grabBlueLabel}</actualResult> diff --git a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 250e1f7ba02fd..1267e846049f6 100644 --- a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -462,7 +462,8 @@ define([ if (showTooltip === 1) { // Connect Tooltip container - .find('[data-option-type="1"], [data-option-type="2"], [data-option-type="0"], [data-option-type="3"]') + .find('[data-option-type="1"], [data-option-type="2"],' + + ' [data-option-type="0"], [data-option-type="3"]') .SwatchRendererTooltip(); } diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php index 11d290a05cca7..395257a0b74a4 100644 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php @@ -19,7 +19,7 @@ class ProductItem extends CatalogProductItem * * @var string */ - protected $swatchSelector = 'div[option-id="%s"]'; + protected $swatchSelector = 'div[data-option-id="%s"]'; /** * Selector for the swatches of the product. diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php index d30aabd7ff370..b908c95b884bf 100644 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php @@ -55,10 +55,10 @@ public function getSwatchAttributesData() $swatchAttributesData = []; $swatchAttributes = $this->_rootElement->getElements($this->swatchAttributesSelector); foreach ($swatchAttributes as $swatchAttribute) { - $attributeCode = $swatchAttribute->getAttribute('attribute-code'); + $attributeCode = $swatchAttribute->getAttribute('data-attribute-code'); $swatchAttributesData[$attributeCode] = [ 'attribute_code' => $attributeCode, - 'attribute_id' => $swatchAttribute->getAttribute('attribute-id'), + 'attribute_id' => $swatchAttribute->getAttribute('data-attribute-id'), 'label' => $swatchAttribute->find($this->swatchAttributesLabelSelector)->getText(), 'options' => $this->getSwatchAttributeOptionsData($swatchAttribute), ]; @@ -77,7 +77,7 @@ private function getSwatchAttributeOptionsData(ElementInterface $swatchAttribute $optionsData = []; $options = $swatchAttribute->getElements($this->swatchAttributeOptionsSelector); foreach ($options as $option) { - $optionId = $option->getAttribute('option-id'); + $optionId = $option->getAttribute('data-option-id'); $optionsData[$optionId] = [ 'option_id' => $optionId, 'label' => $option->getText(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js index 144bfa4a77bce..bdc9410d369af 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js @@ -1,134 +1,134 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Swatches/js/swatch-renderer' -], function ($, SwatchRenderer) { - 'use strict'; - - describe('Testing "_RenderSwatchOptions" method of SwatchRenderer Widget', function () { - var widget, - html, - optionConfig, - attribute, - optionId = 2, - swathImageHeight = '60', - swathImageWidth = '70', - swathThumbImageHeight = '40', - swathThumbImageWidth = '50'; - - beforeEach(function () { - widget = new SwatchRenderer(); - attribute = { - id: 1, - options: [{ - id: optionId - }] - }; - - widget.options = { - classes: { - optionClass: 'swatch-option' - }, - jsonSwatchConfig: { - 1: { - 2: { - type: 2 - } - } - }, - jsonSwatchImageSizeConfig: { - swatchImage: { - width: swathImageWidth, - height: swathImageHeight - }, - swatchThumb: { - width: swathThumbImageWidth, - height: swathThumbImageHeight - } - } - }; - - optionConfig = widget.options.jsonSwatchConfig[attribute.id]; - html = $(widget._RenderSwatchOptions(attribute, 'option-label-control-id-1'))[0]; - }); - - it('check if swatch config has attribute id', function () { - expect(widget.options.jsonSwatchConfig.hasOwnProperty(attribute.id)).toEqual(true); - }); - - it('check if option config has option id', function () { - expect(optionConfig.hasOwnProperty(optionId)).toEqual(true); - }); - - it('check swatch thumbnail image height attribute', function () { - expect(html.hasAttribute('thumb-height')).toBe(true); - expect(html.getAttribute('thumb-height')).toEqual(swathThumbImageHeight); - }); - - it('check swatch thumbnail image width attribute', function () { - expect(html.hasAttribute('thumb-width')).toBe(true); - expect(html.getAttribute('thumb-width')).toEqual(swathThumbImageWidth); - }); - - it('check swatch image styles', function () { - expect(html.style.height).toEqual(swathImageHeight + 'px'); - expect(html.style.width).toEqual(swathImageWidth + 'px'); - }); - - it('check udate price method', function () { - var productPriceMock = { - find: jasmine.createSpy().and.returnValue({ - hide: jasmine.createSpy(), - priceBox: jasmine.createSpy().and.returnValue(''), - trigger: jasmine.createSpy(), - find: jasmine.createSpy().and.returnValue({ - toggleClass: jasmine.createSpy() - }) - }) - }; - - widget.element = { - parents: jasmine.createSpy().and.returnValue(productPriceMock) - }; - widget._getNewPrices = jasmine.createSpy().and.returnValue(undefined); - widget._UpdatePrice(); - expect(productPriceMock.find().find.calls.count()).toBe(1); - }); - - it('check getSelectedOptionPriceIndex', function () { - var optionMock = '<div class="swatch-attribute" attribute-id="2" option-selected="4"></div>', - element = $('<div class="' + widget.options.tooltipClass + - '"><div class="image"></div><div class="title"></div><div class="corner"></div>' + - optionMock + '</div>' - ), - optionPricesMock = { - optionPrices: { - p: { - finalPrice: { - amount: 12 - } - } - } - }; - - widget.element = element; - widget.options.classes.attributeClass = 'swatch-attribute'; - widget.options.jsonConfig = optionPricesMock; - widget.optionsMap = { - 2: { - 4: { - products: 'p' - }, - hasOwnProperty: jasmine.createSpy().and.returnValue(true) - }, - hasOwnProperty: jasmine.createSpy().and.returnValue(true) - }; - - expect(widget._getSelectedOptionPriceIndex()).toBe('p'); - }); - }); -}); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Swatches/js/swatch-renderer' +], function ($, SwatchRenderer) { + 'use strict'; + + describe('Testing "_RenderSwatchOptions" method of SwatchRenderer Widget', function () { + var widget, + html, + optionConfig, + attribute, + optionId = 2, + swathImageHeight = '60', + swathImageWidth = '70', + swathThumbImageHeight = '40', + swathThumbImageWidth = '50'; + + beforeEach(function () { + widget = new SwatchRenderer(); + attribute = { + id: 1, + options: [{ + id: optionId + }] + }; + + widget.options = { + classes: { + optionClass: 'swatch-option' + }, + jsonSwatchConfig: { + 1: { + 2: { + type: 2 + } + } + }, + jsonSwatchImageSizeConfig: { + swatchImage: { + width: swathImageWidth, + height: swathImageHeight + }, + swatchThumb: { + width: swathThumbImageWidth, + height: swathThumbImageHeight + } + } + }; + + optionConfig = widget.options.jsonSwatchConfig[attribute.id]; + html = $(widget._RenderSwatchOptions(attribute, 'option-label-control-id-1'))[0]; + }); + + it('check if swatch config has attribute id', function () { + expect(widget.options.jsonSwatchConfig.hasOwnProperty(attribute.id)).toEqual(true); + }); + + it('check if option config has option id', function () { + expect(optionConfig.hasOwnProperty(optionId)).toEqual(true); + }); + + it('check swatch thumbnail image height attribute', function () { + expect(html.hasAttribute('data-thumb-height')).toBe(true); + expect(html.getAttribute('data-thumb-height')).toEqual(swathThumbImageHeight); + }); + + it('check swatch thumbnail image width attribute', function () { + expect(html.hasAttribute('data-thumb-width')).toBe(true); + expect(html.getAttribute('data-thumb-width')).toEqual(swathThumbImageWidth); + }); + + it('check swatch image styles', function () { + expect(html.style.height).toEqual(swathImageHeight + 'px'); + expect(html.style.width).toEqual(swathImageWidth + 'px'); + }); + + it('check udate price method', function () { + var productPriceMock = { + find: jasmine.createSpy().and.returnValue({ + hide: jasmine.createSpy(), + priceBox: jasmine.createSpy().and.returnValue(''), + trigger: jasmine.createSpy(), + find: jasmine.createSpy().and.returnValue({ + toggleClass: jasmine.createSpy() + }) + }) + }; + + widget.element = { + parents: jasmine.createSpy().and.returnValue(productPriceMock) + }; + widget._getNewPrices = jasmine.createSpy().and.returnValue(undefined); + widget._UpdatePrice(); + expect(productPriceMock.find().find.calls.count()).toBe(1); + }); + + it('check getSelectedOptionPriceIndex', function () { + var optionMock = '<div class="swatch-attribute" data-attribute-id="2" data-option-selected="4"></div>', + element = $('<div class="' + widget.options.tooltipClass + + '"><div class="image"></div><div class="title"></div><div class="corner"></div>' + + optionMock + '</div>' + ), + optionPricesMock = { + optionPrices: { + p: { + finalPrice: { + amount: 12 + } + } + } + }; + + widget.element = element; + widget.options.classes.attributeClass = 'swatch-attribute'; + widget.options.jsonConfig = optionPricesMock; + widget.optionsMap = { + 2: { + 4: { + products: 'p' + }, + hasOwnProperty: jasmine.createSpy().and.returnValue(true) + }, + hasOwnProperty: jasmine.createSpy().and.returnValue(true) + }; + + expect(widget._getSelectedOptionPriceIndex()).toBe('p'); + }); + }); +}); From 12ae6e594d260f39f316cf65d4de66516dbdb200 Mon Sep 17 00:00:00 2001 From: Michael Bottens <michael.bottens@blueacorn.com> Date: Tue, 3 Mar 2020 08:06:48 -0500 Subject: [PATCH 250/734] Update wishlist email product link to match wishlist page --- .../Wishlist/view/frontend/templates/email/items.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml index a7cfabc7da35f..8c71a8de033dc 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml @@ -17,13 +17,13 @@ <?php $_product = $item->getProduct(); ?> <td class="col product"> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> <?= /* @noEscape */ $block->getImage($block->getProductForThumbnail($item), 'product_small_image')->toHtml() ?> </a> </p> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> <strong><?= $block->escapeHtml($_product->getName()) ?></strong> </a> </p> @@ -34,7 +34,7 @@ </p> <?php endif; ?> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> <?= $block->escapeHtml(__('View Product')) ?> </a> </p> From b03b0a065a08c9e6643f11dd5edaf100cff00e76 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 3 Mar 2020 11:32:43 -0500 Subject: [PATCH 251/734] fix date format, test names (magento/magento2#22833: Short-term admin accounts) --- .../Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml | 4 ++-- .../Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index 919d2523927fa..02a8170b308d3 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.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="AdminUserCreateNewUserWithInvalidExpirationTest"> + <test name="AdminCreateNewUserWithInvalidExpirationTest"> <annotations> <features value="Security"/> <stories value="Create new user with invalid expiration date."/> @@ -26,7 +26,7 @@ </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="-5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <generateDate date="-5 day" format="M j, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index a00872d848472..dc971f2044760 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.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="AdminUserCreateNewUserWithValidExpirationTest"> + <test name="AdminCreateNewUserWithValidExpirationTest"> <annotations> <features value="Security"/> <stories value="Create new user with valid expiration date."/> @@ -25,7 +25,7 @@ </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> - <generateDate date="+5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <generateDate date="+5 day" format="M j, Y g:i:s A" stepKey="expiresDateTime"/> <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> <argument name="expires_at" value="{$expiresDateTime}"/> </actionGroup> From c8a41b7d1867679281c0445eb79767a674c6819b Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 3 Mar 2020 13:00:58 -0500 Subject: [PATCH 252/734] make constants private scope (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/UserExpirationInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Model/UserExpirationInterface.php b/app/code/Magento/Security/Model/UserExpirationInterface.php index 06f8c258983ba..d7699f162c469 100644 --- a/app/code/Magento/Security/Model/UserExpirationInterface.php +++ b/app/code/Magento/Security/Model/UserExpirationInterface.php @@ -14,9 +14,9 @@ interface UserExpirationInterface { - const EXPIRES_AT = 'expires_at'; + private const EXPIRES_AT = 'expires_at'; - const USER_ID = 'user_id'; + private const USER_ID = 'user_id'; /** * `expires_at` getter. From 107cb5f599b9533a1453a7e0cec4365460561667 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 3 Mar 2020 13:09:57 -0500 Subject: [PATCH 253/734] revert constant private scope (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Model/UserExpirationInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Model/UserExpirationInterface.php b/app/code/Magento/Security/Model/UserExpirationInterface.php index d7699f162c469..d422227f65d2b 100644 --- a/app/code/Magento/Security/Model/UserExpirationInterface.php +++ b/app/code/Magento/Security/Model/UserExpirationInterface.php @@ -14,9 +14,9 @@ interface UserExpirationInterface { - private const EXPIRES_AT = 'expires_at'; + public const EXPIRES_AT = 'expires_at'; - private const USER_ID = 'user_id'; + public const USER_ID = 'user_id'; /** * `expires_at` getter. From bef0bd5e51f30c3538eebb09ee45d135d625e09b Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Tue, 3 Mar 2020 19:14:05 -0500 Subject: [PATCH 254/734] move interface into Api/Data, make it extensible (magento/magento2#22833: Short-term admin accounts) --- .../Data}/UserExpirationInterface.php | 19 ++++++++++++++++-- .../Security/Model/Plugin/AdminUserForm.php | 2 +- .../Magento/Security/Model/UserExpiration.php | 20 ++++++++++++++++++- .../Security/Model/UserExpirationManager.php | 2 +- .../AdminUserAuthenticateBeforeTest.php | 13 +++++++++--- app/code/Magento/Security/etc/di.xml | 2 +- .../Security/Model/Plugin/AuthSessionTest.php | 4 ++-- .../Model/UserExpirationManagerTest.php | 4 ++-- 8 files changed, 53 insertions(+), 13 deletions(-) rename app/code/Magento/Security/{Model => Api/Data}/UserExpirationInterface.php (55%) diff --git a/app/code/Magento/Security/Model/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php similarity index 55% rename from app/code/Magento/Security/Model/UserExpirationInterface.php rename to app/code/Magento/Security/Api/Data/UserExpirationInterface.php index d422227f65d2b..3a930aeb584ab 100644 --- a/app/code/Magento/Security/Model/UserExpirationInterface.php +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -6,12 +6,12 @@ declare(strict_types=1); -namespace Magento\Security\Model; +namespace Magento\Security\Api\Data; /** * Interface UserExpirationInterface to be used as a DTO for expires_at property on User model. */ -interface UserExpirationInterface +interface UserExpirationInterface extends \Magento\Framework\Api\ExtensibleDataInterface { public const EXPIRES_AT = 'expires_at'; @@ -47,4 +47,19 @@ public function getUserId(); * @return $this */ public function setUserId($userId); + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\Security\Api\Data\UserExpirationExtensionInterface|null + */ + public function getExtensionAttributes(); + + /** + * Set an extension attributes object. + * + * @param \Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes(\Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes); } diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index 320809d288246..ad3c352b7f369 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -24,7 +24,7 @@ class AdminUserForm private $userExpirationResource; /** - * @var \Magento\Security\Model\UserExpirationInterfaceFactory + * @var \Magento\Security\Api\Data\UserExpirationInterfaceFactory */ private $userExpirationFactory; diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index b220a8092602f..51a41b5c0a5be 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -7,10 +7,12 @@ namespace Magento\Security\Model; +use Magento\Security\Api\Data\UserExpirationInterface; + /** * Admin User Expiration model. */ -class UserExpiration extends \Magento\Framework\Model\AbstractModel implements UserExpirationInterface +class UserExpiration extends \Magento\Framework\Model\AbstractExtensibleModel implements UserExpirationInterface { /** @@ -64,4 +66,20 @@ public function setUserId($userId) { return $this->setData(self::USER_ID, $userId); } + + /** + * @inheritDoc + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritDoc + */ + public function setExtensionAttributes(\Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } } diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index ffcaa9f8d9457..b9645ba52ff67 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -134,7 +134,7 @@ private function processExpiredUsers(ExpiredUsersCollection $expiredRecords): vo public function isUserExpired(string $userId): bool { $isExpired = false; - /** @var \Magento\Security\Model\UserExpirationInterface $expiredRecord */ + /** @var \Magento\Security\Api\Data\UserExpirationInterface $expiredRecord */ $expiredRecord = $this->userExpirationCollectionFactory->create() ->addExpiredRecordsForUserFilter($userId) ->getFirstItem(); diff --git a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php index 8768a2a647f0e..3f5717abe2a2c 100644 --- a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -48,7 +48,7 @@ class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase private $eventMock; /** - * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationInterface + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Api\Data\UserExpirationInterface */ private $userExpirationMock; @@ -77,8 +77,15 @@ protected function setUp() $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getUsername']); $this->userExpirationMock = $this->createPartialMock( - \Magento\Security\Model\UserExpirationInterface::class, - ['getUserId', 'getExpiresAt', 'setUserId', 'setExpiresAt'] + \Magento\Security\Api\Data\UserExpirationInterface::class, + [ + 'getUserId', + 'getExpiresAt', + 'setUserId', + 'setExpiresAt', + 'getExtensionAttributes', + 'setExtensionAttributes' + ] ); } diff --git a/app/code/Magento/Security/etc/di.xml b/app/code/Magento/Security/etc/di.xml index 0e415acb93ab3..3b07bb84b1161 100644 --- a/app/code/Magento/Security/etc/di.xml +++ b/app/code/Magento/Security/etc/di.xml @@ -18,5 +18,5 @@ </argument> </arguments> </type> - <preference for="Magento\Security\Model\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> + <preference for="Magento\Security\Api\Data\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> </config> diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index a7a18716874cf..b0c333737bfb8 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -160,8 +160,8 @@ public function testProcessProlongWithExpiredUser() $user = $this->objectManager->create(\Magento\User\Model\User::class); $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); $userExpirationFactory = - $this->objectManager->create(\Magento\Security\Model\UserExpirationInterfaceFactory::class); - /** @var \Magento\Security\Model\UserExpirationInterface $userExpiration */ + $this->objectManager->create(\Magento\Security\Api\Data\UserExpirationInterfaceFactory::class); + /** @var \Magento\Security\Api\Data\UserExpirationInterface $userExpiration */ $userExpiration = $userExpirationFactory->create(); $userExpiration->setId($user->getId()) ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php index e8f99d6759302..adb7b7a120f1f 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -156,8 +156,8 @@ private function expireUser(\Magento\User\Model\User $user) { $expireDate = new \DateTime(); $expireDate->modify('-10 days'); - /** @var \Magento\Security\Model\UserExpirationInterface $userExpiration */ - $userExpiration = $this->objectManager->create(\Magento\Security\Model\UserExpirationInterface::class); + /** @var \Magento\Security\Api\Data\UserExpirationInterface $userExpiration */ + $userExpiration = $this->objectManager->create(\Magento\Security\Api\Data\UserExpirationInterface::class); $userExpiration->setId($user->getId()) ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) ->save(); From 7dc8227ccd8353eb4cf8e8eecc276c1cd1be805b Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Wed, 4 Mar 2020 11:50:32 +0530 Subject: [PATCH 255/734] Styling for sortBy component --- .../web/css/source/module/_masonry-grid.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less index 9dd42246dac3f..7e2eeb59e116b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -114,5 +114,16 @@ } } } + + &-sortby { + select { + border: none; + background-image: url(../images/arrows-bg.svg); + padding-right: 3.2rem; + &:active { + background-image+: url('../images/arrows-bg.svg'); + } + } + } } } From 08aea80eb5a28a94ba65dfde01b8e669ada1f6b5 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Wed, 4 Mar 2020 11:59:37 +0530 Subject: [PATCH 256/734] Missed changes from previous commit --- app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html index 14f1da81fbc8b..104c92bf784f0 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<div if="isVisible" class="masonry-image-sorting"> +<div if="isVisible" class="masonry-image-sortby"> <b><!-- ko i18n: 'Sort by' --><!-- /ko -->:</b> <select class="admin__control-select" data-bind=" options: options, From d076b4d47505ef2defd565741d91f359b37edfb3 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 4 Mar 2020 09:28:24 +0200 Subject: [PATCH 257/734] MC-30395: De-couple Worldpay payment methods integrations from core in 2.4.0 --- app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml index 9ff952d04925d..64a3af472faa6 100644 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml @@ -62,10 +62,6 @@ <magento_code>authorizenet_directpost</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> </payment_method> - <payment_method name="worldpay"> - <magento_code>worldpay</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> <payment_method name="eway"> <magento_code>eway</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> From d255b7d55bf7f759869a9074bd3cf4a77ca2b406 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 4 Mar 2020 09:39:55 +0200 Subject: [PATCH 258/734] MC-30394: De-couple eWay payment methods integrations from core in 2.4.0 --- app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml index 9ff952d04925d..23e0d6b07dfda 100644 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml @@ -66,10 +66,6 @@ <magento_code>worldpay</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> </payment_method> - <payment_method name="eway"> - <magento_code>eway</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> <payment_method name="cybersource"> <magento_code>cybersource</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> From 87b561fdc2c2d41b426179fc8f51136eb9fd04de Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 4 Mar 2020 09:55:21 +0200 Subject: [PATCH 259/734] MC-30393: De-couple Cybersource payment methods integrations from core in 2.4.0 --- app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml index 9ff952d04925d..0cec7082fb4e3 100644 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml @@ -70,10 +70,6 @@ <magento_code>eway</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> </payment_method> - <payment_method name="cybersource"> - <magento_code>cybersource</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> <payment_method name="free"> <magento_code>free</magento_code> <signifyd_code>FREE</signifyd_code> From a1252d8d16e5eb065ae07283201bf3f1a31add56 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 4 Mar 2020 10:36:30 +0200 Subject: [PATCH 260/734] MC-30392: De-couple Authorize.net payment methods integrations from core in 2.4.0 --- app/code/Magento/Analytics/etc/di.xml | 16 +- .../Order/View/Info/FraudDetails.php | 60 - .../Order/View/Info/PaymentDetails.php | 29 - .../Authorizenet/Block/Transparent/Iframe.php | 83 -- .../Directpost/Payment/AddConfigured.php | 20 - .../Directpost/Payment/Cancel.php | 20 - .../Payment/ConfigureProductToAdd.php | 20 - .../Payment/ConfigureQuoteItems.php | 20 - .../Authorizenet/Directpost/Payment/Index.php | 17 - .../Directpost/Payment/LoadBlock.php | 17 - .../Authorizenet/Directpost/Payment/Place.php | 157 --- .../Directpost/Payment/ProcessData.php | 20 - .../Directpost/Payment/Redirect.php | 142 --- .../Directpost/Payment/Reorder.php | 20 - .../Directpost/Payment/ReturnQuote.php | 51 - .../Authorizenet/Directpost/Payment/Save.php | 17 - .../Directpost/Payment/ShowUpdateResult.php | 17 - .../Authorizenet/Directpost/Payment/Start.php | 20 - .../Controller/Directpost/Payment.php | 160 --- .../Directpost/Payment/BackendResponse.php | 107 -- .../Controller/Directpost/Payment/Place.php | 161 --- .../Directpost/Payment/Redirect.php | 58 - .../Directpost/Payment/Response.php | 53 - .../Directpost/Payment/ReturnQuote.php | 33 - .../Authorizenet/Helper/Backend/Data.php | 109 -- app/code/Magento/Authorizenet/Helper/Data.php | 344 ------ .../Authorizenet/Helper/DataFactory.php | 60 - app/code/Magento/Authorizenet/LICENSE.txt | 48 - app/code/Magento/Authorizenet/LICENSE_AFL.txt | 48 - .../Authorizenet/Model/Authorizenet.php | 449 ------- app/code/Magento/Authorizenet/Model/Debug.php | 38 - .../Magento/Authorizenet/Model/Directpost.php | 1070 ----------------- .../Authorizenet/Model/Directpost/Request.php | 280 ----- .../Model/Directpost/Request/Factory.php | 30 - .../Model/Directpost/Response.php | 116 -- .../Model/Directpost/Response/Factory.php | 30 - .../Authorizenet/Model/Directpost/Session.php | 82 -- .../Magento/Authorizenet/Model/Request.php | 18 - .../Authorizenet/Model/Request/Factory.php | 54 - .../Model/ResourceModel/Debug.php | 25 - .../Model/ResourceModel/Debug/Collection.php | 28 - .../Magento/Authorizenet/Model/Response.php | 18 - .../Authorizenet/Model/Response/Factory.php | 54 - .../Authorizenet/Model/Source/Cctype.php | 27 - .../Model/Source/PaymentAction.php | 34 - .../Authorizenet/Model/TransactionService.php | 183 --- .../Observer/AddFieldsToResponseObserver.php | 109 -- .../Observer/SaveOrderAfterSubmitObserver.php | 48 - .../UpdateAllEditIncrementsObserver.php | 48 - app/code/Magento/Authorizenet/README.md | 42 - .../Authorizenet/Test/Mftf/LICENSE.txt | 48 - .../Authorizenet/Test/Mftf/LICENSE_AFL.txt | 48 - .../Magento/Authorizenet/Test/Mftf/README.md | 3 - .../Directpost/Payment/RedirectTest.php | 312 ----- .../Directpost/Payment/PlaceTest.php | 332 ----- .../Directpost/Payment/RedirectTest.php | 89 -- .../Test/Unit/Helper/Backend/DataTest.php | 135 --- .../Test/Unit/Helper/DataTest.php | 193 --- .../Model/Directpost/Request/FactoryTest.php | 49 - .../Unit/Model/Directpost/RequestTest.php | 80 -- .../Model/Directpost/Response/FactoryTest.php | 49 - .../Unit/Model/Directpost/ResponseTest.php | 128 -- .../Unit/Model/Directpost/SessionTest.php | 58 - .../Test/Unit/Model/DirectpostTest.php | 885 -------------- .../Test/Unit/Model/Request/FactoryTest.php | 49 - .../Test/Unit/Model/Response/FactoryTest.php | 49 - .../Unit/Model/TransactionServiceTest.php | 167 --- .../AddFieldsToResponseObserverTest.php | 237 ---- app/code/Magento/Authorizenet/composer.json | 34 - .../Magento/Authorizenet/etc/adminhtml/di.xml | 22 - .../Authorizenet/etc/adminhtml/events.xml | 12 - .../Authorizenet/etc/adminhtml/routes.xml | 14 - .../Authorizenet/etc/adminhtml/system.xml | 101 -- app/code/Magento/Authorizenet/etc/config.xml | 41 - app/code/Magento/Authorizenet/etc/di.xml | 43 - .../Magento/Authorizenet/etc/frontend/di.xml | 45 - .../Authorizenet/etc/frontend/events.xml | 15 - .../Authorizenet/etc/frontend/page_types.xml | 11 - .../Authorizenet/etc/frontend/routes.xml | 14 - .../Authorizenet/etc/frontend/sections.xml | 14 - app/code/Magento/Authorizenet/etc/module.xml | 16 - app/code/Magento/Authorizenet/etc/payment.xml | 15 - app/code/Magento/Authorizenet/i18n/en_US.csv | 75 -- .../Magento/Authorizenet/registration.php | 9 - ...thorizenet_directpost_payment_redirect.xml | 12 - .../layout/sales_order_create_index.xml | 17 - ...order_create_load_block_billing_method.xml | 17 - .../adminhtml/layout/sales_order_view.xml | 14 - .../templates/directpost/iframe.phtml | 31 - .../adminhtml/templates/directpost/info.phtml | 152 --- .../order/view/info/fraud_details.phtml | 56 - .../view/adminhtml/web/js/direct-post.js | 345 ------ ...net_directpost_payment_backendresponse.xml | 12 - ...thorizenet_directpost_payment_redirect.xml | 12 - ...thorizenet_directpost_payment_response.xml | 12 - .../frontend/layout/checkout_index_index.xml | 49 - .../view/frontend/requirejs-config.js | 13 - .../web/js/view/payment/authorizenet.js | 22 - .../authorizenet-directpost.js | 64 - .../payment/authorizenet-directpost.html | 71 -- .../AuthorizenetAcceptjs/Block/Form.php | 64 - .../AuthorizenetAcceptjs/Block/Info.php | 33 - .../AuthorizenetAcceptjs/Block/Payment.php | 76 -- .../Command/AcceptPaymentStrategyCommand.php | 77 -- .../Command/CaptureStrategyCommand.php | 143 --- .../Command/FetchTransactionInfoCommand.php | 90 -- .../Gateway/Command/GatewayQueryCommand.php | 103 -- .../RefundTransactionStrategyCommand.php | 106 -- .../AuthorizenetAcceptjs/Gateway/Config.php | 189 --- .../Gateway/Http/Client.php | 131 -- .../Payload/Filter/RemoveFieldsFilter.php | 45 - .../Gateway/Http/Payload/FilterInterface.php | 26 - .../Gateway/Http/TransferFactory.php | 61 - .../Gateway/Request/AcceptFdsDataBuilder.php | 68 -- .../Gateway/Request/AddressDataBuilder.php | 80 -- .../Gateway/Request/AmountDataBuilder.php | 49 - .../Request/AuthenticationDataBuilder.php | 62 - .../Gateway/Request/AuthorizeDataBuilder.php | 72 -- .../Gateway/Request/CaptureDataBuilder.php | 76 -- .../Gateway/Request/CustomSettingsBuilder.php | 65 - .../Gateway/Request/CustomerDataBuilder.php | 55 - .../Gateway/Request/OrderDataBuilder.php | 51 - .../Request/PassthroughDataBuilder.php | 61 - .../Gateway/Request/PaymentDataBuilder.php | 59 - .../Gateway/Request/PoDataBuilder.php | 55 - .../Request/RefundPaymentDataBuilder.php | 61 - .../RefundReferenceTransactionDataBuilder.php | 56 - .../RefundTransactionTypeDataBuilder.php | 34 - .../Gateway/Request/RequestTypeBuilder.php | 48 - .../Gateway/Request/SaleDataBuilder.php | 72 -- .../Gateway/Request/ShippingDataBuilder.php | 59 - .../Gateway/Request/SolutionDataBuilder.php | 56 - .../Gateway/Request/StoreConfigBuilder.php | 46 - .../Gateway/Request/StubDataBuilder.php | 31 - .../Request/TransactionDetailsDataBuilder.php | 72 -- .../Gateway/Request/VoidDataBuilder.php | 63 - .../CloseParentTransactionHandler.php | 48 - .../ClosePartialTransactionHandler.php | 30 - .../Response/CloseTransactionHandler.php | 68 -- .../Response/PaymentResponseHandler.php | 58 - .../Response/PaymentReviewStatusHandler.php | 66 - .../TransactionDetailsResponseHandler.php | 68 -- .../Gateway/Response/TransactionIdHandler.php | 57 - .../Gateway/Response/VoidResponseHandler.php | 52 - .../Gateway/SubjectReader.php | 100 -- .../Validator/GeneralResponseValidator.php | 82 -- .../Validator/TransactionHashValidator.php | 201 ---- .../TransactionResponseValidator.php | 102 -- .../Magento/AuthorizenetAcceptjs/LICENSE.txt | 48 - .../AuthorizenetAcceptjs/LICENSE_AFL.txt | 48 - .../Model/Adminhtml/Source/Cctype.php | 28 - .../Model/Adminhtml/Source/Environment.php | 40 - .../Model/Adminhtml/Source/PaymentAction.php | 35 - .../Model/PassthroughDataObject.php | 22 - .../Model/Ui/ConfigProvider.php | 63 - .../Observer/DataAssignObserver.php | 55 - .../Magento/AuthorizenetAcceptjs/README.md | 29 - .../Setup/Patch/Data/CopyCurrentConfig.php | 233 ---- .../Test/Mftf/LICENSE.txt | 48 - .../Test/Mftf/LICENSE_AFL.txt | 48 - .../AuthorizenetAcceptjs/Test/Mftf/README.md | 3 - .../Test/Unit/Block/FormTest.php | 59 - .../Test/Unit/Block/InfoTest.php | 80 -- .../Test/Unit/Block/PaymentTest.php | 53 - .../AcceptPaymentStrategyCommandTest.php | 131 -- .../Command/CaptureStrategyCommandTest.php | 181 --- .../FetchTransactionInfoCommandTest.php | 132 -- .../Command/GatewayQueryCommandTest.php | 196 --- .../RefundTransactionStrategyCommandTest.php | 225 ---- .../Test/Unit/Gateway/ConfigTest.php | 136 --- .../Test/Unit/Gateway/Http/ClientTest.php | 218 ---- .../Payload/Filter/RemoveFieldsFilterTest.php | 36 - .../Unit/Gateway/Http/TransferFactoryTest.php | 73 -- .../Request/AcceptFdsDataBuilderTest.php | 70 -- .../Request/AddressDataBuilderTest.php | 138 --- .../Gateway/Request/AmountDataBuilderTest.php | 42 - .../Request/AuthenticationDataBuilderTest.php | 78 -- .../Request/AuthorizationDataBuilderTest.php | 70 -- .../Request/CaptureDataBuilderTest.php | 77 -- .../Request/CustomSettingsBuilderTest.php | 74 -- .../Request/CustomerDataBuilderTest.php | 79 -- .../Gateway/Request/OrderDataBuilderTest.php | 74 -- .../Request/PassthroughDataBuilderTest.php | 52 - .../Request/PaymentDataBuilderTest.php | 72 -- .../Gateway/Request/PoDataBuilderTest.php | 61 - .../Request/RefundPaymentDataBuilderTest.php | 70 -- ...undReferenceTransactionDataBuilderTest.php | 69 -- .../RefundTransactionTypeDataBuilderTest.php | 30 - .../Request/RequestTypeBuilderTest.php | 36 - .../Gateway/Request/SaleDataBuilderTest.php | 70 -- .../Request/ShippingDataBuilderTest.php | 75 -- .../Request/SolutionDataBuilderTest.php | 75 -- .../Request/StoreConfigBuilderTest.php | 68 -- .../TransactionDetailsDataBuilderTest.php | 89 -- .../Gateway/Request/VoidDataBuilderTest.php | 68 -- .../CloseParentTransactionHandlerTest.php | 62 - .../Response/CloseTransactionHandlerTest.php | 62 - .../Response/PaymentResponseHandlerTest.php | 112 -- .../PaymentReviewStatusHandlerTest.php | 136 --- .../TransactionDetailsResponseHandlerTest.php | 82 -- .../Response/TransactionIdHandlerTest.php | 92 -- .../Response/VoidResponseHandlerTest.php | 72 -- .../Test/Unit/Gateway/SubjectReaderTest.php | 119 -- .../GeneralResponseValidatorTest.php | 161 --- .../TransactionHashValidatorTest.php | 280 ----- .../TransactionResponseValidatorTest.php | 231 ---- .../Test/Unit/Model/Ui/ConfigProviderTest.php | 76 -- .../Unit/Observer/DataAssignObserverTest.php | 80 -- .../Patch/Data/CopyCurrentConfigTest.php | 149 --- .../AuthorizenetAcceptjs/composer.json | 31 - .../AuthorizenetAcceptjs/etc/adminhtml/di.xml | 39 - .../etc/adminhtml/system.xml | 139 --- .../authorizenet_acceptjs_error_mapping.xml | 15 - .../AuthorizenetAcceptjs/etc/config.xml | 57 - .../Magento/AuthorizenetAcceptjs/etc/di.xml | 435 ------- .../AuthorizenetAcceptjs/etc/events.xml | 13 - .../AuthorizenetAcceptjs/etc/frontend/di.xml | 30 - .../AuthorizenetAcceptjs/etc/module.xml | 19 - .../AuthorizenetAcceptjs/etc/payment.xml | 15 - .../AuthorizenetAcceptjs/i18n/en_US.csv | 22 - .../AuthorizenetAcceptjs/registration.php | 11 - .../layout/sales_order_create_index.xml | 24 - ...order_create_load_block_billing_method.xml | 17 - .../view/adminhtml/templates/form/cc.phtml | 92 -- .../adminhtml/templates/payment/script.phtml | 24 - .../view/adminhtml/web/js/authorizenet.js | 196 --- .../view/adminhtml/web/js/payment-form.js | 17 - .../view/base/requirejs-config.js | 19 - .../web/js/view/payment/acceptjs-client.js | 73 -- .../web/js/view/payment/acceptjs-factory.js | 38 - .../web/js/view/payment/response-validator.js | 38 - .../web/js/view/payment/validator-handler.js | 59 - .../frontend/layout/checkout_index_index.xml | 49 - .../web/js/view/payment/authorizenet.js | 20 - .../method-renderer/authorizenet-accept.js | 148 --- .../payment/authorizenet-acceptjs.html | 46 - .../Request/Authorize3DSecureBuilder.php | 85 -- .../Validator/CavvResponseValidator.php | 86 -- .../Magento/AuthorizenetCardinal/LICENSE.txt | 48 - .../AuthorizenetCardinal/LICENSE_AFL.txt | 48 - .../Model/Checkout/ConfigProvider.php | 48 - .../AuthorizenetCardinal/Model/Config.php | 52 - .../Observer/DataAssignObserver.php | 66 - .../Magento/AuthorizenetCardinal/README.md | 23 - .../Unit/Observer/DataAssignObserverTest.php | 100 -- .../AuthorizenetCardinal/composer.json | 31 - .../etc/adminhtml/system.xml | 22 - .../AuthorizenetCardinal/etc/config.xml | 16 - .../Magento/AuthorizenetCardinal/etc/di.xml | 32 - .../AuthorizenetCardinal/etc/events.xml | 13 - .../AuthorizenetCardinal/etc/frontend/di.xml | 18 - .../AuthorizenetCardinal/etc/module.xml | 15 - .../AuthorizenetCardinal/registration.php | 9 - .../view/frontend/requirejs-config.js | 15 - .../web/js/authorizenet-accept-mixin.js | 72 -- .../Model/AuthorizenetDataProvider.php | 71 -- .../Magento/AuthorizenetGraphQl/README.md | 9 - .../Magento/AuthorizenetGraphQl/composer.json | 25 - .../AuthorizenetGraphQl/etc/graphql/di.xml | 16 - .../AuthorizenetGraphQl/etc/module.xml | 10 - .../AuthorizenetGraphQl/etc/schema.graphqls | 12 - .../AuthorizenetGraphQl/registration.php | 10 - ...dminCardinalCommerceSettingsHiddenTest.xml | 35 - .../Signifyd/etc/signifyd_payment_mapping.xml | 4 - .../Unit/App/Config/Type/TranslationTest.php | 1 - composer.json | 4 - .../Gateway/Http/MockClient.php | 135 --- .../Validator/TransactionHashValidator.php | 29 - .../TestModuleAuthorizenetAcceptjs/etc/di.xml | 11 - .../etc/module.xml | 10 - .../registration.php | 13 - .../Customer/SetPaymentMethodTest.php | 291 ----- .../Guest/SetPaymentMethodTest.php | 198 --- .../Customer/GetSelectedPaymentMethodTest.php | 5 +- .../GraphQl/Quote/Customer/PlaceOrderTest.php | 3 - .../SetOfflinePaymentMethodsOnCartTest.php | 6 +- .../Customer/SetPaymentMethodOnCartTest.php | 3 +- ...etPurchaseOrderPaymentMethodOnCartTest.php | 8 +- .../Guest/AllowGuestCheckoutOptionTest.php | 9 +- .../Guest/GetSelectedPaymentMethodTest.php | 5 +- .../GraphQl/Quote/Guest/PlaceOrderTest.php | 3 - .../SetOfflinePaymentMethodsOnCartTest.php | 6 +- .../SetPaymentMethodAndPlaceOrderTest.php | 3 +- .../Guest/SetPaymentMethodOnCartTest.php | 3 +- ...etPurchaseOrderPaymentMethodOnCartTest.php | 8 +- dev/tests/functional/credentials.xml.dist | 8 - .../etc/repository_replacer_payments.xml | 7 - .../Directpost/Payment/PlaceTest.php | 166 --- .../Directpost/Payment/PlaceTesting.php | 21 - .../Payment/BackendResponseTest.php | 63 - .../Directpost/Payment/ResponseTest.php | 245 ---- .../Controller/Directpost/PaymentTest.php | 25 - .../Model/Directpost/RequestTest.php | 102 -- .../Authorizenet/Model/DirectpostTest.php | 252 ---- .../Magento/Authorizenet/_files/order.php | 86 -- .../_files/transaction_details.xml | 81 -- .../Fixture/full_order_with_capture.php | 69 -- .../full_order_with_capture_rollback.php | 28 - .../Fixture/order_auth_only.php | 74 -- .../Fixture/order_auth_only_rollback.php | 9 - .../Fixture/order_captured.php | 77 -- .../Fixture/order_captured_rollback.php | 28 - .../AuthorizenetAcceptjs/Fixture/payment.php | 19 - .../Gateway/AbstractTest.php | 91 -- .../Gateway/Command/AcceptFdsCommandTest.php | 49 - .../Gateway/Command/AuthorizeCommandTest.php | 70 -- .../Gateway/Command/CancelCommandTest.php | 62 - .../FetchTransactionInfoCommandTest.php | 184 --- .../Command/RefundSettledCommandTest.php | 79 -- .../Gateway/Command/SaleCommandTest.php | 71 -- .../Gateway/Command/SettleCommandTest.php | 52 - .../Command/TransactionDetailsCommandTest.php | 50 - .../Gateway/Command/VoidCommandTest.php | 53 - .../Gateway/ConfigTest.php | 51 - .../Response/CloseTransactionHandlerTest.php | 47 - .../_files/expected_request/accept_fds.php | 20 - .../_files/expected_request/authorize.php | 66 - .../_files/expected_request/refund.php | 56 - .../_files/expected_request/sale.php | 66 - .../_files/expected_request/settle.php | 28 - .../expected_request/transaction_details.php | 17 - .../transaction_details_authorized.php | 17 - .../_files/expected_request/void.php | 20 - .../_files/full_order.php | 129 -- .../_files/response/authorize.php | 47 - .../_files/response/generic_success.php | 17 - .../_files/response/refund.php | 40 - .../_files/response/sale.php | 47 - .../_files/response/settle.php | 40 - .../transaction_details_authorized.php | 22 - .../response/transaction_details_declined.php | 22 - .../transaction_details_fds_pending.php | 22 - .../transaction_details_settled_capture.php | 22 - .../response/transaction_details_voided.php | 22 - .../_files/response/void.php | 30 - .../Fixture/expected_request/authorize.php | 70 -- .../Fixture/expected_request/sale.php | 70 -- .../Fixture/full_order_with_3dsecure.php | 21 - .../Fixture/response/authorize.php | 47 - .../Fixture/response/cardinal_jwt.php | 48 - .../Gateway/Command/AuthorizeCommandTest.php | 187 --- .../Gateway/Command/SaleCommandTest.php | 85 -- .../PlaceOrderWithAuthorizeNetTest.php | 158 --- ...SetAuthorizeNetPaymentMethodOnCartTest.php | 102 -- .../Guest/PlaceOrderWithAuthorizeNetTest.php | 148 --- ...SetAuthorizeNetPaymentMethodOnCartTest.php | 89 -- .../add_simple_products_authorizenet.php | 28 - .../_files/request_authorize.php | 65 - .../_files/request_authorize_customer.php | 65 - .../_files/response_authorize.php | 47 - .../set_new_billing_address_authorizenet.php | 44 - .../set_new_shipping_address_authorizenet.php | 43 - .../_files/simple_product_authorizenet.php | 47 - .../simple_product_authorizenet_rollback.php | 8 - .../testFromCreateProject/composer.lock | 75 -- .../_files/testSkeleton/composer.lock | 75 -- .../_files/enable_authorizenetacceptjs.php | 32 - .../enable_authorizenetacceptjs_rollback.php | 26 - .../_files/enable_offline_payment_methods.php | 2 - ...nable_offline_payment_methods_rollback.php | 1 - .../_files/dependency_test/tables_ce.php | 1 - .../Test/Js/_files/blacklist/magento.txt | 1 - .../Test/Legacy/_files/obsolete_classes.php | 200 +++ .../Php/_files/phpcpd/blacklist/common.txt | 1 - 364 files changed, 226 insertions(+), 25859 deletions(-) delete mode 100644 app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php delete mode 100644 app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php delete mode 100644 app/code/Magento/Authorizenet/Block/Transparent/Iframe.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Cancel.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureProductToAdd.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureQuoteItems.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Index.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/LoadBlock.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Place.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Redirect.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ReturnQuote.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ShowUpdateResult.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Start.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php delete mode 100644 app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php delete mode 100644 app/code/Magento/Authorizenet/Helper/Backend/Data.php delete mode 100644 app/code/Magento/Authorizenet/Helper/Data.php delete mode 100644 app/code/Magento/Authorizenet/Helper/DataFactory.php delete mode 100644 app/code/Magento/Authorizenet/LICENSE.txt delete mode 100644 app/code/Magento/Authorizenet/LICENSE_AFL.txt delete mode 100644 app/code/Magento/Authorizenet/Model/Authorizenet.php delete mode 100644 app/code/Magento/Authorizenet/Model/Debug.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost/Request.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost/Response.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php delete mode 100644 app/code/Magento/Authorizenet/Model/Directpost/Session.php delete mode 100644 app/code/Magento/Authorizenet/Model/Request.php delete mode 100644 app/code/Magento/Authorizenet/Model/Request/Factory.php delete mode 100644 app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php delete mode 100644 app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php delete mode 100644 app/code/Magento/Authorizenet/Model/Response.php delete mode 100644 app/code/Magento/Authorizenet/Model/Response/Factory.php delete mode 100644 app/code/Magento/Authorizenet/Model/Source/Cctype.php delete mode 100644 app/code/Magento/Authorizenet/Model/Source/PaymentAction.php delete mode 100644 app/code/Magento/Authorizenet/Model/TransactionService.php delete mode 100644 app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php delete mode 100644 app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php delete mode 100644 app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php delete mode 100644 app/code/Magento/Authorizenet/README.md delete mode 100644 app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt delete mode 100644 app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt delete mode 100644 app/code/Magento/Authorizenet/Test/Mftf/README.md delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php delete mode 100644 app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php delete mode 100644 app/code/Magento/Authorizenet/composer.json delete mode 100644 app/code/Magento/Authorizenet/etc/adminhtml/di.xml delete mode 100644 app/code/Magento/Authorizenet/etc/adminhtml/events.xml delete mode 100644 app/code/Magento/Authorizenet/etc/adminhtml/routes.xml delete mode 100644 app/code/Magento/Authorizenet/etc/adminhtml/system.xml delete mode 100644 app/code/Magento/Authorizenet/etc/config.xml delete mode 100644 app/code/Magento/Authorizenet/etc/di.xml delete mode 100644 app/code/Magento/Authorizenet/etc/frontend/di.xml delete mode 100644 app/code/Magento/Authorizenet/etc/frontend/events.xml delete mode 100644 app/code/Magento/Authorizenet/etc/frontend/page_types.xml delete mode 100644 app/code/Magento/Authorizenet/etc/frontend/routes.xml delete mode 100644 app/code/Magento/Authorizenet/etc/frontend/sections.xml delete mode 100644 app/code/Magento/Authorizenet/etc/module.xml delete mode 100644 app/code/Magento/Authorizenet/etc/payment.xml delete mode 100644 app/code/Magento/Authorizenet/i18n/en_US.csv delete mode 100644 app/code/Magento/Authorizenet/registration.php delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/layout/adminhtml_authorizenet_directpost_payment_redirect.xml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml delete mode 100644 app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js delete mode 100644 app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_backendresponse.xml delete mode 100644 app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_redirect.xml delete mode 100644 app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_response.xml delete mode 100644 app/code/Magento/Authorizenet/view/frontend/layout/checkout_index_index.xml delete mode 100644 app/code/Magento/Authorizenet/view/frontend/requirejs-config.js delete mode 100644 app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/authorizenet.js delete mode 100644 app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/method-renderer/authorizenet-directpost.js delete mode 100644 app/code/Magento/Authorizenet/view/frontend/web/template/payment/authorizenet-directpost.html delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Block/Form.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Block/Info.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Block/Payment.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/TransferFactory.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/TransactionDetailsDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Environment.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Model/Ui/ConfigProvider.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/README.md delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/composer.json delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/config.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/di.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/events.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/module.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/registration.php delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_index.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js delete mode 100644 app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html delete mode 100644 app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/LICENSE.txt delete mode 100644 app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt delete mode 100644 app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/Model/Config.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/README.md delete mode 100644 app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/composer.json delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/config.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/di.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/events.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/etc/module.xml delete mode 100644 app/code/Magento/AuthorizenetCardinal/registration.php delete mode 100644 app/code/Magento/AuthorizenetCardinal/view/frontend/requirejs-config.js delete mode 100644 app/code/Magento/AuthorizenetCardinal/view/frontend/web/js/authorizenet-accept-mixin.js delete mode 100644 app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php delete mode 100644 app/code/Magento/AuthorizenetGraphQl/README.md delete mode 100644 app/code/Magento/AuthorizenetGraphQl/composer.json delete mode 100644 app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml delete mode 100644 app/code/Magento/AuthorizenetGraphQl/etc/module.xml delete mode 100644 app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls delete mode 100644 app/code/Magento/AuthorizenetGraphQl/registration.php delete mode 100644 app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml delete mode 100644 dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php delete mode 100644 dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php delete mode 100644 dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml delete mode 100644 dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml delete mode 100644 dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php delete mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php delete mode 100644 dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php delete mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php delete mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php diff --git a/app/code/Magento/Analytics/etc/di.xml b/app/code/Magento/Analytics/etc/di.xml index b9bb9cc9ff00c..89b643485861a 100644 --- a/app/code/Magento/Analytics/etc/di.xml +++ b/app/code/Magento/Analytics/etc/di.xml @@ -161,15 +161,13 @@ <item name="25" xsi:type="string">payment/banktransfer/active</item> <item name="26" xsi:type="string">payment/cashondelivery/title</item> <item name="27" xsi:type="string">payment/cashondelivery/active</item> - <item name="28" xsi:type="string">payment/authorizenet_directpost/title</item> - <item name="29" xsi:type="string">payment/authorizenet_directpost/active</item> - <item name="30" xsi:type="string">payment/paypal_billing_agreement/title</item> - <item name="31" xsi:type="string">payment/paypal_billing_agreement/active</item> - <item name="32" xsi:type="string">payment/braintree/title</item> - <item name="33" xsi:type="string">payment/braintree/active</item> - <item name="34" xsi:type="string">payment/braintree_paypal/title</item> - <item name="35" xsi:type="string">payment/braintree_paypal/active</item> - <item name="36" xsi:type="string">analytics/general/vertical</item> + <item name="28" xsi:type="string">payment/paypal_billing_agreement/title</item> + <item name="29" xsi:type="string">payment/paypal_billing_agreement/active</item> + <item name="30" xsi:type="string">payment/braintree/title</item> + <item name="31" xsi:type="string">payment/braintree/active</item> + <item name="32" xsi:type="string">payment/braintree_paypal/title</item> + <item name="33" xsi:type="string">payment/braintree_paypal/active</item> + <item name="34" xsi:type="string">analytics/general/vertical</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php b/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php deleted file mode 100644 index c693ebe95d52b..0000000000000 --- a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Block\Adminhtml\Order\View\Info; - -use Magento\Authorizenet\Model\Directpost; - -/** - * Fraud information block for Authorize.net payment method - * - * @api - * @since 100.0.2 - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class FraudDetails extends \Magento\Backend\Block\Template -{ - /** - * @var \Magento\Framework\Registry - */ - protected $registry; - - /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param array $data - */ - public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Framework\Registry $registry, - array $data = [] - ) { - $this->registry = $registry; - parent::__construct($context, $data); - } - - /** - * Return payment method model - * - * @return \Magento\Sales\Model\Order\Payment - */ - public function getPayment() - { - $order = $this->registry->registry('current_order'); - return $order->getPayment(); - } - - /** - * Produce and return the block's HTML output - * - * @return string - */ - protected function _toHtml() - { - return ($this->getPayment()->getMethod() === Directpost::METHOD_CODE) ? parent::_toHtml() : ''; - } -} diff --git a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php b/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php deleted file mode 100644 index 23034270640dd..0000000000000 --- a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Block\Adminhtml\Order\View\Info; - -use Magento\Framework\Phrase; -use Magento\Payment\Block\ConfigurableInfo; - -/** - * Payment information block for Authorize.net payment method - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class PaymentDetails extends ConfigurableInfo -{ - /** - * Returns localized label for payment info block - * - * @param string $field - * @return string | Phrase - */ - protected function getLabel($field) - { - return __($field); - } -} diff --git a/app/code/Magento/Authorizenet/Block/Transparent/Iframe.php b/app/code/Magento/Authorizenet/Block/Transparent/Iframe.php deleted file mode 100644 index 65161413cb18f..0000000000000 --- a/app/code/Magento/Authorizenet/Block/Transparent/Iframe.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Block\Transparent; - -use Magento\Payment\Block\Transparent\Iframe as TransparentIframe; - -/** - * Transparent Iframe block for Authorize.net payments - * @api - * @since 100.0.2 - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Iframe extends TransparentIframe -{ - /** - * @var \Magento\Authorizenet\Helper\DataFactory - */ - protected $dataFactory; - - /** - * @var \Magento\Framework\Message\ManagerInterface - */ - private $messageManager; - - /** - * Constructor - * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Authorizenet\Helper\DataFactory $dataFactory - * @param \Magento\Framework\Message\ManagerInterface $messageManager - * @param array $data - */ - public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Authorizenet\Helper\DataFactory $dataFactory, - \Magento\Framework\Message\ManagerInterface $messageManager, - array $data = [] - ) { - $this->dataFactory = $dataFactory; - $this->messageManager = $messageManager; - parent::__construct($context, $registry, $data); - } - - /** - * Get helper data - * - * @param string $area - * @return \Magento\Authorizenet\Helper\Backend\Data|\Magento\Authorizenet\Helper\Data - */ - public function getHelper($area) - { - return $this->dataFactory->create($area); - } - - /** - * {inheritdoc} - */ - protected function _beforeToHtml() - { - $this->addSuccessMessage(); - return parent::_beforeToHtml(); - } - - /** - * Add success message - * - * @return void - */ - private function addSuccessMessage() - { - $params = $this->getParams(); - if (isset($params['redirect_parent'])) { - $this->messageManager->addSuccess(__('You created the order.')); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php deleted file mode 100644 index f71314613fc1f..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPutActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create\AddConfigured as BaseAddConfigured; - -/** - * Class AddConfigured - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class AddConfigured extends BaseAddConfigured implements HttpPutActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Cancel.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Cancel.php deleted file mode 100644 index 3ebea4704db7e..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Cancel.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create\Cancel as BaseCancel; - -/** - * Class Cancel - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Cancel extends BaseCancel implements HttpPostActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureProductToAdd.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureProductToAdd.php deleted file mode 100644 index 19eb4571a852e..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureProductToAdd.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPutActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create\ConfigureProductToAdd as BaseConfigureProductToAdd; - -/** - * Class ConfigureProductToAdd - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class ConfigureProductToAdd extends BaseConfigureProductToAdd implements HttpPutActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureQuoteItems.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureQuoteItems.php deleted file mode 100644 index d314149059c72..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ConfigureQuoteItems.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPutActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create\ConfigureQuoteItems as BaseConfigureQuoteItems; - -/** - * Class ConfigureQuoteItems - * @deprecated 2.3 Authorize.net is removing all support for this payment method - */ -class ConfigureQuoteItems extends BaseConfigureQuoteItems implements HttpPutActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Index.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Index.php deleted file mode 100644 index 33ac620499e71..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Index.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class Index - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Index extends \Magento\Sales\Controller\Adminhtml\Order\Create\Index -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/LoadBlock.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/LoadBlock.php deleted file mode 100644 index 577840c0a9ba4..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/LoadBlock.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class LoadBlock - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class LoadBlock extends \Magento\Sales\Controller\Adminhtml\Order\Create\LoadBlock -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Place.php deleted file mode 100644 index fc4cce07bd08f..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Place.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Authorizenet\Helper\Backend\Data as DataHelper; -use Magento\Backend\App\Action\Context; -use Magento\Backend\Model\View\Result\ForwardFactory; -use Magento\Catalog\Helper\Product; -use Magento\Framework\Escaper; -use Magento\Framework\View\Result\PageFactory; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create; - -/** - * Class Place - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Place extends Create implements HttpPostActionInterface -{ - /** - * @var DataHelper - */ - protected $helper; - - /** - * Constructor - * - * @param Context $context - * @param Product $productHelper - * @param Escaper $escaper - * @param PageFactory $resultPageFactory - * @param ForwardFactory $resultForwardFactory - * @param DataHelper $helper - */ - public function __construct( - Context $context, - Product $productHelper, - Escaper $escaper, - PageFactory $resultPageFactory, - ForwardFactory $resultForwardFactory, - DataHelper $helper - ) { - $this->helper = $helper; - parent::__construct($context, $productHelper, $escaper, $resultPageFactory, $resultForwardFactory); - } - - /** - * Send request to authorize.net - * - * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) - */ - public function execute() - { - $paymentParam = $this->getRequest()->getParam('payment'); - $controller = $this->getRequest()->getParam('controller'); - $this->getRequest()->setPostValue('collect_shipping_rates', 1); - $this->_processActionData('save'); - - //get confirmation by email flag - $orderData = $this->getRequest()->getPost('order'); - $sendConfirmationFlag = 0; - if ($orderData) { - $sendConfirmationFlag = !empty($orderData['send_confirmation']) ? 1 : 0; - } else { - $orderData = []; - } - - if (isset($paymentParam['method'])) { - $result = []; - //create order partially - $this->_getOrderCreateModel()->setPaymentData($paymentParam); - $this->_getOrderCreateModel()->getQuote()->getPayment()->addData($paymentParam); - - $orderData['send_confirmation'] = 0; - $this->getRequest()->setPostValue('order', $orderData); - - try { - //do not cancel old order. - $oldOrder = $this->_getOrderCreateModel()->getSession()->getOrder(); - $oldOrder->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_CANCEL, false); - - $order = $this->_getOrderCreateModel()->setIsValidate( - true - )->importPostData( - $this->getRequest()->getPost('order') - )->createOrder(); - - $payment = $order->getPayment(); - if ($payment && $payment->getMethod() == $this->_objectManager->create( - \Magento\Authorizenet\Model\Directpost::class - )->getCode() - ) { - //return json with data. - $session = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $session->addCheckoutOrderIncrementId($order->getIncrementId()); - $session->setLastOrderIncrementId($order->getIncrementId()); - - /** @var \Magento\Authorizenet\Model\Directpost $method */ - $method = $payment->getMethodInstance(); - $method->setDataHelper($this->helper); - $requestToAuthorizenet = $method->generateRequestFromOrder($order); - $requestToAuthorizenet->setControllerActionName($controller); - $requestToAuthorizenet->setOrderSendConfirmation($sendConfirmationFlag); - $requestToAuthorizenet->setStoreId($this->_getOrderCreateModel()->getQuote()->getStoreId()); - - $adminUrl = $this->_objectManager->get(\Magento\Backend\Model\UrlInterface::class); - if ($adminUrl->useSecretKey()) { - $requestToAuthorizenet->setKey( - $adminUrl->getSecretKey('adminhtml', 'authorizenet_directpost_payment', 'redirect') - ); - } - $result['directpost'] = ['fields' => $requestToAuthorizenet->getData()]; - } - - $result['success'] = 1; - $isError = false; - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $message = $e->getMessage(); - if (!empty($message)) { - $this->messageManager->addErrorMessage($message); - } - $isError = true; - } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Order saving error: %1', $e->getMessage())); - $isError = true; - } - - if ($isError) { - $result['success'] = 0; - $result['error'] = 1; - $result['redirect'] = $this->_objectManager->get( - \Magento\Backend\Model\UrlInterface::class - )->getUrl( - 'sales/order_create/' - ); - } - - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($result) - ); - } else { - $result = ['error_messages' => __('Please choose a payment method.')]; - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($result) - ); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php deleted file mode 100644 index 3d0d572bd6265..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Sales\Controller\Adminhtml\Order\Create\ProcessData as BaseProcessData; -use Magento\Framework\App\Action\HttpPostActionInterface; - -/** - * Class ProcessData - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class ProcessData extends BaseProcessData implements HttpPostActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Redirect.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Redirect.php deleted file mode 100644 index 333751f93653a..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Redirect.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Backend\App\Action; -use Magento\Backend\Model\View\Result\ForwardFactory; -use Magento\Framework\View\Result\LayoutFactory; -use Magento\Framework\View\Result\PageFactory; -use Magento\Payment\Block\Transparent\Iframe; -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create; - -/** - * Class Redirect - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Redirect extends Create implements HttpGetActionInterface, HttpPostActionInterface -{ - /** - * Core registry - * - * @var \Magento\Framework\Registry - */ - protected $_coreRegistry; - - /** - * @var LayoutFactory - */ - protected $resultLayoutFactory; - - /** - * @var \Magento\Authorizenet\Helper\Backend\Data - */ - protected $helper; - - /** - * @param Action\Context $context - * @param \Magento\Catalog\Helper\Product $productHelper - * @param \Magento\Framework\Escaper $escaper - * @param PageFactory $resultPageFactory - * @param ForwardFactory $resultForwardFactory - * @param \Magento\Framework\Registry $coreRegistry - * @param LayoutFactory $resultLayoutFactory - * @param \Magento\Authorizenet\Helper\Backend\Data $helper - */ - public function __construct( - Action\Context $context, - \Magento\Catalog\Helper\Product $productHelper, - \Magento\Framework\Escaper $escaper, - PageFactory $resultPageFactory, - ForwardFactory $resultForwardFactory, - \Magento\Framework\Registry $coreRegistry, - LayoutFactory $resultLayoutFactory, - \Magento\Authorizenet\Helper\Backend\Data $helper - ) { - $this->_coreRegistry = $coreRegistry; - $this->resultLayoutFactory = $resultLayoutFactory; - $this->helper = $helper; - parent::__construct( - $context, - $productHelper, - $escaper, - $resultPageFactory, - $resultForwardFactory - ); - } - - /** - * Return quote - * - * @param bool $cancelOrder - * @param string $errorMsg - * @return void - */ - protected function _returnQuote($cancelOrder, $errorMsg) - { - $directpostSession = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $incrementId = $directpostSession->getLastOrderIncrementId(); - if ($incrementId && $directpostSession->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - $directpostSession->removeCheckoutOrderIncrementId($order->getIncrementId()); - if ($cancelOrder && $order->getState() == \Magento\Sales\Model\Order::STATE_PENDING_PAYMENT) { - $order->registerCancellation($errorMsg)->save(); - } - } - } - } - - /** - * Retrieve params and put javascript into iframe - * - * @return \Magento\Framework\View\Result\Layout - */ - public function execute() - { - $redirectParams = $this->getRequest()->getParams(); - $params = []; - if (!empty($redirectParams['success']) - && isset($redirectParams['x_invoice_num']) - && isset($redirectParams['controller_action_name']) - ) { - $params['redirect_parent'] = $this->helper->getSuccessOrderUrl($redirectParams); - $directpostSession = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $directpostSession->unsetData('quote_id'); - //cancel old order - $oldOrder = $this->_getOrderCreateModel()->getSession()->getOrder(); - if ($oldOrder->getId()) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class) - ->loadByIncrementId($redirectParams['x_invoice_num']); - - if ($order->getId()) { - $oldOrder->cancel()->save(); - $order->save(); - $this->_getOrderCreateModel()->getSession()->unsOrderId(); - } - } - //clear sessions - $this->_getSession()->clearStorage(); - $directpostSession->removeCheckoutOrderIncrementId($redirectParams['x_invoice_num']); - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->clearStorage(); - $this->messageManager->addSuccess(__('You created the order.')); - } - - if (!empty($redirectParams['error_msg'])) { - $cancelOrder = empty($redirectParams['x_invoice_num']); - $this->_returnQuote($cancelOrder, $redirectParams['error_msg']); - } - - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, array_merge($params, $redirectParams)); - return $this->resultLayoutFactory->create(); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php deleted file mode 100644 index 06a6403915ff1..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create\Reorder as BaseReorder; - -/** - * Class Reorder - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Reorder extends BaseReorder implements HttpPostActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ReturnQuote.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ReturnQuote.php deleted file mode 100644 index c42e7ecbeef00..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ReturnQuote.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create; - -/** - * Class ReturnQuote - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class ReturnQuote extends Create implements HttpPostActionInterface, HttpGetActionInterface -{ - /** - * Return quote - * - * @return void - */ - protected function _returnQuote() - { - $directpostSession = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $incrementId = $directpostSession->getLastOrderIncrementId(); - if ($incrementId && $directpostSession->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - $directpostSession->removeCheckoutOrderIncrementId($order->getIncrementId()); - } - } - } - - /** - * Return order quote by ajax - * - * @return void - */ - public function execute() - { - $this->_returnQuote(); - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode(['success' => 1]) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php deleted file mode 100644 index cc93ce5daedeb..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class Save - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Save extends \Magento\Sales\Controller\Adminhtml\Order\Create\Save -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ShowUpdateResult.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ShowUpdateResult.php deleted file mode 100644 index af80bde10831a..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ShowUpdateResult.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class ShowUpdateResult - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class ShowUpdateResult extends \Magento\Sales\Controller\Adminhtml\Order\Create\ShowUpdateResult -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Start.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Start.php deleted file mode 100644 index 689b30d63be68..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Start.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Sales\Controller\Adminhtml\Order\Create; - -/** - * Class Start - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -abstract class Start extends Create implements HttpPostActionInterface -{ -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment.php deleted file mode 100644 index cfaa5f1cfcd08..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost; - -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Payment\Block\Transparent\Iframe; -use Magento\Framework\App\Action\Action; - -/** - * DirectPost Payment Controller - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -abstract class Payment extends Action implements HttpGetActionInterface, HttpPostActionInterface -{ - /** - * Core registry - * - * @var \Magento\Framework\Registry - */ - protected $_coreRegistry = null; - - /** - * @var \Magento\Authorizenet\Helper\DataFactory - */ - protected $dataFactory; - - /** - * Constructor - * - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Authorizenet\Helper\DataFactory $dataFactory - */ - public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Authorizenet\Helper\DataFactory $dataFactory - ) { - $this->_coreRegistry = $coreRegistry; - $this->dataFactory = $dataFactory; - parent::__construct($context); - } - - /** - * Get checkout model - * - * @return \Magento\Checkout\Model\Session - */ - protected function _getCheckout() - { - return $this->_objectManager->get(\Magento\Checkout\Model\Session::class); - } - - /** - * Get session model - * - * @return \Magento\Authorizenet\Model\Directpost\Session - */ - protected function _getDirectPostSession() - { - return $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - } - - /** - * Response action. - * - * Action for Authorize.net SIM Relay Request. - * - * @param string $area - * @return void - */ - protected function _responseAction($area = 'frontend') - { - $helper = $this->dataFactory->create($area); - - $params = []; - $data = $this->getRequest()->getParams(); - - /* @var $paymentMethod \Magento\Authorizenet\Model\DirectPost */ - $paymentMethod = $this->_objectManager->create(\Magento\Authorizenet\Model\Directpost::class); - - $result = []; - if (!empty($data['x_invoice_num'])) { - $result['x_invoice_num'] = $data['x_invoice_num']; - $params['order_success'] = $helper->getSuccessOrderUrl($result); - } - - try { - if (!empty($data['store_id'])) { - $paymentMethod->setStore($data['store_id']); - } - $paymentMethod->process($data); - $result['success'] = 1; - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $result['success'] = 0; - $result['error_msg'] = $e->getMessage(); - } catch (\Exception $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $result['success'] = 0; - $result['error_msg'] = __('We can\'t process your order right now. Please try again later.'); - } - - if (!empty($data['controller_action_name']) - && strpos($data['controller_action_name'], 'sales_order_') === false - ) { - if (!empty($data['key'])) { - $result['key'] = $data['key']; - } - $result['controller_action_name'] = $data['controller_action_name']; - $result['is_secure'] = isset($data['is_secure']) ? $data['is_secure'] : false; - $params['redirect'] = $helper->getRedirectIframeUrl($result); - } - - //registering parameter for iframe content - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, $params); - } - - /** - * Return customer quote - * - * @param bool $cancelOrder - * @param string $errorMsg - * @return void - */ - protected function _returnCustomerQuote($cancelOrder = false, $errorMsg = '') - { - $incrementId = $this->_getDirectPostSession()->getLastOrderIncrementId(); - if ($incrementId && $this->_getDirectPostSession()->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - try { - /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ - $quoteRepository = $this->_objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $quoteRepository->get($order->getQuoteId()); - - $quote->setIsActive(1)->setReservedOrderId(null); - $quoteRepository->save($quote); - $this->_getCheckout()->replaceQuote($quote); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - } - $this->_getDirectPostSession()->removeCheckoutOrderIncrementId($incrementId); - $this->_getDirectPostSession()->unsetData('quote_id'); - if ($cancelOrder) { - $order->registerCancellation($errorMsg)->save(); - } - } - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php deleted file mode 100644 index e0610a92feb6a..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\Authorizenet\Helper\DataFactory; -use Magento\Authorizenet\Model\Directpost; -use Magento\Authorizenet\Model\DirectpostFactory; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\CsrfAwareActionInterface; -use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\Controller\ResultFactory; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Registry; -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Psr\Log\LoggerInterface; - -/** - * Class BackendResponse - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class BackendResponse extends \Magento\Authorizenet\Controller\Directpost\Payment implements - CsrfAwareActionInterface, - HttpGetActionInterface, - HttpPostActionInterface -{ - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var DirectpostFactory - */ - private $directpostFactory; - - /** - * BackendResponse constructor. - * - * @param Context $context - * @param Registry $coreRegistry - * @param DataFactory $dataFactory - * @param DirectpostFactory $directpostFactory - * @param LoggerInterface|null $logger - */ - public function __construct( - Context $context, - Registry $coreRegistry, - DataFactory $dataFactory, - DirectpostFactory $directpostFactory, - LoggerInterface $logger = null - ) { - parent::__construct($context, $coreRegistry, $dataFactory); - $this->directpostFactory = $directpostFactory ?: $this->_objectManager->create(DirectpostFactory::class); - $this->logger = $logger ?: $this->_objectManager->get(LoggerInterface::class); - } - - /** - * @inheritDoc - */ - public function createCsrfValidationException( - RequestInterface $request - ): ?InvalidRequestException { - return null; - } - - /** - * @inheritDoc - */ - public function validateForCsrf(RequestInterface $request): ?bool - { - return true; - } - - /** - * Response action. - * - * Action for Authorize.net SIM Relay Request. - * - * @return \Magento\Framework\Controller\ResultInterface - */ - public function execute() - { - $data = $this->getRequest()->getParams(); - /** @var Directpost $paymentMethod */ - $paymentMethod = $this->directpostFactory->create(); - if (!empty($data['store_id'])) { - $paymentMethod->setStore($data['store_id']); - } - $paymentMethod->setResponseData($data); - try { - $paymentMethod->validateResponse(); - } catch (LocalizedException $e) { - $this->logger->critical($e->getMessage()); - return $this->_redirect('noroute'); - } - $this->_responseAction('adminhtml'); - return $this->resultFactory->create(ResultFactory::TYPE_PAGE); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php deleted file mode 100644 index 7d672a75f5b17..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Authorizenet\Controller\Directpost\Payment; -use Magento\Authorizenet\Helper\DataFactory; -use Magento\Checkout\Model\Type\Onepage; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\Response\Http; -use Magento\Framework\DataObject; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Json\Helper\Data as JsonHelper; -use Magento\Framework\Registry; -use Magento\Payment\Model\IframeConfigProvider; -use Magento\Quote\Api\CartManagementInterface; -use Psr\Log\LoggerInterface; - -/** - * Class Place - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Place extends Payment implements HttpPostActionInterface -{ - /** - * @var \Magento\Quote\Api\CartManagementInterface - */ - protected $cartManagement; - - /** - * @var \Magento\Framework\Event\ManagerInterface - */ - protected $eventManager; - - /** - * @var \Magento\Checkout\Model\Type\Onepage - */ - protected $onepageCheckout; - - /** - * @var \Magento\Framework\Json\Helper\Data - */ - protected $jsonHelper; - - /** - * Logger for exception details - * - * @var LoggerInterface - */ - private $logger; - - /** - * @param Context $context - * @param Registry $coreRegistry - * @param DataFactory $dataFactory - * @param CartManagementInterface $cartManagement - * @param Onepage $onepageCheckout - * @param JsonHelper $jsonHelper - * @param LoggerInterface|null $logger - */ - public function __construct( - Context $context, - Registry $coreRegistry, - DataFactory $dataFactory, - CartManagementInterface $cartManagement, - Onepage $onepageCheckout, - JsonHelper $jsonHelper, - LoggerInterface $logger = null - ) { - $this->eventManager = $context->getEventManager(); - $this->cartManagement = $cartManagement; - $this->onepageCheckout = $onepageCheckout; - $this->jsonHelper = $jsonHelper; - $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); - parent::__construct($context, $coreRegistry, $dataFactory); - } - - /** - * Send request to authorize.net - * - * @return string - */ - public function execute() - { - $paymentParam = $this->getRequest()->getParam('payment'); - $controller = $this->getRequest()->getParam('controller'); - $response = $this->getResponse(); - - if (isset($paymentParam['method'])) { - $this->_getDirectPostSession()->setQuoteId($this->_getCheckout()->getQuote()->getId()); - /** - * Current workaround depends on Onepage checkout model defect - * Method Onepage::getCheckoutMethod performs setCheckoutMethod - */ - $this->onepageCheckout->getCheckoutMethod(); - - if ($controller == IframeConfigProvider::CHECKOUT_IDENTIFIER) { - return $this->placeCheckoutOrder(); - } - - $params = $this->dataFactory - ->create(DataFactory::AREA_FRONTEND) - ->getSaveOrderUrlParams($controller); - $this->_forward( - $params['action'], - $params['controller'], - $params['module'], - $this->getRequest()->getParams() - ); - } else { - $result = ['error_messages' => __('Please choose a payment method.'), 'goto_section' => 'payment']; - if ($response instanceof Http) { - $response->representJson($this->jsonHelper->jsonEncode($result)); - } - } - } - - /** - * Place order for checkout flow - * - * @return void - */ - protected function placeCheckoutOrder() - { - $result = new DataObject(); - $response = $this->getResponse(); - try { - $this->cartManagement->placeOrder($this->_getCheckout()->getQuote()->getId()); - $result->setData('success', true); - $this->eventManager->dispatch( - 'checkout_directpost_placeOrder', - [ - 'result' => $result, - 'action' => $this - ] - ); - } catch (LocalizedException $exception) { - $this->logger->critical($exception); - $result->setData('error', true); - $result->setData('error_messages', $exception->getMessage()); - } catch (\Exception $exception) { - $this->logger->critical($exception); - $result->setData('error', true); - $result->setData( - 'error_messages', - __('A server error stopped your order from being placed. Please try to place your order again.') - ); - } - if ($response instanceof Http) { - $response->representJson($this->jsonHelper->jsonEncode($result)); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php deleted file mode 100644 index 8c9510243f610..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\Authorizenet\Controller\Directpost\Payment; -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Payment\Block\Transparent\Iframe; - -/** - * Class Redirect - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Redirect extends Payment implements HttpGetActionInterface, HttpPostActionInterface -{ - /** - * Retrieve params and put javascript into iframe - * - * @return void - */ - public function execute() - { - $helper = $this->dataFactory->create('frontend'); - - $redirectParams = $this->getRequest()->getParams(); - $params = []; - if (!empty($redirectParams['success']) - && isset($redirectParams['x_invoice_num']) - && isset($redirectParams['controller_action_name']) - ) { - $this->_getDirectPostSession()->unsetData('quote_id'); - $params['redirect_parent'] = $helper->getSuccessOrderUrl([]); - } - - if (!empty($redirectParams['error_msg'])) { - $cancelOrder = empty($redirectParams['x_invoice_num']); - $this->_returnCustomerQuote($cancelOrder, $redirectParams['error_msg']); - $params['error_msg'] = $redirectParams['error_msg']; - } - - if (isset($redirectParams['controller_action_name']) - && strpos($redirectParams['controller_action_name'], 'sales_order_') !== false - ) { - unset($redirectParams['controller_action_name']); - unset($params['redirect_parent']); - } - - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, $params); - $this->_view->addPageLayoutHandles(); - $this->_view->loadLayout(false)->renderLayout(); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php deleted file mode 100644 index 17fc3cb72e454..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\Framework\App\CsrfAwareActionInterface; -use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\App\RequestInterface; -use Magento\Authorizenet\Controller\Directpost\Payment; -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; - -/** - * Class Response - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Response extends Payment implements CsrfAwareActionInterface, HttpGetActionInterface, HttpPostActionInterface -{ - /** - * @inheritDoc - */ - public function createCsrfValidationException( - RequestInterface $request - ): ?InvalidRequestException { - return null; - } - - /** - * @inheritDoc - */ - public function validateForCsrf(RequestInterface $request): ?bool - { - return true; - } - - /** - * Response action. - * - * Action for Authorize.net SIM Relay Request. - * - * @return \Magento\Framework\Controller\ResultInterface - */ - public function execute() - { - $this->_responseAction('frontend'); - return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php deleted file mode 100644 index c974632f584b0..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Authorizenet\Controller\Directpost\Payment; - -/** - * Class ReturnQuote - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class ReturnQuote extends Payment implements HttpPostActionInterface, HttpGetActionInterface -{ - /** - * Return customer quote by ajax - * - * @return void - */ - public function execute() - { - $this->_returnCustomerQuote(); - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode(['success' => 1]) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Helper/Backend/Data.php b/app/code/Magento/Authorizenet/Helper/Backend/Data.php deleted file mode 100644 index d291125ccae06..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/Backend/Data.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Helper\Backend; - -use Magento\Authorizenet\Helper\Data as FrontendDataHelper; -use Magento\Framework\App\Helper\Context; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Sales\Model\OrderFactory; -use Magento\Backend\Model\UrlInterface; - -/** - * Authorize.net Backend Data Helper - * - * @api - * @since 100.0.2 - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Data extends FrontendDataHelper -{ - /** - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Sales\Model\OrderFactory $orderFactory - * @param \Magento\Backend\Model\UrlInterface $backendUrl - */ - public function __construct( - Context $context, - StoreManagerInterface $storeManager, - OrderFactory $orderFactory, - UrlInterface $backendUrl - ) { - parent::__construct($context, $storeManager, $orderFactory); - $this->_urlBuilder = $backendUrl; - } - - /** - * Return URL for admin area - * - * @param string $route - * @param array $params - * @return string - */ - protected function _getUrl($route, $params = []) - { - return $this->_urlBuilder->getUrl($route, $params); - } - - /** - * Retrieve place order url in admin - * - * @return string - */ - public function getPlaceOrderAdminUrl() - { - return $this->_getUrl('adminhtml/authorizenet_directpost_payment/place', []); - } - - /** - * Retrieve place order url - * - * @param array $params - * @return string - */ - public function getSuccessOrderUrl($params) - { - $param = []; - $route = 'sales/order/view'; - $order = $this->orderFactory->create()->loadByIncrementId($params['x_invoice_num']); - $param['order_id'] = $order->getId(); - return $this->_getUrl($route, $param); - } - - /** - * Retrieve redirect iframe url - * - * @param array $params - * @return string - */ - public function getRedirectIframeUrl($params) - { - return $this->_getUrl('adminhtml/authorizenet_directpost_payment/redirect', $params); - } - - /** - * Get direct post relay url - * - * @param null|int|string $storeId - * @return string - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getRelayUrl($storeId = null) - { - $defaultStore = $this->storeManager->getDefaultStoreView(); - if (!$defaultStore) { - $allStores = $this->storeManager->getStores(); - if (isset($allStores[0])) { - $defaultStore = $allStores[0]; - } - } - $baseUrl = $defaultStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); - return $baseUrl . 'authorizenet/directpost_payment/backendResponse'; - } -} diff --git a/app/code/Magento/Authorizenet/Helper/Data.php b/app/code/Magento/Authorizenet/Helper/Data.php deleted file mode 100644 index e240cd692a13f..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/Data.php +++ /dev/null @@ -1,344 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Helper; - -use Magento\Framework\App\Helper\AbstractHelper; -use Magento\Framework\App\Helper\Context; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Sales\Model\OrderFactory; -use Magento\Authorizenet\Model\Directpost; -use Magento\Authorizenet\Model\Authorizenet; - -/** - * Authorize.net Data Helper - * - * @api - * @since 100.0.2 - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Data extends AbstractHelper -{ - /** - * @var \Magento\Store\Model\StoreManagerInterface - */ - protected $storeManager; - - /** - * @var \Magento\Sales\Model\OrderFactory - */ - protected $orderFactory; - - /** - * Allowed currencies - * - * @var array - */ - protected $allowedCurrencyCodes = ['USD']; - - /** - * Transaction statuses key to value map - * - * @var array - */ - protected $transactionStatuses = [ - 'authorizedPendingCapture' => 'Authorized/Pending Capture', - 'capturedPendingSettlement' => 'Captured/Pending Settlement', - 'refundSettledSuccessfully' => 'Refund/Settled Successfully', - 'refundPendingSettlement' => 'Refund/Pending Settlement', - 'declined' => 'Declined', - 'expired' => 'Expired', - 'voided' => 'Voided', - 'FDSPendingReview' => 'FDS - Pending Review', - 'FDSAuthorizedPendingReview' => 'FDS - Authorized/Pending Review' - ]; - - /** - * Fraud filter actions key to value map - * - * @var array - */ - protected $fdsFilterActions = [ - 'decline ' => 'Decline', - 'hold' => 'Hold', - 'authAndHold' => 'Authorize and Hold', - 'report' => 'Report Only' - ]; - - /** - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Sales\Model\OrderFactory $orderFactory - */ - public function __construct( - Context $context, - StoreManagerInterface $storeManager, - OrderFactory $orderFactory - ) { - $this->storeManager = $storeManager; - $this->orderFactory = $orderFactory; - parent::__construct($context); - } - - /** - * Set secure url checkout is secure for current store. - * - * @param string $route - * @param array $params - * @return string - */ - protected function _getUrl($route, $params = []) - { - $params['_type'] = \Magento\Framework\UrlInterface::URL_TYPE_LINK; - if (isset($params['is_secure'])) { - $params['_secure'] = (bool)$params['is_secure']; - } elseif ($this->storeManager->getStore()->isCurrentlySecure()) { - $params['_secure'] = true; - } - return parent::_getUrl($route, $params); - } - - /** - * Retrieve save order url params - * - * @param string $controller - * @return array - */ - public function getSaveOrderUrlParams($controller) - { - $route = []; - switch ($controller) { - case 'onepage': - $route['action'] = 'saveOrder'; - $route['controller'] = 'onepage'; - $route['module'] = 'checkout'; - break; - - case 'sales_order_create': - case 'sales_order_edit': - $route['action'] = 'save'; - $route['controller'] = 'sales_order_create'; - $route['module'] = 'admin'; - break; - - default: - break; - } - - return $route; - } - - /** - * Retrieve redirect iframe url - * - * @param array $params - * @return string - */ - public function getRedirectIframeUrl($params) - { - return $this->_getUrl('authorizenet/directpost_payment/redirect', $params); - } - - /** - * Retrieve place order url - * - * @param array $params - * @return string - */ - public function getSuccessOrderUrl($params) - { - return $this->_getUrl('checkout/onepage/success', $params); - } - - /** - * Update all child and parent order's edit increment numbers. - * - * Needed for Admin area. - * - * @param \Magento\Sales\Model\Order $order - * @return void - */ - public function updateOrderEditIncrements(\Magento\Sales\Model\Order $order) - { - if ($order->getId() && $order->getOriginalIncrementId()) { - $collection = $order->getCollection(); - $quotedIncrId = $collection->getConnection()->quote($order->getOriginalIncrementId()); - $collection->getSelect()->where( - "original_increment_id = {$quotedIncrId} OR increment_id = {$quotedIncrId}" - ); - - foreach ($collection as $orderToUpdate) { - $orderToUpdate->setEditIncrement($order->getEditIncrement()); - $orderToUpdate->save(); - } - } - } - - /** - * Converts a lot of messages to message - * - * @param array $messages - * @return string - */ - public function convertMessagesToMessage($messages) - { - return implode(' | ', $messages); - } - - /** - * Return message for gateway transaction request - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param string $requestType - * @param string $lastTransactionId - * @param \Magento\Framework\DataObject $card - * @param bool|float $amount - * @param bool|string $exception - * @param bool|string $additionalMessage - * @return bool|string - */ - public function getTransactionMessage( - $payment, - $requestType, - $lastTransactionId, - $card, - $amount = false, - $exception = false, - $additionalMessage = false - ) { - $message[] = __('Credit Card: xxxx-%1', $card->getCcLast4()); - if ($amount) { - $message[] = __('amount %1', $this->formatPrice($payment, $amount)); - } - $operation = $this->getOperation($requestType); - if (!$operation) { - return false; - } else { - $message[] = $operation; - } - $message[] = ($exception) ? '- ' . __('failed.') : '- ' . __('successful.'); - if ($lastTransactionId !== null) { - $message[] = __('Authorize.Net Transaction ID %1.', $lastTransactionId); - } - if ($additionalMessage) { - $message[] = $additionalMessage; - } - if ($exception) { - $message[] = $exception; - } - return implode(' ', $message); - } - - /** - * Return operation name for request type - * - * @param string $requestType - * @return \Magento\Framework\Phrase|bool - */ - protected function getOperation($requestType) - { - switch ($requestType) { - case Authorizenet::REQUEST_TYPE_AUTH_ONLY: - return __('authorize'); - case Authorizenet::REQUEST_TYPE_AUTH_CAPTURE: - return __('authorize and capture'); - case Authorizenet::REQUEST_TYPE_PRIOR_AUTH_CAPTURE: - return __('capture'); - case Authorizenet::REQUEST_TYPE_CREDIT: - return __('refund'); - case Authorizenet::REQUEST_TYPE_VOID: - return __('void'); - default: - return false; - } - } - - /** - * Format price with currency sign - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return string - */ - protected function formatPrice($payment, $amount) - { - return $payment->getOrder()->getBaseCurrency()->formatTxt($amount); - } - - /** - * Get payment method step html - * - * @param \Magento\Framework\App\ViewInterface $view - * @return string - */ - public function getPaymentMethodsHtml(\Magento\Framework\App\ViewInterface $view) - { - $layout = $view->getLayout(); - $update = $layout->getUpdate(); - $update->load('checkout_onepage_paymentmethod'); - $layout->generateXml(); - $layout->generateElements(); - $output = $layout->getOutput(); - return $output; - } - - /** - * Get direct post relay url - * - * @param null|int|string $storeId - * @return string - */ - public function getRelayUrl($storeId = null) - { - $baseUrl = $this->storeManager->getStore($storeId) - ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); - return $baseUrl . 'authorizenet/directpost_payment/response'; - } - - /** - * Get allowed currencies - * - * @return array - */ - public function getAllowedCurrencyCodes() - { - return $this->allowedCurrencyCodes; - } - - /** - * Get translated filter action label - * - * @param string $key - * @return \Magento\Framework\Phrase|string - */ - public function getFdsFilterActionLabel($key) - { - return isset($this->fdsFilterActions[$key]) ? __($this->fdsFilterActions[$key]) : $key; - } - - /** - * Get translated transaction status label - * - * @param string $key - * @return \Magento\Framework\Phrase|string - */ - public function getTransactionStatusLabel($key) - { - return isset($this->transactionStatuses[$key]) ? __($this->transactionStatuses[$key]) : $key; - } - - /** - * Gateway error response wrapper - * - * @param string $text - * @return \Magento\Framework\Phrase - */ - public function wrapGatewayError($text) - { - return __('Gateway error: %1', $text); - } -} diff --git a/app/code/Magento/Authorizenet/Helper/DataFactory.php b/app/code/Magento/Authorizenet/Helper/DataFactory.php deleted file mode 100644 index 71f16ab4af646..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/DataFactory.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Helper; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\ObjectManagerInterface; - -/** - * Class DataFactory - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class DataFactory -{ - const AREA_FRONTEND = 'frontend'; - const AREA_BACKEND = 'adminhtml'; - - /** - * @var ObjectManagerInterface - */ - protected $objectManager; - - /** - * @var array - */ - protected $helperMap = [ - self::AREA_FRONTEND => \Magento\Authorizenet\Helper\Data::class, - self::AREA_BACKEND => \Magento\Authorizenet\Helper\Backend\Data::class - ]; - - /** - * Constructor - * - * @param ObjectManagerInterface $objectManager - */ - public function __construct(ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * Create data helper - * - * @param string $area - * @return \Magento\Authorizenet\Helper\Backend\Data|\Magento\Authorizenet\Helper\Data - * @throws LocalizedException - */ - public function create($area) - { - if (!isset($this->helperMap[$area])) { - throw new LocalizedException(__(sprintf('For this area <%s> no suitable helper', $area))); - } - - return $this->objectManager->get($this->helperMap[$area]); - } -} diff --git a/app/code/Magento/Authorizenet/LICENSE.txt b/app/code/Magento/Authorizenet/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Authorizenet/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Authorizenet/LICENSE_AFL.txt b/app/code/Magento/Authorizenet/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Authorizenet/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Authorizenet/Model/Authorizenet.php b/app/code/Magento/Authorizenet/Model/Authorizenet.php deleted file mode 100644 index 9370b649a23c7..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Authorizenet.php +++ /dev/null @@ -1,449 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -use Magento\Authorizenet\Model\TransactionService; -use Magento\Framework\HTTP\ZendClientFactory; - -/** - * Model for Authorize.net payment method - * - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -abstract class Authorizenet extends \Magento\Payment\Model\Method\Cc -{ - /** - * AIM gateway url - */ - const CGI_URL = 'https://secure.authorize.net/gateway/transact.dll'; - - const REQUEST_METHOD_CC = 'CC'; - - const REQUEST_TYPE_AUTH_CAPTURE = 'AUTH_CAPTURE'; - - const REQUEST_TYPE_AUTH_ONLY = 'AUTH_ONLY'; - - const REQUEST_TYPE_CAPTURE_ONLY = 'CAPTURE_ONLY'; - - const REQUEST_TYPE_CREDIT = 'CREDIT'; - - const REQUEST_TYPE_VOID = 'VOID'; - - const REQUEST_TYPE_PRIOR_AUTH_CAPTURE = 'PRIOR_AUTH_CAPTURE'; - - const RESPONSE_DELIM_CHAR = '(~)'; - - const RESPONSE_CODE_APPROVED = 1; - - const RESPONSE_CODE_DECLINED = 2; - - const RESPONSE_CODE_ERROR = 3; - - const RESPONSE_CODE_HELD = 4; - - const RESPONSE_REASON_CODE_APPROVED = 1; - - const RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED = 252; - - const RESPONSE_REASON_CODE_PENDING_REVIEW = 253; - - const RESPONSE_REASON_CODE_PENDING_REVIEW_DECLINED = 254; - - /** - * Transaction fraud state key - */ - const TRANSACTION_FRAUD_STATE_KEY = 'is_transaction_fraud'; - - /** - * Real transaction id key - */ - const REAL_TRANSACTION_ID_KEY = 'real_transaction_id'; - - /** - * Gateway actions locked state key - */ - const GATEWAY_ACTIONS_LOCKED_STATE_KEY = 'is_gateway_actions_locked'; - - /** - * @var \Magento\Authorizenet\Helper\Data - */ - protected $dataHelper; - - /** - * Request factory - * - * @var \Magento\Authorizenet\Model\RequestFactory - */ - protected $requestFactory; - - /** - * Response factory - * - * @var \Magento\Authorizenet\Model\ResponseFactory - */ - protected $responseFactory; - - /** - * @var \Magento\Authorizenet\Model\TransactionService; - */ - protected $transactionService; - - /** - * Fields that should be replaced in debug with '***' - * - * @var array - */ - protected $_debugReplacePrivateDataKeys = ['merchantAuthentication', 'x_login']; - - /** - * @var \Magento\Framework\HTTP\ZendClientFactory - */ - protected $httpClientFactory; - - /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory - * @param \Magento\Payment\Helper\Data $paymentData - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Payment\Model\Method\Logger $logger - * @param \Magento\Framework\Module\ModuleListInterface $moduleList - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Authorizenet\Helper\Data $dataHelper - * @param \Magento\Authorizenet\Model\Request\Factory $requestFactory - * @param \Magento\Authorizenet\Model\Response\Factory $responseFactory - * @param \Magento\Authorizenet\Model\TransactionService $transactionService - * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection - * @param array $data - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, - \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, - \Magento\Payment\Helper\Data $paymentData, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Payment\Model\Method\Logger $logger, - \Magento\Framework\Module\ModuleListInterface $moduleList, - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Authorizenet\Helper\Data $dataHelper, - \Magento\Authorizenet\Model\Request\Factory $requestFactory, - \Magento\Authorizenet\Model\Response\Factory $responseFactory, - TransactionService $transactionService, - ZendClientFactory $httpClientFactory, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] - ) { - $this->dataHelper = $dataHelper; - $this->requestFactory = $requestFactory; - $this->responseFactory = $responseFactory; - $this->transactionService = $transactionService; - $this->httpClientFactory = $httpClientFactory; - - parent::__construct( - $context, - $registry, - $extensionFactory, - $customAttributeFactory, - $paymentData, - $scopeConfig, - $logger, - $moduleList, - $localeDate, - $resource, - $resourceCollection, - $data - ); - } - - /** - * Check method for processing with base currency - * - * @param string $currencyCode - * @return bool - */ - public function canUseForCurrency($currencyCode) - { - if (!in_array($currencyCode, $this->getAcceptedCurrencyCodes())) { - return false; - } - return true; - } - - /** - * Return array of currency codes supplied by Payment Gateway - * - * @return array - */ - public function getAcceptedCurrencyCodes() - { - if (!$this->hasData('_accepted_currency')) { - $acceptedCurrencyCodes = $this->dataHelper->getAllowedCurrencyCodes(); - $acceptedCurrencyCodes[] = $this->getConfigData('currency'); - $this->setData('_accepted_currency', $acceptedCurrencyCodes); - } - return $this->_getData('_accepted_currency'); - } - - /** - * Cancel the payment through gateway - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @return $this - */ - public function cancel(\Magento\Payment\Model\InfoInterface $payment) - { - return $this->void($payment); - } - - /** - * Fetch fraud details - * - * @param string $transactionId - * @return \Magento\Framework\DataObject - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function fetchTransactionFraudDetails($transactionId) - { - $responseXmlDocument = $this->transactionService->getTransactionDetails($this, $transactionId); - $response = new \Magento\Framework\DataObject(); - - if (empty($responseXmlDocument->transaction->FDSFilters->FDSFilter)) { - return $response; - } - - $response->setFdsFilterAction( - $this->dataHelper->getFdsFilterActionLabel((string)$responseXmlDocument->transaction->FDSFilterAction) - ); - $response->setAvsResponse((string)$responseXmlDocument->transaction->AVSResponse); - $response->setCardCodeResponse((string)$responseXmlDocument->transaction->cardCodeResponse); - $response->setCavvResponse((string)$responseXmlDocument->transaction->CAVVResponse); - $response->setFraudFilters($this->getFraudFilters($responseXmlDocument->transaction->FDSFilters)); - - return $response; - } - - /** - * Get fraud filters - * - * @param \Magento\Framework\Simplexml\Element $fraudFilters - * @return array - */ - protected function getFraudFilters($fraudFilters) - { - $result = []; - - foreach ($fraudFilters->FDSFilter as $filer) { - $result[] = [ - 'name' => (string)$filer->name, - 'action' => $this->dataHelper->getFdsFilterActionLabel((string)$filer->action) - ]; - } - - return $result; - } - - /** - * Return authorize payment request - * - * @return \Magento\Authorizenet\Model\Request - */ - protected function getRequest() - { - $request = $this->requestFactory->create() - ->setXVersion(3.1) - ->setXDelimData('True') - ->setXRelayResponse('False') - ->setXTestRequest($this->getConfigData('test') ? 'TRUE' : 'FALSE') - ->setXLogin($this->getConfigData('login')) - ->setXTranKey($this->getConfigData('trans_key')); - return $request; - } - - /** - * Prepare request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @return \Magento\Authorizenet\Model\Request - * @link http://www.authorize.net/support/AIM_guide.pdf - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function buildRequest(\Magento\Framework\DataObject $payment) - { - /** @var \Magento\Sales\Model\Order $order */ - $order = $payment->getOrder(); - $this->setStore($order->getStoreId()); - $request = $this->getRequest() - ->setXType($payment->getAnetTransType()) - ->setXMethod(self::REQUEST_METHOD_CC); - - if ($order && $order->getIncrementId()) { - $request->setXInvoiceNum($order->getIncrementId()); - } - - if ($payment->getAmount()) { - $request->setXAmount($payment->getAmount(), 2); - $request->setXCurrencyCode($order->getBaseCurrencyCode()); - } - - switch ($payment->getAnetTransType()) { - case self::REQUEST_TYPE_AUTH_CAPTURE: - $request->setXAllowPartialAuth($this->getConfigData('allow_partial_authorization') ? 'True' : 'False'); - break; - case self::REQUEST_TYPE_AUTH_ONLY: - $request->setXAllowPartialAuth($this->getConfigData('allow_partial_authorization') ? 'True' : 'False'); - break; - case self::REQUEST_TYPE_CREDIT: - /** - * Send last 4 digits of credit card number to authorize.net - * otherwise it will give an error - */ - $request->setXCardNum($payment->getCcLast4()); - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_VOID: - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE: - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_CAPTURE_ONLY: - $request->setXAuthCode($payment->getCcAuthCode()); - break; - } - - if (!empty($order)) { - $billing = $order->getBillingAddress(); - if (!empty($billing)) { - $request->setXFirstName($billing->getFirstname()) - ->setXLastName($billing->getLastname()) - ->setXCompany($billing->getCompany()) - ->setXAddress($billing->getStreetLine(1)) - ->setXCity($billing->getCity()) - ->setXState($billing->getRegion()) - ->setXZip($billing->getPostcode()) - ->setXCountry($billing->getCountryId()) - ->setXPhone($billing->getTelephone()) - ->setXFax($billing->getFax()) - ->setXCustId($order->getCustomerId()) - ->setXCustomerIp($order->getRemoteIp()) - ->setXCustomerTaxId($billing->getTaxId()) - ->setXEmail($order->getCustomerEmail()) - ->setXEmailCustomer($this->getConfigData('email_customer')) - ->setXMerchantEmail($this->getConfigData('merchant_email')); - } - - $shipping = $order->getShippingAddress(); - if (!empty($shipping)) { - $request->setXShipToFirstName($shipping->getFirstname()) - ->setXShipToLastName($shipping->getLastname()) - ->setXShipToCompany($shipping->getCompany()) - ->setXShipToAddress($shipping->getStreetLine(1)) - ->setXShipToCity($shipping->getCity()) - ->setXShipToState($shipping->getRegion()) - ->setXShipToZip($shipping->getPostcode()) - ->setXShipToCountry($shipping->getCountryId()); - } - - $request->setXPoNum($payment->getPoNumber()) - ->setXTax($order->getBaseTaxAmount()) - ->setXFreight($order->getBaseShippingAmount()); - } - - if ($payment->getCcNumber()) { - $request->setXCardNum($payment->getCcNumber()) - ->setXExpDate(sprintf('%02d-%04d', $payment->getCcExpMonth(), $payment->getCcExpYear())) - ->setXCardCode($payment->getCcCid()); - } - - return $request; - } - - /** - * Post request to gateway and return response - * - * @param \Magento\Authorizenet\Model\Request $request - * @return \Magento\Authorizenet\Model\Response - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function postRequest(\Magento\Authorizenet\Model\Request $request) - { - $result = $this->responseFactory->create(); - /** @var \Magento\Framework\HTTP\ZendClient $client */ - $client = $this->httpClientFactory->create(); - $url = $this->getConfigData('cgi_url') ?: self::CGI_URL; - $debugData = ['url' => $url, 'request' => $request->getData()]; - $client->setUri($url); - $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); - - foreach ($request->getData() as $key => $value) { - $request->setData($key, str_replace(self::RESPONSE_DELIM_CHAR, '', $value)); - } - - $request->setXDelimChar(self::RESPONSE_DELIM_CHAR); - $client->setParameterPost($request->getData()); - $client->setMethod(\Zend_Http_Client::POST); - - try { - $response = $client->request(); - $responseBody = $response->getBody(); - $debugData['response'] = $responseBody; - } catch (\Exception $e) { - $result->setXResponseCode(-1) - ->setXResponseReasonCode($e->getCode()) - ->setXResponseReasonText($e->getMessage()); - - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($e->getMessage()) - ); - } finally { - $this->_debug($debugData); - } - - $r = explode(self::RESPONSE_DELIM_CHAR, $responseBody); - if ($r) { - $result->setXResponseCode((int)str_replace('"', '', $r[0])) - ->setXResponseReasonCode((int)str_replace('"', '', $r[2])) - ->setXResponseReasonText($r[3]) - ->setXAvsCode($r[5]) - ->setXTransId($r[6]) - ->setXInvoiceNum($r[7]) - ->setXAmount($r[9]) - ->setXMethod($r[10]) - ->setXType($r[11]) - ->setData('x_MD5_Hash', $r[37]) - ->setXAccountNumber($r[50]); - } else { - throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong in the payment gateway.') - ); - } - return $result; - } - - /** - * If gateway actions are locked return true - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @return bool - */ - protected function isGatewayActionsLocked($payment) - { - return $payment->getAdditionalInformation(self::GATEWAY_ACTIONS_LOCKED_STATE_KEY); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Debug.php b/app/code/Magento/Authorizenet/Model/Debug.php deleted file mode 100644 index 93d508cc744e1..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Debug.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -/** - * Authorize.net debug payment method model - * - * @method string getRequestBody() - * @method \Magento\Authorizenet\Model\Debug setRequestBody(string $value) - * @method string getResponseBody() - * @method \Magento\Authorizenet\Model\Debug setResponseBody(string $value) - * @method string getRequestSerialized() - * @method \Magento\Authorizenet\Model\Debug setRequestSerialized(string $value) - * @method string getResultSerialized() - * @method \Magento\Authorizenet\Model\Debug setResultSerialized(string $value) - * @method string getRequestDump() - * @method \Magento\Authorizenet\Model\Debug setRequestDump(string $value) - * @method string getResultDump() - * @method \Magento\Authorizenet\Model\Debug setResultDump(string $value) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Debug extends \Magento\Framework\Model\AbstractModel -{ - /** - * Construct debug class - * - * @return void - */ - protected function _construct() - { - $this->_init(\Magento\Authorizenet\Model\ResourceModel\Debug::class); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php deleted file mode 100644 index 946ec8ba01a0e..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ /dev/null @@ -1,1070 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -use Magento\Framework\App\ObjectManager; -use Magento\Payment\Model\Method\ConfigInterface; -use Magento\Payment\Model\Method\TransparentInterface; - -/** - * Authorize.net DirectPost payment method model. - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements TransparentInterface, ConfigInterface -{ - const METHOD_CODE = 'authorizenet_directpost'; - - /** - * @var string - */ - protected $_formBlockType = \Magento\Payment\Block\Transparent\Info::class; - - /** - * @var string - */ - protected $_infoBlockType = \Magento\Authorizenet\Block\Adminhtml\Order\View\Info\PaymentDetails::class; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_isGateway = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canAuthorize = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canCapture = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canRefund = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canRefundInvoicePartial = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canVoid = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_canFetchTransactionInfo = true; - - /** - * Payment Method feature - * - * @var bool - */ - protected $_isInitializeNeeded = true; - - /** - * @var \Magento\Store\Model\StoreManagerInterface - */ - protected $storeManager; - - /** - * @var \Magento\Quote\Api\CartRepositoryInterface - */ - protected $quoteRepository; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Response - */ - protected $response; - - /** - * @var \Magento\Sales\Model\Order\Email\Sender\OrderSender - */ - protected $orderSender; - - /** - * Order factory - * - * @var \Magento\Sales\Model\OrderFactory - */ - protected $orderFactory; - - /** - * @var \Magento\Sales\Api\TransactionRepositoryInterface - */ - protected $transactionRepository; - - /** - * @var \Psr\Log\LoggerInterface - */ - private $psrLogger; - - /** - * @var \Magento\Sales\Api\PaymentFailuresInterface - */ - private $paymentFailures; - - /** - * @var \Magento\Sales\Model\Order - */ - private $order; - - /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory - * @param \Magento\Payment\Helper\Data $paymentData - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Payment\Model\Method\Logger $logger - * @param \Magento\Framework\Module\ModuleListInterface $moduleList - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Authorizenet\Helper\Data $dataHelper - * @param \Magento\Authorizenet\Model\Directpost\Request\Factory $requestFactory - * @param \Magento\Authorizenet\Model\Directpost\Response\Factory $responseFactory - * @param \Magento\Authorizenet\Model\TransactionService $transactionService - * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory - * @param \Magento\Sales\Model\OrderFactory $orderFactory - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository - * @param \Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender - * @param \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection - * @param array $data - * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, - \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, - \Magento\Payment\Helper\Data $paymentData, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Payment\Model\Method\Logger $logger, - \Magento\Framework\Module\ModuleListInterface $moduleList, - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Authorizenet\Helper\Data $dataHelper, - \Magento\Authorizenet\Model\Directpost\Request\Factory $requestFactory, - \Magento\Authorizenet\Model\Directpost\Response\Factory $responseFactory, - \Magento\Authorizenet\Model\TransactionService $transactionService, - \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory, - \Magento\Sales\Model\OrderFactory $orderFactory, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, - \Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender, - \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null - ) { - $this->orderFactory = $orderFactory; - $this->storeManager = $storeManager; - $this->quoteRepository = $quoteRepository; - $this->response = $responseFactory->create(); - $this->orderSender = $orderSender; - $this->transactionRepository = $transactionRepository; - $this->_code = static::METHOD_CODE; - $this->paymentFailures = $paymentFailures ? : ObjectManager::getInstance() - ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); - - parent::__construct( - $context, - $registry, - $extensionFactory, - $customAttributeFactory, - $paymentData, - $scopeConfig, - $logger, - $moduleList, - $localeDate, - $dataHelper, - $requestFactory, - $responseFactory, - $transactionService, - $httpClientFactory, - $resource, - $resourceCollection, - $data - ); - } - - /** - * Set data helper - * - * @param \Magento\Authorizenet\Helper\Data $dataHelper - * @return void - */ - public function setDataHelper(\Magento\Authorizenet\Helper\Data $dataHelper) - { - $this->dataHelper = $dataHelper; - } - - /** - * Do not validate payment form using server methods - * - * @return bool - */ - public function validate() - { - return true; - } - - /** - * Send authorize request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - $payment->setAdditionalInformation('payment_type', $this->getConfigData('payment_action')); - } - - /** - * Send capture request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function capture(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - if ($amount <= 0) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid amount for capture.')); - } - - $payment->setAmount($amount); - - if ($payment->getParentTransactionId()) { - $payment->setAnetTransType(self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - } else { - $payment->setAnetTransType(self::REQUEST_TYPE_AUTH_CAPTURE); - } - - $result = $this->getResponse(); - if (empty($result->getData())) { - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - } - - return $this->processCapture($result, $payment); - } - - /** - * Process capture request - * - * @param \Magento\Authorizenet\Model\Directpost\Response $result - * @param \Magento\Payment\Model\InfoInterface $payment - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function processCapture($result, $payment) - { - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - case self::RESPONSE_CODE_HELD: - if (in_array( - $result->getXResponseReasonCode(), - [ - self::RESPONSE_REASON_CODE_APPROVED, - self::RESPONSE_REASON_CODE_PENDING_REVIEW, - self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED - ] - ) - ) { - if (!$payment->getParentTransactionId() - || $result->getXTransId() != $payment->getParentTransactionId() - ) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(0) - ->setTransactionAdditionalInfo( - self::REAL_TRANSACTION_ID_KEY, - $result->getXTransId() - ); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment capturing error.')); - } - } - - /** - * Void the payment through gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function void(\Magento\Payment\Model\InfoInterface $payment) - { - if (!$payment->getParentTransactionId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid transaction ID.')); - } - - $payment->setAnetTransType(self::REQUEST_TYPE_VOID); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - if ($result->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) { - if ($result->getXTransId() != $payment->getParentTransactionId()) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(1) - ->setShouldCloseParentTransaction(1) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $result->getXTransId()); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment voiding error.')); - } - } - - /** - * Refund the amount need to decode last 4 digits for request. - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return $this - * @throws \Exception - */ - public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - $last4 = $payment->getCcLast4(); - $payment->setCcLast4($payment->decrypt($last4)); - try { - $this->processRefund($payment, $amount); - } catch (\Exception $e) { - $payment->setCcLast4($last4); - throw $e; - } - $payment->setCcLast4($last4); - return $this; - } - - /** - * Refund the amount with transaction id - * - * @param \Magento\Framework\DataObject $payment - * @param float $amount - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function processRefund(\Magento\Framework\DataObject $payment, $amount) - { - if ($amount <= 0) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid amount for refund.')); - } - - if (!$payment->getParentTransactionId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid transaction ID.')); - } - - $payment->setAnetTransType(self::REQUEST_TYPE_CREDIT); - $payment->setAmount($amount); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - if ($result->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) { - if ($result->getXTransId() != $payment->getParentTransactionId()) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(true) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $result->getXTransId()); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment refunding error.')); - } - } - - /** - * Get CGI url - * - * @return string - */ - public function getCgiUrl() - { - $uri = $this->getConfigData('cgi_url'); - return $uri ? $uri : self::CGI_URL; - } - - /** - * Return URL on which Authorize.net server will return payment result data in hidden request. - * - * @param int $storeId - * @return string - */ - public function getRelayUrl($storeId = null) - { - if ($storeId == null && $this->getStore()) { - $storeId = $this->getStore(); - } - return $this->dataHelper->getRelayUrl($storeId); - } - - /** - * Return response. - * - * @return \Magento\Authorizenet\Model\Directpost\Response - */ - public function getResponse() - { - return $this->response; - } - - /** - * Instantiate state and set it to state object - * - * @param string $paymentAction - * @param \Magento\Framework\DataObject $stateObject - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function initialize($paymentAction, $stateObject) - { - $requestType = null; - switch ($paymentAction) { - case self::ACTION_AUTHORIZE: - $requestType = self::REQUEST_TYPE_AUTH_ONLY; - //intentional - case self::ACTION_AUTHORIZE_CAPTURE: - $requestType = $requestType ?: self::REQUEST_TYPE_AUTH_CAPTURE; - $payment = $this->getInfoInstance(); - $order = $payment->getOrder(); - $order->setCanSendNewEmailFlag(false); - $payment->setBaseAmountAuthorized($order->getBaseTotalDue()); - $payment->setAmountAuthorized($order->getTotalDue()); - $payment->setAnetTransType($requestType); - break; - default: - break; - } - } - - /** - * Generate request object and fill its fields from Quote or Order object - * - * @param \Magento\Sales\Model\Order $order Quote or order object. - * @return \Magento\Authorizenet\Model\Directpost\Request - */ - public function generateRequestFromOrder(\Magento\Sales\Model\Order $order) - { - $request = $this->requestFactory->create() - ->setConstantData($this) - ->setDataFromOrder($order, $this) - ->signRequestData(); - - $this->_debug(['request' => $request->getData()]); - - return $request; - } - - /** - * Fill response with data. - * - * @param array $postData - * @return $this - */ - public function setResponseData(array $postData) - { - $this->getResponse()->setData($postData); - return $this; - } - - /** - * Validate response data. Needed in controllers. - * - * @return bool true in case of validation success. - * @throws \Magento\Framework\Exception\LocalizedException In case of validation error - */ - public function validateResponse() - { - $response = $this->getResponse(); - $hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5'; - - //hash check - if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login')) - ) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The transaction was declined because the response hash validation failed.') - ); - } - - return true; - } - - /** - * Operate with order using data from $_POST which came from authorize.net by Relay URL. - * - * @param array $responseData data from Authorize.net from $_POST - * @return void - * @throws \Magento\Framework\Exception\LocalizedException In case of validation error or order creation error - */ - public function process(array $responseData) - { - $this->_debug(['response' => $responseData]); - - $this->setResponseData($responseData); - - //check MD5 error or others response errors - //throws exception on false. - $this->validateResponse(); - - $response = $this->getResponse(); - $responseText = $this->dataHelper->wrapGatewayError($response->getXResponseReasonText()); - $isError = false; - if ($this->getOrderIncrementId()) { - $order = $this->getOrderFromResponse(); - //check payment method - $payment = $order->getPayment(); - if (!$payment || $payment->getMethod() != $this->getCode()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('This payment didn\'t work out because we can\'t find this order.') - ); - } - if ($order->getId()) { - //operate with order - $this->processOrder($order); - } else { - $isError = true; - } - } else { - $isError = true; - } - - if ($isError) { - $responseText = $responseText && !$response->isApproved() - ? $responseText - : __('This payment didn\'t work out because we can\'t find this order.'); - throw new \Magento\Framework\Exception\LocalizedException($responseText); - } - } - - /** - * Fill payment with credit card data from response from Authorize.net. - * - * @param \Magento\Framework\DataObject $payment - * @return void - */ - protected function fillPaymentByResponse(\Magento\Framework\DataObject $payment) - { - $response = $this->getResponse(); - $payment->setTransactionId($response->getXTransId()) - ->setParentTransactionId(null) - ->setIsTransactionClosed(0) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $response->getXTransId()); - - if ($response->getXMethod() == self::REQUEST_METHOD_CC) { - $payment->setCcAvsStatus($response->getXAvsCode()) - ->setCcLast4($payment->encrypt(substr($response->getXAccountNumber(), -4))); - } - - if ($response->getXResponseCode() == self::RESPONSE_CODE_HELD) { - $payment->setIsTransactionPending(true) - ->setIsFraudDetected(true); - } - - $additionalInformationKeys = explode(',', $this->getValue('paymentInfoKeys')); - foreach ($additionalInformationKeys as $paymentInfoKey) { - $paymentInfoValue = $response->getDataByKey($paymentInfoKey); - if ($paymentInfoValue !== null) { - $payment->setAdditionalInformation($paymentInfoKey, $paymentInfoValue); - } - } - } - - /** - * Check response code came from Authorize.net. - * - * @return true in case of Approved response - * @throws \Magento\Framework\Exception\LocalizedException In case of Declined or Error response from Authorize.net - */ - public function checkResponseCode() - { - switch ($this->getResponse()->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - case self::RESPONSE_CODE_HELD: - return true; - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - $errorMessage = $this->dataHelper->wrapGatewayError($this->getResponse()->getXResponseReasonText()); - $order = $this->getOrderFromResponse(); - $this->paymentFailures->handle((int)$order->getQuoteId(), (string)$errorMessage); - throw new \Magento\Framework\Exception\LocalizedException($errorMessage); - default: - throw new \Magento\Framework\Exception\LocalizedException( - __('There was a payment authorization error.') - ); - } - } - - /** - * Check transaction id came from Authorize.net - * - * @return true in case of right transaction id - * @throws \Magento\Framework\Exception\LocalizedException In case of bad transaction id. - */ - public function checkTransId() - { - if (!$this->getResponse()->getXTransId()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Please enter a transaction ID to authorize this payment.') - ); - } - return true; - } - - /** - * Compare amount with amount from the response from Authorize.net. - * - * @param float $amount - * @return bool - */ - protected function matchAmount($amount) - { - return sprintf('%.2F', $amount) == sprintf('%.2F', $this->getResponse()->getXAmount()); - } - - /** - * Operate with order using information from Authorize.net. - * - * Authorize order or authorize and capture it. - * - * @param \Magento\Sales\Model\Order $order - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Exception - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function processOrder(\Magento\Sales\Model\Order $order) - { - try { - $this->checkResponseCode(); - $this->checkTransId(); - } catch (\Exception $e) { - //decline the order (in case of wrong response code) but don't return money to customer. - $message = $e->getMessage(); - $this->declineOrder($order, $message, false); - - throw $e; - } - - $response = $this->getResponse(); - - //create transaction. need for void if amount will not match. - $payment = $order->getPayment(); - $this->fillPaymentByResponse($payment); - $payment->getMethodInstance()->setIsInitializeNeeded(false); - $payment->getMethodInstance()->setResponseData($response->getData()); - $this->processPaymentFraudStatus($payment); - $payment->place(); - $this->addStatusComment($payment); - $order->save(); - - //match amounts. should be equals for authorization. - //decline the order if amount does not match. - if (!$this->matchAmount($payment->getBaseAmountAuthorized())) { - $message = __( - 'Something went wrong: the paid amount doesn\'t match the order amount.' - . ' Please correct this and try again.' - ); - $this->declineOrder($order, $message, true); - throw new \Magento\Framework\Exception\LocalizedException($message); - } - - try { - if (!$response->hasOrderSendConfirmation() || $response->getOrderSendConfirmation()) { - $this->orderSender->send($order); - } - - $quote = $this->quoteRepository->get($order->getQuoteId())->setIsActive(false); - $this->quoteRepository->save($quote); - } catch (\Exception $e) { - // do not cancel order if we couldn't send email - } - } - - /** - * Process fraud status - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return $this - */ - protected function processPaymentFraudStatus(\Magento\Sales\Model\Order\Payment $payment) - { - try { - $fraudDetailsResponse = $payment->getMethodInstance() - ->fetchTransactionFraudDetails($this->getResponse()->getXTransId()); - $fraudData = $fraudDetailsResponse->getData(); - - if (empty($fraudData)) { - $payment->setIsFraudDetected(false); - return $this; - } - - $fdsFilterAction = (string)$fraudDetailsResponse->getFdsFilterAction(); - if ($this->fdsFilterActionIsReportOnly($fdsFilterAction) === false) { - $payment->setIsFraudDetected(true); - } - - $payment->setAdditionalInformation('fraud_details', $fraudData); - } catch (\Exception $e) { - //this request is optional - } - - return $this; - } - - /** - * Add status comment to history - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return $this - */ - protected function addStatusComment(\Magento\Sales\Model\Order\Payment $payment) - { - try { - $transactionId = $this->getResponse()->getXTransId(); - $data = $this->transactionService->getTransactionDetails($this, $transactionId); - $transactionStatus = (string)$data->transaction->transactionStatus; - $fdsFilterAction = (string)$data->transaction->FDSFilterAction; - - if ($payment->getIsTransactionPending()) { - $message = 'Amount of %1 is pending approval on the gateway.<br/>' - . 'Transaction "%2" status is "%3".<br/>' - . 'Transaction FDS Filter Action is "%4"'; - $message = __( - $message, - $payment->getOrder()->getBaseCurrency()->formatTxt($this->getResponse()->getXAmount()), - $transactionId, - $this->dataHelper->getTransactionStatusLabel($transactionStatus), - $this->dataHelper->getFdsFilterActionLabel($fdsFilterAction) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } - } catch (\Exception $e) { - $this->getPsrLogger()->critical($e); - //this request is optional - } - return $this; - } - - /** - * Register order cancellation. Return money to customer if needed. - * - * @param \Magento\Sales\Model\Order $order - * @param string $message - * @param bool $voidPayment - * @return void - */ - protected function declineOrder(\Magento\Sales\Model\Order $order, $message = '', $voidPayment = true) - { - try { - $response = $this->getResponse(); - if ($voidPayment - && $response->getXTransId() - && strtoupper($response->getXType()) == self::REQUEST_TYPE_AUTH_ONLY - ) { - $order->getPayment() - ->setTransactionId(null) - ->setParentTransactionId($response->getXTransId()) - ->void($response); - } - $order->registerCancellation($message)->save(); - $this->_eventManager->dispatch('order_cancel_after', ['order' => $order ]); - } catch (\Exception $e) { - //quiet decline - $this->getPsrLogger()->critical($e); - } - } - - /** - * Return additional information`s transaction_id value of parent transaction model - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return string - */ - protected function getRealParentTransactionId($payment) - { - $transaction = $this->transactionRepository->getByTransactionId( - $payment->getParentTransactionId(), - $payment->getId(), - $payment->getOrder()->getId() - ); - return $transaction->getAdditionalInformation(self::REAL_TRANSACTION_ID_KEY); - } - - /** - * {inheritdoc} - */ - public function getConfigInterface() - { - return $this; - } - - /** - * Getter for specified value according to set payment method code - * - * @param mixed $key - * @param mixed $storeId - * @return mixed - */ - public function getValue($key, $storeId = null) - { - return $this->getConfigData($key, $storeId); - } - - /** - * Set initialization requirement state - * - * @param bool $isInitializeNeeded - * @return void - */ - public function setIsInitializeNeeded($isInitializeNeeded = true) - { - $this->_isInitializeNeeded = (bool)$isInitializeNeeded; - } - - /** - * Get whether it is possible to capture - * - * @return bool - */ - public function canCapture() - { - return !$this->isGatewayActionsLocked($this->getInfoInstance()); - } - - /** - * Fetch transaction details info - * - * Update transaction info if there is one placing transaction only - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param string $transactionId - * @return array - */ - public function fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId) - { - $transaction = $this->transactionRepository->getByTransactionId( - $transactionId, - $payment->getId(), - $payment->getOrder()->getId() - ); - - $response = $this->getTransactionResponse($transactionId); - if ($response->getXResponseCode() == self::RESPONSE_CODE_APPROVED) { - if ($response->getTransactionStatus() == 'voided') { - $payment->setIsTransactionDenied(true); - $payment->setIsTransactionClosed(true); - $transaction->close(); - } else { - $transaction->setAdditionalInformation(self::TRANSACTION_FRAUD_STATE_KEY, false); - $payment->setIsTransactionApproved(true); - } - } elseif ($response->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_PENDING_REVIEW_DECLINED) { - $payment->setIsTransactionDenied(true); - } - $this->addStatusCommentOnUpdate($payment, $response, $transactionId); - return $response->getData(); - } - - /** - * Add status comment on update - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @param \Magento\Framework\DataObject $response - * @param string $transactionId - * @return $this - */ - protected function addStatusCommentOnUpdate( - \Magento\Sales\Model\Order\Payment $payment, - \Magento\Framework\DataObject $response, - $transactionId - ) { - if ($payment->getIsTransactionApproved()) { - $message = __( - 'Transaction %1 has been approved. Amount %2. Transaction status is "%3"', - $transactionId, - $payment->getOrder()->getBaseCurrency()->formatTxt($payment->getAmountAuthorized()), - $this->dataHelper->getTransactionStatusLabel($response->getTransactionStatus()) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } elseif ($payment->getIsTransactionDenied()) { - $message = __( - 'Transaction %1 has been voided/declined. Transaction status is "%2". Amount %3.', - $transactionId, - $this->dataHelper->getTransactionStatusLabel($response->getTransactionStatus()), - $payment->getOrder()->getBaseCurrency()->formatTxt($payment->getAmountAuthorized()) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } - return $this; - } - - /** - * Sets method code - * - * @param string $methodCode - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @return void - */ - public function setMethodCode($methodCode) - { - } - - /** - * Sets path pattern - * - * @param string $pathPattern - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @return void - */ - public function setPathPattern($pathPattern) - { - } - - /** - * This function returns full transaction details for a specified transaction ID. - * - * @param string $transactionId - * @return \Magento\Framework\DataObject - * @throws \Magento\Framework\Exception\LocalizedException - * @link http://www.authorize.net/support/ReportingGuide_XML.pdf - * @link http://developer.authorize.net/api/transaction_details/ - */ - protected function getTransactionResponse($transactionId) - { - $responseXmlDocument = $this->transactionService->getTransactionDetails($this, $transactionId); - - $response = new \Magento\Framework\DataObject(); - $response->setXResponseCode((string)$responseXmlDocument->transaction->responseCode) - ->setXResponseReasonCode((string)$responseXmlDocument->transaction->responseReasonCode) - ->setTransactionStatus((string)$responseXmlDocument->transaction->transactionStatus); - - return $response; - } - - /** - * Get psr logger. - * - * @return \Psr\Log\LoggerInterface - * @deprecated 100.1.0 - */ - private function getPsrLogger() - { - if (null === $this->psrLogger) { - $this->psrLogger = ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class); - } - return $this->psrLogger; - } - - /** - * Fetch order by increment id from response. - * - * @return \Magento\Sales\Model\Order - */ - private function getOrderFromResponse(): \Magento\Sales\Model\Order - { - if (!$this->order) { - $this->order = $this->orderFactory->create(); - - if ($incrementId = $this->getOrderIncrementId()) { - $this->order = $this->order->loadByIncrementId($incrementId); - } - } - - return $this->order; - } - - /** - * Fetch order increment id from response. - * - * @return string - */ - private function getOrderIncrementId(): string - { - return $this->getResponse()->getXInvoiceNum(); - } - - /** - * Checks if filter action is Report Only. - * - * Transactions that trigger this filter are processed as normal, - * but are also reported in the Merchant Interface as triggering this filter. - * - * @param string $fdsFilterAction - * @return bool - */ - private function fdsFilterActionIsReportOnly($fdsFilterAction) - { - return $fdsFilterAction === (string)$this->dataHelper->getFdsFilterActionLabel('report'); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php deleted file mode 100644 index 10be4cd5febf6..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ /dev/null @@ -1,280 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Directpost; - -use Magento\Authorizenet\Model\Request as AuthorizenetRequest; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Intl\DateTimeFactory; - -/** - * Authorize.net request model for DirectPost model - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Request extends AuthorizenetRequest -{ - /** - * @var string - */ - protected $_transKey = null; - - /** - * Hexadecimal signature key. - * - * @var string - */ - private $signatureKey = ''; - - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @param array $data - * @param DateTimeFactory $dateTimeFactory - */ - public function __construct( - array $data = [], - DateTimeFactory $dateTimeFactory = null - ) { - $this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance() - ->get(DateTimeFactory::class); - parent::__construct($data); - } - - /** - * Return merchant transaction key. - * - * Needed to generate MD5 sign. - * - * @return string - */ - protected function _getTransactionKey() - { - return $this->_transKey; - } - - /** - * Set merchant transaction key. - * - * Needed to generate MD5 sign. - * - * @param string $transKey - * @return $this - */ - protected function _setTransactionKey($transKey) - { - $this->_transKey = $transKey; - return $this; - } - - /** - * Generates the MD5 fingerprint for request. - * - * @param string $merchantApiLoginId - * @param string $merchantTransactionKey - * @param string $amount - * @param string $currencyCode - * @param string $fpSequence An invoice number or random number. - * @param string $fpTimestamp - * @return string The fingerprint. - */ - public function generateRequestSign( - $merchantApiLoginId, - $merchantTransactionKey, - $amount, - $currencyCode, - $fpSequence, - $fpTimestamp - ) { - return hash_hmac( - "md5", - $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode, - $merchantTransactionKey - ); - } - - /** - * Set Authorizenet data to request. - * - * @param \Magento\Authorizenet\Model\Directpost $paymentMethod - * @return $this - */ - public function setConstantData(\Magento\Authorizenet\Model\Directpost $paymentMethod) - { - $this->setXVersion('3.1')->setXDelimData('FALSE')->setXRelayResponse('TRUE'); - - $this->setSignatureKey($paymentMethod->getConfigData('signature_key')); - - $this->setXLogin($paymentMethod->getConfigData('login')) - ->setXMethod(\Magento\Authorizenet\Model\Authorizenet::REQUEST_METHOD_CC) - ->setXRelayUrl($paymentMethod->getRelayUrl()); - - $this->_setTransactionKey($paymentMethod->getConfigData('trans_key')); - return $this; - } - - /** - * Set entity data to request - * - * @param \Magento\Sales\Model\Order $order - * @param \Magento\Authorizenet\Model\Directpost $paymentMethod - * @return $this - */ - public function setDataFromOrder( - \Magento\Sales\Model\Order $order, - \Magento\Authorizenet\Model\Directpost $paymentMethod - ) { - $payment = $order->getPayment(); - - $this->setXType($payment->getAnetTransType()); - $this->setXFpSequence($order->getQuoteId()); - $this->setXInvoiceNum($order->getIncrementId()); - $this->setXAmount($payment->getBaseAmountAuthorized()); - $this->setXCurrencyCode($order->getBaseCurrencyCode()); - $this->setXTax( - sprintf('%.2F', $order->getBaseTaxAmount()) - )->setXFreight( - sprintf('%.2F', $order->getBaseShippingAmount()) - ); - - //need to use (string) because NULL values IE6-8 decodes as "null" in JSON in JavaScript, - //but we need "" for null values. - $billing = $order->getBillingAddress(); - if (!empty($billing)) { - $this->setXFirstName((string)$billing->getFirstname()) - ->setXLastName((string)$billing->getLastname()) - ->setXCompany((string)$billing->getCompany()) - ->setXAddress((string)$billing->getStreetLine(1)) - ->setXCity((string)$billing->getCity()) - ->setXState((string)$billing->getRegion()) - ->setXZip((string)$billing->getPostcode()) - ->setXCountry((string)$billing->getCountryId()) - ->setXPhone((string)$billing->getTelephone()) - ->setXFax((string)$billing->getFax()) - ->setXCustId((string)$billing->getCustomerId()) - ->setXCustomerIp((string)$order->getRemoteIp()) - ->setXCustomerTaxId((string)$billing->getTaxId()) - ->setXEmail((string)$order->getCustomerEmail()) - ->setXEmailCustomer((string)$paymentMethod->getConfigData('email_customer')) - ->setXMerchantEmail((string)$paymentMethod->getConfigData('merchant_email')); - } - - $shipping = $order->getShippingAddress(); - if (!empty($shipping)) { - $this->setXShipToFirstName( - (string)$shipping->getFirstname() - )->setXShipToLastName( - (string)$shipping->getLastname() - )->setXShipToCompany( - (string)$shipping->getCompany() - )->setXShipToAddress( - (string)$shipping->getStreetLine(1) - )->setXShipToCity( - (string)$shipping->getCity() - )->setXShipToState( - (string)$shipping->getRegion() - )->setXShipToZip( - (string)$shipping->getPostcode() - )->setXShipToCountry( - (string)$shipping->getCountryId() - ); - } - - $this->setXPoNum((string)$payment->getPoNumber()); - - return $this; - } - - /** - * Set sign hash into the request object. - * - * All needed fields should be placed in the object first. - * - * @return $this - */ - public function signRequestData() - { - $fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - $fpTimestamp = $fpDate->getTimestamp(); - - if (!empty($this->getSignatureKey())) { - $hash = $this->generateSha2RequestSign( - (string)$this->getXLogin(), - (string)$this->getSignatureKey(), - (string)$this->getXAmount(), - (string)$this->getXCurrencyCode(), - (string)$this->getXFpSequence(), - $fpTimestamp - ); - } else { - $hash = $this->generateRequestSign( - $this->getXLogin(), - $this->_getTransactionKey(), - $this->getXAmount(), - $this->getXCurrencyCode(), - $this->getXFpSequence(), - $fpTimestamp - ); - } - - $this->setXFpTimestamp($fpTimestamp); - $this->setXFpHash($hash); - - return $this; - } - - /** - * Generates the SHA2 fingerprint for request. - * - * @param string $merchantApiLoginId - * @param string $merchantSignatureKey - * @param string $amount - * @param string $currencyCode - * @param string $fpSequence An invoice number or random number. - * @param int $fpTimestamp - * @return string The fingerprint. - */ - private function generateSha2RequestSign( - string $merchantApiLoginId, - string $merchantSignatureKey, - string $amount, - string $currencyCode, - string $fpSequence, - int $fpTimestamp - ): string { - $message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode; - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey))); - } - - /** - * Return merchant hexadecimal signature key. - * - * Needed to generate SHA2 sign. - * - * @return string - */ - private function getSignatureKey(): string - { - return $this->signatureKey; - } - - /** - * Set merchant hexadecimal signature key. - * - * Needed to generate SHA2 sign. - * - * @param string $signatureKey - * @return void - */ - private function setSignatureKey(string $signatureKey) - { - $this->signatureKey = $signatureKey; - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php b/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php deleted file mode 100644 index 6036935f57be1..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Directpost\Request; - -use Magento\Authorizenet\Model\Request\Factory as AuthorizenetRequestFactory; - -/** - * Factory class for @see \Magento\Authorizenet\Model\Directpost\Request - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Factory extends AuthorizenetRequestFactory -{ - /** - * Factory constructor - * - * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param string $instanceName - */ - public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Authorizenet\Model\Directpost\Request::class - ) { - parent::__construct($objectManager, $instanceName); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Response.php b/app/code/Magento/Authorizenet/Model/Directpost/Response.php deleted file mode 100644 index b5604a78cb9cd..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Response.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Directpost; - -use Magento\Authorizenet\Model\Response as AuthorizenetResponse; -use Magento\Framework\Encryption\Helper\Security; - -/** - * Authorize.net response model for DirectPost model - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Response extends AuthorizenetResponse -{ - /** - * Generates an Md5 hash to compare against AuthNet's. - * - * @param string $merchantMd5 - * @param string $merchantApiLogin - * @param string $amount - * @param string $transactionId - * @return string - */ - public function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) - { - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); - } - - /** - * Return if is valid order id. - * - * @param string $storedHash - * @param string $merchantApiLogin - * @return bool - */ - public function isValidHash($storedHash, $merchantApiLogin) - { - if (empty($this->getData('x_amount'))) { - $this->setData('x_amount', '0.00'); - } - - if (!empty($this->getData('x_SHA2_Hash'))) { - $hash = $this->generateSha2Hash($storedHash); - return Security::compareStrings($hash, $this->getData('x_SHA2_Hash')); - } elseif (!empty($this->getData('x_MD5_Hash'))) { - $hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); - return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); - } - - return false; - } - - /** - * Return if this is approved response from Authorize.net auth request. - * - * @return bool - */ - public function isApproved() - { - return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED; - } - - /** - * Generates an SHA2 hash to compare against AuthNet's. - * - * @param string $signatureKey - * @return string - * @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement - */ - private function generateSha2Hash(string $signatureKey): string - { - $hashFields = [ - 'x_trans_id', - 'x_test_request', - 'x_response_code', - 'x_auth_code', - 'x_cvv2_resp_code', - 'x_cavv_response', - 'x_avs_code', - 'x_method', - 'x_account_number', - 'x_amount', - 'x_company', - 'x_first_name', - 'x_last_name', - 'x_address', - 'x_city', - 'x_state', - 'x_zip', - 'x_country', - 'x_phone', - 'x_fax', - 'x_email', - 'x_ship_to_company', - 'x_ship_to_first_name', - 'x_ship_to_last_name', - 'x_ship_to_address', - 'x_ship_to_city', - 'x_ship_to_state', - 'x_ship_to_zip', - 'x_ship_to_country', - 'x_invoice_num', - ]; - - $message = '^'; - foreach ($hashFields as $field) { - $message .= ($this->getData($field) ?? '') . '^'; - } - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey))); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php b/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php deleted file mode 100644 index 4fda5ac62b498..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Directpost\Response; - -use Magento\Authorizenet\Model\Response\Factory as AuthorizenetResponseFactory; - -/** - * Factory class for @see \Magento\Authorizenet\Model\Directpost\Response - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Factory extends AuthorizenetResponseFactory -{ - /** - * Factory constructor - * - * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param string $instanceName - */ - public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Authorizenet\Model\Directpost\Response::class - ) { - parent::__construct($objectManager, $instanceName); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Session.php b/app/code/Magento/Authorizenet/Model/Directpost/Session.php deleted file mode 100644 index 26c5ff0cb7e36..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Session.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Directpost; - -use Magento\Framework\Session\SessionManager; - -/** - * Authorize.net DirectPost session model - * - * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Session extends SessionManager -{ - /** - * Add order IncrementId to session - * - * @param string $orderIncrementId - * @return void - */ - public function addCheckoutOrderIncrementId($orderIncrementId) - { - $orderIncIds = $this->getDirectPostOrderIncrementIds(); - if (!$orderIncIds) { - $orderIncIds = []; - } - $orderIncIds[$orderIncrementId] = 1; - $this->setDirectPostOrderIncrementIds($orderIncIds); - } - - /** - * Remove order IncrementId from session - * - * @param string $orderIncrementId - * @return void - */ - public function removeCheckoutOrderIncrementId($orderIncrementId) - { - $orderIncIds = $this->getDirectPostOrderIncrementIds(); - - if (!is_array($orderIncIds)) { - return; - } - - if (isset($orderIncIds[$orderIncrementId])) { - unset($orderIncIds[$orderIncrementId]); - } - $this->setDirectPostOrderIncrementIds($orderIncIds); - } - - /** - * Return if order incrementId is in session. - * - * @param string $orderIncrementId - * @return bool - */ - public function isCheckoutOrderIncrementIdExist($orderIncrementId) - { - $orderIncIds = $this->getDirectPostOrderIncrementIds(); - if (is_array($orderIncIds) && isset($orderIncIds[$orderIncrementId])) { - return true; - } - return false; - } - - /** - * Set quote id to session - * - * @param int|string $id - * @return $this - */ - public function setQuoteId($id) - { - $this->storage->setQuoteId($id); - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Model/Request.php b/app/code/Magento/Authorizenet/Model/Request.php deleted file mode 100644 index 552439fc8bb9b..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Request.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -use Magento\Framework\DataObject; - -/** - * Request object - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Request extends DataObject -{ -} diff --git a/app/code/Magento/Authorizenet/Model/Request/Factory.php b/app/code/Magento/Authorizenet/Model/Request/Factory.php deleted file mode 100644 index a7a636280e28d..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Request/Factory.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Request; - -/** - * Factory class for @see \Magento\Authorizenet\Model\Request - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Factory -{ - /** - * Object Manager instance - * - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $objectManager; - - /** - * Instance name to create - * - * @var string - */ - protected $instanceName; - - /** - * Factory constructor - * - * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param string $instanceName - */ - public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Authorizenet\Model\Request::class - ) { - $this->objectManager = $objectManager; - $this->instanceName = $instanceName; - } - - /** - * Create class instance with specified parameters - * - * @param array $data - * @return \Magento\Authorizenet\Model\Request - */ - public function create(array $data = []) - { - return $this->objectManager->create($this->instanceName, $data); - } -} diff --git a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php b/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php deleted file mode 100644 index 2c21d0e2e28e0..0000000000000 --- a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\ResourceModel; - -/** - * Resource Authorize.net debug model - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Debug extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb -{ - /** - * Resource initialization - * - * @return void - */ - protected function _construct() - { - $this->_init('authorizenet_debug', 'debug_id'); - } -} diff --git a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php b/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php deleted file mode 100644 index b84ee1e72a2d4..0000000000000 --- a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\ResourceModel\Debug; - -/** - * Resource Authorize.net debug collection model - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection -{ - /** - * Resource initialization - * - * @return void - */ - protected function _construct() - { - $this->_init( - \Magento\Authorizenet\Model\Debug::class, - \Magento\Authorizenet\Model\ResourceModel\Debug::class - ); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Response.php b/app/code/Magento/Authorizenet/Model/Response.php deleted file mode 100644 index c552663a15373..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Response.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -use Magento\Framework\DataObject; - -/** - * Response object - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Response extends DataObject -{ -} diff --git a/app/code/Magento/Authorizenet/Model/Response/Factory.php b/app/code/Magento/Authorizenet/Model/Response/Factory.php deleted file mode 100644 index 4578095566004..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Response/Factory.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Response; - -/** - * Factory class for @see \Magento\Authorizenet\Model\Response - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Factory -{ - /** - * Object Manager instance - * - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $objectManager; - - /** - * Instance name to create - * - * @var string - */ - protected $instanceName; - - /** - * Factory constructor - * - * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param string $instanceName - */ - public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Authorizenet\Model\Response::class - ) { - $this->objectManager = $objectManager; - $this->instanceName = $instanceName; - } - - /** - * Create class instance with specified parameters - * - * @param array $data - * @return \Magento\Authorizenet\Model\Response - */ - public function create(array $data = []) - { - return $this->objectManager->create($this->instanceName, $data); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Source/Cctype.php b/app/code/Magento/Authorizenet/Model/Source/Cctype.php deleted file mode 100644 index ffb3584722450..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Source/Cctype.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Source; - -use Magento\Payment\Model\Source\Cctype as PaymentCctype; - -/** - * Authorize.net Payment CC Types Source Model - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class Cctype extends PaymentCctype -{ - /** - * Return all supported credit card types - * - * @return string[] - */ - public function getAllowedTypes() - { - return ['VI', 'MC', 'AE', 'DI', 'JCB', 'DN']; - } -} diff --git a/app/code/Magento/Authorizenet/Model/Source/PaymentAction.php b/app/code/Magento/Authorizenet/Model/Source/PaymentAction.php deleted file mode 100644 index c6e57557f65c5..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Source/PaymentAction.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model\Source; - -use Magento\Framework\Option\ArrayInterface; - -/** - * Authorize.net Payment Action Dropdown source - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class PaymentAction implements ArrayInterface -{ - /** - * @inheritdoc - */ - public function toOptionArray() - { - return [ - [ - 'value' => \Magento\Authorizenet\Model\Authorizenet::ACTION_AUTHORIZE, - 'label' => __('Authorize Only'), - ], - [ - 'value' => \Magento\Authorizenet\Model\Authorizenet::ACTION_AUTHORIZE_CAPTURE, - 'label' => __('Authorize and Capture') - ] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Model/TransactionService.php b/app/code/Magento/Authorizenet/Model/TransactionService.php deleted file mode 100644 index af0b02e94cf45..0000000000000 --- a/app/code/Magento/Authorizenet/Model/TransactionService.php +++ /dev/null @@ -1,183 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Model; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Simplexml\Element; -use Magento\Framework\Xml\Security; -use Magento\Authorizenet\Model\Authorizenet; -use Magento\Payment\Model\Method\Logger; - -/** - * Class TransactionService - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class TransactionService -{ - /** - * Transaction Details gateway url - */ - const CGI_URL_TD = 'https://apitest.authorize.net/xml/v1/request.api'; - - const PAYMENT_UPDATE_STATUS_CODE_SUCCESS = 'Ok'; - - const CONNECTION_TIMEOUT = 45; - - /** - * Stored information about transaction - * - * @var array - */ - protected $transactionDetails = []; - - /** - * @var \Magento\Framework\Xml\Security - */ - protected $xmlSecurityHelper; - - /** - * @var \Magento\Payment\Model\Method\Logger - */ - protected $logger; - - /** - * @var \Magento\Framework\HTTP\ZendClientFactory - */ - protected $httpClientFactory; - - /** - * Fields that should be replaced in debug with '***' - * - * @var array - */ - protected $debugReplacePrivateDataKeys = ['merchantAuthentication', 'x_login']; - - /** - * @param Security $xmlSecurityHelper - * @param Logger $logger - * @param ZendClientFactory $httpClientFactory - */ - public function __construct( - Security $xmlSecurityHelper, - Logger $logger, - ZendClientFactory $httpClientFactory - ) { - $this->xmlSecurityHelper = $xmlSecurityHelper; - $this->logger = $logger; - $this->httpClientFactory = $httpClientFactory; - } - - /** - * Get transaction information - * - * @param \Magento\Authorizenet\Model\Authorizenet $context - * @param string $transactionId - * @return \Magento\Framework\Simplexml\Element - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function getTransactionDetails(Authorizenet $context, $transactionId) - { - return isset($this->transactionDetails[$transactionId]) - ? $this->transactionDetails[$transactionId] - : $this->loadTransactionDetails($context, $transactionId); - } - - /** - * Load transaction details - * - * @param \Magento\Authorizenet\Model\Authorizenet $context - * @param string $transactionId - * @return \Magento\Framework\Simplexml\Element - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function loadTransactionDetails(Authorizenet $context, $transactionId) - { - - $requestBody = $this->getRequestBody( - $context->getConfigData('login'), - $context->getConfigData('trans_key'), - $transactionId - ); - - /** @var \Magento\Framework\HTTP\ZendClient $client */ - $client = $this->httpClientFactory->create(); - $url = $context->getConfigData('cgi_url_td') ?: self::CGI_URL_TD; - $client->setUri($url); - $client->setConfig(['timeout' => self::CONNECTION_TIMEOUT]); - $client->setHeaders(['Content-Type: text/xml']); - $client->setMethod(\Zend_Http_Client::POST); - $client->setRawData($requestBody); - - $debugData = ['url' => $url, 'request' => $this->removePrivateDataFromXml($requestBody)]; - - try { - $responseBody = $client->request()->getBody(); - if (!$this->xmlSecurityHelper->scan($responseBody)) { - $this->logger->critical('Attempt loading of external XML entities in response from Authorizenet.'); - throw new \Exception(); - } - $debugData['response'] = $responseBody; - libxml_use_internal_errors(true); - $responseXmlDocument = new Element($responseBody); - libxml_use_internal_errors(false); - } catch (\Exception $e) { - throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); - } finally { - $context->debugData($debugData); - } - - if (!isset($responseXmlDocument->messages->resultCode) - || $responseXmlDocument->messages->resultCode != static::PAYMENT_UPDATE_STATUS_CODE_SUCCESS - ) { - throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); - } - - $this->transactionDetails[$transactionId] = $responseXmlDocument; - return $responseXmlDocument; - } - - /** - * Create request body to get transaction details - * - * @param string $login - * @param string $transactionKey - * @param string $transactionId - * @return string - */ - private function getRequestBody($login, $transactionKey, $transactionId) - { - $requestBody = sprintf( - '<?xml version="1.0" encoding="utf-8"?>' . - '<getTransactionDetailsRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">' . - '<merchantAuthentication><name>%s</name><transactionKey>%s</transactionKey></merchantAuthentication>' . - '<transId>%s</transId>' . - '</getTransactionDetailsRequest>', - $login, - $transactionKey, - $transactionId - ); - return $requestBody; - } - - /** - * Remove nodes with private data from XML string - * - * Uses values from $_debugReplacePrivateDataKeys property - * - * @param string $xml - * @return string - */ - private function removePrivateDataFromXml($xml) - { - foreach ($this->debugReplacePrivateDataKeys as $key) { - $xml = preg_replace(sprintf('~(?<=<%s>).*?(?=</%s>)~', $key, $key), Logger::DEBUG_KEYS_MASK, $xml); - } - return $xml; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php b/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php deleted file mode 100644 index bdd10437927c8..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Observer; - -use Magento\Framework\Event\ObserverInterface; -use Magento\Sales\Model\Order; - -/** - * Class AddFieldsToResponseObserver - * - * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class AddFieldsToResponseObserver implements ObserverInterface -{ - /** - * - * @var \Magento\Framework\Registry - */ - protected $coreRegistry; - - /** - * @var \Magento\Authorizenet\Model\Directpost - */ - protected $payment; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Session - */ - protected $session; - - /** - * @var \Magento\Store\Model\StoreManagerInterface - */ - protected $storeManager; - - /** - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Authorizenet\Model\Directpost $payment - * @param \Magento\Authorizenet\Model\Directpost\Session $session - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - */ - public function __construct( - \Magento\Framework\Registry $coreRegistry, - \Magento\Authorizenet\Model\Directpost $payment, - \Magento\Authorizenet\Model\Directpost\Session $session, - \Magento\Store\Model\StoreManagerInterface $storeManager - ) { - $this->coreRegistry = $coreRegistry; - $this->payment = $payment; - $this->session = $session; - $this->storeManager = $storeManager; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $this->coreRegistry->registry('directpost_order'); - - if (!$order || !$order->getId()) { - return $this; - } - - $payment = $order->getPayment(); - - if (!$payment || $payment->getMethod() != $this->payment->getCode()) { - return $this; - } - - $result = $observer->getData('result')->getData(); - - if (!empty($result['error'])) { - return $this; - } - - // if success, then set order to session and add new fields - $this->session->addCheckoutOrderIncrementId($order->getIncrementId()); - $this->session->setLastOrderIncrementId($order->getIncrementId()); - - $requestToAuthorizenet = $payment->getMethodInstance() - ->generateRequestFromOrder($order); - $requestToAuthorizenet->setControllerActionName( - $observer->getData('action') - ->getRequest() - ->getControllerName() - ); - $requestToAuthorizenet->setIsSecure( - (string)$this->storeManager->getStore() - ->isCurrentlySecure() - ); - - $result[$this->payment->getCode()] = ['fields' => $requestToAuthorizenet->getData()]; - - $observer->getData('result')->setData($result); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php b/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php deleted file mode 100644 index 45f0adfa96f4f..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Observer; - -use Magento\Framework\Event\ObserverInterface; -use Magento\Sales\Model\Order; - -/** - * Class SaveOrderAfterSubmitObserver - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class SaveOrderAfterSubmitObserver implements ObserverInterface -{ - /** - * - * @var \Magento\Framework\Registry - */ - protected $coreRegistry; - - /** - * @param \Magento\Framework\Registry $coreRegistry - */ - public function __construct( - \Magento\Framework\Registry $coreRegistry - ) { - $this->coreRegistry = $coreRegistry; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $observer->getEvent()->getData('order'); - $this->coreRegistry->register('directpost_order', $order, true); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php b/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php deleted file mode 100644 index d6cc51eb63c01..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Observer; - -use Magento\Framework\Event\ObserverInterface; -use Magento\Sales\Model\Order; - -/** - * Class UpdateAllEditIncrementsObserver - * @deprecated 2.3.1 Authorize.net is removing all support for this payment method - */ -class UpdateAllEditIncrementsObserver implements ObserverInterface -{ - /** - * - * @var \Magento\Authorizenet\Helper\Data - */ - protected $authorizenetData; - - /** - * @param \Magento\Authorizenet\Helper\Data $authorizenetData - */ - public function __construct( - \Magento\Authorizenet\Helper\Data $authorizenetData - ) { - $this->authorizenetData = $authorizenetData; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $observer->getEvent()->getData('order'); - $this->authorizenetData->updateOrderEditIncrements($order); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/README.md b/app/code/Magento/Authorizenet/README.md deleted file mode 100644 index 62598837bee6d..0000000000000 --- a/app/code/Magento/Authorizenet/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Magento_Authorizenet module - -The Magento_Authorizenet module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento. - -## Extensibility - -Extension developers can interact with the Magento_Authorizenet module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_Authorizenet module. - -### Events - -This module dispatches the following events: - - - `checkout_directpost_placeOrder` event in the `\Magento\Authorizenet\Controller\Directpost\Payment\Place::placeCheckoutOrder()` method. Parameters: - - `result` is a data object (`\Magento\Framework\DataObject` class). - - `action` is a controller object (`\Magento\Authorizenet\Controller\Directpost\Payment\Place`). - - - `order_cancel_after` event in the `\Magento\Authorizenet\Model\Directpost::declineOrder()` method. Parameters: - - `order` is an order object (`\Magento\Sales\Model\Order` class). - - -This module observes the following events: - - - `checkout_submit_all_after` event in the `Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver` file. - - `checkout_directpost_placeOrder` event in the `Magento\Authorizenet\Observer\AddFieldsToResponseObserver` file. - -For information about events in Magento 2, see [Events and observers](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). - -### Layouts - -This module introduces the following layouts and layout handles in the `view/adminhtml/layout` directory: - -- `adminhtml_authorizenet_directpost_payment_redirect` - -This module introduces the following layouts and layout handles in the `view/frontend/layout` directory: - -- `authorizenet_directpost_payment_backendresponse` -- `authorizenet_directpost_payment_redirect` -- `authorizenet_directpost_payment_response` - -For more information about layouts in Magento 2, see the [Layout documentation](https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/layouts/layout-overview.html). diff --git a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt b/app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Authorizenet/Test/Mftf/README.md b/app/code/Magento/Authorizenet/Test/Mftf/README.md deleted file mode 100644 index 9391126a85c94..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Authorizenet Functional Tests - -The Functional Test Module for **Magento Authorizenet** module. diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php deleted file mode 100644 index b26c9bf22f153..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php +++ /dev/null @@ -1,312 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Redirect; -use Magento\Payment\Block\Transparent\Iframe; - -/** - * Class RedirectTest - * - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class RedirectTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Directpost - */ - protected $directpost; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Session|\PHPUnit_Framework_MockObject_MockObject - */ - protected $directpostSessionMock; - - /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var \Magento\Sales\Model\Order|\PHPUnit_Framework_MockObject_MockObject - */ - protected $orderMock; - - /** - * @var \Magento\Sales\Model\AdminOrder\Create|\PHPUnit_Framework_MockObject_MockObject - */ - protected $adminOrderCreateMock; - - /** - * @var \Magento\Backend\Model\Session\Quote|\PHPUnit_Framework_MockObject_MockObject - */ - protected $sessionQuoteMock; - - /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - - /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $messageManagerMock; - - /** - * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; - - /** - * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productHelperMock; - - /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject - */ - protected $escaperMock; - - /** - * @var \Magento\Framework\View\Result\PageFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultPageFactoryMock; - - /** - * @var \Magento\Backend\Model\View\Result\ForwardFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $forwardFactoryMock; - - /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject - */ - protected $coreRegistryMock; - - /** - * @var \Magento\Framework\View\Result\LayoutFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultLayoutFactoryMock; - - /** - * @var \Magento\Authorizenet\Helper\Backend\Data|\PHPUnit_Framework_MockObject_MockObject - */ - protected $helperMock; - - protected function setUp() - { - $this->directpostSessionMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->setMethods([ - 'getLastOrderIncrementId', - 'removeCheckoutOrderIncrementId', - 'isCheckoutOrderIncrementIdExist', - 'unsetData' - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->setMethods(['getId', 'getState', 'getIncrementId', 'registerCancellation', 'loadByIncrementId', 'save']) - ->disableOriginalConstructor() - ->getMock(); - $this->adminOrderCreateMock = $this->getMockBuilder(\Magento\Sales\Model\AdminOrder\Create::class) - ->setMethods(['getSession']) - ->disableOriginalConstructor() - ->getMock(); - $sessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionQuoteMock = $this->getMockBuilder(\Magento\Backend\Model\Session\Quote::class) - ->setMethods(['getOrder', 'clearStorage']) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManagerMock->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - [\Magento\Authorizenet\Model\Directpost\Session::class, $this->directpostSessionMock], - [\Magento\Sales\Model\AdminOrder\Create::class, $this->adminOrderCreateMock], - [\Magento\Backend\Model\Session\Quote::class, $this->sessionQuoteMock], - [\Magento\Backend\Model\Session::class, $sessionMock], - ]); - $this->objectManagerMock->expects($this->any()) - ->method('create') - ->with(\Magento\Sales\Model\Order::class) - ->willReturn($this->orderMock); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->setMethods(['getParams']) - ->getMockForAbstractClass(); - $responseMock = $this->getMockForAbstractClass(\Magento\Framework\App\ResponseInterface::class); - $redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); - - $this->contextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) - ->setMethods(['getObjectManager', 'getRequest', 'getResponse', 'getRedirect', 'getMessageManager']) - ->disableOriginalConstructor() - ->getMock(); - $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($this->objectManagerMock); - $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); - $this->contextMock->expects($this->any())->method('getResponse')->willReturn($responseMock); - $this->contextMock->expects($this->any())->method('getRedirect')->willReturn($redirectMock); - $this->contextMock->expects($this->any())->method('getMessageManager')->willReturn($this->messageManagerMock); - - $this->productHelperMock = $this->getMockBuilder(\Magento\Catalog\Helper\Product::class) - ->disableOriginalConstructor() - ->getMock(); - $this->escaperMock = $this->getMockBuilder(\Magento\Framework\Escaper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultPageFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->forwardFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\ForwardFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->coreRegistryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->setMethods(['register']) - ->disableOriginalConstructor() - ->getMock(); - $resultLayoutMock = $this->getMockBuilder(\Magento\Framework\View\Result\Layout::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultLayoutFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\LayoutFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->resultLayoutFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($resultLayoutMock); - $this->helperMock = $this->getMockBuilder(\Magento\Authorizenet\Helper\Backend\Data::class) - ->setMethods(['getSuccessOrderUrl']) - ->disableOriginalConstructor() - ->getMock(); - - $this->controller = new Redirect( - $this->contextMock, - $this->productHelperMock, - $this->escaperMock, - $this->resultPageFactoryMock, - $this->forwardFactoryMock, - $this->coreRegistryMock, - $this->resultLayoutFactoryMock, - $this->helperMock - ); - } - - public function testExecuteErrorMsgWithoutCancelOrder() - { - $params = ['success' => 0, 'error_msg' => 'Error message']; - $incrementId = 1; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - $this->directpostSessionMock->expects($this->once()) - ->method('getLastOrderIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('isCheckoutOrderIncrementIdExist') - ->with($incrementId) - ->willReturn(true); - - $this->orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with($incrementId) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('getIncrementId') - ->willReturn($incrementId); - $this->orderMock->expects($this->once()) - ->method('getState') - ->willReturn(\Magento\Sales\Model\Order::STATE_PENDING_PAYMENT); - $this->orderMock->expects($this->once()) - ->method('registerCancellation') - ->with($params['error_msg']) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('save'); - - $this->directpostSessionMock->expects($this->once()) - ->method('removeCheckoutOrderIncrementId') - ->with($incrementId); - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } - - public function testExecuteErrorMsgWithCancelOrder() - { - $params = ['success' => 0, 'error_msg' => 'Error message', 'x_invoice_num' => 1]; - $incrementId = 1; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - $this->directpostSessionMock->expects($this->once()) - ->method('getLastOrderIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('isCheckoutOrderIncrementIdExist') - ->with($incrementId) - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with($incrementId) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('getIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('removeCheckoutOrderIncrementId') - ->with($incrementId); - - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } - - public function testExecuteSuccess() - { - $params = ['success' => 1, 'controller_action_name' => 'action', 'x_invoice_num' => 1]; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - - $this->helperMock->expects($this->once()) - ->method('getSuccessOrderUrl') - ->willReturn('redirect_parent_url'); - - $this->directpostSessionMock->expects($this->once()) - ->method('unsetData') - ->with('quote_id'); - - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(null); - - $this->sessionQuoteMock->expects($this->atLeastOnce()) - ->method('getOrder') - ->willReturn($this->orderMock); - - $this->adminOrderCreateMock->expects($this->atLeastOnce()) - ->method('getSession') - ->willReturn($this->sessionQuoteMock); - - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php deleted file mode 100644 index c0a50e66759ba..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php +++ /dev/null @@ -1,332 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment; - -use Magento\Authorizenet\Controller\Directpost\Payment\Place; -use Magento\Authorizenet\Helper\DataFactory; -use Magento\Authorizenet\Model\Directpost\Session as DirectpostSession; -use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Checkout\Model\Type\Onepage; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\App\Response\Http; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Json\Helper\Data; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\Registry; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Payment\Model\IframeConfigProvider; -use Magento\Quote\Api\CartManagementInterface; -use Magento\Quote\Model\Quote; - -/** - * Class PlaceTest - * - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PlaceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - protected $objectManager; - - /** - * @var Place - */ - protected $placeOrderController; - - /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; - - /** - * @var Registry|\PHPUnit_Framework_MockObject_MockObject - */ - protected $coreRegistryMock; - - /** - * @var DataFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dataFactoryMock; - - /** - * @var CartManagementInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $cartManagementMock; - - /** - * @var Onepage|\PHPUnit_Framework_MockObject_MockObject - */ - protected $onepageCheckout; - - /** - * @var Data|\PHPUnit_Framework_MockObject_MockObject - */ - protected $jsonHelperMock; - - /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - - /** - * @var Http|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; - - /** - * @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var DirectpostSession|\PHPUnit_Framework_MockObject_MockObject - */ - protected $directpostSessionMock; - - /** - * @var Quote|\PHPUnit_Framework_MockObject_MockObject - */ - protected $quoteMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $loggerMock; - - /** - * @var CheckoutSession|\PHPUnit_Framework_MockObject_MockObject - */ - protected $checkoutSessionMock; - - protected function setUp() - { - $this->directpostSessionMock = $this - ->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->quoteMock = $this - ->getMockBuilder(\Magento\Quote\Model\Quote::class) - ->disableOriginalConstructor() - ->getMock(); - $this->checkoutSessionMock = $this - ->getMockBuilder(\Magento\Checkout\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->checkoutSessionMock->expects($this->any()) - ->method('getQuote') - ->will($this->returnValue($this->quoteMock)); - $this->objectManagerMock = $this - ->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->getMockForAbstractClass(); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap([ - [\Magento\Authorizenet\Model\Directpost\Session::class, $this->directpostSessionMock], - [\Magento\Checkout\Model\Session::class, $this->checkoutSessionMock], - ]); - $this->coreRegistryMock = $this - ->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dataFactoryMock = $this - ->getMockBuilder(\Magento\Authorizenet\Helper\DataFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->cartManagementMock = $this - ->getMockBuilder(\Magento\Quote\Api\CartManagementInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->onepageCheckout = $this - ->getMockBuilder(\Magento\Checkout\Model\Type\Onepage::class) - ->disableOriginalConstructor() - ->getMock(); - $this->jsonHelperMock = $this - ->getMockBuilder(\Magento\Framework\Json\Helper\Data::class) - ->disableOriginalConstructor() - ->getMock(); - $this->requestMock = $this - ->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->getMockForAbstractClass(); - $this->responseMock = $this - ->getMockBuilder(\Magento\Framework\App\Response\Http::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->loggerMock = $this - ->getMockBuilder(\Psr\Log\LoggerInterface::class) - ->getMock(); - - $this->objectManager = new ObjectManager($this); - $this->placeOrderController = $this->objectManager->getObject( - \Magento\Authorizenet\Controller\Directpost\Payment\Place::class, - [ - 'request' => $this->requestMock, - 'response' => $this->responseMock, - 'objectManager' => $this->objectManagerMock, - 'coreRegistry' => $this->coreRegistryMock, - 'dataFactory' => $this->dataFactoryMock, - 'cartManagement' => $this->cartManagementMock, - 'onepageCheckout' => $this->onepageCheckout, - 'jsonHelper' => $this->jsonHelperMock, - 'logger' => $this->loggerMock, - ] - ); - } - - /** - * @param $paymentMethod - * @param $controller - * @param $quoteId - * @param $orderId - * @param $result - * @dataProvider textExecuteDataProvider - */ - public function testExecute( - $paymentMethod, - $controller, - $quoteId, - $orderId, - $result - ) { - $this->requestMock->expects($this->at(0)) - ->method('getParam') - ->with('payment') - ->will($this->returnValue($paymentMethod)); - - $this->requestMock->expects($this->at(1)) - ->method('getParam') - ->with('controller') - ->will($this->returnValue($controller)); - - $this->quoteMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue($quoteId)); - - $this->cartManagementMock->expects($this->any()) - ->method('placeOrder') - ->will($this->returnValue($orderId)); - - $this->jsonHelperMock->expects($this->any()) - ->method('jsonEncode') - ->with($result); - - $this->placeOrderController->execute(); - } - - /** - * @param $paymentMethod - * @param $controller - * @param $quoteId - * @param $result - * @param \Exception $exception Exception to check - * @dataProvider textExecuteFailedPlaceOrderDataProvider - */ - public function testExecuteFailedPlaceOrder( - $paymentMethod, - $controller, - $quoteId, - $result, - $exception - ) { - $this->requestMock->expects($this->at(0)) - ->method('getParam') - ->with('payment') - ->will($this->returnValue($paymentMethod)); - - $this->requestMock->expects($this->at(1)) - ->method('getParam') - ->with('controller') - ->will($this->returnValue($controller)); - - $this->quoteMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue($quoteId)); - - $this->cartManagementMock->expects($this->once()) - ->method('placeOrder') - ->willThrowException($exception); - - $this->loggerMock->expects($this->once()) - ->method('critical') - ->with($exception); - - $this->jsonHelperMock->expects($this->any()) - ->method('jsonEncode') - ->with($result); - - $this->placeOrderController->execute(); - } - - /** - * @return array - */ - public function textExecuteDataProvider() - { - $objectSuccess = new \Magento\Framework\DataObject(); - $objectSuccess->setData('success', true); - - return [ - [ - ['method' => null], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - 1, - ['error_messages' => __('Please choose a payment method.'), 'goto_section' => 'payment'] - ], - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - 1, - $objectSuccess - ], - ]; - } - - /** - * @return array - */ - public function textExecuteFailedPlaceOrderDataProvider() - { - $objectFailed1 = new \Magento\Framework\DataObject( - [ - 'error' => true, - 'error_messages' => __( - 'A server error stopped your order from being placed. Please try to place your order again.' - ) - ] - ); - $generalException = new \Exception('Exception logging will save the world!'); - $localizedException = new LocalizedException(__('Electronic payments save the trees.')); - $objectFailed2 = new \Magento\Framework\DataObject( - [ - 'error' => true, - 'error_messages' => $localizedException->getMessage() - ] - ); - - return [ - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - $objectFailed1, - $generalException, - ], - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - $objectFailed2, - $localizedException, - ], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php deleted file mode 100644 index 0801d3c020119..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment; - -use Magento\Authorizenet\Controller\Directpost\Payment\Redirect; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\App\ViewInterface; -use Magento\Framework\Registry; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Payment\Block\Transparent\Iframe; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class RedirectTest - */ -class RedirectTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var RequestInterface|MockObject - */ - private $request; - - /** - * @var ViewInterface|MockObject - */ - private $view; - - /** - * @var Registry|MockObject - */ - private $coreRegistry; - - /** - * @var Redirect - */ - private $controller; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->request = static::getMockForAbstractClass(RequestInterface::class); - - $this->view = static::getMockForAbstractClass(ViewInterface::class); - - $this->coreRegistry = static::getMockBuilder(Registry::class) - ->disableOriginalConstructor() - ->setMethods(['register']) - ->getMock(); - - $this->controller = $objectManager->getObject(Redirect::class, [ - 'request' => $this->request, - 'view' => $this->view, - 'coreRegistry' => $this->coreRegistry - ]); - } - - /** - * @covers \Magento\Authorizenet\Controller\Directpost\Payment\Redirect::execute - */ - public function testExecute() - { - $url = 'http://test.com/redirect?=test'; - $params = [ - 'order_success' => $url - ]; - $this->request->expects(static::once()) - ->method('getParams') - ->willReturn($params); - - $this->coreRegistry->expects(static::once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY, []); - - $this->view->expects(static::once()) - ->method('addPageLayoutHandles'); - $this->view->expects(static::once()) - ->method('loadLayout') - ->with(false) - ->willReturnSelf(); - $this->view->expects(static::once()) - ->method('renderLayout'); - - $this->controller->execute(); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php b/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php deleted file mode 100644 index 14ecd7debc972..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Helper\Backend; - -/** - * Class DataTest - */ -class DataTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Helper\Backend\Data - */ - protected $dataHelper; - - /** - * @var \Magento\Backend\Model\Url|\PHPUnit_Framework_MockObject_MockObject - */ - protected $urlBuilderMock; - - /** - * @var \Magento\Sales\Model\OrderFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $orderFactoryMock; - - /** - * @var \Magento\Store\Model\StoreManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManagerMock; - - protected function setUp() - { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->urlBuilderMock = $this->createPartialMock(\Magento\Backend\Model\Url::class, ['getUrl']); - - $contextMock = $this->createMock(\Magento\Framework\App\Helper\Context::class); - $contextMock->expects($this->any()) - ->method('getUrlBuilder') - ->willReturn($this->urlBuilderMock); - - $this->orderFactoryMock = $this->createPartialMock(\Magento\Sales\Model\OrderFactory::class, ['create']); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManager::class); - - $this->dataHelper = $helper->getObject( - \Magento\Authorizenet\Helper\Backend\Data::class, - [ - 'context' => $contextMock, - 'storeManager' =>$this->storeManagerMock, - 'orderFactory' =>$this->orderFactoryMock, - 'backendUrl' =>$this->urlBuilderMock - ] - ); - } - - public function testGetPlaceOrderAdminUrl() - { - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('adminhtml/authorizenet_directpost_payment/place') - ->willReturn('some value'); - - $this->assertEquals('some value', $this->dataHelper->getPlaceOrderAdminUrl()); - } - - public function testGetSuccessOrderUrl() - { - $orderMock = $this->createPartialMock( - \Magento\Sales\Model\Order::class, - ['loadByIncrementId', 'getId', '__wakeup'] - ); - $orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with('invoice number') - ->willReturnSelf(); - - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn('order id'); - - $this->orderFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($orderMock); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sales/order/view', ['order_id' => 'order id']) - ->willReturn('some value'); - - $this->assertEquals( - 'some value', - $this->dataHelper->getSuccessOrderUrl(['x_invoice_num' => 'invoice number', 'some param']) - ); - } - - public function testGetRedirectIframeUrl() - { - $params = ['some params', '_secure' => true]; - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('adminhtml/authorizenet_directpost_payment/redirect', $params) - ->willReturn('some value'); - - $this->assertEquals('some value', $this->dataHelper->getRedirectIframeUrl($params)); - } - - public function testGetRelayUrl() - { - $baseUrl = 'http://base.url/'; - - $defaultStoreMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $defaultStoreMock->expects($this->once()) - ->method('getBaseUrl') - ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) - ->willReturn($baseUrl); - - $this->storeManagerMock->expects($this->once()) - ->method('getDefaultStoreView') - ->willReturn(null); - - $this->storeManagerMock->expects($this->once()) - ->method('getStores') - ->willReturn([$defaultStoreMock]); - - $this->assertSame( - 'http://base.url/authorizenet/directpost_payment/backendResponse', - $this->dataHelper->getRelayUrl() - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php b/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php deleted file mode 100644 index 28dd5f866a609..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Helper; - -class DataTest extends \PHPUnit\Framework\TestCase -{ - /** - * Last 4 digit of cc - */ - const LAST4 = 1111; - - /** - * Transaction ID - */ - const TRID = '2217041665'; - - /** - * @var \Magento\Authorizenet\Helper\Data - */ - protected $dataHelper; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManagerMock; - - protected function setUp() - { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->getMockForAbstractClass(); - - $this->dataHelper = $helper->getObject( - \Magento\Authorizenet\Helper\Data::class, - ['storeManager' => $this->storeManagerMock] - ); - } - - /** - * @param $type - * @param $amount - * @param $exception - * @param $additionalMessage - * @param $expected - * @dataProvider getMessagesParamDataProvider - */ - public function testGetTransactionMessage($type, $amount, $exception, $additionalMessage, $expected) - { - $currency = $this->createPartialMock(\Magento\Directory\Model\Currency::class, ['formatTxt', '__wakeup']); - $currency->expects($this->any()) - ->method('formatTxt') - ->will($this->returnValue($amount)); - $order = $this->createPartialMock(\Magento\Sales\Model\Order::class, ['getBaseCurrency', '__wakeup']); - $order->expects($this->any()) - ->method('getBaseCurrency') - ->will($this->returnValue($currency)); - $payment = $this->createPartialMock(\Magento\Payment\Model\Info::class, ['getOrder', '__wakeup']); - $payment->expects($this->any()) - ->method('getOrder') - ->will($this->returnValue($order)); - $card = new \Magento\Framework\DataObject(['cc_last_4' => self::LAST4]); - $message = $this->dataHelper->getTransactionMessage( - $payment, - $type, - self::TRID, - $card, - $amount, - $exception, - $additionalMessage - ); - - $this->assertEquals($expected, $message); - } - - /** - * @return array - */ - public function getMessagesParamDataProvider() - { - $amount = 12.30; - $additionalMessage = 'Addition message.'; - return [ - [ - 'AUTH_ONLY', - $amount, - false, - $additionalMessage, - 'Credit Card: xxxx-' . self::LAST4 . ' amount 12.3 authorize - successful. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. Addition message.', - ], - [ - 'AUTH_CAPTURE', - $amount, - 'some exception', - false, - 'Credit Card: xxxx-' . self::LAST4 . ' amount 12.3 authorize and capture - failed. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. some exception' - ], - [ - 'CREDIT', - false, - false, - $additionalMessage, - 'Credit Card: xxxx-' . self::LAST4 . ' refund - successful. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. Addition message.' - ], - ]; - } - - public function testGetRelayUrl() - { - $storeId = 10; - $baseUrl = 'http://base.url/'; - - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $storeMock->expects($this->once()) - ->method('getBaseUrl') - ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) - ->willReturn($baseUrl); - - $this->storeManagerMock->expects($this->once()) - ->method('getStore') - ->with($storeId) - ->willReturn($storeMock); - - $this->assertSame( - 'http://base.url/authorizenet/directpost_payment/response', - $this->dataHelper->getRelayUrl($storeId) - ); - } - - /** - * @param string $code - * @param string $expected - * - * @dataProvider getFdsFilterActionLabelDataProvider - */ - public function testGetFdsFilterActionLabel($code, $expected) - { - $this->assertSame($expected, (string)$this->dataHelper->getFdsFilterActionLabel($code)); - } - - /** - * @return array - */ - public function getFdsFilterActionLabelDataProvider() - { - return [ - ['decline ', 'Decline'], - ['hold', 'Hold'], - ['authAndHold', 'Authorize and Hold'], - ['report', 'Report Only'], - ['unknown_status', 'unknown_status'] - ]; - } - - /** - * @param string $code - * @param string $expected - * - * @dataProvider getTransactionStatusLabelDataProvider - */ - public function testGetTransactionStatusLabel($code, $expected) - { - $this->assertSame($expected, (string)$this->dataHelper->getTransactionStatusLabel($code)); - } - - /** - * @return array - */ - public function getTransactionStatusLabelDataProvider() - { - return [ - ['authorizedPendingCapture', 'Authorized/Pending Capture'], - ['capturedPendingSettlement', 'Captured/Pending Settlement'], - ['refundSettledSuccessfully', 'Refund/Settled Successfully'], - ['refundPendingSettlement', 'Refund/Pending Settlement'], - ['declined', 'Declined'], - ['expired', 'Expired'], - ['voided', 'Voided'], - ['FDSPendingReview', 'FDS - Pending Review'], - ['FDSAuthorizedPendingReview', 'FDS - Authorized/Pending Review'], - ['unknown_status', 'unknown_status'] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php deleted file mode 100644 index 99f2729fcdddf..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Directpost\Request; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class FactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Directpost\Request\Factory - */ - protected $requestFactory; - - /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Request|\PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->requestMock = $this->createMock(\Magento\Authorizenet\Model\Directpost\Request::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost\Request::class, []) - ->willReturn($this->requestMock); - - $this->requestFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Request\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->requestMock, $this->requestFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php deleted file mode 100644 index 94d8f3a0d27a7..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Directpost; - -use Magento\Authorizenet\Model\Directpost\Request; -use Magento\Framework\Intl\DateTimeFactory; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -class RequestTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var DateTimeFactory|MockObject - */ - private $dateTimeFactory; - - /** - * @var Request - */ - private $requestModel; - - protected function setUp() - { - $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC')); - $this->dateTimeFactory->method('create') - ->willReturn($dateTime); - - $this->requestModel = new Request([], $this->dateTimeFactory); - } - - /** - * @param string $signatureKey - * @param string $expectedHash - * @dataProvider signRequestDataProvider - */ - public function testSignRequestData(string $signatureKey, string $expectedHash) - { - /** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */ - $paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class); - $paymentMethod->method('getConfigData') - ->willReturnMap( - [ - ['test', null, true], - ['login', null, 'login'], - ['trans_key', null, 'trans_key'], - ['signature_key', null, $signatureKey], - ] - ); - - $this->requestModel->setConstantData($paymentMethod); - $this->requestModel->signRequestData(); - $signHash = $this->requestModel->getXFpHash(); - - $this->assertEquals($expectedHash, $signHash); - } - - /** - * @return array - */ - public function signRequestDataProvider() - { - return [ - [ - 'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' . - '70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F', - 'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' . - '1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1' - ], - [ - 'signatureKey' => '', - 'expectedHash' => '3656211f2c41d1e4c083606f326c0460' - ], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php deleted file mode 100644 index bf59a73c14940..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Directpost\Response; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class FactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Directpost\Response\Factory - */ - protected $responseFactory; - - /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Response|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->responseMock = $this->createMock(\Magento\Authorizenet\Model\Directpost\Response::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost\Response::class, []) - ->willReturn($this->responseMock); - - $this->responseFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Response\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->responseMock, $this->responseFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php deleted file mode 100644 index ff4aa8b5ee361..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Directpost; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Authorizenet\Model\Directpost; - -class ResponseTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Directpost\Response - */ - private $responseModel; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - $this->responseModel = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Response::class - ); - } - - /** - * @param $merchantMd5 - * @param $merchantApiLogin - * @param $amount - * @param $transactionId - * @return string - */ - protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) - { - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); - } - - /** - * @param string $storedHash - * @param string $hashKey - * @param string $merchantApiLogin - * @param float|null $amount - * @param string $transactionId - * @param string $hash - * @param bool $expectedValue - * @dataProvider isValidHashDataProvider - */ - public function testIsValidHash( - string $storedHash, - string $hashKey, - string $merchantApiLogin, - $amount, - string $transactionId, - string $hash, - bool $expectedValue - ) { - $this->responseModel->setXAmount($amount); - $this->responseModel->setXTransId($transactionId); - $this->responseModel->setData($hashKey, $hash); - $result = $this->responseModel->isValidHash($storedHash, $merchantApiLogin); - - $this->assertEquals($expectedValue, $result); - } - - /** - * @return array - */ - public function isValidHashDataProvider() - { - $signatureKey = '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF6570C8C' . - '29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F'; - $expectedSha2Hash = '368D48E0CD1274BF41C059138DA69985594021A4AD5B4C5526AE88C8F' . - '7C5769B13C5E1E4358900F3E51076FB69D14B0A797904C22E8A11A52AA49CDE5FBB703C'; - return [ - [ - 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', - 'hashKey' => 'x_MD5_Hash', - 'merchantApiLogin' => 'Magento', - 'amount' => null, - 'transactionId' => '1', - 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', - 'expectedValue' => true - ], - [ - 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', - 'hashKey' => 'x_MD5_Hash', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'transactionId' => '2', - 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', - 'expectedValue' => false - ], - [ - 'signatureKey' => $signatureKey, - 'hashKey' => 'x_SHA2_Hash', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'transactionId' => '2', - 'hash' => $expectedSha2Hash, - 'expectedValue' => true - ] - ]; - } - - /** - * @param int $xResponseCode - * @param bool $expectedValue - * @dataProvider isApprovedDataProvider - */ - public function testIsApproved($xResponseCode, $expectedValue) - { - $this->responseModel->setXResponseCode($xResponseCode); - $this->assertSame($expectedValue, $this->responseModel->isApproved()); - } - - /** - * @return array - */ - public function isApprovedDataProvider() - { - return [ - [Directpost::RESPONSE_CODE_APPROVED, true], - [Directpost::RESPONSE_CODE_DECLINED, false], - [Directpost::RESPONSE_CODE_ERROR, false], - [Directpost::RESPONSE_CODE_HELD, false], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php deleted file mode 100644 index 35f7a4e15219c..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Directpost; - -use Magento\Authorizenet\Model\Directpost\Session; -use Magento\Framework\Session\StorageInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class SessionTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - protected $objectManager; - - /** - * @var Session - */ - protected $session; - - /** - * @var StorageInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storageMock; - - protected function setUp() - { - $this->storageMock = $this - ->getMockBuilder(\Magento\Framework\Session\StorageInterface::class) - ->setMethods(['setQuoteId']) - ->getMockForAbstractClass(); - - $this->objectManager = new ObjectManager($this); - $this->session = $this->objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Session::class, - [ - 'storage' => $this->storageMock, - ] - ); - } - - public function testSetQuoteId() - { - $quoteId = 1; - - $this->storageMock->expects($this->once()) - ->method('setQuoteId') - ->with($quoteId); - - $this->assertInstanceOf( - \Magento\Authorizenet\Model\Directpost\Session::class, - $this->session->setQuoteId($quoteId) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php deleted file mode 100644 index a1547a0563461..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php +++ /dev/null @@ -1,885 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Authorizenet\Test\Unit\Model; - -use Magento\Authorizenet\Helper\Backend\Data; -use Magento\Authorizenet\Helper\Data as HelperData; -use Magento\Authorizenet\Model\Directpost\Response; -use Magento\Authorizenet\Model\Directpost\Response\Factory as ResponseFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Payment\Model\InfoInterface; -use Magento\Payment\Model\Method\ConfigInterface; -use Magento\Sales\Api\PaymentFailuresInterface; -use Magento\Framework\Simplexml\Element; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Authorizenet\Model\Directpost; -use Magento\Authorizenet\Model\TransactionService; -use Magento\Authorizenet\Model\Request; -use Magento\Authorizenet\Model\Directpost\Request\Factory; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\Repository as TransactionRepository; -use PHPUnit\Framework\MockObject_MockBuilder; -use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject; -use ReflectionClass; - -/** - * Class DirectpostTest - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class DirectpostTest extends TestCase -{ - const TOTAL_AMOUNT = 100.02; - const INVOICE_NUM = '00000001'; - const TRANSACTION_ID = '41a23x34fd124'; - - /** - * @var Directpost - */ - protected $directpost; - - /** - * @var ScopeConfigInterface|PHPUnit_Framework_MockObject_MockObject - */ - protected $scopeConfigMock; - - /** - * @var InfoInterface|PHPUnit_Framework_MockObject_MockObject - */ - protected $paymentMock; - - /** - * @var HelperData|PHPUnit_Framework_MockObject_MockObject - */ - protected $dataHelperMock; - - /** - * @var ResponseFactory|PHPUnit_Framework_MockObject_MockObject - */ - protected $responseFactoryMock; - - /** - * @var TransactionRepository|PHPUnit_Framework_MockObject_MockObject - */ - protected $transactionRepositoryMock; - - /** - * @var Response|PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; - - /** - * @var TransactionService|PHPUnit_Framework_MockObject_MockObject - */ - protected $transactionServiceMock; - - /** - * @var ZendClient|PHPUnit_Framework_MockObject_MockObject - */ - protected $httpClientMock; - - /** - * @var Factory|PHPUnit_Framework_MockObject_MockObject - */ - protected $requestFactory; - - /** - * @var PaymentFailuresInterface|PHPUnit_Framework_MockObject_MockObject - */ - private $paymentFailures; - - /** - * @var ZendClientFactory|PHPUnit_Framework_MockObject_MockObject - */ - private $httpClientFactoryMock; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->initPaymentMock(); - $this->initResponseFactoryMock(); - $this->initHttpClientMock(); - - $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)->getMock(); - $this->dataHelperMock = $this->getMockBuilder(HelperData::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock = $this->getMockBuilder(TransactionRepository::class) - ->disableOriginalConstructor() - ->setMethods(['getByTransactionId']) - ->getMock(); - $this->transactionServiceMock = $this->getMockBuilder(TransactionService::class) - ->disableOriginalConstructor() - ->setMethods(['getTransactionDetails']) - ->getMock(); - $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->requestFactory = $this->getMockBuilder(Factory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $helper = new ObjectManagerHelper($this); - $this->directpost = $helper->getObject( - Directpost::class, - [ - 'scopeConfig' => $this->scopeConfigMock, - 'dataHelper' => $this->dataHelperMock, - 'requestFactory' => $this->requestFactory, - 'responseFactory' => $this->responseFactoryMock, - 'transactionRepository' => $this->transactionRepositoryMock, - 'transactionService' => $this->transactionServiceMock, - 'httpClientFactory' => $this->httpClientFactoryMock, - 'paymentFailures' => $this->paymentFailures, - ] - ); - } - - /** - * Create mock for response factory - * - * @return void - */ - private function initResponseFactoryMock() - { - $this->responseFactoryMock = $this->getMockBuilder(ResponseFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->responseMock = $this->getMockBuilder(Response::class) - ->setMethods( - [ - 'isValidHash', - 'getXTransId', - 'getXResponseCode', - 'getXResponseReasonCode', - 'getXResponseReasonText', - 'getXAmount', - 'setXResponseCode', - 'setXResponseReasonCode', - 'setXAvsCode', - 'setXResponseReasonText', - 'setXTransId', - 'setXInvoiceNum', - 'setXAmount', - 'setXMethod', - 'setXType', - 'setData', - 'getData', - 'setXAccountNumber', - '__wakeup' - ] - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->responseFactoryMock->expects($this->any())->method('create')->willReturn($this->responseMock); - } - - /** - * Create mock for payment - * - * @return void - */ - private function initPaymentMock() - { - $this->paymentMock = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'getOrder', - 'setAmount', - 'setAnetTransType', - 'setXTransId', - 'getId', - 'setAdditionalInformation', - 'getAdditionalInformation', - 'setIsTransactionDenied', - 'setIsTransactionClosed', - 'decrypt', - 'getCcLast4', - 'getParentTransactionId', - 'getPoNumber' - ] - ) - ->getMock(); - } - - /** - * Create a mock for http client - * - * @return void - */ - private function initHttpClientMock() - { - $this->httpClientMock = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['request', 'getBody', '__wakeup']) - ->getMock(); - } - - public function testGetConfigInterface() - { - $this->assertInstanceOf(ConfigInterface::class, $this->directpost->getConfigInterface()); - } - - public function testGetConfigValue() - { - $field = 'some_field'; - $returnValue = 'expected'; - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/' . $field) - ->willReturn($returnValue); - $this->assertEquals($returnValue, $this->directpost->getValue($field)); - } - - public function testSetDataHelper() - { - $storeId = 'store-id'; - $expectedResult = 'relay-url'; - - $helperDataMock = $this->getMockBuilder(Data::class) - ->disableOriginalConstructor() - ->getMock(); - - $helperDataMock->expects($this->once()) - ->method('getRelayUrl') - ->with($storeId) - ->willReturn($expectedResult); - - $this->directpost->setDataHelper($helperDataMock); - $this->assertEquals($expectedResult, $this->directpost->getRelayUrl($storeId)); - } - - public function testAuthorize() - { - $paymentAction = 'some_action'; - - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/payment_action', 'store', null) - ->willReturn($paymentAction); - $this->paymentMock->expects($this->once()) - ->method('setAdditionalInformation') - ->with('payment_type', $paymentAction); - - $this->directpost->authorize($this->paymentMock, 10); - } - - /** - * @dataProvider dataProviderCaptureWithInvalidAmount - * @expectedExceptionMessage Invalid amount for capture. - * @expectedException \Magento\Framework\Exception\LocalizedException - * - * @param int $invalidAmount - */ - public function testCaptureWithInvalidAmount($invalidAmount) - { - $this->directpost->capture($this->paymentMock, $invalidAmount); - } - - /** - * @return array - */ - public function dataProviderCaptureWithInvalidAmount() - { - return [ - [0], - [0.000], - [-1.000], - [-1], - [null], - ]; - } - - /** - * Test capture has parent transaction id. - * - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCaptureHasParentTransactionId() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(1); - $this->paymentMock->expects($this->once())->method('setAnetTransType')->willReturn('PRIOR_AUTH_CAPTURE'); - - $this->paymentMock->expects($this->once())->method('getId')->willReturn(1); - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getId')->willReturn(1); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - - $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->with(1, 1, 1) - ->willReturn($transactionMock); - - $this->paymentMock->expects($this->once())->method('setXTransId'); - $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); - - $this->directpost->capture($this->paymentMock, 10); - } - - /** - * @@expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCaptureWithoutParentTransactionId() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->once())->method('getParentTransactionId')->willReturn(null); - $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); - - $this->directpost->capture($this->paymentMock, 10); - } - - public function testCaptureWithoutParentTransactionIdWithoutData() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(null); - $this->responseMock->expects($this->once())->method('getData')->willReturn([]); - - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(0) - ->willReturnSelf(); - - $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); - $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); - - $this->buildRequestTest(); - $this->postRequestTest(); - - $this->directpost->capture($this->paymentMock, 10); - } - - private function buildRequestTest() - { - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); - $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - - $this->addRequestMockToRequestFactoryMock(); - } - - private function postRequestTest() - { - $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); - $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseReasonCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseReasonText')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXAvsCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXTransId')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXInvoiceNum')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXAmount')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXMethod')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXType')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setData')->willReturnSelf(); - - $response = $this->getRefundResponseBody( - Directpost::RESPONSE_CODE_APPROVED, - Directpost::RESPONSE_REASON_CODE_APPROVED, - 'Successful' - ); - $this->httpClientMock->expects($this->once())->method('getBody')->willReturn($response); - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn(Directpost::RESPONSE_CODE_APPROVED); - $this->responseMock->expects($this->once()) - ->method('getXResponseReasonCode') - ->willReturn(Directpost::RESPONSE_REASON_CODE_APPROVED); - $this->dataHelperMock->expects($this->never())->method('wrapGatewayError'); - } - - public function testGetCgiUrl() - { - $url = 'cgi/url'; - - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/cgi_url', 'store', null) - ->willReturn($url); - - $this->assertEquals($url, $this->directpost->getCgiUrl()); - } - - public function testGetCgiUrlWithEmptyConfigValue() - { - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/cgi_url', 'store', null) - ->willReturn(null); - - $this->assertEquals(Directpost::CGI_URL, $this->directpost->getCgiUrl()); - } - - public function testGetRelayUrl() - { - $storeId = 100; - $url = 'relay/url'; - $this->directpost->setData('store', $storeId); - - $this->dataHelperMock->expects($this->exactly(2)) - ->method('getRelayUrl') - ->with($storeId) - ->willReturn($url); - - $this->assertEquals($url, $this->directpost->getRelayUrl()); - $this->assertEquals($url, $this->directpost->getRelayUrl($storeId)); - } - - public function testGetResponse() - { - $this->assertSame($this->responseMock, $this->directpost->getResponse()); - } - - public function testSetResponseData() - { - $data = [ - 'key' => 'value' - ]; - - $this->responseMock->expects($this->once()) - ->method('setData') - ->with($data) - ->willReturnSelf(); - - $this->assertSame($this->directpost, $this->directpost->setResponseData($data)); - } - - public function testValidateResponseSuccess() - { - $this->prepareTestValidateResponse('some_md5', 'login', true); - $this->assertEquals(true, $this->directpost->validateResponse()); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testValidateResponseFailure() - { - $this->prepareTestValidateResponse('some_md5', 'login', false); - $this->directpost->validateResponse(); - } - - /** - * @param string $transMd5 - * @param string $login - * @param bool $isValidHash - */ - protected function prepareTestValidateResponse($transMd5, $login, $isValidHash) - { - $this->scopeConfigMock->expects($this->exactly(2)) - ->method('getValue') - ->willReturnMap( - [ - ['payment/authorizenet_directpost/trans_md5', 'store', null, $transMd5], - ['payment/authorizenet_directpost/login', 'store', null, $login] - ] - ); - $this->responseMock->expects($this->exactly(1)) - ->method('isValidHash') - ->with($transMd5, $login) - ->willReturn($isValidHash); - } - - public function testCheckTransIdSuccess() - { - $this->responseMock->expects($this->once()) - ->method('getXTransId') - ->willReturn('111'); - - $this->assertEquals(true, $this->directpost->checkTransId()); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCheckTransIdFailure() - { - $this->responseMock->expects($this->once()) - ->method('getXTransId') - ->willReturn(null); - - $this->directpost->checkTransId(); - } - - /** - * @param bool $responseCode - * - * @dataProvider checkResponseCodeSuccessDataProvider - */ - public function testCheckResponseCodeSuccess($responseCode) - { - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn($responseCode); - - $this->assertEquals(true, $this->directpost->checkResponseCode()); - } - - /** - * @return array - */ - public function checkResponseCodeSuccessDataProvider() - { - return [ - ['responseCode' => Directpost::RESPONSE_CODE_APPROVED], - ['responseCode' => Directpost::RESPONSE_CODE_HELD] - ]; - } - - /** - * Checks response failures behaviour. - * - * @return void - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCheckResponseCodeFailureDefault() - { - $responseCode = 999999; - $this->responseMock->expects($this->once())->method('getXResponseCode')->willReturn($responseCode); - - $this->directpost->checkResponseCode(); - } - - /** - * Checks response failures behaviour. - * - * @param int $responseCode - * @param int $failuresHandlerCalls - * @return void - * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @dataProvider checkResponseCodeFailureDataProvider - */ - public function testCheckResponseCodeFailureDeclinedOrError(int $responseCode, int $failuresHandlerCalls): void - { - $reasonText = 'reason text'; - - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn($responseCode); - $this->responseMock->expects($this->once())->method('getXResponseReasonText')->willReturn($reasonText); - $this->dataHelperMock->expects($this->once()) - ->method('wrapGatewayError') - ->with($reasonText) - ->willReturn(__('Gateway error: %1', $reasonText)); - - $this->paymentFailures->expects($this->exactly($failuresHandlerCalls))->method('handle')->with(1); - $orderMock = $this->getOrderMock($failuresHandlerCalls); - - $orderMock->expects($this->exactly($failuresHandlerCalls))->method('getQuoteId')->willReturn(1); - $reflection = new ReflectionClass($this->directpost); - $order = $reflection->getProperty('order'); - $order->setAccessible(true); - $order->setValue($this->directpost, $orderMock); - - $this->directpost->checkResponseCode(); - } - - /** - * @return array - */ - public function checkResponseCodeFailureDataProvider(): array - { - return [ - ['responseCode' => Directpost::RESPONSE_CODE_DECLINED, 1], - ['responseCode' => Directpost::RESPONSE_CODE_ERROR, 1], - ]; - } - - /** - * @param bool $isInitializeNeeded - * - * @dataProvider setIsInitializeNeededDataProvider - */ - public function testSetIsInitializeNeeded($isInitializeNeeded) - { - $this->directpost->setIsInitializeNeeded($isInitializeNeeded); - $this->assertEquals($isInitializeNeeded, $this->directpost->isInitializeNeeded()); - } - - /** - * @return array - */ - public function setIsInitializeNeededDataProvider() - { - return [ - ['isInitializationNeeded' => true], - ['isInitializationNeeded' => false] - ]; - } - - /** - * @param bool $isGatewayActionsLocked - * @param bool $canCapture - * - * @dataProvider canCaptureDataProvider - */ - public function testCanCapture($isGatewayActionsLocked, $canCapture) - { - $this->directpost->setData('info_instance', $this->paymentMock); - - $this->paymentMock->expects($this->once()) - ->method('getAdditionalInformation') - ->with(Directpost::GATEWAY_ACTIONS_LOCKED_STATE_KEY) - ->willReturn($isGatewayActionsLocked); - - $this->assertEquals($canCapture, $this->directpost->canCapture()); - } - - /** - * @return array - */ - public function canCaptureDataProvider() - { - return [ - ['isGatewayActionsLocked' => false, 'canCapture' => true], - ['isGatewayActionsLocked' => true, 'canCapture' => false] - ]; - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::fetchTransactionInfo - * - * @param $transactionId - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return void - * - * @dataProvider dataProviderTransaction - */ - public function testFetchVoidedTransactionInfo($transactionId, $resultStatus, $responseStatus, $responseCode) - { - $paymentId = 36; - $orderId = 36; - - $this->paymentMock->expects($this->once())->method('getId')->willReturn($paymentId); - - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getId')->willReturn($orderId); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->with($transactionId, $paymentId, $orderId) - ->willReturn($transactionMock); - $document = $this->getTransactionXmlDocument( - $transactionId, - TransactionService::PAYMENT_UPDATE_STATUS_CODE_SUCCESS, - $resultStatus, - $responseStatus, - $responseCode - ); - $this->transactionServiceMock->expects($this->once()) - ->method('getTransactionDetails') - ->with($this->directpost, $transactionId) - ->willReturn($document); - - // transaction should be closed - $this->paymentMock->expects($this->once())->method('setIsTransactionDenied')->with(true); - $this->paymentMock->expects($this->once())->method('setIsTransactionClosed')->with(true); - $transactionMock->expects($this->once())->method('close'); - - $this->directpost->fetchTransactionInfo($this->paymentMock, $transactionId); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::refund() - * @return void - */ - public function testSuccessRefund() - { - $card = 1111; - - $this->paymentMock->expects($this->exactly(1))->method('getCcLast4')->willReturn($card); - $this->paymentMock->expects($this->once())->method('decrypt')->willReturn($card); - $this->paymentMock->expects($this->exactly(3)) - ->method('getParentTransactionId') - ->willReturn(self::TRANSACTION_ID . '-capture'); - $this->paymentMock->expects($this->once())->method('getPoNumber')->willReturn(self::INVOICE_NUM); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true) - ->willReturnSelf(); - - $this->addRequestMockToRequestFactoryMock(); - - $orderMock = $this->getOrderMock(); - - $orderMock->expects($this->once())->method('getId')->willReturn(1); - $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); - $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); - - $this->paymentMock->expects($this->exactly(2))->method('getOrder')->willReturn($orderMock); - - $transactionMock = $this->getMockBuilder(Transaction::class) - ->disableOriginalConstructor() - ->setMethods(['getAdditionalInformation']) - ->getMock(); - $transactionMock->expects($this->once()) - ->method('getAdditionalInformation') - ->with(Directpost::REAL_TRANSACTION_ID_KEY) - ->willReturn(self::TRANSACTION_ID); - - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->willReturn($transactionMock); - - $this->postRequestTest(); - - $this->directpost->refund($this->paymentMock, self::TOTAL_AMOUNT); - } - - /** - * Get data for tests - * @return array - */ - public function dataProviderTransaction() - { - return [ - [ - 'transactionId' => '9941997799', - 'resultStatus' => 'Successful.', - 'responseStatus' => 'voided', - 'responseCode' => 1 - ] - ]; - } - - /** - * Get transaction data - * @param $transactionId - * @param $resultCode - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return Element - */ - private function getTransactionXmlDocument( - $transactionId, - $resultCode, - $resultStatus, - $responseStatus, - $responseCode - ) { - $body = sprintf( - '<?xml version="1.0" encoding="utf-8"?> - <getTransactionDetailsResponse - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"> - <messages> - <resultCode>%s</resultCode> - <message> - <code>I00001</code> - <text>%s</text> - </message> - </messages> - <transaction> - <transId>%s</transId> - <transactionType>authOnlyTransaction</transactionType> - <transactionStatus>%s</transactionStatus> - <responseCode>%s</responseCode> - <responseReasonCode>%s</responseReasonCode> - </transaction> - </getTransactionDetailsResponse>', - $resultCode, - $resultStatus, - $transactionId, - $responseStatus, - $responseCode, - $responseCode - ); - libxml_use_internal_errors(true); - $document = new Element($body); - libxml_use_internal_errors(false); - return $document; - } - - /** - * Get mock for authorize.net request factory - */ - private function addRequestMockToRequestFactoryMock() - { - $request = $this->getMockBuilder(Request::class) - ->disableOriginalConstructor() - ->setMethods(['__wakeup']) - ->getMock(); - $this->requestFactory->expects($this->once()) - ->method('create') - ->willReturn($request); - } - - /** - * Get mock for order - * @return PHPUnit_Framework_MockObject_MockObject - */ - private function getOrderMock() - { - return $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'getId', - 'getQuoteId', - 'getIncrementId', - 'getStoreId', - 'getBillingAddress', - 'getShippingAddress', - 'getBaseCurrencyCode', - 'getBaseTaxAmount', - '__wakeup' - ] - ) - ->getMock(); - } - - /** - * Get mocked response for refund transaction - * @param $code - * @param $reasonCode - * @param $reasonText - * @return string - */ - private function getRefundResponseBody($code, $reasonCode, $reasonText) - { - $result = array_fill(0, 50, ''); - $result[0] = $code; // XResponseCode - $result[2] = $reasonCode; // XResponseReasonCode - $result[3] = $reasonText; // XResponseReasonText - $result[6] = self::TRANSACTION_ID; // XTransId - $result[7] = self::INVOICE_NUM; // XInvoiceNum - $result[9] = self::TOTAL_AMOUNT; // XAmount - $result[10] = Directpost::REQUEST_METHOD_CC; // XMethod - $result[11] = Directpost::REQUEST_TYPE_CREDIT; // XType - // @codingStandardsIgnoreStart - $result[37] = md5(self::TRANSACTION_ID); // x_MD5_Hash - // @codingStandardsIgnoreEnd - $result[50] = '48329483921'; // setXAccountNumber - return implode(Directpost::RESPONSE_DELIM_CHAR, $result); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php deleted file mode 100644 index 4ac98238f9ed1..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Request; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class FactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Request\Factory - */ - protected $requestFactory; - - /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var \Magento\Authorizenet\Model\Request|\PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->requestMock = $this->createMock(\Magento\Authorizenet\Model\Request::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Request::class, []) - ->willReturn($this->requestMock); - - $this->requestFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Request\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->requestMock, $this->requestFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php deleted file mode 100644 index ddc4f28de8b81..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model\Response; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class FactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorizenet\Model\Response\Factory - */ - protected $responseFactory; - - /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $objectManagerMock; - - /** - * @var \Magento\Authorizenet\Model\Response|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->responseMock = $this->createMock(\Magento\Authorizenet\Model\Response::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Response::class, []) - ->willReturn($this->responseMock); - - $this->responseFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Response\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->responseMock, $this->responseFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php deleted file mode 100644 index 092d633648482..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Model; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\Simplexml\Element; -use Magento\Authorizenet\Model\TransactionService; - -class TransactionServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject - */ - protected $httpClientMock; - - /** - * @var \Magento\Authorizenet\Model\Authorizenet|\PHPUnit_Framework_MockObject_MockObject - */ - protected $authorizenetMock; - - /** - * @var \Magento\Authorizenet\Model\TransactionService - */ - protected $transactionService; - - protected function setUp() - { - $httpClientFactoryMock = $this->getHttpClientFactoryMock(); - - $this->authorizenetMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->authorizenetMock->method('getConfigData') - ->willReturnMap([ - ['login', 'test login'], - ['trans_key', 'test key'], - ['cgi_url_td', 'https://apitest.authorize.net/xml/v1/request.api'] - ]); - - $objectManagerHelper = new ObjectManager($this); - $xmlSecurity = $objectManagerHelper->getObject(\Magento\Framework\Xml\Security::class); - $this->transactionService = $objectManagerHelper->getObject( - \Magento\Authorizenet\Model\TransactionService::class, - [ - 'xmlSecurityHelper' => $xmlSecurity, - 'httpClientFactory' => $httpClientFactoryMock - ] - ); - } - - /** - * @covers \Magento\Authorizenet\Model\TransactionService::loadTransactionDetails - * @param $transactionId - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return void - * - * @dataProvider dataProviderTransaction - */ - public function testLoadVoidedTransactionDetails($transactionId, $resultStatus, $responseStatus, $responseCode) - { - $document = $this->getResponseBody( - $transactionId, - TransactionService::PAYMENT_UPDATE_STATUS_CODE_SUCCESS, - $resultStatus, - $responseStatus, - $responseCode - ); - $this->httpClientMock->expects(static::once()) - ->method('getBody') - ->willReturn($document); - - $result = $this->transactionService->getTransactionDetails($this->authorizenetMock, $transactionId); - - static::assertEquals($responseCode, (string)$result->transaction->responseCode); - static::assertEquals($responseCode, (string)$result->transaction->responseReasonCode); - static::assertEquals($responseStatus, (string)$result->transaction->transactionStatus); - } - - /** - * Get data for tests - * @return array - */ - public function dataProviderTransaction() - { - return [ - [ - 'transactionId' => '9941997799', - 'resultStatus' => 'Successful.', - 'responseStatus' => 'voided', - 'responseCode' => 1 - ] - ]; - } - - /** - * Create and return mock for http client factory - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getHttpClientFactoryMock() - { - $this->httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['request', 'getBody', '__wakeup']) - ->getMock(); - - $this->httpClientMock->expects(static::once()) - ->method('request') - ->willReturnSelf(); - - $httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $httpClientFactoryMock->expects(static::once()) - ->method('create') - ->willReturn($this->httpClientMock); - return $httpClientFactoryMock; - } - - /** - * Get body for xml request - * @param string $transactionId - * @param int $resultCode - * @param string $resultStatus - * @param string $responseStatus - * @param string $responseCode - * @return string - */ - private function getResponseBody($transactionId, $resultCode, $resultStatus, $responseStatus, $responseCode) - { - return sprintf( - '<?xml version="1.0" encoding="utf-8"?> - <getTransactionDetailsResponse - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"> - <messages> - <resultCode>%s</resultCode> - <message> - <code>I00001</code> - <text>%s</text> - </message> - </messages> - <transaction> - <transId>%s</transId> - <transactionType>authOnlyTransaction</transactionType> - <transactionStatus>%s</transactionStatus> - <responseCode>%s</responseCode> - <responseReasonCode>%s</responseReasonCode> - </transaction> - </getTransactionDetailsResponse>', - $resultCode, - $resultStatus, - $transactionId, - $responseStatus, - $responseCode, - $responseCode - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php b/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php deleted file mode 100644 index d59d5eeaa5cae..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php +++ /dev/null @@ -1,237 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Unit\Observer; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -/** - * Class AddFieldsToResponseObserverTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class AddFieldsToResponseObserverTest extends \PHPUnit\Framework\TestCase -{ - /** - * Core registry - * - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject - */ - protected $coreRegistryMock; - - /** - * @var \Magento\Authorizenet\Model\Directpost|\PHPUnit_Framework_MockObject_MockObject - */ - protected $paymentMock; - - /** - * @var \Magento\Authorizenet\Model\Directpost\Session|\PHPUnit_Framework_MockObject_MockObject - */ - protected $sessionMock; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManagerMock; - - /** - * @var \Magento\Checkout\Controller\Onepage\SaveOrder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $actionMock; - - /** - * @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultMock; - - /** - * @var \Magento\Authorizenet\Observer\AddFieldsToResponseObserver - */ - protected $addFieldsToResponseObserver; - - /** - * Set up - * - * @return void - */ - protected function setUp() - { - $helper = new ObjectManager($this); - - $this->coreRegistryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); - $this->paymentMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->disableOriginalConstructor() - ->setMethods(['setLastOrderIncrementId', 'addCheckoutOrderIncrementId']) - ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->actionMock = $this->getMockBuilder(\Magento\Checkout\Controller\Onepage\SaveOrder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->addFieldsToResponseObserver = $helper->getObject( - \Magento\Authorizenet\Observer\AddFieldsToResponseObserver::class, - [ - 'coreRegistry' => $this->coreRegistryMock, - 'payment' => $this->paymentMock, - 'session' => $this->sessionMock, - 'storeManager' => $this->storeManagerMock, - ] - ); - } - - /** - * Test for addFieldsToResponse method - * - * @return void - */ - public function testAddFieldsToResponseSuccess() - { - $testData = $this->getAddFieldsToResponseSuccessTestData(); - - $observerMock = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) - ->disableOriginalConstructor() - ->getMock(); - $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->disableOriginalConstructor() - ->getMock(); - $orderPaymentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment::class) - ->disableOriginalConstructor() - ->getMock(); - $instanceMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $requestToAuthorizenetMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Request::class) - ->disableOriginalConstructor() - ->setMethods(['setControllerActionName', 'setIsSecure', 'getData']) - ->getMock(); - $requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getControllerName']) - ->getMockForAbstractClass(); - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->coreRegistryMock->expects($this->once()) - ->method('registry') - ->with('directpost_order') - ->willReturn($orderMock); - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn($testData['order.getId']); - $orderMock->expects($this->once()) - ->method('getPayment') - ->willReturn($orderPaymentMock); - $orderPaymentMock->expects($this->once()) - ->method('getMethod') - ->willReturn($testData['orderPayment.getMethod']); - $this->paymentMock->expects($this->exactly(2)) - ->method('getCode') - ->willReturn($testData['payment.getCode']); - $observerMock->expects($this->atLeastOnce()) - ->method('getData') - ->willReturnMap($testData['observer.getData']); - $this->resultMock->expects($this->once()) - ->method('getData') - ->willReturn($testData['result.getData']); - $orderMock->expects($this->atLeastOnce()) - ->method('getIncrementId') - ->willReturn($testData['order.getIncrementId']); - $this->sessionMock->expects($this->once()) - ->method('addCheckoutOrderIncrementId') - ->with($testData['session.addCheckoutOrderIncrementId']); - $this->sessionMock->expects($this->once()) - ->method('setLastOrderIncrementId') - ->with($testData['session.setLastOrderIncrementId']); - $orderPaymentMock->expects($this->once()) - ->method('getMethodInstance') - ->willReturn($instanceMock); - $instanceMock->expects($this->once()) - ->method('generateRequestFromOrder') - ->with($orderMock) - ->willReturn($requestToAuthorizenetMock); - $this->actionMock->expects($this->once()) - ->method('getRequest') - ->willReturn($requestMock); - $requestMock->expects($this->once()) - ->method('getControllerName') - ->willReturn($testData['request.getControllerName']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('setControllerActionName') - ->with($testData['requestToAuthorizenet.setControllerActionName']); - $this->storeManagerMock->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); - $storeMock->expects($this->once()) - ->method('isCurrentlySecure') - ->willReturn($testData['store.isCurrentlySecure']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('setIsSecure') - ->with($testData['requestToAuthorizenet.setIsSecure']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('getData') - ->willReturn($testData['requestToAuthorizenet.getData']); - $this->resultMock->expects($this->once()) - ->method('setData') - ->with($testData['result.setData']); - - $this->addFieldsToResponseObserver->execute($observerMock); - } - - /** - * Get data for test testAddFieldsToResponseSuccess - * - * @return array - */ - protected function getAddFieldsToResponseSuccessTestData() - { - $requestFields = [ - 'field-1' => 'field-value-1', - 'field-2' => 'field-value-2', - 'field-3' => 'field-value-3', - ]; - $secure = 'test-currently-secure'; - $controllerName = 'test-controller-name'; - $incrementId = '0000000001'; - $paymentCode = 'test-payment-code'; - - return [ - 'order.getId' => 77, - 'orderPayment.getMethod' => $paymentCode, - 'payment.getCode' => $paymentCode, - 'observer.getData' => [ - ['action', null, $this->actionMock], - ['result', null, $this->resultMock], - ], - 'result.getData' => [ - 'error' => false - ], - 'order.getIncrementId' => $incrementId, - 'session.addCheckoutOrderIncrementId' => $incrementId, - 'session.setLastOrderIncrementId' => $incrementId, - 'request.getControllerName' => $controllerName, - 'requestToAuthorizenet.setControllerActionName' => $controllerName, - 'store.isCurrentlySecure' => $secure, - 'requestToAuthorizenet.setIsSecure' => $secure, - 'requestToAuthorizenet.getData' => $requestFields, - 'result.setData' => [ - 'error' => false, - 'test-payment-code' => [ - 'fields' => $requestFields - ] - ] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json deleted file mode 100644 index 9dcbc7ec7dfb3..0000000000000 --- a/app/code/Magento/Authorizenet/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "magento/module-authorizenet", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-checkout": "*", - "magento/module-payment": "*", - "magento/module-quote": "*", - "magento/module-sales": "*", - "magento/module-store": "*" - }, - "suggest": { - "magento/module-config": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - } -} diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/di.xml b/app/code/Magento/Authorizenet/etc/adminhtml/di.xml deleted file mode 100644 index 9305ac521c50a..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/di.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Authorizenet\Model\Directpost"> - <arguments> - <argument name="session" xsi:type="object">Magento\Backend\Model\Session\Quote</argument> - <argument name="data" xsi:type="array"> - <item name="formBlockType" xsi:type="string">Magento\Payment\Block\Adminhtml\Transparent\Form</item> - </argument> - </arguments> - </type> - <type name="Magento\Authorizenet\Model\Authorizenet"> - <arguments> - <argument name="session" xsi:type="object">Magento\Backend\Model\Session\Quote</argument> - </arguments> - </type> -</config> diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml b/app/code/Magento/Authorizenet/etc/adminhtml/events.xml deleted file mode 100644 index 085598d3fa95c..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="checkout_submit_all_after"> - <observer name="directpost_update_all_edit_increments" instance="Magento\Authorizenet\Observer\UpdateAllEditIncrementsObserver" /> - </event> -</config> diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml b/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml deleted file mode 100644 index 00bb0475766f9..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - <router id="admin"> - <route id="adminhtml"> - <module name="Magento_Authorizenet" before="Magento_Backend" /> - </route> - </router> -</config> diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml deleted file mode 100644 index fe91967ed4a62..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ /dev/null @@ -1,101 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="payment"> - <group id="authorizenet_directpost" translate="label" type="text" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Authorize.Net Direct Post (Deprecated)</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Enabled</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Payment Action</label> - <source_model>Magento\Authorizenet\Model\Source\PaymentAction</source_model> - </field> - <field id="title" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> - <label>Title</label> - </field> - <field id="login" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>API Login ID</label> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - </field> - <field id="trans_key" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Transaction Key</label> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - </field> - <field id="signature_key" translate="label" type="obscure" sortOrder="55" showInDefault="1" showInWebsite="1"> - <label>Signature Key</label> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - </field> - <field id="trans_md5" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Merchant MD5</label> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - </field> - <field id="order_status" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>New Order Status</label> - <source_model>Magento\Sales\Model\Config\Source\Order\Status\Processing</source_model> - </field> - <field id="test" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Test Mode</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="cgi_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Gateway URL</label> - </field> - <field id="cgi_url_td" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Transaction Details URL</label> - </field> - <field id="currency" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Accepted Currency</label> - <source_model>Magento\Config\Model\Config\Source\Locale\Currency</source_model> - </field> - <field id="debug" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Debug</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="email_customer" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Email Customer</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="merchant_email" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1"> - <label>Merchant's Email</label> - <validate>validate-email</validate> - </field> - <field id="cctypes" translate="label" type="multiselect" sortOrder="150" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Credit Card Types</label> - <source_model>Magento\Authorizenet\Model\Source\Cctype</source_model> - </field> - <field id="useccv" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1"> - <label>Credit Card Verification</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> - <label>Payment from Applicable Countries</label> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="180" showInDefault="1" showInWebsite="1"> - <label>Payment from Specific Countries</label> - <source_model>Magento\Directory\Model\Config\Source\Country</source_model> - </field> - <field id="min_order_total" translate="label" type="text" sortOrder="190" showInDefault="1" showInWebsite="1"> - <label>Minimum Order Total</label> - <validate>validate-number validate-zero-or-greater</validate> - </field> - <field id="max_order_total" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1"> - <label>Maximum Order Total</label> - <validate>validate-number validate-zero-or-greater</validate> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="210" showInDefault="1" showInWebsite="1"> - <label>Sort Order</label> - <frontend_class>validate-number</frontend_class> - </field> - </group> - </section> - </system> -</config> diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml deleted file mode 100644 index 60356460f553f..0000000000000 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> - <default> - <payment> - <authorizenet_directpost> - <active>0</active> - <cctypes>AE,VI,MC,DI,JCB,DN</cctypes> - <debug>0</debug> - <email_customer>0</email_customer> - <login backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> - <merchant_email /> - <model>Magento\Authorizenet\Model\Directpost</model> - <order_status>processing</order_status> - <payment_action>authorize</payment_action> - <test>1</test> - <title>Credit Card Direct Post (Authorize.Net) - - - - 0 - USD - 1 - / - x_card_code,x_exp_date,x_card_num - authorizenet/directpost_payment/place - https://test.authorize.net/gateway/transact.dll - https://secure.authorize.net/gateway/transact.dll - https://apitest.authorize.net/xml/v1/request.api - https://api2.authorize.net/xml/v1/request.api - x_card_type,x_account_number,x_avs_code,x_auth_code,x_response_reason_text,x_cvv2_resp_code - authorizenet - - - - diff --git a/app/code/Magento/Authorizenet/etc/di.xml b/app/code/Magento/Authorizenet/etc/di.xml deleted file mode 100644 index 69d24019f2fb7..0000000000000 --- a/app/code/Magento/Authorizenet/etc/di.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - authorizenet_directpost - - - - - Magento\Authorizenet\Model\Directpost\Session\Storage - - - - - - 1 - 1 - 1 - 1 - 1 - 1 - 1 - - - 1 - 1 - 1 - 1 - - - - - - Magento\Authorizenet\Model\Directpost - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/di.xml b/app/code/Magento/Authorizenet/etc/frontend/di.xml deleted file mode 100644 index a0e5d907b15aa..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/di.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - Magento\Checkout\Model\Session - - - - - - /authorizenet/ - - - - - - Magento\Checkout\Model\Session - - - - - - Magento\Authorizenet\Model\Directpost::METHOD_CODE - - - - - - Magento\Authorizenet\Model\Directpost::METHOD_CODE - - - - - - DirectpostIframeCcConfigProvider - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/events.xml b/app/code/Magento/Authorizenet/etc/frontend/events.xml deleted file mode 100644 index 9910dc4776fbe..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/events.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml b/app/code/Magento/Authorizenet/etc/frontend/page_types.xml deleted file mode 100644 index 56bfca71231cd..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/routes.xml b/app/code/Magento/Authorizenet/etc/frontend/routes.xml deleted file mode 100644 index e570f24f66c9c..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/sections.xml b/app/code/Magento/Authorizenet/etc/frontend/sections.xml deleted file mode 100644 index 1239398b06a10..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/sections.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - -
-
- - diff --git a/app/code/Magento/Authorizenet/etc/module.xml b/app/code/Magento/Authorizenet/etc/module.xml deleted file mode 100644 index a30fd34927746..0000000000000 --- a/app/code/Magento/Authorizenet/etc/module.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/payment.xml b/app/code/Magento/Authorizenet/etc/payment.xml deleted file mode 100644 index 1d2cac374d8dc..0000000000000 --- a/app/code/Magento/Authorizenet/etc/payment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/i18n/en_US.csv b/app/code/Magento/Authorizenet/i18n/en_US.csv deleted file mode 100644 index d724bd960d310..0000000000000 --- a/app/code/Magento/Authorizenet/i18n/en_US.csv +++ /dev/null @@ -1,75 +0,0 @@ -"You created the order.","You created the order." -"Order saving error: %1","Order saving error: %1" -"Please choose a payment method.","Please choose a payment method." -"We can't process your order right now. Please try again later.","We can't process your order right now. Please try again later." -"An error occurred on the server. Please try to place the order again.","An error occurred on the server. Please try to place the order again." -"Credit Card: xxxx-%1","Credit Card: xxxx-%1" -"amount %1","amount %1" -failed.,failed. -successful.,successful. -"Authorize.Net Transaction ID %1.","Authorize.Net Transaction ID %1." -authorize,authorize -"authorize and capture","authorize and capture" -capture,capture -refund,refund -void,void -"Gateway error: %1","Gateway error: %1" -"Something went wrong in the payment gateway.","Something went wrong in the payment gateway." -"Invalid amount for capture.","Invalid amount for capture." -"Payment capturing error.","Payment capturing error." -"Invalid transaction ID.","Invalid transaction ID." -"Payment voiding error.","Payment voiding error." -"Invalid amount for refund.","Invalid amount for refund." -"Payment refunding error.","Payment refunding error." -"The transaction was declined because the response hash validation failed.","The transaction was declined because the response hash validation failed." -"This payment didn't work out because we can't find this order.","This payment didn't work out because we can't find this order." -"There was a payment authorization error.","There was a payment authorization error." -"Please enter a transaction ID to authorize this payment.","Please enter a transaction ID to authorize this payment." -"Something went wrong: the paid amount doesn't match the order amount. Please correct this and try again.","Something went wrong: the paid amount doesn't match the order amount. Please correct this and try again." -"Transaction %1 has been approved. Amount %2. Transaction status is ""%3""","Transaction %1 has been approved. Amount %2. Transaction status is ""%3""" -"Transaction %1 has been voided/declined. Transaction status is ""%2"". Amount %3.","Transaction %1 has been voided/declined. Transaction status is ""%2"". Amount %3." -"Authorize Only","Authorize Only" -"Authorize and Capture","Authorize and Capture" -"Unable to get transaction details. Try again later.","Unable to get transaction details. Try again later." -"Electronic payments save the trees.","Electronic payments save the trees." -"Credit Card Type","Credit Card Type" -"Please Select","Please Select" -"Credit Card Number","Credit Card Number" -"Expiration Date","Expiration Date" -"Card Verification Number","Card Verification Number" -"Fraud Detection ","Fraud Detection " -"FDS Filter Action","FDS Filter Action" -"AVS Response","AVS Response" -"Card Code Response","Card Code Response" -"CAVV Response","CAVV Response" -"Fraud Filters","Fraud Filters" -"Place Order","Place Order" -"Sorry, but something went wrong. Please contact the seller.","Sorry, but something went wrong. Please contact the seller." -"Authorize.Net Direct Post (Deprecated)","Authorize.Net Direct Post (Deprecated)" -Enabled,Enabled -"Payment Action","Payment Action" -Title,Title -"API Login ID","API Login ID" -"Transaction Key","Transaction Key" -"Merchant MD5","Merchant MD5" -"New Order Status","New Order Status" -"Test Mode","Test Mode" -"Gateway URL","Gateway URL" -"Transaction Details URL","Transaction Details URL" -"Accepted Currency","Accepted Currency" -Debug,Debug -"Email Customer","Email Customer" -"Merchant's Email","Merchant's Email" -"Credit Card Types","Credit Card Types" -"Credit Card Verification","Credit Card Verification" -"Payment from Applicable Countries","Payment from Applicable Countries" -"Payment from Specific Countries","Payment from Specific Countries" -"Minimum Order Total","Minimum Order Total" -"Maximum Order Total","Maximum Order Total" -"Sort Order","Sort Order" -"x_card_type","Credit Card Type" -"x_account_number", "Credit Card Number" -"x_avs_code","AVS Response Code" -"x_auth_code","Processor Authentication Code" -"x_response_reason_text","Processor Response Text" -"x_cvv2_resp_code","CVV2 Response Code" diff --git a/app/code/Magento/Authorizenet/registration.php b/app/code/Magento/Authorizenet/registration.php deleted file mode 100644 index cb3bedaaee27d..0000000000000 --- a/app/code/Magento/Authorizenet/registration.php +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml deleted file mode 100644 index b675e49d72618..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - authorizenet_directpost - Magento_Authorizenet::directpost/info.phtml - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml deleted file mode 100644 index ae14700836667..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - authorizenet_directpost - Magento_Authorizenet::directpost/info.phtml - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml deleted file mode 100644 index 57aeffce62c27..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml deleted file mode 100644 index 3088713989453..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml +++ /dev/null @@ -1,31 +0,0 @@ -getParams(); -$helper = $block->getHelper('adminhtml'); -?> - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml deleted file mode 100644 index bec87738a83c1..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml +++ /dev/null @@ -1,152 +0,0 @@ -escapeHtml($block->getMethodCode()); -$method = $block->getMethod(); -$controller = $block->escapeHtml($block->getRequest()->getControllerName()); -$orderUrl = $block->escapeUrl($block->getHelper('adminhtml')->getPlaceOrderAdminUrl()); -$ccType = $block->getInfoData('cc_type'); -$ccExpMonth = $block->getInfoData('cc_exp_month'); -$ccExpYear = $block->getInfoData('cc_exp_year'); -?> - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml deleted file mode 100644 index 15325e15de1e1..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml +++ /dev/null @@ -1,56 +0,0 @@ -getPayment(); -$fraudDetails = $payment->getAdditionalInformation('fraud_details'); -?> - - -
- escapeHtml(__('Fraud Detection ')) ?> -
- -
-
- - escapeHtml(__('FDS Filter Action')) ?>: - escapeHtml($fraudDetails['fds_filter_action']) ?> -
- - - - escapeHtml(__('AVS Response')) ?>: - escapeHtml($fraudDetails['avs_response']) ?> -
- - - - escapeHtml(__('Card Code Response')) ?>: - escapeHtml($fraudDetails['card_code_response']) ?> -
- - - - escapeHtml(__('CAVV Response')) ?>: - escapeHtml($fraudDetails['cavv_response']) ?> -
- - - - escapeHtml(__('Fraud Filters')) ?>: -
- - escapeHtml($filter['name']) ?>: - escapeHtml($filter['action']) ?> -
- - -
-
- diff --git a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js b/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js deleted file mode 100644 index eb162034bc04d..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/backend/validation', - 'prototype' -], function (jQuery) { - window.directPost = Class.create(); - directPost.prototype = { - initialize: function (methodCode, iframeId, controller, orderSaveUrl, cgiUrl, nativeAction) { - var prepare = function (event, method) { - if (method === 'authorizenet_directpost') { - this.preparePayment(); - } else { - jQuery('#edit_form') - .off('submitOrder.authorizenet'); - } - }; - - this.iframeId = iframeId; - this.controller = controller; - this.orderSaveUrl = orderSaveUrl; - this.nativeAction = nativeAction; - this.cgiUrl = cgiUrl; - this.code = methodCode; - this.inputs = ['cc_type', 'cc_number', 'expiration', 'expiration_yr', 'cc_cid']; - this.headers = []; - this.isValid = true; - this.paymentRequestSent = false; - this.orderIncrementId = false; - this.successUrl = false; - this.hasError = false; - this.tmpForm = false; - - this.onLoadIframe = this.loadIframe.bindAsEventListener(this); - this.onLoadOrderIframe = this.loadOrderIframe.bindAsEventListener(this); - this.onSubmitAdminOrder = this.submitAdminOrder.bindAsEventListener(this); - - jQuery('#edit_form').on('changePaymentMethod', prepare.bind(this)); - - jQuery('#edit_form').trigger( - 'changePaymentMethod', - [ - jQuery('#edit_form').find(':radio[name="payment[method]"]:checked').val() - ] - ); - }, - - validate: function () { - this.isValid = true; - this.inputs.each(function (elemIndex) { - if ($(this.code + '_' + elemIndex)) { - if (!jQuery.validator.validateElement($(this.code + '_' + elemIndex))) { - this.isValid = false; - } - } - }, this); - - return this.isValid; - }, - - changeInputOptions: function (param, value) { - this.inputs.each(function (elemIndex) { - if ($(this.code + '_' + elemIndex)) { - $(this.code + '_' + elemIndex).writeAttribute(param, value); - } - }, this); - }, - - preparePayment: function () { - this.changeInputOptions('autocomplete', 'off'); - jQuery('#edit_form') - .off('submitOrder') - .on('submitOrder.authorizenet', this.submitAdminOrder.bind(this)); - - if ($(this.iframeId)) { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - jQuery('.scalable.save:not(disabled)').removeAttr('onclick'); - jQuery(document).off('click.directPost'); - jQuery(document).on( - 'click.directPost', - '.scalable.save:not(disabled)', - jQuery.proxy(this.onSubmitAdminOrder, this) - ); - $('order-' + this.iframeId).observe('load', this.onLoadOrderIframe); - $(this.iframeId).observe('load', this.onLoadIframe); - } - }, - - loadIframe: function () { - if (this.paymentRequestSent) { - if (!this.orderRequestSent) { - this.paymentRequestSent = false; - - if (!this.hasError) { - this.returnQuote(); - } else { - this.changeInputOptions('disabled', false); - jQuery('body').trigger('processStop'); - enableElements('save'); - } - } - - if (this.tmpForm) { - document.body.removeChild(this.tmpForm); - } - } - }, - - loadOrderIframe: function () { - if (this.orderRequestSent) { - $(this.iframeId).hide(); - var data = $('order-' + this.iframeId).contentWindow.document.body.getElementsByTagName('pre')[0].innerHTML; - - this.saveAdminOrderSuccess(data); - this.orderRequestSent = false; - } - }, - - showError: function (msg) { - this.hasError = true; - - if (this.controller == 'onepage') { - $(this.iframeId).hide(); - this.resetLoadWaiting(); - } - alert(msg); - }, - - returnQuote: function () { - var url = this.orderSaveUrl.replace('place', 'returnQuote'); - - new Ajax.Request(url, { - onSuccess: function (transport) { - try { - response = transport.responseText.evalJSON(true); - } catch (e) { - response = {}; - } - - if (response.error_message) { - alert(response.error_message); - } - $(this.iframeId).show(); - this.changeInputOptions('disabled', false); - jQuery('body').trigger('processStop'); - enableElements('save'); - }.bind(this) - }); - }, - - setLoadWaiting: function () { - this.headers.each(function (header) { - header.removeClassName('allow'); - }); - checkout.setLoadWaiting('review'); - }, - - resetLoadWaiting: function () { - this.headers.each(function (header) { - header.addClassName('allow'); - }); - checkout.setLoadWaiting(false); - }, - - submitAdminOrder: function () { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - var editForm = jQuery('#edit_form'); - - if (editForm.valid()) { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - paymentMethodEl = editForm.find(':radio[name="payment[method]"]:checked'); - this.hasError = false; - - if (paymentMethodEl.val() == this.code) { - jQuery('body').trigger('processStart'); - setLoaderPosition(); - this.changeInputOptions('disabled', 'disabled'); - this.paymentRequestSent = true; - this.orderRequestSent = true; - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - editForm.attr('action', this.orderSaveUrl); - editForm.attr('target', - jQuery('#order-' + this.iframeId).attr('name')); - editForm.append(this.createHiddenElement('controller', this.controller)); - disableElements('save'); - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - order._realSubmit(); - } else { - editForm.attr('action', this.nativeAction); - editForm.attr('target', '_top'); - disableElements('save'); - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - order._realSubmit(); - } - } - }, - - recollectQuote: function () { - var area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage']; - - area = order.prepareArea(area); - var url = order.loadBaseUrl + 'block/' + area; - var info = $('order-items_grid').select('input', 'select', 'textarea'); - var data = {}; - - for (var i = 0; i < info.length; i++) { - if (!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { - data[info[i].name] = info[i].getValue(); - } - } - data.reset_shipping = true; - data.update_items = true; - - if ($('coupons:code') && $F('coupons:code')) { - data['order[coupon][code]'] = $F('coupons:code'); - } - data.json = true; - new Ajax.Request(url, { - parameters: data, - loaderArea: 'html-body', - onSuccess: function (transport) { - jQuery('#edit_form').submit(); - } - }); - - }, - - saveAdminOrderSuccess: function (data) { - try { - response = data.evalJSON(true); - } catch (e) { - response = {}; - } - - if (response.directpost) { - this.orderIncrementId = response.directpost.fields.x_invoice_num; - var paymentData = {}; - - for (var key in response.directpost.fields) { - paymentData[key] = response.directpost.fields[key]; - } - var preparedData = this.preparePaymentRequest(paymentData); - - this.sendPaymentRequest(preparedData); - } else { - if (response.redirect) { - window.location = response.redirect; - } - - if (response.error_messages) { - var msg = response.error_messages; - - if (typeof msg == 'object') { - msg = msg.join('\n'); - } - - if (msg) { - alert(msg); - } - } - } - }, - - preparePaymentRequest: function (data) { - if ($(this.code + '_cc_cid')) { - data.x_card_code = $(this.code + '_cc_cid').value; - } - var year = $(this.code + '_expiration_yr').value; - - if (year.length > 2) { - year = year.substring(2); - } - var month = parseInt($(this.code + '_expiration').value, 10); - - if (month < 10) { - month = '0' + month; - } - - data.x_exp_date = month + '/' + year; - data.x_card_num = $(this.code + '_cc_number').value; - - return data; - }, - - sendPaymentRequest: function (preparedData) { - this.recreateIframe(); - this.tmpForm = document.createElement('form'); - this.tmpForm.style.display = 'none'; - this.tmpForm.enctype = 'application/x-www-form-urlencoded'; - this.tmpForm.method = 'POST'; - document.body.appendChild(this.tmpForm); - this.tmpForm.action = this.cgiUrl; - this.tmpForm.target = $(this.iframeId).readAttribute('name'); - this.tmpForm.setAttribute('target', $(this.iframeId).readAttribute('name')); - - for (var param in preparedData) { - this.tmpForm.appendChild(this.createHiddenElement(param, preparedData[param])); - } - - this.paymentRequestSent = true; - this.tmpForm.submit(); - }, - - createHiddenElement: function (name, value) { - var field; - - if (isIE) { - field = document.createElement('input'); - field.setAttribute('type', 'hidden'); - field.setAttribute('name', name); - field.setAttribute('value', value); - } else { - field = document.createElement('input'); - field.type = 'hidden'; - field.name = name; - field.value = value; - } - - return field; - }, - - recreateIframe: function () { - if ($(this.iframeId)) { - var nextElement = $(this.iframeId).next(); - var src = $(this.iframeId).readAttribute('src'); - var name = $(this.iframeId).readAttribute('name'); - - $(this.iframeId).stopObserving(); - $(this.iframeId).remove(); - var iframe = ' -
- -
-
- - - -
- -
-
- -
-
- - diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php deleted file mode 100644 index f669ead967c59..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php +++ /dev/null @@ -1,64 +0,0 @@ -config = $config; - $this->sessionQuote = $sessionQuote; - } - - /** - * Check if cvv validation is available - * - * @return boolean - */ - public function isCvvEnabled(): bool - { - return $this->config->isCvvEnabled($this->sessionQuote->getStoreId()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php deleted file mode 100644 index 1876685998643..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php +++ /dev/null @@ -1,33 +0,0 @@ -config = $config; - $this->json = $json; - } - - /** - * Retrieves the config that should be used by the block - * - * @return string - */ - public function getPaymentConfig(): string - { - $payment = $this->config->getConfig()['payment']; - $config = $payment[$this->getMethodCode()]; - $config['code'] = $this->getMethodCode(); - - return $this->json->serialize($config); - } - - /** - * Returns the method code for this payment method - * - * @return string - */ - public function getMethodCode(): string - { - return Config::METHOD; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php deleted file mode 100644 index d59edde212760..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php +++ /dev/null @@ -1,77 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - if ($this->shouldAcceptInGateway($commandSubject)) { - $this->commandPool->get(self::ACCEPT_FDS) - ->execute($commandSubject); - } - } - - /** - * Determines if the transaction needs to be accepted in the gateway - * - * @param array $commandSubject - * @return bool - * @throws CommandException - */ - private function shouldAcceptInGateway(array $commandSubject): bool - { - $details = $this->commandPool->get('get_transaction_details') - ->execute($commandSubject) - ->get(); - - return in_array($details['transaction']['transactionStatus'], self::NEEDS_APPROVAL_STATUSES); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php deleted file mode 100644 index 4318441014ad7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php +++ /dev/null @@ -1,143 +0,0 @@ -commandPool = $commandPool; - $this->transactionRepository = $repository; - $this->filterBuilder = $filterBuilder; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - /** @var PaymentDataObjectInterface $paymentDO */ - $paymentDO = $this->subjectReader->readPayment($commandSubject); - - $command = $this->getCommand($paymentDO); - $this->commandPool->get($command) - ->execute($commandSubject); - } - - /** - * Get execution command name. - * - * @param PaymentDataObjectInterface $paymentDO - * @return string - */ - private function getCommand(PaymentDataObjectInterface $paymentDO): string - { - $payment = $paymentDO->getPayment(); - ContextHelper::assertOrderPayment($payment); - - // If auth transaction does not exist then execute authorize&capture command - $captureExists = $this->captureTransactionExists($payment); - if (!$payment->getAuthorizationTransaction() && !$captureExists) { - return self::SALE; - } - - return self::CAPTURE; - } - - /** - * Check if capture transaction already exists - * - * @param OrderPaymentInterface $payment - * @return bool - */ - private function captureTransactionExists(OrderPaymentInterface $payment): bool - { - $this->searchCriteriaBuilder->addFilters( - [ - $this->filterBuilder - ->setField('payment_id') - ->setValue($payment->getId()) - ->create(), - ] - ); - - $this->searchCriteriaBuilder->addFilters( - [ - $this->filterBuilder - ->setField('txn_type') - ->setValue(TransactionInterface::TYPE_CAPTURE) - ->create(), - ] - ); - - $searchCriteria = $this->searchCriteriaBuilder->create(); - $count = $this->transactionRepository->getList($searchCriteria) - ->getTotalCount(); - - return $count > 0; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php deleted file mode 100644 index d0c1ceac81378..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php +++ /dev/null @@ -1,90 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - $this->config = $config; - $this->handler = $handler; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): array - { - $paymentDO = $this->subjectReader->readPayment($commandSubject); - $order = $paymentDO->getOrder(); - - $command = $this->commandPool->get('get_transaction_details'); - $result = $command->execute($commandSubject); - $response = $result->get(); - - if ($this->handler) { - $this->handler->handle($commandSubject, $response); - } - - $additionalInformationKeys = $this->config->getTransactionInfoSyncKeys($order->getStoreId()); - $rawDetails = []; - foreach ($additionalInformationKeys as $key) { - if (isset($response['transaction'][$key])) { - $rawDetails[$key] = $response['transaction'][$key]; - } - } - - return $rawDetails; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php deleted file mode 100644 index 7185639936fa4..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php +++ /dev/null @@ -1,103 +0,0 @@ -requestBuilder = $requestBuilder; - $this->transferFactory = $transferFactory; - $this->client = $client; - $this->validator = $validator; - $this->logger = $logger; - } - - /** - * @inheritdoc - * - * @throws Exception - */ - public function execute(array $commandSubject): ResultInterface - { - $transferO = $this->transferFactory->create( - $this->requestBuilder->build($commandSubject) - ); - - try { - $response = $this->client->placeRequest($transferO); - } catch (Exception $e) { - $this->logger->critical($e); - - throw new CommandException(__('There was an error while trying to process the request.')); - } - - $result = $this->validator->validate( - array_merge($commandSubject, ['response' => $response]) - ); - if (!$result->isValid()) { - throw new CommandException(__('There was an error while trying to process the request.')); - } - - return new ArrayResult($response); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php deleted file mode 100644 index de3ded6515ae0..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php +++ /dev/null @@ -1,106 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - $command = $this->getCommand($commandSubject); - - $this->commandPool->get($command) - ->execute($commandSubject); - } - - /** - * Determines the command that should be used based on the status of the transaction - * - * @param array $commandSubject - * @return string - * @throws CommandException - * @throws \Magento\Framework\Exception\NotFoundException - */ - private function getCommand(array $commandSubject): string - { - $details = $this->commandPool->get('get_transaction_details') - ->execute($commandSubject) - ->get(); - - if ($this->canVoid($details, $commandSubject)) { - return self::VOID; - } - - if ($details['transaction']['transactionStatus'] !== 'settledSuccessfully') { - throw new CommandException(__('This transaction cannot be refunded with its current status.')); - } - - return self::REFUND; - } - - /** - * Checks if void command can be performed. - * - * @param array $details - * @param array $commandSubject - * @return bool - * @throws CommandException - */ - private function canVoid(array $details, array $commandSubject) :bool - { - if ($details['transaction']['transactionStatus'] === 'capturedPendingSettlement') { - if ((float) $details['transaction']['authAmount'] !== (float) $commandSubject['amount']) { - throw new CommandException( - __('The transaction has not been settled, a partial refund is not yet available.') - ); - } - - return true; - } - - return false; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php deleted file mode 100644 index f41eb1660da55..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php +++ /dev/null @@ -1,189 +0,0 @@ -getValue(Config::KEY_LOGIN_ID, $storeId); - } - - /** - * Gets the current environment - * - * @param int|null $storeId - * @return string - */ - public function getEnvironment($storeId = null): string - { - return $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - } - - /** - * Gets the transaction key - * - * @param int|null $storeId - * @return string - */ - public function getTransactionKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_TRANSACTION_KEY, $storeId); - } - - /** - * Gets the API endpoint URL - * - * @param int|null $storeId - * @return string - */ - public function getApiUrl($storeId = null): string - { - $environment = $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - - return $environment === Environment::ENVIRONMENT_SANDBOX - ? self::ENDPOINT_URL_SANDBOX - : self::ENDPOINT_URL_PRODUCTION; - } - - /** - * Gets the configured signature key - * - * @param int|null $storeId - * @return string - */ - public function getTransactionSignatureKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_SIGNATURE_KEY, $storeId); - } - - /** - * Gets the configured legacy transaction hash - * - * @param int|null $storeId - * @return string - */ - public function getLegacyTransactionHash($storeId = null): ?string - { - return $this->getValue(Config::KEY_LEGACY_TRANSACTION_HASH, $storeId); - } - - /** - * Gets the configured payment action - * - * @param int|null $storeId - * @return string - */ - public function getPaymentAction($storeId = null): ?string - { - return $this->getValue(Config::KEY_PAYMENT_ACTION, $storeId); - } - - /** - * Gets the configured client key - * - * @param int|null $storeId - * @return string - */ - public function getClientKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_CLIENT_KEY, $storeId); - } - - /** - * Should authorize.net email the customer their receipt. - * - * @param int|null $storeId - * @return bool - */ - public function shouldEmailCustomer($storeId = null): bool - { - return (bool)$this->getValue(Config::KEY_SHOULD_EMAIL_CUSTOMER, $storeId); - } - - /** - * Should the cvv field be shown - * - * @param int|null $storeId - * @return bool - */ - public function isCvvEnabled($storeId = null): bool - { - return (bool)$this->getValue(Config::KEY_CVV_ENABLED, $storeId); - } - - /** - * Retrieves the solution id for the given store based on environment - * - * @param int|null $storeId - * @return string - */ - public function getSolutionId($storeId = null): ?string - { - $environment = $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - - return $environment === Environment::ENVIRONMENT_SANDBOX - ? self::SOLUTION_ID_SANDBOX - : self::SOLUTION_ID_PRODUCTION; - } - - /** - * Returns the keys to be pulled from the transaction and displayed - * - * @param int|null $storeId - * @return string[] - */ - public function getAdditionalInfoKeys($storeId = null): array - { - return explode(',', $this->getValue(Config::KEY_ADDITIONAL_INFO_KEYS, $storeId) ?? ''); - } - - /** - * Returns the keys to be pulled from the transaction and displayed when syncing the transaction - * - * @param int|null $storeId - * @return string[] - */ - public function getTransactionInfoSyncKeys($storeId = null): array - { - return explode(',', $this->getValue(Config::KEY_TRANSACTION_SYNC_KEYS, $storeId) ?? ''); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php deleted file mode 100644 index ebd4240108a09..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php +++ /dev/null @@ -1,131 +0,0 @@ -httpClientFactory = $httpClientFactory; - $this->config = $config; - $this->paymentLogger = $paymentLogger; - $this->logger = $logger; - $this->json = $json; - } - - /** - * Places request to gateway. Returns result as ENV array - * - * @param TransferInterface $transferObject - * @return array - * @throws \Magento\Payment\Gateway\Http\ClientException - */ - public function placeRequest(TransferInterface $transferObject) - { - $request = $transferObject->getBody(); - $log = [ - 'request' => $request, - ]; - $client = $this->httpClientFactory->create(); - $url = $this->config->getApiUrl(); - - $type = $request['payload_type']; - unset($request['payload_type']); - $request = [$type => $request]; - - try { - $client->setUri($url); - $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); - $client->setRawData($this->json->serialize($request), 'application/json'); - $client->setMethod(ZendClient::POST); - - $responseBody = $client->request() - ->getBody(); - - // Strip BOM because Authorize.net sends it in the response - if ($responseBody && substr($responseBody, 0, 3) === pack('CCC', 0xef, 0xbb, 0xbf)) { - $responseBody = substr($responseBody, 3); - } - - $log['response'] = $responseBody; - - try { - $data = $this->json->unserialize($responseBody); - } catch (InvalidArgumentException $e) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception('Invalid JSON was returned by the gateway'); - } - - return $data; - // phpcs:ignore Magento2.Exceptions.ThrowCatch - } catch (\Exception $e) { - $this->logger->critical($e); - - throw new ClientException( - __('Something went wrong in the payment gateway.') - ); - } finally { - $this->paymentLogger->debug($log); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php deleted file mode 100644 index cce878cfbbb16..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php +++ /dev/null @@ -1,45 +0,0 @@ -fields = $fields; - } - - /** - * @inheritdoc - */ - public function filter(array $data): array - { - foreach ($this->fields as $field) { - unset($data[$field]); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php deleted file mode 100644 index dade4bd4ee1f3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -transferBuilder = $transferBuilder; - $this->payloadFilters = $payloadFilters; - } - - /** - * Builds gateway transfer object - * - * @param array $request - * @return TransferInterface - */ - public function create(array $request) - { - foreach ($this->payloadFilters as $filter) { - $request = $filter->filter($request); - } - - return $this->transferBuilder - ->setBody($request) - ->build(); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php deleted file mode 100644 index 4a673112e6a5f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - - if (empty($authorizationTransaction)) { - $transactionId = $payment->getLastTransId(); - } else { - $transactionId = $authorizationTransaction->getParentTxnId(); - - if (empty($transactionId)) { - $transactionId = $authorizationTransaction->getTxnId(); - } - } - - $data = [ - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => $transactionId, - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php deleted file mode 100644 index 07a4921b7d60b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php +++ /dev/null @@ -1,80 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - $billingAddress = $order->getBillingAddress(); - $shippingAddress = $order->getShippingAddress(); - $result = [ - 'transactionRequest' => [] - ]; - - if ($billingAddress) { - $result['transactionRequest']['billTo'] = [ - 'firstName' => $billingAddress->getFirstname(), - 'lastName' => $billingAddress->getLastname(), - 'company' => $billingAddress->getCompany() ?? '', - 'address' => $billingAddress->getStreetLine1(), - 'city' => $billingAddress->getCity(), - 'state' => $billingAddress->getRegionCode(), - 'zip' => $billingAddress->getPostcode(), - 'country' => $billingAddress->getCountryId() - ]; - } - - if ($shippingAddress) { - $result['transactionRequest']['shipTo'] = [ - 'firstName' => $shippingAddress->getFirstname(), - 'lastName' => $shippingAddress->getLastname(), - 'company' => $shippingAddress->getCompany() ?? '', - 'address' => $shippingAddress->getStreetLine1(), - 'city' => $shippingAddress->getCity(), - 'state' => $shippingAddress->getRegionCode(), - 'zip' => $shippingAddress->getPostcode(), - 'country' => $shippingAddress->getCountryId() - ]; - } - - if ($order->getRemoteIp()) { - $result['transactionRequest']['customerIP'] = $order->getRemoteIp(); - } - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php deleted file mode 100644 index 07fae5e536a28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php +++ /dev/null @@ -1,49 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - return [ - 'transactionRequest' => [ - 'amount' => $this->formatPrice($this->subjectReader->readAmount($buildSubject)), - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php deleted file mode 100644 index dec6626dc7524..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php +++ /dev/null @@ -1,62 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * Adds authentication information to the request - * - * @param array $buildSubject - * @return array - */ - public function build(array $buildSubject): array - { - $storeId = $this->subjectReader->readStoreId($buildSubject); - - return [ - 'merchantAuthentication' => [ - 'name' => $this->config->getLoginId($storeId), - 'transactionKey' => $this->config->getTransactionKey($storeId) - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php deleted file mode 100644 index c440da3ca9f4f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php +++ /dev/null @@ -1,72 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_AUTH_ONLY, - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php deleted file mode 100644 index 1e2a8617907a0..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php +++ /dev/null @@ -1,76 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $authTransaction = $payment->getAuthorizationTransaction(); - $refId = $authTransaction->getAdditionalInformation('real_transaction_id'); - - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE, - 'refTransId' => $refId - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php deleted file mode 100644 index 31246497fca92..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php +++ /dev/null @@ -1,65 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $result = []; - - if ($this->config->shouldEmailCustomer($this->subjectReader->readStoreId($buildSubject))) { - $result['transactionRequest'] = [ - 'transactionSettings' => [ - 'setting' => [ - [ - 'settingName' => 'emailCustomer', - 'settingValue' => 'true' - ] - ] - ] - ]; - } - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php deleted file mode 100644 index cfdaa31552960..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - $billingAddress = $order->getBillingAddress(); - $result = [ - 'transactionRequest' => [ - 'customer' => [ - 'id' => $order->getCustomerId(), - 'email' => $billingAddress->getEmail() - ] - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php deleted file mode 100644 index bf0a15f552e6c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - - return [ - 'transactionRequest' => [ - 'order' => [ - 'invoiceNumber' => $order->getOrderIncrementId() - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php deleted file mode 100644 index 6e6ef04972c78..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $fields = []; - - foreach ($this->passthroughData->getData() as $key => $value) { - $fields[] = [ - 'name' => $key, - 'value' => $value - ]; - } - - if (!empty($fields)) { - return [ - 'transactionRequest' => [ - 'userFields' => [ - 'userField' => $fields - ] - ] - ]; - } - - return []; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php deleted file mode 100644 index 99955e9724577..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php +++ /dev/null @@ -1,59 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $dataDescriptor = $payment->getAdditionalInformation('opaqueDataDescriptor'); - $dataValue = $payment->getAdditionalInformation('opaqueDataValue'); - - $data['transactionRequest']['payment'] = [ - 'opaqueData' => [ - 'dataDescriptor' => $dataDescriptor, - 'dataValue' => $dataValue - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php deleted file mode 100644 index 9b56e0852af01..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'poNumber' => $payment->getPoNumber() - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php deleted file mode 100644 index ac5bcb08cb04a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - * @throws \Exception - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => $payment->getAdditionalInformation('ccLast4'), - 'expirationDate' => 'XXXX' - ] - ] - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php deleted file mode 100644 index 65842354b7e2a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $transactionId = $payment->getAuthorizationTransaction()->getParentTxnId(); - $data = [ - 'transactionRequest' => [ - 'refTransId' => $transactionId - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php deleted file mode 100644 index 0f74299ebf5bd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php +++ /dev/null @@ -1,34 +0,0 @@ - [ - 'transactionType' => self::REQUEST_TYPE_REFUND - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php deleted file mode 100644 index d20add70846b8..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php +++ /dev/null @@ -1,48 +0,0 @@ -type = $type; - } - - /** - * Adds the type of the request to the build subject - * - * @param array $buildSubject - * @return array - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function build(array $buildSubject): array - { - return [ - 'payload_type' => $this->type - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php deleted file mode 100644 index 4402fb5af8c82..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php +++ /dev/null @@ -1,72 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_AUTH_AND_CAPTURE, - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php deleted file mode 100644 index ea2cb89971fb5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php +++ /dev/null @@ -1,59 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $order = $paymentDO->getOrder(); - $data = []; - - if ($payment instanceof Payment && $order instanceof Order) { - $data = [ - 'transactionRequest' => [ - 'shipping' => [ - 'amount' => $order->getBaseShippingAmount() - ] - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php deleted file mode 100644 index 8734c0ab454ce..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -config = $config; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - return [ - 'transactionRequest' => [ - 'solution' => [ - 'id' => $this->config->getSolutionId($this->subjectReader->readStoreId($buildSubject)), - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php deleted file mode 100644 index 396ad143466cd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php +++ /dev/null @@ -1,46 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - - return [ - 'store_id' => $order->getStoreId() - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php deleted file mode 100644 index a2766d97d9299..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php +++ /dev/null @@ -1,31 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $data = []; - - if (!empty($buildSubject['transactionId'])) { - $data = [ - 'transId' => $buildSubject['transactionId'] - ]; - } else { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - - if (empty($authorizationTransaction)) { - $transactionId = $payment->getLastTransId(); - } else { - $transactionId = $authorizationTransaction->getParentTxnId(); - - if (empty($transactionId)) { - $transactionId = $authorizationTransaction->getTxnId(); - } - } - - $data = [ - 'transId' => $transactionId - ]; - } - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php deleted file mode 100644 index c830f1f23d17c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $transactionData = []; - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - $refId = $authorizationTransaction->getAdditionalInformation('real_transaction_id'); - if (empty($refId)) { - $refId = $authorizationTransaction->getParentTxnId(); - } - - $transactionData['transactionRequest'] = [ - 'transactionType' => self::REQUEST_TYPE_VOID, - 'refTransId' => $refId - ]; - } - - return $transactionData; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php deleted file mode 100644 index 60c5bb21c0865..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php +++ /dev/null @@ -1,48 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $payment->setShouldCloseParentTransaction(true); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php deleted file mode 100644 index 5279df56b5e28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php +++ /dev/null @@ -1,30 +0,0 @@ -getCreditmemo()->getInvoice()->canRefund(); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php deleted file mode 100644 index 2cccf255ab8e9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - $this->closeTransaction = $closeTransaction; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $payment->setIsTransactionClosed($this->closeTransaction); - $payment->setShouldCloseParentTransaction($this->shouldCloseParentTransaction($payment)); - } - } - - /** - * Whether parent transaction should be closed. - * - * @param Payment $payment - * @return bool - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function shouldCloseParentTransaction(Payment $payment) - { - return true; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php deleted file mode 100644 index e0b192205012f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php +++ /dev/null @@ -1,58 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - $payment->setCcLast4($payment->getAdditionalInformation('ccLast4')); - $payment->setCcAvsStatus($transactionResponse['avsResultCode']); - $payment->setIsTransactionClosed(false); - - if ($transactionResponse['responseCode'] == self::RESPONSE_CODE_HELD) { - $payment->setIsTransactionPending(true) - ->setIsFraudDetected(true); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php deleted file mode 100644 index 41c2ddd2b3271..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php +++ /dev/null @@ -1,66 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - $status = $response['transaction']['transactionStatus']; - // This data is only used when updating the order payment via Get Payment Update - if (!in_array($status, self::REVIEW_PENDING_STATUSES)) { - $denied = in_array($status, self::REVIEW_DECLINED_STATUSES); - $payment->setData('is_transaction_denied', $denied); - $payment->setData('is_transaction_approved', !$denied); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php deleted file mode 100644 index 81bb9c92b15ed..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $storeId = $this->subjectReader->readStoreId($handlingSubject); - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - // Add the keys that should show in the transaction details interface - $additionalInformationKeys = $this->config->getAdditionalInfoKeys($storeId); - $rawDetails = []; - foreach ($additionalInformationKeys as $paymentInfoKey) { - if (isset($transactionResponse[$paymentInfoKey])) { - $rawDetails[$paymentInfoKey] = $transactionResponse[$paymentInfoKey]; - $payment->setAdditionalInformation($paymentInfoKey, $transactionResponse[$paymentInfoKey]); - } - } - $payment->setTransactionAdditionalInfo(Payment\Transaction::RAW_DETAILS, $rawDetails); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php deleted file mode 100644 index f3a9a0a1c4466..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php +++ /dev/null @@ -1,57 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - if (!$payment->getParentTransactionId() - || $transactionResponse['transId'] != $payment->getParentTransactionId() - ) { - $payment->setTransactionId($transactionResponse['transId']); - } - $payment->setTransactionAdditionalInfo( - 'real_transaction_id', - $transactionResponse['transId'] - ); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php deleted file mode 100644 index 7bcb8c6c8dba1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php +++ /dev/null @@ -1,52 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionId = $response['transactionResponse']['transId']; - - if ($payment instanceof Payment) { - $payment->setIsTransactionClosed(true); - $payment->setShouldCloseParentTransaction(true); - $payment->setTransactionAdditionalInfo('real_transaction_id', $transactionId); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php deleted file mode 100644 index b5f1cef94ea46..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php +++ /dev/null @@ -1,100 +0,0 @@ -readPayment($subject) - ->getOrder() - ->getStoreId(); - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock - } catch (\InvalidArgumentException $e) { - // No store id is current set - } - } - - return $storeId ? (int)$storeId : null; - } - - /** - * Reads amount from subject - * - * @param array $subject - * @return string - */ - public function readAmount(array $subject): string - { - return (string)Helper\SubjectReader::readAmount($subject); - } - - /** - * Reads response from subject - * - * @param array $subject - * @return array - */ - public function readResponse(array $subject): ?array - { - return Helper\SubjectReader::readResponse($subject); - } - - /** - * Reads login id from subject - * - * @param array $subject - * @return string|null - */ - public function readLoginId(array $subject): ?string - { - return $subject['merchantAuthentication']['name'] ?? null; - } - - /** - * Reads transaction key from subject - * - * @param array $subject - * @return string|null - */ - public function readTransactionKey(array $subject): ?string - { - return $subject['merchantAuthentication']['transactionKey'] ?? null; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php deleted file mode 100644 index 47065ed96c240..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php +++ /dev/null @@ -1,82 +0,0 @@ -resultFactory = $resultFactory; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $isValid = (isset($response['messages']['resultCode']) - && $response['messages']['resultCode'] === self::RESULT_CODE_SUCCESS); - $errorCodes = []; - $errorMessages = []; - - if (!$isValid) { - if (isset($response['messages']['message']['code'])) { - $errorCodes[] = $response['messages']['message']['code']; - $errorMessages[] = $response['messages']['message']['text']; - } elseif (isset($response['messages']['message'])) { - foreach ($response['messages']['message'] as $message) { - $errorCodes[] = $message['code']; - $errorMessages[] = $message['text']; - } - } elseif (isset($response['errors']['error'])) { - foreach ($response['errors']['error'] as $message) { - $errorCodes[] = $message['errorCode']; - $errorMessages[] = $message['errorText']; - } - } - } - - return $this->createResult($isValid, $errorMessages, $errorCodes); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php deleted file mode 100644 index c11e22110d952..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php +++ /dev/null @@ -1,201 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * Validates the transaction hash matches the configured hash - * - * @param array $validationSubject - * @return ResultInterface - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $storeId = $this->subjectReader->readStoreId($validationSubject); - - if (!empty($response['transactionResponse']['transHashSha2'])) { - return $this->validateHash( - $validationSubject, - $this->config->getTransactionSignatureKey($storeId), - 'transHashSha2', - 'generateSha512Hash' - ); - } elseif (!empty($response['transactionResponse']['transHash'])) { - return $this->validateHash( - $validationSubject, - $this->config->getLegacyTransactionHash($storeId), - 'transHash', - 'generateMd5Hash' - ); - } - - return $this->createResult( - false, - [ - __('The authenticity of the gateway response could not be verified.') - ], - [self::ERROR_TRANSACTION_HASH] - ); - } - - /** - * Validates the response again the legacy MD5 spec - * - * @param array $validationSubject - * @param string $storedHash - * @param string $hashField - * @param string $generateFunction - * @return ResultInterface - */ - private function validateHash( - array $validationSubject, - string $storedHash, - string $hashField, - string $generateFunction - ): ResultInterface { - $storeId = $this->subjectReader->readStoreId($validationSubject); - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - /* - * Authorize.net is inconsistent with how they hash and heuristically trying to detect whether or not they used - * the amount to calculate the hash is risky because their responses are incorrect in some cases. - * Refund uses the amount when referencing a transaction but will use 0 when refunding without a reference. - * Non-refund reference transactions such as (void/capture) don't use the amount. Authorize/auth&capture - * transactions will use amount but if there is an AVS error the response will indicate the transaction was a - * reference transaction so this can't be heuristically detected by looking at combinations of refTransID - * and transId (yes they also mixed the letter casing for "id"). Their documentation doesn't talk about this - * and to make this even better, none of their official SDKs support the new hash field to compare - * implementations. Therefore the only way to safely validate this hash without failing for even more - * unexpected corner cases we simply need to validate with and without the amount. - */ - try { - $amount = $this->subjectReader->readAmount($validationSubject); - } catch (\InvalidArgumentException $e) { - $amount = 0; - } - - $hash = $this->{$generateFunction}( - $storedHash, - $this->config->getLoginId($storeId), - sprintf('%.2F', $amount), - $transactionResponse['transId'] ?? '' - ); - $valid = Security::compareStrings($hash, $transactionResponse[$hashField]); - - if (!$valid && $amount > 0) { - $hash = $this->{$generateFunction}( - $storedHash, - $this->config->getLoginId($storeId), - '0.00', - $transactionResponse['transId'] ?? '' - ); - $valid = Security::compareStrings($hash, $transactionResponse[$hashField]); - } - - if ($valid) { - return $this->createResult(true); - } - - return $this->createResult( - false, - [ - __('The authenticity of the gateway response could not be verified.') - ], - [self::ERROR_TRANSACTION_HASH] - ); - } - - /** - * Generates a Md5 hash to compare against AuthNet's. - * - * @param string $merchantMd5 - * @param string $merchantApiLogin - * @param string $amount - * @param string $transactionId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function generateMd5Hash( - $merchantMd5, - $merchantApiLogin, - $amount, - $transactionId - ) { - // phpcs:disable Magento2.Security.InsecureFunction - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); - } - - /** - * Generates a SHA-512 hash to compare against AuthNet's. - * - * @param string $merchantKey - * @param string $merchantApiLogin - * @param string $amount - * @param string $transactionId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function generateSha512Hash( - $merchantKey, - $merchantApiLogin, - $amount, - $transactionId - ) { - $message = '^' . $merchantApiLogin . '^' . $transactionId . '^' . $amount . '^'; - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantKey))); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php deleted file mode 100644 index 8238aa37dcc0a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php +++ /dev/null @@ -1,102 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - if ($this->isResponseCodeAnError($transactionResponse)) { - $errorCodes = []; - $errorMessages = []; - - if (isset($transactionResponse['messages']['message']['code'])) { - $errorCodes[] = $transactionResponse['messages']['message']['code']; - $errorMessages[] = $transactionResponse['messages']['message']['text']; - } elseif (isset($transactionResponse['messages']['message'])) { - foreach ($transactionResponse['messages']['message'] as $message) { - $errorCodes[] = $message['code']; - $errorMessages[] = $message['description']; - } - } elseif (isset($transactionResponse['errors'])) { - foreach ($transactionResponse['errors'] as $message) { - $errorCodes[] = $message['errorCode']; - $errorMessages[] = $message['errorText']; - } - } - - return $this->createResult(false, $errorMessages, $errorCodes); - } - - return $this->createResult(true); - } - - /** - * Determines if the response code is actually an error - * - * @param array $transactionResponse - * @return bool - */ - private function isResponseCodeAnError(array $transactionResponse): bool - { - $code = $transactionResponse['messages']['message']['code'] - ?? $transactionResponse['messages']['message'][0]['code'] - ?? $transactionResponse['errors'][0]['errorCode'] - ?? null; - - return !in_array($transactionResponse['responseCode'], [self::RESPONSE_CODE_APPROVED, self::RESPONSE_CODE_HELD]) - || $code - && !in_array( - $code, - [ - self::RESPONSE_REASON_CODE_APPROVED, - self::RESPONSE_REASON_CODE_PENDING_REVIEW, - self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED - ] - ); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt b/app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt b/app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php deleted file mode 100644 index cdd1745a6bc1e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php +++ /dev/null @@ -1,28 +0,0 @@ - self::ENVIRONMENT_SANDBOX, - 'label' => 'Sandbox', - ], - [ - 'value' => self::ENVIRONMENT_PRODUCTION, - 'label' => 'Production' - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php deleted file mode 100644 index 953841604bfee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php +++ /dev/null @@ -1,35 +0,0 @@ - 'authorize', - 'label' => __('Authorize Only'), - ], - [ - 'value' => 'authorize_capture', - 'label' => __('Authorize and Capture') - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php b/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php deleted file mode 100644 index 145d8c000e8f7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php +++ /dev/null @@ -1,22 +0,0 @@ -config = $config; - $this->cart = $cart; - } - - /** - * Retrieve assoc array of checkout configuration - * - * @return array - */ - public function getConfig() - { - $storeId = $this->cart->getStoreId(); - - return [ - 'payment' => [ - Config::METHOD => [ - 'clientKey' => $this->config->getClientKey($storeId), - 'apiLoginID' => $this->config->getLoginId($storeId), - 'environment' => $this->config->getEnvironment($storeId), - 'useCvv' => $this->config->isCvvEnabled($storeId), - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php deleted file mode 100644 index 0f989bb032175..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php +++ /dev/null @@ -1,55 +0,0 @@ -readDataArgument($observer); - - $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); - if (!is_array($additionalData)) { - return; - } - - $paymentInfo = $this->readPaymentModelArgument($observer); - - foreach ($this->additionalInformationList as $additionalInformationKey) { - if (isset($additionalData[$additionalInformationKey])) { - $paymentInfo->setAdditionalInformation( - $additionalInformationKey, - $additionalData[$additionalInformationKey] - ); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/README.md b/app/code/Magento/AuthorizenetAcceptjs/README.md deleted file mode 100644 index b507f97a5a223..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Magento_AuthorizenetAcceptjs module - -The Magento_AuthorizenetAcceptjs module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento. - -## Installation details - -Before disabling or uninstalling this module, note that the `Magento_AuthorizenetCardinal` module depends on this module. - -For information about module installation in Magento 2, see [Enable or disable modules](https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-subcommands-enable.html). - -## Structure - -`Gateway/` - the directory that contains payment gateway command interfaces and service classes. - -For information about typical file structure of a module in Magento 2, see [Module file structure](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/build/module-file-structure.html#module-file-structure). - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetAcceptjs module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetAcceptjs module. - -### Events - -This module observes the following events: - -- `payment_method_assign_data_authorizenet_acceptjs` event in the `Magento\AuthorizenetAcceptjs\Observer\DataAssignObserver` file. - -For information about an event in Magento 2, see [Events and observers](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). diff --git a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php b/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php deleted file mode 100644 index aa699569c61f6..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php +++ /dev/null @@ -1,233 +0,0 @@ -scopeConfig = $scopeConfig; - $this->resourceConfig = $resourceConfig; - $this->encryptor = $encryptor; - $this->moduleDataSetup = $moduleDataSetup; - $this->storeManager = $storeManager; - } - - /** - * @inheritdoc - */ - public function apply(): void - { - $this->moduleDataSetup->startSetup(); - $this->migrateDefaultValues(); - $this->migrateWebsiteValues(); - $this->moduleDataSetup->endSetup(); - } - - /** - * Migrate configuration values from DirectPost to Accept.js on default scope - * - * @return void - */ - private function migrateDefaultValues(): void - { - foreach ($this->configFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue); - } - } - - foreach ($this->encryptedConfigFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field); - - if (!empty($configValue)) { - $this->saveNewConfigValue( - $field, - $configValue, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0, - true - ); - } - } - } - - /** - * Migrate configuration values from DirectPost to Accept.js on all website scopes - * - * @return void - */ - private function migrateWebsiteValues(): void - { - foreach ($this->storeManager->getWebsites() as $website) { - $websiteID = (int) $website->getId(); - - foreach ($this->configFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field, ScopeInterface::SCOPE_WEBSITES, $websiteID); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue, ScopeInterface::SCOPE_WEBSITES, $websiteID); - } - } - - foreach ($this->encryptedConfigFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field, ScopeInterface::SCOPE_WEBSITES, $websiteID); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue, ScopeInterface::SCOPE_WEBSITES, $websiteID, true); - } - } - } - } - - /** - * Get old configuration value from the DirectPost module's configuration on the store scope - * - * @param string $field - * @param string $scope - * @param int $scopeID - * @return mixed - */ - private function getOldConfigValue( - string $field, - string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - int $scopeID = null - ) { - return $this->scopeConfig->getValue( - sprintf(self::PAYMENT_PATH_FORMAT, self::DIRECTPOST_PATH, $field), - $scope, - $scopeID - ); - } - - /** - * Save configuration value for AcceptJS - * - * @param string $field - * @param mixed $value - * @param string $scope - * @param int $scopeID - * @param bool $isEncrypted - * @return void - */ - private function saveNewConfigValue( - string $field, - $value, - string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - int $scopeID = 0, - bool $isEncrypted = false - ): void { - $value = $isEncrypted ? $this->encryptor->encrypt($value) : $value; - - $this->resourceConfig->saveConfig( - sprintf(self::PAYMENT_PATH_FORMAT, self::ACCEPTJS_PATH, $field), - $value, - $scope, - $scopeID - ); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md deleted file mode 100644 index aba235e2cfad9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# AuthorizenetAcceptjs Functional Tests - -The Functional Test Module for **Magento AuthorizenetAcceptjs** module. diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php deleted file mode 100644 index 020b651aaaf17..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php +++ /dev/null @@ -1,59 +0,0 @@ -createMock(Context::class); - $this->configMock = $this->createMock(Config::class); - $quoteMock = $this->getMockBuilder(Quote::class) - ->disableOriginalConstructor() - ->setMethods(['getStoreId']) - ->getMock(); - $quoteMock->method('getStoreId') - ->willReturn('123'); - $paymentConfig = $this->createMock(PaymentConfig::class); - - $this->block = new Form( - $contextMock, - $paymentConfig, - $this->configMock, - $quoteMock - ); - } - - public function testIsCvvEnabled() - { - $this->configMock->method('isCvvEnabled') - ->with('123') - ->willReturn(true); - $this->assertTrue($this->block->isCvvEnabled()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php deleted file mode 100644 index 70dfb140e1576..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php +++ /dev/null @@ -1,80 +0,0 @@ -createMock(Context::class); - /** @var Config|MockObject|InvocationMocker $configMock */ - $configMock = $this->createMock(ConfigInterface::class); - $block = new Info($contextMock, $configMock); - /** @var InfoInterface|MockObject|InvocationMocker $payment */ - $payment = $this->createMock(InfoInterface::class); - /** @var RendererInterface|MockObject|InvocationMocker $translationRenderer */ - $translationRenderer = $this->createMock(RendererInterface::class); - - // only foo should be used - $configMock->method('getValue') - ->willReturnMap([ - ['paymentInfoKeys', null, 'foo'], - ['privateInfoKeys', null, ''] - ]); - - // Give more info to ensure only foo is translated - $payment->method('getAdditionalInformation') - ->willReturnCallback(function ($name = null) { - $info = [ - 'foo' => 'bar', - 'baz' => 'bash' - ]; - - if (empty($name)) { - return $info; - } - - return $info[$name]; - }); - - // Foo should be translated to Super Cool String - $translationRenderer->method('render') - ->with(['foo'], []) - ->willReturn('Super Cool String'); - - $previousRenderer = Phrase::getRenderer(); - Phrase::setRenderer($translationRenderer); - - try { - $block->setData('info', $payment); - - $info = $block->getSpecificInformation(); - } finally { - // No matter what, restore the renderer - Phrase::setRenderer($previousRenderer); - } - - // Assert the label was correctly translated - $this->assertSame($info['Super Cool String'], 'bar'); - $this->assertArrayNotHasKey('foo', $info); - $this->assertArrayNotHasKey('baz', $info); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php deleted file mode 100644 index 11ae27f9d2ea7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php +++ /dev/null @@ -1,53 +0,0 @@ -createMock(Context::class); - $this->configMock = $this->createMock(ConfigProvider::class); - $this->block = new Payment($contextMock, $this->configMock, new Json()); - } - - public function testConfigIsCreated() - { - $this->configMock->method('getConfig') - ->willReturn([ - 'payment' => [ - 'authorizenet_acceptjs' => [ - 'foo' => 'bar' - ] - ] - ]); - - $result = $this->block->getPaymentConfig(); - $expected = '{"foo":"bar","code":"authorizenet_acceptjs"}'; - $this->assertEquals($expected, $result); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php deleted file mode 100644 index 316fef5443360..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php +++ /dev/null @@ -1,131 +0,0 @@ -transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->commandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->command = new AcceptPaymentStrategyCommand( - $this->commandPoolMock, - new SubjectReader() - ); - } - - /** - * @param string $status - * @dataProvider inReviewStatusesProvider - */ - public function testCommandWillAcceptInTheGatewayWhenInFDSReview(string $status) - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ['accept_fds', $this->commandMock] - ]); - - $this->transactionResultMock->method('get') - ->willReturn([ - 'transaction' => [ - 'transactionStatus' => $status - ] - ]); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function testCommandWillDoNothingWhenTransactionHasAlreadyBeenAuthorized() - { - // Assert command is never executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ]); - - $this->transactionResultMock->method('get') - ->willReturn([ - 'transaction' => [ - 'transactionStatus' => 'anythingelseisfine' - ] - ]); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function inReviewStatusesProvider() - { - return [ - ['FDSPendingReview'], - ['FDSAuthorizedPendingReview'] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php deleted file mode 100644 index 4cbded9764793..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php +++ /dev/null @@ -1,181 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObject::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->commandMock = $this->createMock(GatewayCommand::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class); - $this->transactionRepositoryMock = $this->createMock(TransactionRepositoryInterface::class); - - // The search criteria builder should return the criteria with the specified filters - $this->filterBuilderMock = $this->createMock(FilterBuilder::class); - // We aren't coupling the implementation to the test. The test only cares how the result is processed - $this->filterBuilderMock->method('setField') - ->willReturnSelf(); - $this->filterBuilderMock->method('setValue') - ->willReturnSelf(); - $searchCriteria = new SearchCriteria(); - $this->searchCriteriaBuilderMock->method('addFilters') - ->willReturnSelf(); - $this->searchCriteriaBuilderMock->method('create') - ->willReturn($searchCriteria); - // The transaction result can be customized per test to simulate different scenarios - $this->transactionsResult = $this->createMock(TransactionSearchResultInterface::class); - $this->transactionRepositoryMock->method('getList') - ->with($searchCriteria) - ->willReturn($this->transactionsResult); - - $this->strategyCommand = new CaptureStrategyCommand( - $this->commandPoolMock, - $this->transactionRepositoryMock, - $this->filterBuilderMock, - $this->searchCriteriaBuilderMock, - new SubjectReader() - ); - } - - public function testExecuteWillAuthorizeWhenNotAuthorizedAndNotCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Hasn't been authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(false); - // Hasn't been captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(0); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('sale') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } - - public function testExecuteWillAuthorizeAndCaptureWhenAlreadyCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Already authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(true); - // And already captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(1); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('settle') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } - - public function testExecuteWillCaptureWhenAlreadyAuthorizedButNotCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Was already authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(true); - // But, hasn't been captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(0); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('settle') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php deleted file mode 100644 index 757500c7e50eb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php +++ /dev/null @@ -1,132 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObject::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->configMock = $this->createMock(Config::class); - $this->configMock->method('getTransactionInfoSyncKeys') - ->willReturn(['foo', 'bar']); - $orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($orderMock); - $this->transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->handlerMock = $this->createMock(HandlerInterface::class); - $this->command = new FetchTransactionInfoCommand( - $this->commandPoolMock, - new SubjectReader(), - $this->configMock, - $this->handlerMock - ); - } - - public function testCommandWillMarkTransactionAsApprovedWhenNotVoid() - { - $response = [ - 'transaction' => [ - 'transactionStatus' => 'authorizedPendingCapture', - 'foo' => 'abc', - 'bar' => 'cba', - 'dontreturnme' => 'justdont' - ] - ]; - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ]); - - $this->transactionResultMock->method('get') - ->willReturn($response); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->handlerMock->expects($this->once()) - ->method('handle') - ->with($buildSubject, $response) - ->willReturn($this->transactionResultMock); - - $result = $this->command->execute($buildSubject); - - $expected = [ - 'foo' => 'abc', - 'bar' => 'cba' - ]; - - $this->assertSame($expected, $result); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php deleted file mode 100644 index e37db34936385..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php +++ /dev/null @@ -1,196 +0,0 @@ -requestBuilderMock = $this->createMock(BuilderInterface::class); - $this->transferFactoryMock = $this->createMock(TransferFactoryInterface::class); - $this->transferMock = $this->createMock(TransferInterface::class); - $this->clientMock = $this->createMock(ClientInterface::class); - $this->loggerMock = $this->createMock(LoggerInterface::class); - $this->validatorMock = $this->createMock(ValidatorInterface::class); - - $this->command = new GatewayQueryCommand( - $this->requestBuilderMock, - $this->transferFactoryMock, - $this->clientMock, - $this->loggerMock, - $this->validatorMock - ); - } - - public function testNormalExecution() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $response = [ - 'transaction' => [ - 'transactionType' => 'foo', - 'transactionStatus' => 'bar', - 'responseCode' => 'baz' - ] - ]; - - $validationSubject = $buildSubject; - $validationSubject['response'] = $response; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willReturn($response); - - $this->validatorMock->method('validate') - ->with($validationSubject) - ->willReturn(new Result(true)); - - $result = $this->command->execute($buildSubject); - - $this->assertInstanceOf(ArrayResult::class, $result); - $this->assertEquals($response, $result->get()); - } - - /** - * @expectedExceptionMessage There was an error while trying to process the request. - * @expectedException \Magento\Payment\Gateway\Command\CommandException - */ - public function testExceptionIsThrownAndLoggedWhenRequestFails() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $e = new \Exception('foobar'); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willThrowException($e); - - // assert the exception is logged - $this->loggerMock->expects($this->once()) - ->method('critical') - ->with($e); - - $this->command->execute($buildSubject); - } - /** - * @expectedExceptionMessage There was an error while trying to process the request. - * @expectedException \Magento\Payment\Gateway\Command\CommandException - */ - public function testExceptionIsThrownWhenResponseIsInvalid() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $response = [ - 'baz' => '456' - ]; - - $validationSubject = $buildSubject; - $validationSubject['response'] = $response; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willReturn($response); - - $this->validatorMock->method('validate') - ->with($validationSubject) - ->willReturn(new Result(false)); - - $this->command->execute($buildSubject); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php deleted file mode 100644 index 79477b06e0e6c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php +++ /dev/null @@ -1,225 +0,0 @@ -transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->commandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->command = new RefundTransactionStrategyCommand( - $this->commandPoolMock, - new SubjectReader() - ); - } - - public function testCommandWillVoidWhenTransactionIsPendingSettlement() - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - [ - 'void', - $this->commandMock - ] - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'capturedPendingSettlement', - 'authAmount' => '20.19', - ] - ] - ); - - $buildSubject = [ - 'foo' => '123', - 'amount' => '20.19', - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - /** - * @expectedException \Magento\Payment\Gateway\Command\CommandException - * @expectedExceptionMessage The transaction has not been settled, a partial refund is not yet available. - */ - public function testCommandWillThrowExceptionWhenVoidTransactionIsPartial() - { - // Assert command is executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'capturedPendingSettlement', - 'authAmount' => '20.19', - ] - ] - ); - - $buildSubject = [ - 'foo' => '123', - 'amount' => '10.19', - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function testCommandWillRefundWhenTransactionIsSettled() - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - [ - 'refund_settled', - $this->commandMock - ] - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'settledSuccessfully' - ] - ] - ); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - /** - * @expectedException \Magento\Payment\Gateway\Command\CommandException - * @expectedExceptionMessage This transaction cannot be refunded with its current status. - */ - public function testCommandWillThrowExceptionWhenTransactionIsInInvalidState() - { - // Assert command is never executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'somethingIsWrong' - ] - ] - ); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php deleted file mode 100644 index 646ad4f195b9d..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php +++ /dev/null @@ -1,136 +0,0 @@ -scopeConfigMock = $this->createMock(ScopeConfigInterface::class); - - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject( - Config::class, - [ - 'scopeConfig' => $this->scopeConfigMock, - 'methodCode' => Config::METHOD, - ] - ); - } - - /** - * @param $getterName - * @param $configField - * @param $configValue - * @param $expectedValue - * @dataProvider configMapProvider - */ - public function testConfigGetters($getterName, $configField, $configValue, $expectedValue) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath($configField), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($configValue); - $this->assertEquals($expectedValue, $this->model->{$getterName}(123)); - } - - /** - * @dataProvider environmentUrlProvider - * @param $environment - * @param $expectedUrl - */ - public function testGetApiUrl($environment, $expectedUrl) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath('environment'), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($environment); - $this->assertEquals($expectedUrl, $this->model->getApiUrl(123)); - } - - /** - * @dataProvider environmentSolutionProvider - * @param $environment - * @param $expectedSolution - */ - public function testGetSolutionIdSandbox($environment, $expectedSolution) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath('environment'), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($environment); - $this->assertEquals($expectedSolution, $this->model->getSolutionId(123)); - } - - /** - * @return array - */ - public function configMapProvider() - { - return [ - ['getLoginId', 'login', 'username', 'username'], - ['getEnvironment', 'environment', 'production', 'production'], - ['getClientKey', 'public_client_key', 'abc', 'abc'], - ['getTransactionKey', 'trans_key', 'password', 'password'], - ['getLegacyTransactionHash', 'trans_md5', 'abc123', 'abc123'], - ['getTransactionSignatureKey', 'trans_signature_key', 'abc123', 'abc123'], - ['getPaymentAction', 'payment_action', 'authorize', 'authorize'], - ['shouldEmailCustomer', 'email_customer', true, true], - ['isCvvEnabled', 'cvv_enabled', true, true], - ['getAdditionalInfoKeys', 'paymentInfoKeys', 'a,b,c', ['a', 'b', 'c']], - ['getTransactionInfoSyncKeys', 'transactionSyncKeys', 'a,b,c', ['a', 'b', 'c']], - ]; - } - - /** - * @return array - */ - public function environmentUrlProvider() - { - return [ - ['sandbox', 'https://apitest.authorize.net/xml/v1/request.api'], - ['production', 'https://api.authorize.net/xml/v1/request.api'], - ]; - } - - /** - * @return array - */ - public function environmentSolutionProvider() - { - return [ - ['sandbox', 'AAA102993'], - ['production', 'AAA175350'], - ]; - } - - /** - * Return config path - * - * @param string $field - * @return string - */ - private function getPath($field) - { - return sprintf(Config::DEFAULT_PATH_PATTERN, Config::METHOD, $field); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php deleted file mode 100644 index 4086195ff4c95..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php +++ /dev/null @@ -1,218 +0,0 @@ -objectManager = new ObjectManager($this); - $this->paymentLogger = $this->createMock(Logger::class); - $this->httpClientFactory = $this->createMock(ZendClientFactory::class); - $this->httpClient = $this->createMock(Zend_Http_Client::class); - $this->httpResponse = $this->createMock(Zend_Http_Response::class); - $this->httpClientFactory->method('create')->will($this->returnValue($this->httpClient)); - $this->httpClient->method('request') - ->willReturn($this->httpResponse); - /** @var MockObject $logger */ - $this->logger = $this->createMock(LoggerInterface::class); - } - - public function testCanSendRequest() - { - // Assert the raw data was set on the client - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - // Authorize.net returns a BOM and refuses to fix it - $response = pack('CCC', 0xef, 0xbb, 0xbf) . '{"foo":{"bar":"baz"}}'; - - $this->httpResponse->method('getBody') - ->willReturn($response); - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => '{"foo":{"bar":"baz"}}']); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'json' => new Json() - ]); - - $result = $apiClient->placeRequest($this->getTransferObjectMock($request)); - - $this->assertSame('baz', $result['foo']['bar']); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Something went wrong in the payment gateway. - */ - public function testExceptionIsThrownWhenEmptyResponseIsReceived() - { - // Assert the client has the raw data set - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $this->httpResponse->method('getBody') - ->willReturn(''); - - // Assert the exception is given to the logger - $this->logger->expects($this->once()) - ->method('critical') - ->with($this->callback(function ($e) { - return $e instanceof \Exception - && $e->getMessage() === 'Invalid JSON was returned by the gateway'; - })); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => '']); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'logger' => $this->logger, - 'json' => new Json() - ]); - - $apiClient->placeRequest($this->getTransferObjectMock($request)); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Something went wrong in the payment gateway. - */ - public function testExceptionIsThrownWhenInvalidResponseIsReceived() - { - // Assert the client was given the raw data - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $this->httpResponse->method('getBody') - ->willReturn('bad'); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => 'bad']); - - // Assert the exception was given to the logger - $this->logger->expects($this->once()) - ->method('critical') - ->with($this->callback(function ($e) { - return $e instanceof \Exception - && $e->getMessage() === 'Invalid JSON was returned by the gateway'; - })); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'logger' => $this->logger, - 'json' => new Json() - ]); - - $apiClient->placeRequest($this->getTransferObjectMock($request)); - } - - /** - * Creates mock object for TransferInterface. - * - * @return TransferInterface|MockObject - */ - private function getTransferObjectMock(array $data) - { - $transferObjectMock = $this->createMock(TransferInterface::class); - $transferObjectMock->method('getBody') - ->willReturn($data); - - return $transferObjectMock; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php deleted file mode 100644 index bcc6279f5b1fe..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php +++ /dev/null @@ -1,36 +0,0 @@ -filter([ - 'some' => 123, - 'data' => 321, - 'foo' => 'to', - 'filter' => ['blah'], - 'bar' => 'fields from' - ]); - - $expected = [ - 'some' => 123, - 'data' => 321, - 'filter' => ['blah'], - ]; - - $this->assertEquals($expected, $actual); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php deleted file mode 100644 index 954fd9782bd3f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php +++ /dev/null @@ -1,73 +0,0 @@ -transferBuilder = $this->createMock(TransferBuilder::class); - $this->transferMock = $this->createMock(TransferInterface::class); - $this->filterMock = $this->createMock(RemoveFieldsFilter::class); - - $this->transferFactory = new TransferFactory( - $this->transferBuilder, - [$this->filterMock] - ); - } - - public function testCreate() - { - $request = ['data1', 'data2']; - - // Assert the filter was created - $this->filterMock->expects($this->once()) - ->method('filter') - ->with($request) - ->willReturn($request); - - // Assert the body of the transfer was set - $this->transferBuilder->expects($this->once()) - ->method('setBody') - ->with($request) - ->willReturnSelf(); - - $this->transferBuilder->method('build') - ->willReturn($this->transferMock); - - $this->assertEquals($this->transferMock, $this->transferFactory->create($request)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php deleted file mode 100644 index 00bb7ee84f98b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new AcceptFdsDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getTxnId') - ->willReturn('foo'); - - $expected = [ - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => 'foo' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php deleted file mode 100644 index 84c2f19040e16..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php +++ /dev/null @@ -1,138 +0,0 @@ - [ - 'method' => 'getFirstname', - 'sampleData' => 'John' - ], - 'lastName' => [ - 'method' => 'getLastname', - 'sampleData' => 'Doe' - ], - 'company' => [ - 'method' => 'getCompany', - 'sampleData' => 'Magento' - ], - 'address' => [ - 'method' => 'getStreetLine1', - 'sampleData' => '11501 Domain Dr' - ], - 'city' => [ - 'method' => 'getCity', - 'sampleData' => 'Austin' - ], - 'state' => [ - 'method' => 'getRegionCode', - 'sampleData' => 'TX' - ], - 'zip' => [ - 'method' => 'getPostcode', - 'sampleData' => '78758' - ], - 'country' => [ - 'method' => 'getCountryId', - 'sampleData' => 'US' - ], - ]; - - protected function setUp() - { - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new AddressDataBuilder(new SubjectReader()); - } - - public function testBuildWithBothAddresses() - { - $billingAddress = $this->createAddressMock('billing'); - $shippingAddress = $this->createAddressMock('shipping'); - $this->orderMock->method('getBillingAddress') - ->willReturn($billingAddress); - $this->orderMock->method('getShippingAddress') - ->willReturn($shippingAddress); - $this->orderMock->method('getRemoteIp') - ->willReturn('abc'); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $result = $this->builder->build($buildSubject); - - $this->validateAddressData($result['transactionRequest']['billTo'], 'billing'); - $this->validateAddressData($result['transactionRequest']['shipTo'], 'shipping'); - $this->assertEquals('abc', $result['transactionRequest']['customerIP']); - } - - /** - * @param $responseData - * @param $addressPrefix - */ - private function validateAddressData($responseData, $addressPrefix) - { - foreach ($this->mockAddressData as $fieldValue => $field) { - $this->assertEquals($addressPrefix . $field['sampleData'], $responseData[$fieldValue]); - } - } - - /** - * @param $prefix - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function createAddressMock($prefix) - { - $addressAdapterMock = $this->createMock(AddressAdapterInterface::class); - - foreach ($this->mockAddressData as $field) { - $addressAdapterMock->method($field['method']) - ->willReturn($prefix . $field['sampleData']); - } - - return $addressAdapterMock; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php deleted file mode 100644 index 9da0139302a30..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php +++ /dev/null @@ -1,42 +0,0 @@ -builder = new AmountDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $expected = [ - 'transactionRequest' => [ - 'amount' => '123.45', - ] - ]; - - $buildSubject = [ - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php deleted file mode 100644 index e9588e51b0fc8..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php +++ /dev/null @@ -1,78 +0,0 @@ -configMock = $this->createMock(Config::class); - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - - $this->builder = new AuthenticationDataBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuild() - { - $this->configMock->method('getLoginId') - ->willReturn('myloginid'); - $this->configMock->method('getTransactionKey') - ->willReturn('mytransactionkey'); - - $expected = [ - 'merchantAuthentication' => [ - 'name' => 'myloginid', - 'transactionKey' => 'mytransactionkey' - ] - ]; - - $buildSubject = []; - - $this->subjectReaderMock->method('readStoreId') - ->with($buildSubject) - ->willReturn(123); - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php deleted file mode 100644 index 438d681a2b5b2..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new AuthorizeDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillAddTransactionType() - { - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction' - ] - ]; - - $buildSubject = [ - 'store_id' => 123, - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('authOnlyTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php deleted file mode 100644 index 537a685f1ff7f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php +++ /dev/null @@ -1,77 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new CaptureDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillCaptureWhenAuthorizeTransactionExists() - { - $transactionMock = $this->createMock(Payment\Transaction::class); - $transactionMock->method('getAdditionalInformation') - ->with('real_transaction_id') - ->willReturn('prevtrans'); - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'priorAuthCaptureTransaction', - 'refTransId' => 'prevtrans' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('priorAuthCaptureTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php deleted file mode 100644 index be7dd7eca1761..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php +++ /dev/null @@ -1,74 +0,0 @@ -configMock = $this->createMock(Config::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - $this->subjectReaderMock->method('readStoreId') - ->willReturn('123'); - - $this->builder = new CustomSettingsBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuildWithEmailCustomerDisabled() - { - $this->configMock->method('shouldEmailCustomer') - ->with('123') - ->willReturn(false); - - $this->assertEquals([], $this->builder->build([])); - } - - public function testBuildWithEmailCustomerEnabled() - { - $this->configMock->method('shouldEmailCustomer') - ->with('123') - ->willReturn(true); - - $expected = [ - 'transactionRequest' => [ - 'transactionSettings' => [ - 'setting' => [ - [ - 'settingName' => 'emailCustomer', - 'settingValue' => 'true' - ] - ] - ] - ] - ]; - - $this->assertEquals($expected, $this->builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php deleted file mode 100644 index 7c9116cad54b1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php +++ /dev/null @@ -1,79 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new CustomerDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $addressAdapterMock = $this->createMock(AddressAdapterInterface::class); - $addressAdapterMock->method('getEmail') - ->willReturn('foo@bar.com'); - $this->orderMock->method('getBillingAddress') - ->willReturn($addressAdapterMock); - $this->orderMock->method('getCustomerId') - ->willReturn('123'); - - $expected = [ - 'transactionRequest' => [ - 'customer' => [ - 'id' => '123', - 'email' => 'foo@bar.com' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php deleted file mode 100644 index d66421d48ca8b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php +++ /dev/null @@ -1,74 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new OrderDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->orderMock->method('getOrderIncrementId') - ->willReturn('10000015'); - - $expected = [ - 'transactionRequest' => [ - 'order' => [ - 'invoiceNumber' => '10000015' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'order' => $this->orderMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php deleted file mode 100644 index f4c5f56efe890..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php +++ /dev/null @@ -1,52 +0,0 @@ - 'bar', - 'baz' => 'bash' - ]); - $builder = new PassthroughDataBuilder($passthroughData); - - $expected = [ - 'transactionRequest' => [ - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'foo', - 'value' => 'bar' - ], - [ - 'name' => 'baz', - 'value' => 'bash' - ], - ] - ] - ] - ]; - - $this->assertEquals($expected, $builder->build([])); - } - - public function testBuildWithNoData() - { - $passthroughData = new PassthroughDataObject(); - $builder = new PassthroughDataBuilder($passthroughData); - $expected = []; - - $this->assertEquals($expected, $builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php deleted file mode 100644 index cf3842b8947bb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php +++ /dev/null @@ -1,72 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PaymentDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->paymentMock->method('getAdditionalInformation') - ->willReturnMap([ - ['opaqueDataDescriptor', 'foo'], - ['opaqueDataValue', 'bar'] - ]); - - $expected = [ - 'transactionRequest' => [ - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'foo', - 'dataValue' => 'bar' - ] - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php deleted file mode 100644 index 97b51c1e1807c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php +++ /dev/null @@ -1,61 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PoDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->paymentMock->method('getPoNumber') - ->willReturn('abc'); - - $expected = [ - 'transactionRequest' => [ - 'poNumber' => 'abc' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php deleted file mode 100644 index c1879b3df83a3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new RefundPaymentDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $this->paymentMock->method('getAdditionalInformation') - ->with('ccLast4') - ->willReturn('1111'); - - $expected = [ - 'transactionRequest' => [ - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => '1111', - 'expirationDate' => 'XXXX' - ] - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php deleted file mode 100644 index cf1803005acee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php +++ /dev/null @@ -1,69 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new RefundReferenceTransactionDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getParentTxnId') - ->willReturn('foo'); - - $expected = [ - 'transactionRequest' => [ - 'refTransId' => 'foo' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php deleted file mode 100644 index 4e0f5f75fb944..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php +++ /dev/null @@ -1,30 +0,0 @@ - [ - 'transactionType' => self::REQUEST_TYPE_REFUND - ] - ]; - - $this->assertEquals($expected, $builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php deleted file mode 100644 index cb03dfc3dac5e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php +++ /dev/null @@ -1,36 +0,0 @@ -builder = new RequestTypeBuilder('foo'); - } - - public function testBuild() - { - $expected = [ - 'payload_type' => 'foo' - ]; - - $buildSubject = []; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php deleted file mode 100644 index 407b9bc85a2c5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new SaleDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillAddTransactionType() - { - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction' - ] - ]; - - $buildSubject = [ - 'store_id' => 123, - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('authCaptureTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php deleted file mode 100644 index d6525e610a285..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php +++ /dev/null @@ -1,75 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new ShippingDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $this->orderMock->method('getBaseShippingAmount') - ->willReturn('43.12'); - - $expected = [ - 'transactionRequest' => [ - 'shipping' => [ - 'amount' => '43.12' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'order' => $this->orderMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php deleted file mode 100644 index 1b06546c2ea8f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php +++ /dev/null @@ -1,75 +0,0 @@ -configMock = $this->createMock(Config::class); - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - - $this->builder = new SolutionDataBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuild() - { - $this->subjectReaderMock->method('readStoreId') - ->willReturn('123'); - $this->configMock->method('getSolutionId') - ->with('123') - ->willReturn('solutionid'); - - $expected = [ - 'transactionRequest' => [ - 'solution' => [ - 'id' => 'solutionid', - ] - ] - ]; - - $buildSubject = []; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php deleted file mode 100644 index 2ed0cb13ed624..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php +++ /dev/null @@ -1,68 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(InfoInterface::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new StoreConfigBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->orderMock->method('getStoreID') - ->willReturn(123); - - $expected = [ - 'store_id' => 123 - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php deleted file mode 100644 index 03c036c027147..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php +++ /dev/null @@ -1,89 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new TransactionDetailsDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getParentTxnId') - ->willReturn('foo'); - - $expected = [ - 'transId' => 'foo' - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } - - public function testBuildWithIncludedTransactionId() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->expects($this->never()) - ->method('getAuthorizationTransaction'); - - $transactionMock->expects($this->never()) - ->method('getParentTxnId'); - - $expected = [ - 'transId' => 'foo' - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'transactionId' => 'foo' - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php deleted file mode 100644 index 84460a1c744b9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php +++ /dev/null @@ -1,68 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new VoidDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - $transactionMock->method('getParentTxnId') - ->willReturn('myref'); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $expected = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_TYPE_VOID, - 'refTransId' => 'myref', - ] - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php deleted file mode 100644 index e9929c631eb15..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new CloseParentTransactionHandler(new SubjectReader()); - } - - public function testHandleClosesTransactionByDefault() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [] - ]; - - // Assert the parent transaction i closed - $this->paymentMock->expects($this->once()) - ->method('setShouldCloseParentTransaction') - ->with(true); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php deleted file mode 100644 index a7093f0dac889..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new CloseTransactionHandler(new SubjectReader()); - } - - public function testHandleClosesTransactionByDefault() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [] - ]; - - // Assert the transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php deleted file mode 100644 index d051c7d2910a5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php +++ /dev/null @@ -1,112 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PaymentResponseHandler(new SubjectReader()); - } - - public function testHandleDefaultResponse() - { - $this->paymentMock->method('getAdditionalInformation') - ->with('ccLast4') - ->willReturn('1234'); - // Assert the avs code is saved - $this->paymentMock->expects($this->once()) - ->method('setCcAvsStatus') - ->with('avshurray'); - $this->paymentMock->expects($this->once()) - ->method('setCcLast4') - ->with('1234'); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(false); - - $response = [ - 'transactionResponse' => [ - 'avsResultCode' => 'avshurray', - 'responseCode' => self::RESPONSE_CODE_APPROVED, - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } - - public function testHandleHeldResponse() - { - // Assert the avs code is saved - $this->paymentMock->expects($this->once()) - ->method('setCcAvsStatus') - ->with('avshurray'); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(false); - // opaque data wasn't provided - $this->paymentMock->expects($this->never()) - ->method('setAdditionalInformation'); - // Assert the payment is flagged for review - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionPending') - ->with(true) - ->willReturnSelf(); - $this->paymentMock->expects($this->once()) - ->method('setIsFraudDetected') - ->with(true); - - $response = [ - 'transactionResponse' => [ - 'avsResultCode' => 'avshurray', - 'responseCode' => self::RESPONSE_CODE_HELD, - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php deleted file mode 100644 index 197dc209ece66..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php +++ /dev/null @@ -1,136 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new PaymentReviewStatusHandler(new SubjectReader()); - } - - public function testApprovesPayment() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => 'approvedOrSomething', - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->exactly(2)) - ->method('setData') - ->withConsecutive( - ['is_transaction_denied', false], - ['is_transaction_approved', true] - ); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } - - /** - * @param string $status - * @dataProvider declinedTransactionStatusesProvider - */ - public function testDeniesPayment(string $status) - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => $status, - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->exactly(2)) - ->method('setData') - ->withConsecutive( - ['is_transaction_denied', true], - ['is_transaction_approved', false] - ); - $this->handler->handle($subject, $response); - } - - /** - * @param string $status - * @dataProvider pendingTransactionStatusesProvider - */ - public function testDoesNothingWhenPending(string $status) - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => $status, - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->never()) - ->method('setData'); - - $this->handler->handle($subject, $response); - } - - /** - * @return array - */ - public function pendingTransactionStatusesProvider() - { - return [ - ['FDSPendingReview'], - ['FDSAuthorizedPendingReview'] - ]; - } - - /** - * @return array - */ - public function declinedTransactionStatusesProvider() - { - return [ - ['void'], - ['declined'] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php deleted file mode 100644 index 016e3a1e95383..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php +++ /dev/null @@ -1,82 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->configMock = $this->createMock(Config::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new TransactionDetailsResponseHandler(new SubjectReader(), $this->configMock); - } - - public function testHandle() - { - $subject = [ - 'payment' => $this->paymentDOMock, - 'store_id' => 123, - ]; - $response = [ - 'transactionResponse' => [ - 'dontsaveme' => 'dontdoti', - 'abc' => 'foobar', - ] - ]; - - // Assert the information comes from the right store config - $this->configMock->method('getAdditionalInfoKeys') - ->with(123) - ->willReturn(['abc']); - - // Assert the payment has the most recent information always set on it - $this->paymentMock->expects($this->once()) - ->method('setAdditionalInformation') - ->with('abc', 'foobar'); - // Assert the transaction has the raw details from the transaction - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('raw_details_info', ['abc' => 'foobar']); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php deleted file mode 100644 index 710f995918495..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php +++ /dev/null @@ -1,92 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new TransactionIdHandler(new SubjectReader()); - } - - public function testHandleDefaultResponse() - { - $this->paymentMock->method('getParentTransactionId') - ->willReturn(null); - // Assert the id is set - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('thetransid'); - // Assert the id is set in the additional info for later - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('real_transaction_id', 'thetransid'); - - $response = [ - 'transactionResponse' => [ - 'transId' => 'thetransid', - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } - - public function testHandleDifferenceInTransactionId() - { - $this->paymentMock->method('getParentTransactionId') - ->willReturn('somethingElse'); - // Assert the id is set - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('thetransid'); - - $response = [ - 'transactionResponse' => [ - 'transId' => 'thetransid', - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php deleted file mode 100644 index f99da2b2ec90b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php +++ /dev/null @@ -1,72 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new VoidResponseHandler(new SubjectReader()); - } - - public function testHandle() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [ - 'transId' => 'abc123', - ] - ]; - - // Assert the transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true); - // Assert the parent transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setShouldCloseParentTransaction') - ->with(true); - // Assert the authorize.net transaction id is saved - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('real_transaction_id', 'abc123'); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php deleted file mode 100644 index 42219024badbf..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php +++ /dev/null @@ -1,119 +0,0 @@ -subjectReader = new SubjectReader(); - } - - public function testReadPayment(): void - { - $paymentDO = $this->createMock(PaymentDataObjectInterface::class); - - $this->assertSame($paymentDO, $this->subjectReader->readPayment(['payment' => $paymentDO])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided - */ - public function testReadPaymentThrowsExceptionWhenNotAPaymentObject(): void - { - $this->subjectReader->readPayment(['payment' => 'nope']); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided - */ - public function testReadPaymentThrowsExceptionWhenNotSet(): void - { - $this->subjectReader->readPayment([]); - } - - public function testReadResponse(): void - { - $expected = ['foo' => 'bar']; - - $this->assertSame($expected, $this->subjectReader->readResponse(['response' => $expected])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Response does not exist - */ - public function testReadResponseThrowsExceptionWhenNotAvailable(): void - { - $this->subjectReader->readResponse([]); - } - - public function testReadStoreId(): void - { - $this->assertEquals(123, $this->subjectReader->readStoreId(['store_id' => '123'])); - } - - public function testReadStoreIdFromOrder(): void - { - $paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $orderMock = $this->createMock(OrderAdapterInterface::class); - $paymentDOMock->method('getOrder') - ->willReturn($orderMock); - $orderMock->method('getStoreID') - ->willReturn('123'); - - $result = $this->subjectReader->readStoreId([ - 'payment' => $paymentDOMock - ]); - - $this->assertEquals(123, $result); - } - - public function testReadLoginId(): void - { - $this->assertEquals('abc', $this->subjectReader->readLoginId([ - 'merchantAuthentication' => ['name' => 'abc'] - ])); - } - - public function testReadTransactionKey(): void - { - $this->assertEquals('abc', $this->subjectReader->readTransactionKey([ - 'merchantAuthentication' => ['transactionKey' => 'abc'] - ])); - } - - public function testReadAmount(): void - { - $this->assertSame('123.12', $this->subjectReader->readAmount(['amount' => 123.12])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Amount should be provided - */ - public function testReadAmountThrowsExceptionWhenNotAvailable(): void - { - $this->subjectReader->readAmount([]); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php deleted file mode 100644 index 347cd071acc3a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php +++ /dev/null @@ -1,161 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->validator = new GeneralResponseValidator($this->resultFactoryMock, new SubjectReader()); - } - - public function testValidateParsesSuccess() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertTrue($args['isValid']); - $this->assertEmpty($args['errorCodes']); - $this->assertEmpty($args['failsDescription']); - } - - public function testValidateParsesErrors() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'errors' => [ - 'resultCode' => 'Error', - 'error' => [ - [ - 'errorCode' => 'foo', - 'errorText' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } - - public function testValidateParsesMessages() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Error', - 'message' => [ - [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } - - public function testValidateParsesErrorsWhenOnlyOneIsReturned() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Error', - 'message' => [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php deleted file mode 100644 index fb3f9d0520d49..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php +++ /dev/null @@ -1,280 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->configMock = $this->createMock(Config::class); - $this->resultMock = $this->createMock(ResultInterface::class); - - $this->validator = new TransactionHashValidator( - $this->resultFactoryMock, - new SubjectReader(), - $this->configMock - ); - } - - /** - * @param $response - * @param $isValid - * @param $errorCodes - * @param $errorDescriptions - * @dataProvider sha512ResponseProvider - */ - public function testValidateSha512HashScenarios( - $response, - $isValid, - $errorCodes, - $errorDescriptions - ) { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->configMock->method('getTransactionSignatureKey') - ->willReturn('abc'); - $this->configMock->method('getLoginId') - ->willReturn('username'); - - $this->validator->validate($response); - - $this->assertSame($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorDescriptions, $args['failsDescription']); - } - - /** - * @param $response - * @param $isValid - * @param $errorCodes - * @param $errorDescriptions - * @dataProvider md5ResponseProvider - */ - public function testValidateMd5HashScenarios( - $response, - $isValid, - $errorCodes, - $errorDescriptions - ) { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->configMock->method('getLegacyTransactionHash') - ->willReturn('abc'); - $this->configMock->method('getLoginId') - ->willReturn('username'); - - $this->validator->validate($response); - - $this->assertSame($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorDescriptions, $args['failsDescription']); - } - - public function md5ResponseProvider() - { - return [ - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transHash' => 'bad' - ] - ] - ], - false, - ['ETHV'], - ['The authenticity of the gateway response could not be verified.'] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'refTransID' => '123', - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - ]; - } - - public function sha512ResponseProvider() - { - return [ - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'refTransID' => '123', - 'transHashSha2' => 'CC0FF465A081D98FFC6E502C40B2DCC7655ACF591F859135B6E66558D' - . '41E3A2C654D5A2ACF4749104F3133711175C232C32676F79F70211C2984B21A33D30DEE' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '0', - 'refTransID' => '123', - 'transHashSha2' => '563D42F4A5189F74334088EF6A02E84F320CD8C005FB0DC436EF96084D' - . 'FAC0C76DE081DFC58A3BF825465C63B7F38E4D463025EAC44597A68C024CBBCE7A3159' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '0', - 'transHashSha2' => 'DEE5309078D9F7A68BA4F706FB3E58618D3991A6A5E4C39DCF9C49E693' - . '673C38BD6BB15C235263C549A6B5F0B6D7019EC729E0C275C9FEA37FB91F8B612D0A5D' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHashSha2' => '1DBD16DED0DA02F52A22A9AD71A49F70BD2ECD42437552889912DD5CE' - . 'CBA0E09A5E8E6221DA74D98A46E5F77F7774B6D9C39CADF3E9A33D85870A6958DA7C8B2' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'refTransID' => '0', - 'transHashSha2' => '1DBD16DED0DA02F52A22A9AD71A49F70BD2ECD42437552889912DD5CE' - . 'CBA0E09A5E8E6221DA74D98A46E5F77F7774B6D9C39CADF3E9A33D85870A6958DA7C8B2' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transHashSha2' => 'bad' - ] - ] - ], - false, - ['ETHV'], - ['The authenticity of the gateway response could not be verified.'] - ], - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php deleted file mode 100644 index c59cf00899af2..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php +++ /dev/null @@ -1,231 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->resultMock = $this->createMock(ResultInterface::class); - - $this->validator = new TransactionResponseValidator( - $this->resultFactoryMock, - new SubjectReader() - ); - } - - /** - * @param $transactionResponse - * @param $isValid - * @param $errorCodes - * @param $errorMessages - * @dataProvider scenarioProvider - */ - public function testValidateScenarios($transactionResponse, $isValid, $errorCodes, $errorMessages) - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->validator->validate([ - 'response' => [ - 'transactionResponse' => $transactionResponse - ] - ]); - - $this->assertEquals($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorMessages, $args['failsDescription']); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function scenarioProvider() - { - return [ - // Test for acceptable reason codes - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_APPROVED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_APPROVED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED, - ] - ] - ], - true, - [], - [] - ], - - // Test for reason codes that aren't acceptable - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - [ - 'description' => 'bar', - 'code' => 'foo', - ] - ] - ] - ], - false, - ['foo'], - ['bar'] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - // Alternate, non-array sytax - 'text' => 'bar', - 'code' => 'foo', - ] - ] - ], - false, - ['foo'], - ['bar'] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_DENIED, - 'errors' => [ - [ - 'errorCode' => self::ERROR_CODE_AVS_MISMATCH, - 'errorText' => 'bar' - ] - ] - ], - false, - [self::ERROR_CODE_AVS_MISMATCH], - ['bar'] - ], - // This validator only cares about successful edge cases so test for default behavior - [ - [ - 'responseCode' => 'foo', - ], - false, - [], - [] - ], - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php deleted file mode 100644 index dea4557fd584c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php +++ /dev/null @@ -1,76 +0,0 @@ -cart = $this->createMock(CartInterface::class); - $this->config = $this->createMock(Config::class); - $this->provider = new ConfigProvider($this->config, $this->cart); - } - - public function testProviderRetrievesValues() - { - $this->cart->method('getStoreId') - ->willReturn('123'); - - $this->config->method('getClientKey') - ->with('123') - ->willReturn('foo'); - - $this->config->method('getLoginId') - ->with('123') - ->willReturn('bar'); - - $this->config->method('getEnvironment') - ->with('123') - ->willReturn('baz'); - - $this->config->method('isCvvEnabled') - ->with('123') - ->willReturn(false); - - $expected = [ - 'payment' => [ - Config::METHOD => [ - 'clientKey' => 'foo', - 'apiLoginID' => 'bar', - 'environment' => 'baz', - 'useCvv' => false, - ] - ] - ]; - - $this->assertEquals($expected, $this->provider->getConfig()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php deleted file mode 100644 index bd439a336786b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php +++ /dev/null @@ -1,80 +0,0 @@ - 'foo', - 'opaqueDataValue' => 'bar', - 'ccLast4' => '1234' - ]; - - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $dataObject = new DataObject([PaymentInterface::KEY_ADDITIONAL_DATA => $additionalInfo]); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, $dataObject] - ] - ); - $paymentInfoModel->expects($this->at(0)) - ->method('setAdditionalInformation') - ->with('opaqueDataDescriptor', 'foo'); - $paymentInfoModel->expects($this->at(1)) - ->method('setAdditionalInformation') - ->with('opaqueDataValue', 'bar'); - $paymentInfoModel->expects($this->at(2)) - ->method('setAdditionalInformation') - ->with('ccLast4', '1234'); - - $observer = new DataAssignObserver(); - $observer->execute($observerContainer); - } - - public function testDoestSetDataWhenEmpty() - { - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, new DataObject()] - ] - ); - $paymentInfoModel->expects($this->never()) - ->method('setAdditionalInformation'); - - $observer = new DataAssignObserver(); - $observer->execute($observerContainer); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php deleted file mode 100644 index 5ac8a6ca9b3f6..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php +++ /dev/null @@ -1,149 +0,0 @@ -scopeConfig = $this->createMock(Config::class); - $this->resourceConfig = $this->createMock(ResourceConfig::class); - $this->encryptor = $this->createMock(Encryptor::class); - $this->setup = $this->createMock(DataSetup::class); - - $this->setup->expects($this->once()) - ->method('startSetup') - ->willReturn(null); - - $this->setup->expects($this->once()) - ->method('endSetup') - ->willReturn(null); - - $this->context = $this->createMock(ModuleContext::class); - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->website = $this->createMock(Website::class); - } - - public function testMigrateData(): void - { - $this->scopeConfig->expects($this->exactly(26)) - ->method('getValue') - ->willReturn('TestValue'); - - $this->resourceConfig->expects($this->exactly(26)) - ->method('saveConfig') - ->willReturn(null); - - $this->encryptor->expects($this->exactly(6)) - ->method('encrypt') - ->willReturn('TestValue'); - - $this->website->expects($this->once()) - ->method('getId') - ->willReturn(1); - - $this->storeManager->expects($this->once()) - ->method('getWebsites') - ->willReturn([$this->website]); - - $objectManager = new ObjectManager($this); - - $installer = $objectManager->getObject( - CopyCurrentConfig::class, - [ - 'moduleDataSetup' => $this->setup, - 'scopeConfig' => $this->scopeConfig, - 'resourceConfig' => $this->resourceConfig, - 'encryptor' => $this->encryptor, - 'storeManager' => $this->storeManager - ] - ); - - $installer->apply($this->context); - } - - public function testMigrateDataNullFields(): void - { - $this->scopeConfig->expects($this->exactly(13)) - ->method('getValue') - ->will($this->onConsecutiveCalls(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); - - $this->resourceConfig->expects($this->exactly(10)) - ->method('saveConfig') - ->willReturn(null); - - $this->encryptor->expects($this->never()) - ->method('encrypt'); - - $this->storeManager->expects($this->once()) - ->method('getWebsites') - ->willReturn([]); - - $objectManager = new ObjectManager($this); - - $installer = $objectManager->getObject( - CopyCurrentConfig::class, - [ - 'moduleDataSetup' => $this->setup, - 'scopeConfig' => $this->scopeConfig, - 'resourceConfig' => $this->resourceConfig, - 'encryptor' => $this->encryptor, - 'storeManager' => $this->storeManager - ] - ); - - $installer->apply($this->context); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/composer.json b/app/code/Magento/AuthorizenetAcceptjs/composer.json deleted file mode 100644 index a54387ca6de28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/module-authorizenet-acceptjs", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-config": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-store": "*", - "magento/module-quote": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml deleted file mode 100644 index f4059aebbe3e3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - AuthorizenetAcceptjsTransactionValidator - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml deleted file mode 100644 index 86b6d3a198d81..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - -
- - - - - Magento\Config\Model\Config\Source\Yesno - - - - - - - 1 - Magento\Config\Block\System\Config\Form\Fieldset - - - payment/authorizenet_acceptjs/title - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Environment - payment/authorizenet_acceptjs/environment - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\PaymentAction - payment/authorizenet_acceptjs/payment_action - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/login - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_key - required-entry - - 1 - - - - - payment/authorizenet_acceptjs/public_client_key - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_signature_key - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_md5 - - 1 - - - - - - 0 - - - Magento\Config\Model\Config\Source\Locale\Currency - payment/authorizenet_acceptjs/currency - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/debug - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/email_customer - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/cvv_enabled - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Cctype - payment/authorizenet_acceptjs/cctypes - - - - Magento\Payment\Model\Config\Source\Allspecificcountries - payment/authorizenet_acceptjs/allowspecific - - - - Magento\Directory\Model\Config\Source\Country - payment/authorizenet_acceptjs/specificcountry - - - - payment/authorizenet_acceptjs/min_order_total - validate-number validate-zero-or-greater - - - - payment/authorizenet_acceptjs/max_order_total - validate-number validate-zero-or-greater - - - - validate-number - payment/authorizenet_acceptjs/sort_order - - - -
-
-
diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml deleted file mode 100644 index 507a9b14f917b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Invalid request to gateway. - Invalid gateway credentials. - Transaction has been declined. Please try again later. - The authenticity of the gateway response could not be verified. - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml deleted file mode 100644 index 6fdbb98a78f8b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - \.authorize\.net/v1/Accept - - - - - - 0 - AE,VI,MC,DI,JCB,DN - 0 - 1 - 1 - 0 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - AuthorizenetAcceptjsFacade - 0 - - processing - authorize - Credit Card (Authorize.Net) - 1 - - - - - 0 - USD - production - authCode,avsResultCode,cvvResultCode,cavvResultCode - accountType,ccLast4,authCode,avsResultCode,cvvResultCode,cavvResultCode - transactionStatus,responseCode,responseReasonCode,authCode,AVSResponse,cardCodeResponse,CAVVResponse - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml deleted file mode 100644 index 1bff19e15a65f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - - - - - authorizenet_acceptjs - Magento\AuthorizenetAcceptjs\Block\Form - AuthorizenetAcceptjsInfoBlock - AuthorizenetAcceptjsValueHandlerPool - AuthorizenetAcceptjsValidatorPool - AuthorizenetAcceptjsCommandPool - - - - - - AuthorizenetAcceptjsAuthorizeCommand - AuthorizenetAcceptjsCaptureCommand - AuthorizenetAcceptjsSaleCommand - AuthorizenetAcceptjsSettleCommand - AuthorizenetAcceptjsVoidCommand - AuthorizenetAcceptjsRefundCommand - AuthorizenetAcceptjsRefundSettledCommand - AuthorizenetAcceptjsCancelCommand - AuthorizenetAcceptjsAcceptPaymentCommand - AuthorizenetAcceptjsAcceptFdsCommand - AuthorizenetAcceptjsCancelCommand - AuthorizenetAcceptjsTransactionDetailsCommand - AuthorizenetAcceptjsFetchTransactionInfoCommand - - - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - - - - - - - true - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionResponseValidator - Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - - AuthorizenetAcceptjsCountryValidator - - - - - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsTransactionDetailsRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsTransactionDetailsValidator - - - - - AuthorizenetAcceptjsAuthorizeRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsAuthorizationHandler - AuthorizenetAcceptjsTransactionValidator - AuthorizenetAcceptjsVirtualErrorMessageMapper - - - - - AuthorizenetAcceptjsAcceptsFdsRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsAcceptsFdsRequestValidator - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsSaleRequest - AuthorizenetAcceptjsSaleHandler - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsRefundRequest - AuthorizenetAcceptjsRefundSettledHandler - AuthorizenetAcceptjsTransactionValidator - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsCaptureRequest - AuthorizenetAcceptjsCaptureTransactionHandler - AuthorizenetAcceptjsTransactionValidator - - - - - AuthorizenetAcceptjsVoidRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsVoidHandler - AuthorizenetAcceptjsTransactionValidator - AuthorizenetAcceptjsVirtualErrorMessageMapper - - - - - AuthorizenetAcceptjsCancelHandler - - - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentReviewStatusHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentResponseHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionDetailsResponseHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler - - - - - - false - - - - - - CloseCaptureTransactionHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\ClosePartialTransactionHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\VoidResponseHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler - - - - - - - - - - - AuthorizenetAcceptjsTransactionDetailsRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\TransactionDetailsDataBuilder - - - - - - - AuthorizenetAcceptjsAcceptsFdsRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AcceptFdsDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StubDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Request\SaleDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundTransactionTypeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundPaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundReferenceTransactionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CaptureDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\VoidDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - - - createTransactionRequest - - - - - getTransactionDetailsRequest - - - - - updateHeldTransactionRequest - - - - - - - - authorizenet_acceptjs_error_mapping.xml - - - - - AuthorizenetAcceptjsErrorMappingConfigReader - authorizenet_acceptjs_error_mapper - - - - - AuthorizenetAcceptjsErrorMappingData - - - - - - - AuthorizenetAcceptjsPaymentReviewStatusHandler - - - - - - AuthorizenetAcceptjsCommandManager - - - - - - - 1 - 1 - 1 - 1 - 1 - 1 - - - 1 - 1 - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - - AuthorizenetAcceptjsDefaultValueHandler - - - - - - AuthorizenetAcceptjsCommandPool - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - AuthorizenetAcceptjsLogger - - - - - - store_id - - - - - - - AuthorizenetAcceptjsRemoveStoreConfigFilter - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml deleted file mode 100644 index 93dc448d1d895..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml deleted file mode 100644 index 8b0e570abbd2e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - 1 - - - - - - - Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml deleted file mode 100644 index 6bc8fe3c4daee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml deleted file mode 100644 index b9f8d40b03006..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - 0 - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv b/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv deleted file mode 100644 index a8b5dbd2df525..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv +++ /dev/null @@ -1,22 +0,0 @@ -"Authorize.Net (Deprecated)","Authorize.Net (Deprecated)" -"Gateway URL","Gateway URL" -"Invalid payload type.","Invalid payload type." -"Something went wrong in the payment gateway.","Something went wrong in the payment gateway." -"Merchant MD5 (deprecated","Merchant MD5 (deprecated" -"Signature Key","Signature Key" -"Basic Authorize.Net Settings","Basic Authorize.Net Settings" -"Advanced Authorize.Net Settings","Advanced Authorize.Net Settings" -"Public Client Key","Public Client Key" -"Environment","Environment" -"Production","Production" -"Sandbox","Sandbox" -"accountType","Account Type" -"authCode", "Processor Response Text" -"avsResultCode", "AVS Response Code" -"cvvResultCode","CVV Response Code" -"cavvResultCode","CAVV Response Code" -"Enable Credit Card Verification Field","Enable Credit Card Verification Field" -"ccLast4","Last 4 Digits of Card" -"There was an error while trying to process the refund.","There was an error while trying to process the refund." -"This transaction cannot be refunded with its current status.","This transaction cannot be refunded with its current status." -"The transaction has not been settled, a partial refund is not yet available.","The transaction has not been settled, a partial refund is not yet available." diff --git a/app/code/Magento/AuthorizenetAcceptjs/registration.php b/app/code/Magento/AuthorizenetAcceptjs/registration.php deleted file mode 100644 index 52a0c497a0993..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/registration.php +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - Magento_AuthorizenetAcceptjs::form/cc.phtml - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml deleted file mode 100644 index 13f6d38e2b81a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - Magento_AuthorizenetAcceptjs::form/cc.phtml - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml deleted file mode 100644 index b757e55aaddee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml +++ /dev/null @@ -1,92 +0,0 @@ -escapeHtml($block->getMethodCode()); -$ccType = $block->getInfoData('cc_type'); -$ccExpMonth = $block->getInfoData('cc_exp_month'); -$ccExpYear = $block->getInfoData('cc_exp_year'); -?> - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml deleted file mode 100644 index 6be6008dba507..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml +++ /dev/null @@ -1,24 +0,0 @@ - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js deleted file mode 100644 index 0eb865d7666b3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -define([ - 'jquery', - 'uiComponent', - 'Magento_Ui/js/modal/alert', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client' -], function ($, Class, alert, AcceptjsClient) { - 'use strict'; - - return Class.extend({ - defaults: { - acceptjsClient: null, - $selector: null, - selector: 'edit_form', - container: 'payment_form_authorizenet_acceptjs', - active: false, - imports: { - onActiveChange: 'active' - } - }, - - /** - * @{inheritdoc} - */ - initConfig: function (config) { - this._super(); - - this.acceptjsClient = AcceptjsClient({ - environment: config.environment - }); - - return this; - }, - - /** - * @{inheritdoc} - */ - initObservable: function () { - this.$selector = $('#' + this.selector); - this._super() - .observe('active'); - - // re-init payment method events - this.$selector.off('changePaymentMethod.' + this.code) - .on('changePaymentMethod.' + this.code, this.changePaymentMethod.bind(this)); - - return this; - }, - - /** - * Enable/disable current payment method - * - * @param {Object} event - * @param {String} method - * @returns {Object} - */ - changePaymentMethod: function (event, method) { - this.active(method === this.code); - - return this; - }, - - /** - * Triggered when payment changed - * - * @param {Boolean} isActive - */ - onActiveChange: function (isActive) { - if (!isActive) { - - return; - } - - this.disableEventListeners(); - - window.order.addExcludedPaymentMethod(this.code); - - this.enableEventListeners(); - }, - - /** - * Sets the payment details on the form - * - * @param {Object} tokens - */ - setPaymentDetails: function (tokens) { - var $ccNumber = $(this.getSelector('cc_number')), - ccLast4 = $ccNumber.val().replace(/[^\d]/g, '').substr(-4); - - $(this.getSelector('opaque_data_descriptor')).val(tokens.opaqueDataDescriptor); - $(this.getSelector('opaque_data_value')).val(tokens.opaqueDataValue); - $(this.getSelector('cc_last_4')).val(ccLast4); - $ccNumber.val(''); - $(this.getSelector('cc_exp_month')).val(''); - $(this.getSelector('cc_exp_year')).val(''); - - if (this.useCvv) { - $(this.getSelector('cc_cid')).val(''); - } - }, - - /** - * Trigger order submit - */ - submitOrder: function () { - var authData = {}, - cardData = {}, - secureData = {}; - - this.$selector.validate().form(); - this.$selector.trigger('afterValidate.beforeSubmit'); - - authData.clientKey = this.clientKey; - authData.apiLoginID = this.apiLoginID; - - cardData.cardNumber = $(this.getSelector('cc_number')).val(); - cardData.month = $(this.getSelector('cc_exp_month')).val(); - cardData.year = $(this.getSelector('cc_exp_year')).val(); - - if (this.useCvv) { - cardData.cardCode = $(this.getSelector('cc_cid')).val(); - } - - secureData.authData = authData; - secureData.cardData = cardData; - - this.disableEventListeners(); - - this.acceptjsClient.createTokens(secureData) - .always(function () { - $('body').trigger('processStop'); - this.enableEventListeners(); - }.bind(this)) - .done(function (tokens) { - this.setPaymentDetails(tokens); - this.placeOrder(); - }.bind(this)) - .fail(function (messages) { - this.tokens = null; - - if (messages.length > 0) { - this._showError(messages[0]); - } - }.bind(this)); - - return false; - }, - - /** - * Place order - */ - placeOrder: function () { - this.$selector.trigger('realOrder'); - }, - - /** - * Get jQuery selector - * - * @param {String} field - * @returns {String} - */ - getSelector: function (field) { - return '#' + this.code + '_' + field; - }, - - /** - * Show alert message - * - * @param {String} message - */ - _showError: function (message) { - alert({ - content: message - }); - }, - - /** - * Enable form event listeners - */ - enableEventListeners: function () { - this.$selector.on('submitOrder.authorizenetacceptjs', this.submitOrder.bind(this)); - }, - - /** - * Disable form event listeners - */ - disableEventListeners: function () { - this.$selector.off('submitOrder'); - this.$selector.off('submit'); - } - - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js deleted file mode 100644 index e3a0886797d63..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -define([ - 'Magento_AuthorizenetAcceptjs/js/authorizenet', - 'jquery' -], function (AuthorizenetAcceptjs, $) { - 'use strict'; - - return function (config, element) { - var $form = $(element); - - config.active = $form.length > 0 && !$form.is(':hidden'); - new AuthorizenetAcceptjs(config); - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js deleted file mode 100644 index 83ddd1094ea1a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -var config = { - shim: { - acceptjs: { - exports: 'Accept' - }, - acceptjssandbox: { - exports: 'Accept' - } - }, - paths: { - acceptjssandbox: 'https://jstest.authorize.net/v1/Accept', - acceptjs: 'https://js.authorize.net/v1/Accept' - } -}; diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js deleted file mode 100644 index 935465f5298eb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'uiClass', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-factory', - 'Magento_AuthorizenetAcceptjs/js/view/payment/validator-handler' -], function ($, Class, acceptjsFactory, validatorHandler) { - 'use strict'; - - return Class.extend({ - defaults: { - environment: 'production' - }, - - /** - * @{inheritdoc} - */ - initialize: function () { - validatorHandler.initialize(); - - this._super(); - - return this; - }, - - /** - * Creates the token pair with the provided data - * - * @param {Object} data - * @return {jQuery.Deferred} - */ - createTokens: function (data) { - var deferred = $.Deferred(); - - if (this.acceptjsClient) { - this._createTokens(deferred, data); - } else { - acceptjsFactory(this.environment) - .done(function (client) { - this.acceptjsClient = client; - this._createTokens(deferred, data); - }.bind(this)); - } - - return deferred.promise(); - }, - - /** - * Creates a token from the payment information in the form - * - * @param {jQuery.Deferred} deferred - * @param {Object} data - */ - _createTokens: function (deferred, data) { - this.acceptjsClient.dispatchData(data, function (response) { - validatorHandler.validate(response, function (valid, messages) { - if (valid) { - deferred.resolve({ - opaqueDataDescriptor: response.opaqueData.dataDescriptor, - opaqueDataValue: response.opaqueData.dataValue - }); - } else { - deferred.reject(messages); - } - }); - }); - } - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js deleted file mode 100644 index e98a204e36cee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery' -], function ($) { - 'use strict'; - - return function (environment) { - var deferred = $.Deferred(), - dependency = 'acceptjs'; - - if (environment === 'sandbox') { - dependency = 'acceptjssandbox'; - } - - require([dependency], function (accept) { - var $body = $('body'); - - /* - * Acceptjs doesn't safely load dependent files which leads to a race condition when trying to use - * the sdk right away. - * @see https://community.developer.authorize.net/t5/Integration-and-Testing/ - * Dynamically-loading-Accept-js-E-WC-03-Accept-js-is-not-loaded/td-p/63283 - */ - $body.on('handshake.acceptjs', function () { - deferred.resolve(accept); - $body.off('handshake.acceptjs'); - }); - }, - deferred.reject - ); - - return deferred.promise(); - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js deleted file mode 100644 index 3c44ca2f9e490..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/translate' -], function ($, $t) { - 'use strict'; - - return { - /** - * Validate Authorizenet-Acceptjs response - * - * @param {Object} context - * @returns {jQuery.Deferred} - */ - validate: function (context) { - var state = $.Deferred(), - messages = []; - - if (context.messages.resultCode === 'Ok') { - state.resolve(); - } else { - if (context.messages.message.length > 0) { - $.each(context.messages.message, function (index, element) { - messages.push($t(element.text)); - }); - } - state.reject(messages); - } - - return state.promise(); - } - }; -}); - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js deleted file mode 100644 index 109f159c9a77c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_AuthorizenetAcceptjs/js/view/payment/response-validator' -], function ($, responseValidator) { - 'use strict'; - - return { - validators: [], - - /** - * Init list of validators - */ - initialize: function () { - this.add(responseValidator); - }, - - /** - * Add new validator - * @param {Object} validator - */ - add: function (validator) { - this.validators.push(validator); - }, - - /** - * Run pull of validators - * @param {Object} context - * @param {Function} callback - */ - validate: function (context, callback) { - var self = this, - deferred; - - // no available validators - if (!self.validators.length) { - callback(true); - - return; - } - - // get list of deferred validators - deferred = $.map(self.validators, function (current) { - return current.validate(context); - }); - - $.when.apply($, deferred) - .done(function () { - callback(true); - }).fail(function (error) { - callback(false, error); - }); - } - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml deleted file mode 100644 index f31b06c9be9b9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - uiComponent - - - - - - - - Magento_AuthorizenetAcceptjs/js/view/payment/authorizenet - - - true - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js deleted file mode 100644 index a05fe739a444a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'uiComponent', - 'Magento_Checkout/js/model/payment/renderer-list' -], -function (Component, rendererList) { - 'use strict'; - - rendererList.push({ - type: 'authorizenet_acceptjs', - component: 'Magento_AuthorizenetAcceptjs/js/view/payment/method-renderer/authorizenet-accept' - }); - - /** Add view logic here if needed */ - return Component.extend({}); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js deleted file mode 100644 index bba1290a9eedd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Payment/js/view/payment/cc-form', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client', - 'Magento_Checkout/js/model/full-screen-loader', - 'Magento_Ui/js/model/messageList', - 'Magento_Payment/js/model/credit-card-validation/validator' -], function ($, Component, AcceptjsClient, fullScreenLoader, globalMessageList) { - 'use strict'; - - return Component.extend({ - defaults: { - active: false, - template: 'Magento_AuthorizenetAcceptjs/payment/authorizenet-acceptjs', - tokens: null, - ccForm: 'Magento_Payment/payment/cc-form', - acceptjsClient: null - }, - - /** - * Set list of observable attributes - * - * @returns {exports.initObservable} - */ - initObservable: function () { - this._super() - .observe(['active']); - - return this; - }, - - /** - * @returns {String} - */ - getCode: function () { - return 'authorizenet_acceptjs'; - }, - - /** - * Initialize form elements for validation - */ - initFormElement: function (element) { - this.formElement = element; - this.acceptjsClient = AcceptjsClient({ - environment: window.checkoutConfig.payment[this.getCode()].environment - }); - $(this.formElement).validation(); - }, - - /** - * @returns {Object} - */ - getData: function () { - return { - method: this.getCode(), - 'additional_data': { - opaqueDataDescriptor: this.tokens ? this.tokens.opaqueDataDescriptor : null, - opaqueDataValue: this.tokens ? this.tokens.opaqueDataValue : null, - ccLast4: this.creditCardNumber().substr(-4) - } - }; - }, - - /** - * Check if payment is active - * - * @returns {Boolean} - */ - isActive: function () { - var active = this.getCode() === this.isChecked(); - - this.active(active); - - return active; - }, - - /** - * Prepare data to place order - */ - beforePlaceOrder: function () { - var authData = {}, - cardData = {}, - secureData = {}; - - if (!$(this.formElement).valid()) { - return; - } - - authData.clientKey = window.checkoutConfig.payment[this.getCode()].clientKey !== null ? - window.checkoutConfig.payment[this.getCode()].clientKey : ''; - authData.apiLoginID = window.checkoutConfig.payment[this.getCode()].apiLoginID !== null ? - window.checkoutConfig.payment[this.getCode()].apiLoginID : ''; - - cardData.cardNumber = this.creditCardNumber(); - cardData.month = this.creditCardExpMonth(); - cardData.year = this.creditCardExpYear(); - - if (this.hasVerification()) { - cardData.cardCode = this.creditCardVerificationNumber(); - } - - secureData.authData = authData; - secureData.cardData = cardData; - - fullScreenLoader.startLoader(); - - this.acceptjsClient.createTokens(secureData) - .always(function () { - fullScreenLoader.stopLoader(); - }) - .done(function (tokens) { - this.tokens = tokens; - this.placeOrder(); - }.bind(this)) - .fail(function (messages) { - this.tokens = null; - this._showErrors(messages); - }.bind(this)); - }, - - /** - * Should the cvv field be used - * - * @return {Boolean} - */ - hasVerification: function () { - return window.checkoutConfig.payment[this.getCode()].useCvv; - }, - - /** - * Show error messages - * - * @param {String[]} errorMessages - */ - _showErrors: function (errorMessages) { - $.each(errorMessages, function (index, message) { - globalMessageList.addErrorMessage({ - message: message - }); - }); - } - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html deleted file mode 100644 index 1e41c2b49adba..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html +++ /dev/null @@ -1,46 +0,0 @@ - -
-
- - -
-
- -
- -
-
- - -
- -
-
-
- -
-
-
-
diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php deleted file mode 100644 index bf8e1661a3f61..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php +++ /dev/null @@ -1,85 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - $this->jwtParser = $jwtParser; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - if ($this->config->isActive() === false) { - return []; - } - - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $cardinalJwt = (string)$payment->getAdditionalInformation('cardinalJWT'); - $jwtPayload = $this->jwtParser->execute($cardinalJwt); - $eciFlag = $jwtPayload['Payload']['Payment']['ExtendedData']['ECIFlag'] ?? ''; - $cavv = $jwtPayload['Payload']['Payment']['ExtendedData']['CAVV'] ?? ''; - $data = [ - 'transactionRequest' => [ - 'cardholderAuthentication' => [ - 'authenticationIndicator' => $eciFlag, - 'cardholderAuthenticationValue' => $cavv - ], - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php deleted file mode 100644 index 35287406a12d5..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php +++ /dev/null @@ -1,86 +0,0 @@ -resultFactory = $resultFactory; - $this->subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - if ($this->config->isActive() === false) { - return $this->createResult(true); - } - - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - $cavvResultCode = $transactionResponse['cavvResultCode'] ?? ''; - $isValid = $cavvResultCode === self::RESULT_CODE_SUCCESS; - $errorCodes = []; - $errorMessages = []; - - if (!$isValid) { - $errorCodes[] = $transactionResponse['cavvResultCode']; - $errorMessages[] = 'CAVV failed validation'; - } - - return $this->createResult($isValid, $errorMessages, $errorCodes); - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/LICENSE.txt b/app/code/Magento/AuthorizenetCardinal/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt b/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php b/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php deleted file mode 100644 index 8f09395874dce..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php +++ /dev/null @@ -1,48 +0,0 @@ -config = $config; - } - - /** - * @inheritdoc - */ - public function getConfig(): array - { - $config['cardinal'] = [ - 'isActiveFor' => [ - 'authorizenet' => $this->config->isActive() - ] - ]; - - return $config; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Config.php b/app/code/Magento/AuthorizenetCardinal/Model/Config.php deleted file mode 100644 index 798fb846c160e..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Model/Config.php +++ /dev/null @@ -1,52 +0,0 @@ -scopeConfig = $scopeConfig; - } - - /** - * If this config option set to false no AuthorizenetCardinal integration should be available - * - * @param int|null $storeId - * @return bool - */ - public function isActive(?int $storeId = null): bool - { - $enabled = $this->scopeConfig->isSetFlag( - 'three_d_secure/cardinal/enabled_authorizenet', - ScopeInterface::SCOPE_STORE, - $storeId - ); - - return $enabled; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php deleted file mode 100644 index aa5fbee327fe5..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php +++ /dev/null @@ -1,66 +0,0 @@ -config = $config; - } - - /** - * @inheritdoc - */ - public function execute(Observer $observer) - { - if ($this->config->isActive() === false) { - return; - } - - $data = $this->readDataArgument($observer); - $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); - if (!is_array($additionalData)) { - return; - } - - $paymentInfo = $this->readPaymentModelArgument($observer); - if (isset($additionalData[self::JWT_KEY])) { - $paymentInfo->setAdditionalInformation( - self::JWT_KEY, - $additionalData[self::JWT_KEY] - ); - } - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/README.md b/app/code/Magento/AuthorizenetCardinal/README.md deleted file mode 100644 index 0bd63130471bd..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Magento_AuthorizenetCardinal module - -Use the Magento_AuthorizenetCardinal module to enable 3D Secure 2.0 support for AuthorizenetAcceptjs payment integrations. - -## Structure - -`Gateway/` - the directory that contains payment gateway command interfaces and service classes. - -For information about typical file structure of a module in Magento 2, see [Module file structure](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/build/module-file-structure.html#module-file-structure). - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetCardinal module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetCardinal module. - -### Events - -This module observes the following events: - -- `payment_method_assign_data_authorizenet_acceptjs` event in the `Magento\AuthorizenetCardinal\Observer\DataAssignObserver` file. - -For information about an event in Magento 2, see [Events and observers](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). diff --git a/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php b/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php deleted file mode 100644 index 9f560507e34db..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php +++ /dev/null @@ -1,100 +0,0 @@ - 'foo' - ]; - - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(true); - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $dataObject = new DataObject([PaymentInterface::KEY_ADDITIONAL_DATA => $additionalInfo]); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, $dataObject] - ] - ); - $paymentInfoModel->expects($this->once()) - ->method('setAdditionalInformation') - ->with('cardinalJWT', 'foo'); - - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } - - /** - * Tests case when Cardinal JWT is absent. - */ - public function testDoesntSetDataWhenEmpty() - { - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(true); - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, new DataObject()] - ] - ); - $paymentInfoModel->expects($this->never()) - ->method('setAdditionalInformation'); - - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } - - /** - * Tests case when CardinalCommerce is disabled. - */ - public function testDoesntSetDataWhenDisabled() - { - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(false); - $observerContainer = $this->createMock(Observer::class); - $observerContainer->expects($this->never()) - ->method('getEvent'); - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/composer.json b/app/code/Magento/AuthorizenetCardinal/composer.json deleted file mode 100644 index 8b913f7056033..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/module-authorizenet-cardinal", - "description": "Provides a possibility to enable 3-D Secure 2.0 support for Authorize.Net Acceptjs.", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/module-authorizenet-acceptjs": "*", - "magento/framework": "*", - "magento/module-cardinal-commerce": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-quote": "*", - "magento/module-checkout": "*", - "magento/module-store": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetCardinal\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml deleted file mode 100644 index cf8ad28d26d0e..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - -
- - - - - Magento\Config\Model\Config\Source\Yesno - three_d_secure/cardinal/enabled_authorizenet - - - -
-
-
diff --git a/app/code/Magento/AuthorizenetCardinal/etc/config.xml b/app/code/Magento/AuthorizenetCardinal/etc/config.xml deleted file mode 100644 index d94bcdc479008..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/config.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - 0 - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/di.xml b/app/code/Magento/AuthorizenetCardinal/etc/di.xml deleted file mode 100644 index 568cb6f4cfc4c..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/di.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Magento\AuthorizenetCardinal\Gateway\Request\Authorize3DSecureBuilder - - - - - - - true - - - AuthorizenetAcceptjsTransactionValidator - Magento\AuthorizenetCardinal\Gateway\Validator\CavvResponseValidator - - - - - - Magento\AuthorizenetCardinal\Gateway\Validator\VirtualTransactionValidator - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/events.xml b/app/code/Magento/AuthorizenetCardinal/etc/events.xml deleted file mode 100644 index 5b0afbe684699..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/events.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml b/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml deleted file mode 100644 index 13c7a223e82d9..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - Magento\AuthorizenetCardinal\Model\Checkout\ConfigProvider - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/module.xml b/app/code/Magento/AuthorizenetCardinal/etc/module.xml deleted file mode 100644 index fdf8151311f43..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/module.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/registration.php b/app/code/Magento/AuthorizenetCardinal/registration.php deleted file mode 100644 index 7d663df3c3e3a..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -arrayManager = $arrayManager; - } - - /** - * Return additional data - * - * @param array $data - * @return array - * @throws GraphQlInputException - */ - public function getData(array $data): array - { - if (!isset($data[self::PATH_ADDITIONAL_DATA])) { - throw new GraphQlInputException( - __('Required parameter "authorizenet_acceptjs" for "payment_method" is missing.') - ); - } - - $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $data); - foreach ($additionalData as $key => $value) { - $additionalData[$this->convertSnakeCaseToCamelCase($key)] = $value; - unset($additionalData[$key]); - } - return $additionalData; - } - - /** - * Convert an input string from snake_case to camelCase. - * - * @param string $input - * @return string - */ - private function convertSnakeCaseToCamelCase($input): string - { - return lcfirst(str_replace('_', '', ucwords($input, '_'))); - } -} diff --git a/app/code/Magento/AuthorizenetGraphQl/README.md b/app/code/Magento/AuthorizenetGraphQl/README.md deleted file mode 100644 index 2af2b6a1024af..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Magento_AuthorizenetGraphQl module - -The Magento_AuthorizenetGraphQl module defines the data types needed to pass payment information data from the client to Magento. - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetGraphQl module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetGraphQl module. diff --git a/app/code/Magento/AuthorizenetGraphQl/composer.json b/app/code/Magento/AuthorizenetGraphQl/composer.json deleted file mode 100644 index 2b54049bab434..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/module-authorizenet-graph-ql", - "description": "N/A", - "type": "magento2-module", - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-quote-graph-ql": "*" - }, - "suggest": { - "magento/module-graph-ql": "*" - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetGraphQl\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml b/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml deleted file mode 100644 index e8ea45091c044..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Magento\AuthorizenetGraphQl\Model\AuthorizenetDataProvider - - - - diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/module.xml b/app/code/Magento/AuthorizenetGraphQl/etc/module.xml deleted file mode 100644 index 85a780a881975..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/module.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls b/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls deleted file mode 100644 index b6e817cc91d61..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright © Magento, Inc. All rights reserved. -# See COPYING.txt for license details. - -input PaymentMethodInput { - authorizenet_acceptjs: AuthorizenetInput @doc(description: "Defines the required attributes for Authorize.Net payments") -} - -input AuthorizenetInput { - opaque_data_descriptor: String! @doc(description: "Authorize.Net's description of the transaction request") - opaque_data_value: String! @doc(description: "The nonce returned by Authorize.Net") - cc_last_4: Int! @doc(description: "The last four digits of the credit or debit card") -} \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetGraphQl/registration.php b/app/code/Magento/AuthorizenetGraphQl/registration.php deleted file mode 100644 index 2e50f9fe92aaa..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/registration.php +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - <description value="CardinalCommerce config shouldn't be visible if the 3D secure is disabled for Authorize.Net."/> - <severity value="MINOR"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 1" stepKey="enableCardinalCommerce"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 0" stepKey="disableCardinalCommerce"/> - </after> - - <amOnPage url="{{AdminThreeDSecurePage.url}}" stepKey="openCurrencyOptionsPage" /> - <conditionalClick dependentSelector="{{AdminCardinalCommerceSection.enabled}}" visible="false" selector="{{AdminCardinalCommerceSection.head}}" stepKey="openCollapsibleBlock"/> - <see selector="{{AdminCardinalCommerceSection.environment}}" userInput="Production" stepKey="seeEnvironmentProduction"/> - <selectOption selector="{{AdminCardinalCommerceSection.enabled}}" userInput="0" stepKey="disableCardinalCommerceOption"/> - <dontSeeElement selector="{{AdminCardinalCommerceSection.environment}}" stepKey="dontSeeEnvironmentProduction"/> - </test> -</tests> diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml index 9ff952d04925d..3307582f6c465 100644 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml @@ -58,10 +58,6 @@ <magento_code>hosted_pro</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> </payment_method> - <payment_method name="authorizenet_directpost"> - <magento_code>authorizenet_directpost</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> <payment_method name="worldpay"> <magento_code>worldpay</magento_code> <signifyd_code>PAYMENT_CARD</signifyd_code> diff --git a/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php b/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php index 6b262b9e9f93c..8759c9048fe0f 100644 --- a/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php +++ b/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Translation\Test\Unit\App\Config\Type; -use Magento\Authorizenet\Helper\Backend\Data; use Magento\Framework\App\Cache\Type\Translate; use Magento\Framework\App\Config\ConfigSourceInterface; use Magento\Framework\Cache\FrontendInterface; diff --git a/composer.json b/composer.json index db34b0a9c2fd0..5f87ed040a929 100644 --- a/composer.json +++ b/composer.json @@ -110,10 +110,6 @@ "magento/module-analytics": "*", "magento/module-asynchronous-operations": "*", "magento/module-authorization": "*", - "magento/module-authorizenet": "*", - "magento/module-authorizenet-acceptjs": "*", - "magento/module-authorizenet-cardinal": "*", - "magento/module-authorizenet-graph-ql": "*", "magento/module-advanced-search": "*", "magento/module-backend": "*", "magento/module-backup": "*", diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php deleted file mode 100644 index 3c82d4eba740f..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http; - -use Magento\Framework\Math\Random; -use Magento\Framework\Stdlib\ArrayManager; -use Magento\Payment\Gateway\Http\ClientInterface; -use Magento\Payment\Gateway\Http\TransferInterface; - -/** - * A client for mocking communicate with the Authorize.net API - */ -class MockClient implements ClientInterface -{ - /** - * @var Random - */ - private $random; - - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @param Random $random - * @param ArrayManager $arrayManager - */ - public function __construct( - Random $random, - ArrayManager $arrayManager - ) { - $this->random = $random; - $this->arrayManager = $arrayManager; - } - - /** - * Places request to gateway. Returns result as ENV array - * - * @param TransferInterface $transferObject - * @return array - */ - public function placeRequest(TransferInterface $transferObject): array - { - $request = $transferObject->getBody(); - $nonce = $this->arrayManager->get('transactionRequest/payment/opaqueData/dataValue', $request); - $descriptor = $this->arrayManager->get('transactionRequest/payment/opaqueData/dataDescriptor', $request); - - $approve = true; - if ($nonce !== 'fake-nonce' || $descriptor !== 'COMMON.ACCEPT.INAPP.PAYMENT') { - $approve = false; - } - - return $this->createResponse($approve); - } - - /** - * Create mock response body - * - * @param bool $approve - * @return array - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function createResponse(bool $approve): array - { - return [ - 'transactionResponse' => [ - 'responseCode' => $approve ? '1' : '2', - 'authCode' => strtoupper($this->random->getRandomString(6)), - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => random_int(10000000000, 99999999999), - 'refTransId' => '', - 'transHash' => '', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => $approve ? $this->getApprovalMessage() : $this->getDeclineMessage(), - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - 'transHashSha2' => 'fake-hash', - 'SupplementalDataQualificationIndicator' => '0', - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.', - ], - ], - ], - ]; - } - - /** - * Provide approval message for response - * - * @return array - */ - private function getApprovalMessage(): array - { - return [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.', - ], - ]; - } - - /** - * Provide decline message for response - * - * @return array - */ - private function getDeclineMessage(): array - { - return [ - [ - 'code' => '2', - 'description' => 'This transaction has been declined.', - ], - ]; - } -} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php deleted file mode 100644 index b0e281e9faa5c..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator; - -use Magento\Payment\Gateway\Validator\AbstractValidator; -use Magento\Payment\Gateway\Validator\ResultInterface; - -/** - * Force validation of the transaction hash - */ -class TransactionHashValidator extends AbstractValidator -{ - /** - * Skip validation of transaction hash in mock response - * - * @param array $validationSubject - * @return ResultInterface - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function validate(array $validationSubject): ResultInterface - { - return $this->createResult(true); - } -} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml deleted file mode 100644 index 9f19743cfc205..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\AuthorizenetAcceptjs\Gateway\Http\Client" type="Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http\MockClient" /> - <preference for="Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator" type="Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator" /> -</config> diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml deleted file mode 100644 index 378b61946ef3a..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_TestModuleAuthorizenetAcceptjs" /> -</config> \ No newline at end of file diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php deleted file mode 100644 index 28c5861ed5fb8..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Component\ComponentRegistrar; - -$registrar = new ComponentRegistrar(); -if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleAuthorizenetAcceptjs') === null) { - ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleAuthorizenetAcceptjs', __DIR__); -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php deleted file mode 100644 index d6954c249f209..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php +++ /dev/null @@ -1,291 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\AuthorizenetAcceptjs\Customer; - -use Magento\Framework\Registry; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test setting payment method and placing order with AuthorizenetAcceptjs - */ -class SetPaymentMethodTest extends GraphQlAbstract -{ - private const VALID_DESCRIPTOR = 'COMMON.ACCEPT.INAPP.PAYMENT'; - private const VALID_NONCE = 'fake-nonce'; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var GetMaskedQuoteIdByReservedOrderId - */ - private $getMaskedQuoteIdByReservedOrderId; - - /** - * @var CollectionFactory - */ - private $orderCollectionFactory; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class); - $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class); - $this->registry = Bootstrap::getObjectManager()->get(Registry::class); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @magentoApiDataFixture Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store carriers/tablerate/active 1 - * @magentoConfigFixture default_store carriers/freeshipping/active 1 - * @param string $nonce - * @param string $descriptor - * @param bool $expectSuccess - * @dataProvider dataProviderTestPlaceOrder - */ - public function testPlaceOrder(string $nonce, string $descriptor, bool $expectSuccess) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $this->getSetPaymentMutation($maskedQuoteId, $descriptor, $nonce); - $setPaymentResponse = $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); - - $this->assertSetPaymentMethodResponse($setPaymentResponse, 'authorizenet_acceptjs'); - - $placeOrderQuery = $this->getPlaceOrderMutation($maskedQuoteId); - - if (!$expectSuccess) { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Transaction has been declined. Please try again later.'); - } - - $placeOrderResponse = $this->graphQlMutation($placeOrderQuery, [], '', $this->getHeaderMap()); - - $this->assertPlaceOrderResponse($placeOrderResponse, $reservedOrderId); - } - - public function dataProviderTestPlaceOrder(): array - { - return [ - [static::VALID_NONCE, static::VALID_DESCRIPTOR, true], - ['nonce', static::VALID_DESCRIPTOR, false], - [static::VALID_NONCE, 'descriptor', false], - ]; - } - - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login def_login - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key def_trans_key - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/public_client_key def_public_client_key - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key def_trans_signature_key - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @dataProvider dataProviderSetPaymentInvalidInput - * @param \Closure $getMutationClosure - * @param array $expectedMessages - * @expectedException \Exception - */ - public function testSetPaymentInvalidInput(\Closure $getMutationClosure, array $expectedMessages) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $getMutationClosure($maskedQuoteId); - - foreach ($expectedMessages as $expectedMessage) { - $this->expectExceptionMessage($expectedMessage); - } - $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); - } - - /** - * Data provider for testSetPaymentInvalidInput - * - * @return array - */ - public function dataProviderSetPaymentInvalidInput(): array - { - return [ - [ - function (string $maskedQuoteId) { - return $this->getInvalidSetPaymentMutation($maskedQuoteId); - }, - [ - 'Required parameter "authorizenet_acceptjs" for "payment_method" is missing.' - ] - ] - ]; - } - - /** - * Get setPaymentMethodOnCart missing additional data property - * - * @param string $maskedQuoteId - * @return string - */ - private function getInvalidSetPaymentMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void - { - self::assertArrayHasKey('placeOrder', $response); - self::assertArrayHasKey('order', $response['placeOrder']); - self::assertArrayHasKey('order_number', $response['placeOrder']['order']); - self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); - } - - private function assertSetPaymentMethodResponse(array $response, string $methodCode): void - { - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - } - - /** - * Create setPaymentMethodOnCart mutation - * - * @param string $maskedQuoteId - * @param string $descriptor - * @param string $nonce - * @return string - */ - private function getSetPaymentMutation(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Create placeOrder mutation - * - * @param string $maskedQuoteId - * @return string - */ - private function getPlaceOrderMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { - order { - order_number - } - } -} -QUERY; - } - - /** - * Get authorization headers for requests - * - * @param string $username - * @param string $password - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array - { - $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - return $headerMap; - } - - /** - * @inheritdoc - */ - public function tearDown() - { - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', true); - - $orderCollection = $this->orderCollectionFactory->create(); - foreach ($orderCollection as $order) { - $this->orderRepository->delete($order); - } - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', false); - - parent::tearDown(); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php deleted file mode 100644 index 322d984f5fa75..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\AuthorizenetAcceptjs\Guest; - -use Magento\Framework\Registry; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test setting payment method and placing order with AuthorizenetAcceptjs - */ -class SetPaymentMethodTest extends GraphQlAbstract -{ - private const VALID_DESCRIPTOR = 'COMMON.ACCEPT.INAPP.PAYMENT'; - private const VALID_NONCE = 'fake-nonce'; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var GetMaskedQuoteIdByReservedOrderId - */ - private $getMaskedQuoteIdByReservedOrderId; - - /** - * @var CollectionFactory - */ - private $orderCollectionFactory; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class); - $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class); - $this->registry = Bootstrap::getObjectManager()->get(Registry::class); - } - - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @magentoApiDataFixture Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store carriers/tablerate/active 1 - * @magentoConfigFixture default_store carriers/freeshipping/active 1 - * @param string $nonce - * @param string $descriptor - * @param bool $expectSuccess - * @dataProvider dataProviderTestPlaceOrder - */ - public function testPlaceOrder(string $nonce, string $descriptor, bool $expectSuccess) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $this->getSetPaymentMutation($maskedQuoteId, $descriptor, $nonce); - $setPaymentResponse = $this->graphQlMutation($setPaymentMutation); - - $this->assertSetPaymentMethodResponse($setPaymentResponse, 'authorizenet_acceptjs'); - - $placeOrderQuery = $this->getPlaceOrderMutation($maskedQuoteId); - - if (!$expectSuccess) { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Transaction has been declined. Please try again later.'); - } - - $placeOrderResponse = $this->graphQlMutation($placeOrderQuery); - - $this->assertPlaceOrderResponse($placeOrderResponse, $reservedOrderId); - } - - public function dataProviderTestPlaceOrder(): array - { - return [ - [static::VALID_NONCE, static::VALID_DESCRIPTOR, true], - ['nonce', static::VALID_DESCRIPTOR, false], - [static::VALID_NONCE, 'descriptor', false], - ]; - } - - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void - { - self::assertArrayHasKey('placeOrder', $response); - self::assertArrayHasKey('order', $response['placeOrder']); - self::assertArrayHasKey('order_number', $response['placeOrder']['order']); - self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); - } - - private function assertSetPaymentMethodResponse(array $response, string $methodCode): void - { - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - } - - /** - * Create setPaymentMethodOnCart mutation - * - * @param string $maskedQuoteId - * @param string $descriptor - * @param string $nonce - * @return string - */ - private function getSetPaymentMutation(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Create placeOrder mutation - * - * @param string $maskedQuoteId - * @return string - */ - private function getPlaceOrderMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { - order { - order_number - } - } -} -QUERY; - } - - /** - * @inheritdoc - */ - public function tearDown() - { - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', true); - - $orderCollection = $this->orderCollectionFactory->create(); - foreach ($orderCollection as $order) { - $this->orderRepository->delete($order); - } - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', false); - - parent::tearDown(); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php index 74a311932991e..cd96e3af6f012 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php @@ -34,7 +34,6 @@ class GetSelectedPaymentMethodTest extends GraphQlAbstract * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -60,7 +59,6 @@ public function testGetSelectedPaymentMethod() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -103,7 +101,6 @@ public function testGetSelectedPaymentMethodFromNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php @@ -152,7 +149,7 @@ private function getQuery(string $maskedQuoteId): string { return <<<QUERY { - cart(cart_id:"$maskedQuoteId") { + cart(cart_id:"$maskedQuoteId") { selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php index 88c57cf2fb282..11dc10beb72e2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php @@ -69,7 +69,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -236,7 +235,6 @@ public function testPlaceOrderWithOutOfStockProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -265,7 +263,6 @@ public function testPlaceOrderOfGuestCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php index 138d27ef26483..dbd6bb90f9a03 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php @@ -51,7 +51,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @param string $methodCode * @param string $methodTitle @@ -98,7 +97,6 @@ public function offlinePaymentMethodDataProvider(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethod() { @@ -110,7 +108,7 @@ public function testSetPurchaseOrderPaymentMethod() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" purchase_order_number: "{$poNumber}" @@ -155,7 +153,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index 74f8e3c2e37dd..fa3cbb5a9b457 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -251,7 +251,6 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -288,7 +287,7 @@ private function getQuery( payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php index 6b9e46fd153eb..1e64679b4abd8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php @@ -49,7 +49,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() { @@ -65,7 +64,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code @@ -97,7 +96,6 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @expectedException Exception * @expectedExceptionMessage Purchase order number is a required field. @@ -114,7 +112,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithoutPurchaseOrderNumbe payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code @@ -150,7 +148,7 @@ public function testSetDisabledPurchaseOrderPaymentMethodOnCart() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php index 60c3cc2e8b24e..f67638015988b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php @@ -17,7 +17,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Allow guest checkout option test + * Test for guest checkout. */ class AllowGuestCheckoutOptionTest extends GraphQlAbstract { @@ -169,7 +169,7 @@ public function testSetPaymentOnCartWithGuestCheckoutDisabled() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } @@ -254,9 +254,9 @@ public function testSetShippingMethodOnCartWithGuestCheckoutDisabled() $query = <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$carrierCode" method_code: "$methodCode" @@ -284,7 +284,6 @@ public function testSetShippingMethodOnCartWithGuestCheckoutDisabled() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php index 9536f078402a4..7619212942812 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php @@ -34,7 +34,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php @@ -60,7 +59,6 @@ public function testGetSelectedPaymentMethod() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ @@ -100,7 +98,6 @@ public function testGetSelectedPaymentMethodFromNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -126,7 +123,7 @@ private function getQuery(string $maskedQuoteId): string { return <<<QUERY { - cart(cart_id:"$maskedQuoteId") { + cart(cart_id:"$maskedQuoteId") { selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php index bf31d3c6fa3f4..4879ff39b709e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php @@ -61,7 +61,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -104,7 +103,6 @@ public function testPlaceOrderIfCartIdIsEmpty() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -257,7 +255,6 @@ public function testPlaceOrderWithOutOfStockProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php index 6c45227c47742..7a92ef8df201d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php @@ -43,7 +43,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @param string $methodCode * @param string $methodTitle @@ -89,7 +88,6 @@ public function offlinePaymentMethodDataProvider(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethod() { @@ -101,7 +99,7 @@ public function testSetPurchaseOrderPaymentMethod() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" purchase_order_number: "{$poNumber}" @@ -147,7 +145,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php index e38ccf78d420b..e506c7c784f3f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php @@ -155,7 +155,6 @@ public function testSetPaymentOnNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -230,7 +229,7 @@ private function getQuery( payment_method: { code: "$methodCode" } - }) { + }) { order { order_number } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 08c7bdd8dbc52..7c02589261a4a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -200,7 +200,6 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -250,7 +249,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php index 8b6317bd76c36..067c65fe85b6c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php @@ -41,7 +41,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() { @@ -57,7 +56,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code @@ -88,7 +87,6 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @expectedException Exception * @expectedExceptionMessage Purchase order number is a required field. @@ -105,7 +103,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithoutPurchaseOrderNumbe payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code @@ -140,7 +138,7 @@ public function testSetDisabledPurchaseOrderPaymentMethodOnCart() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/functional/credentials.xml.dist b/dev/tests/functional/credentials.xml.dist index 01e3a35be9a2d..80640ba592432 100644 --- a/dev/tests/functional/credentials.xml.dist +++ b/dev/tests/functional/credentials.xml.dist @@ -28,14 +28,6 @@ <field replace="carriers_dhl_password_eu" value="" /> <field replace="carriers_dhl_account_eu" value="" /> - <field replace="payment_authorizenet_login" value="" /> - <field replace="payment_authorizenet_trans_key" value="" /> - <field replace="payment_authorizenet_trans_md5" value="" /> - - <field replace="authorizenet_fraud_review_login" value="" /> - <field replace="authorizenet_fraud_review_trans_key" value="" /> - <field replace="authorizenet_fraud_review_md5" value="" /> - <field replace="braintree_enabled_fraud_merchant_account_id" value="" /> <field replace="braintree_enabled_fraud_merchant_id" value="" /> <field replace="braintree_enabled_fraud_public_key" value="" /> diff --git a/dev/tests/functional/etc/repository_replacer_payments.xml b/dev/tests/functional/etc/repository_replacer_payments.xml index a0ecca61eb372..baed7a68dd74c 100644 --- a/dev/tests/functional/etc/repository_replacer_payments.xml +++ b/dev/tests/functional/etc/repository_replacer_payments.xml @@ -15,13 +15,6 @@ </dataset> </repository> - <repository class="Magento\Authorizenet\Test\Repository\AuthorizenetSandboxCustomer"> - <dataset name="sandbox_fraud_hold_review"> - <field name="login_id" xsi:type="string">AUTHORIZENET_LOGIN_ID</field> - <field name="password" xsi:type="string">AUTHORIZENET_PASSWORD</field> - </dataset> - </repository> - <repository class="Magento\Signifyd\Test\Repository\SignifydAccount"> <dataset name="sandbox_default"> <field name="email" xsi:type="string">SIGNIFYD_EMAIL</field> diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php deleted file mode 100644 index 1b8b1b716ce5f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php +++ /dev/null @@ -1,166 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -use Magento\Authorizenet\Model\Directpost; -use Magento\Backend\App\Action\Context as BackendActionContext; -use Magento\Backend\Model\Session\Quote as SessionQuote; -use Magento\Backend\Model\UrlInterface; -use Magento\Framework\Json\Helper\Data as JsonHelper; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\Quote\Payment; -use Magento\Sales\Model\AdminOrder\Create as AdminOrderCreate; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\AbstractBackendController; -use PHPUnit\Framework\MockObject\MockObject; - -/** - * Verify AuthorizeNet Controller for PlaceOrder - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PlaceTest extends AbstractBackendController -{ - /** - * Test requestToAuthorizenetData returning - * @magentoAppArea adminhtml - */ - public function testExecuteAuthorizenetDataReturning() - { - $requestToAuthorizenetData = ['Authorizenet' => 'data']; - - $this->getRequest()->setParam('payment', ['method' => 'authorizenet_directpost']); - $this->getRequest()->setParam('controller', 'order_create'); - $orderCreateMock = $this->getOrderCreateMock($requestToAuthorizenetData); - $directpostMock = $this->getMockBuilder(Directpost::class) - ->setMethods(['getCode']) - ->disableOriginalConstructor() - ->getMock(); - $directpostMock->expects($this->once()) - ->method('getCode') - ->willReturn('authorizenet_directpost'); - $jsonHelper = $this->_objectManager->get(JsonHelper::class); - $objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) - ->setMethods(['create', 'get']) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('create') - ->with(Directpost::class) - ->willReturn($directpostMock); - $authorizenetSessionMock = $this->getMockBuilder(Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $urlMock = $this->getMockBuilder(UrlInterface::class) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - [AdminOrderCreate::class, $orderCreateMock], - [JsonHelper::class, $jsonHelper], - [Directpost\Session::class, $authorizenetSessionMock], - [UrlInterface::class, $urlMock], - ]); - - $context = $this->getObjectManager()->create( - BackendActionContext::class, - [ - 'objectManager' => $objectManagerMock - ] - ); - - $controller = $this->getObjectManager()->create( - PlaceTesting::class, - ['context' => $context] - ); - $controller->execute(); - $this->assertContains(json_encode($requestToAuthorizenetData), $this->getResponse()->getBody()); - } - - /** - * @param array $requestToAuthorizenetData - * @return AdminOrderCreate|MockObject - */ - private function getOrderCreateMock($requestToAuthorizenetData) - { - $methodInstanceMock = $this->getMockBuilder(Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $directpostRequestMock = $this->getMockBuilder(Directpost\Request::class) - ->setMethods(['getData']) - ->disableOriginalConstructor() - ->getMock(); - $directpostRequestMock->expects($this->once()) - ->method('getData') - ->willReturn($requestToAuthorizenetData); - $methodInstanceMock->expects($this->once()) - ->method('generateRequestFromOrder') - ->willReturn($directpostRequestMock); - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['getMethod', 'getMethodInstance']) - ->disableOriginalConstructor() - ->getMock(); - $paymentMock->expects($this->once()) - ->method('getMethod') - ->willReturn('authorizenet_directpost'); - $paymentMock->expects($this->once()) - ->method('getMethodInstance') - ->willReturn($methodInstanceMock); - $quoteMock = $this->getMockBuilder(Quote::class) - ->setMethods(['getPayment', 'getStoreId']) - ->disableOriginalConstructor() - ->getMock(); - $quoteMock->expects($this->any()) - ->method('getPayment') - ->willReturn($paymentMock); - $orderMock = $this->getMockBuilder(Order::class) - ->setMethods(['getPayment']) - ->disableOriginalConstructor() - ->getMock(); - $orderMock->expects($this->any()) - ->method('getPayment') - ->willReturn($paymentMock); - $sessionQuoteMock = $this->getMockBuilder(SessionQuote::class) - ->setMethods(['getOrder']) - ->disableOriginalConstructor() - ->getMock(); - $sessionQuoteMock->expects($this->once()) - ->method('getOrder') - ->willReturn($orderMock); - $orderCreateMock = $this->getMockBuilder(AdminOrderCreate::class) - ->setMethods(['getQuote', 'getSession', 'setIsValidate', 'importPostData', 'createOrder', 'setPaymentData']) - ->disableOriginalConstructor() - ->getMock(); - $orderCreateMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $orderCreateMock->expects($this->once()) - ->method('getSession') - ->willReturn($sessionQuoteMock); - $orderCreateMock->expects($this->once()) - ->method('setIsValidate') - ->willReturnSelf(); - $orderCreateMock->expects($this->once()) - ->method('importPostData') - ->willReturnSelf(); - $orderCreateMock->expects($this->once()) - ->method('createOrder') - ->willReturn($orderMock); - - return $orderCreateMock; - } - - /** - * @return ObjectManagerInterface - */ - private function getObjectManager(): ObjectManagerInterface - { - return Bootstrap::getObjectManager(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php deleted file mode 100644 index cb13f81381fde..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class PlaceTesting extended test class, used to substitute calls to parent methods - */ -class PlaceTesting extends Place -{ - /** - * {@inheritdoc} - * This method tested in Magento\Sales\Controller\Adminhtml\Order\CreateTest - */ - protected function _processActionData($action = null) - { - // - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php deleted file mode 100644 index 7ab55dc7fd928..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\TestFramework\TestCase\AbstractController; - -class BackendResponseTest extends AbstractController -{ - /** - * @var string - */ - private static $entryPoint = 'authorizenet/directpost_payment/backendresponse'; - - /** - * Checks a test case when request is processed from different to Authorize.net entry point. - */ - public function testUnauthorizedRequest() - { - $data = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_invoice_num' => '1', - 'x_amount' => 16, - 'x_trans_id' => '32iiw5ve', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_MD5_Hash' => 'h6a4s2h' - ]; - $this->getRequest()->setPostValue($data); - $this->dispatch(self::$entryPoint); - - self::assertEquals(302, $this->getResponse()->getHttpResponseCode()); - self::assertEmpty($this->getResponse()->getBody()); - } - - /** - * Checks a test case when request is successfully processed. - * - * @magentoConfigFixture current_store payment/authorizenet_directpost/trans_md5 n4v2c5n0d - * @magentoConfigFixture current_store payment/authorizenet_directpost/login merch1 - */ - public function testSuccess() - { - $data = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_invoice_num' => '1', - 'x_amount' => 16, - 'x_trans_id' => '32iiw5ve', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_MD5_Hash' => '0EAD2F65D3D879CCB0D1A6F24883AC92' - ]; - $this->getRequest()->setPostValue($data); - $this->dispatch(self::$entryPoint); - self::assertEquals(200, $this->getResponse()->getHttpResponseCode()); - self::assertContains('/sales/order/view', $this->getResponse()->getBody()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php deleted file mode 100644 index 043bae9f2417b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -/** - * Class ResponseTest - * - * @magentoAppArea frontend - */ -class ResponseTest extends \Magento\TestFramework\TestCase\AbstractController -{ - /** - * Tests the controller for declines - * - * @param int $invoiceNum - * @param string $hash - * @param string $errorMsg - * @param string[] $params - * - * @dataProvider responseActionAuthorizeCaptureDeclineDataProvider - */ - public function testResponseActionAuthorizeCaptureDecline($invoiceNum, $hash, $errorMsg, $params) - { - $controllerName = 'directpost_payment'; - $controllerModule = 'authorizenet'; - $controllerAction = 'response'; - $params['x_invoice_num'] = $invoiceNum; - $params['x_MD5_Hash'] = $hash; - $this->getRequest()->setControllerName( - $controllerName - )->setControllerModule( - $controllerModule - )->setActionName( - $controllerAction - )->setRouteName( - $controllerModule - )->setRequestUri("/{$controllerModule}/{$controllerName}/{$controllerAction}") - ->setParams($params); - - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - /** @var \Magento\Authorizenet\Controller\Directpost\Payment\Response */ - $controller = $objectManager->create(\Magento\Authorizenet\Controller\Directpost\Payment\Response::class); - - $response = $controller->execute(); - $output = $response->getLayout()->getOutput(); - - $expectedString = "{$controllerModule}/{$controllerName}/redirect/x_invoice_num/{$params['x_invoice_num']}/" - . "success/0/error_msg/{$errorMsg}/controller_action_name/{$controllerName}/"; - - $this->assertContains('window.location', $output); - $this->assertContains($expectedString, $output); - } - - /** - * Tests the controller for created blocks used for sending emails that should not affect layout response - * - * @param string $hash - * @param string[] $params - * - * @dataProvider responseActionAuthorizeCaptureSuccessDataProvider - */ - public function testBlockCreationAffectingResult($hash, $params) - { - $controllerName = 'directpost_payment'; - $controllerModule = 'authorizenet'; - $controllerAction = 'response'; - $params['x_invoice_num'] = 100000002; - $params['x_MD5_Hash'] = $hash; - $this->getRequest()->setControllerName( - $controllerName - )->setControllerModule( - $controllerModule - )->setActionName( - $controllerAction - )->setRouteName( - $controllerModule - )->setRequestUri("/{$controllerModule}/{$controllerName}/{$controllerAction}") - ->setParams($params); - - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $directpostMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->setMethods(['create']) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost::class) - ->willReturn($directpostMock); - $context = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\App\Action\Context::class, - [ - 'objectManager' => $objectManagerMock - ] - ); - - /** @var \Magento\Authorizenet\Controller\Directpost\Payment\Response $controller */ - $controller = $objectManager->create( - \Magento\Authorizenet\Controller\Directpost\Payment\Response::class, - [ - 'context' => $context - ] - ); - - // create one block for potential layout stack modification that should not affect response - /** @var \Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails $block */ - $block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails::class); - $block->setTemplate('Magento_Payment::order/view/info/fraud_details.phtml'); - - $response = $controller->execute(); - $output = $response->getLayout()->getOutput(); - - $expectedString = "{$controllerModule}/{$controllerName}/redirect/x_invoice_num/{$params['x_invoice_num']}/" - . "success/1/controller_action_name/{$controllerName}/"; - - $this->assertContains('window.location', $output); - $this->assertContains($expectedString, $output); - } - - /** - * @return array - */ - public function responseActionAuthorizeCaptureDeclineDataProvider() - { - $postArray = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'G0L0XR', - 'x_trans_id' => '60016479791', - 'x_method' => 'CC', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_first_name' => 'Name', - 'x_last_name' => 'Surname', - 'x_company' => null, - 'x_address' => 'Address', - 'x_city' => 'Austin', - 'x_state' => 'Texas', - 'x_zip' => '78753', - 'x_country' => 'US', - 'x_phone' => '5127242323', - 'x_fax' => null, - 'x_email' => 'customer@example.com', - 'x_description' => null, - 'x_type' => 'auth_capture', - 'x_cust_id' => null, - 'x_ship_to_first_name' => null, - 'x_ship_to_last_name' => null, - 'x_ship_to_company' => null, - 'x_ship_to_address' => null, - 'x_ship_to_city' => null, - 'x_ship_to_state' => null, - 'x_ship_to_zip' => null, - 'x_ship_to_country' => null, - 'x_amount' => 100.00, - 'x_tax' => 0.00, - 'x_duty' => 0.00, - 'x_freight' => 0.00, - 'x_tax_exempt' => false, - 'x_po_num' => null, - 'x_SHA2_Hash' => null, - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => 2, - 'x_test_request' => false, - 'controller_action_name' => 'directpost_payment', - 'is_secure' => null - ]; - return [ - 'error_hash' => [ - 'invoice_num' => '1231231', - 'x_MD5_Hash' => 'F9AE81A5DA36057D1312D71C904FCCF2', - 'error_msg' => 'The%20transaction%20was%20declined%20because%20the%20' - . 'response%20hash%20validation%20failed.', - 'post' => $postArray - ] - ]; - } - - /** - * @return array - */ - public function responseActionAuthorizeCaptureSuccessDataProvider() - { - $postArray = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'G0L0XR', - 'x_trans_id' => '60016479791', - 'x_method' => 'CC', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_first_name' => 'Name', - 'x_last_name' => 'Surname', - 'x_company' => null, - 'x_address' => 'Address', - 'x_city' => 'Austin', - 'x_state' => 'Texas', - 'x_zip' => '78753', - 'x_country' => 'US', - 'x_phone' => '5127242323', - 'x_fax' => null, - 'x_email' => 'integrationtest@magento.com', - 'x_description' => null, - 'x_type' => 'auth_capture', - 'x_cust_id' => null, - 'x_ship_to_first_name' => null, - 'x_ship_to_last_name' => null, - 'x_ship_to_company' => null, - 'x_ship_to_address' => null, - 'x_ship_to_city' => null, - 'x_ship_to_state' => null, - 'x_ship_to_zip' => null, - 'x_ship_to_country' => null, - 'x_amount' => 120.15, - 'x_tax' => 0.00, - 'x_duty' => 0.00, - 'x_freight' => 0.00, - 'x_tax_exempt' => false, - 'x_po_num' => null, - 'x_SHA2_Hash' => null, - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => 2, - 'x_test_request' => false, - 'controller_action_name' => 'directpost_payment', - 'is_secure' => null - ]; - return [ - 'success' => [ - 'x_MD5_Hash' => '35DCF749F7760193FB8254886E1D1522', - 'post' => $postArray - ], - ]; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php deleted file mode 100644 index f6ca9db995023..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Directpost; - -/** - * Class PaymentTest - */ -class PaymentTest extends \Magento\TestFramework\TestCase\AbstractController -{ - public function testResponseActionValidationFailed() - { - $this->getRequest()->setPostValue('controller_action_name', 'onepage'); - $this->dispatch('authorizenet/directpost_payment/response'); - // @codingStandardsIgnoreStart - $this->assertContains( - 'authorizenet/directpost_payment/redirect/success/0/error_msg/The%20transaction%20was' - . '%20declined%20because%20the%20response%20hash%20validation%20failed.', - // @codingStandardsIgnoreEnd - $this->getResponse()->getBody() - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php deleted file mode 100644 index 8f3cce9679953..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Model\Directpost; - -use Magento\Authorizenet\Model\Directpost; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -/** - * Class contains tests for Authorize.net Direct Post request handler - */ -class RequestTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Order - */ - private $order; - - /** - * @var Request - */ - private $request; - - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->order = $this->getOrder(); - $this->request = $this->objectManager->get(Request::class); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost\Request::setDataFromOrder - * @magentoDataFixture Magento/Authorizenet/_files/order.php - */ - public function testSetDataFromOrder() - { - $customerEmail = 'john.doe@example.com'; - $merchantEmail = 'merchant@example.com'; - - /** @var Directpost|MockObject $payment */ - $payment = $this->getMockBuilder(Directpost::class) - ->disableOriginalConstructor() - ->setMethods(['getConfigData']) - ->getMock(); - - $payment->expects(static::exactly(2)) - ->method('getConfigData') - ->willReturnMap([ - ['email_customer', null, $customerEmail], - ['merchant_email', null, $merchantEmail] - ]); - - $result = $this->request->setDataFromOrder($this->order, $payment); - - static::assertEquals('US', $result->getXCountry()); - static::assertEquals('UK', $result->getXShipToCountry()); - static::assertEquals($customerEmail, $result->getXEmailCustomer()); - static::assertEquals($merchantEmail, $result->getXMerchantEmail()); - } - - /** - * Get stored order - * @return Order - */ - private function getOrder() - { - /** @var FilterBuilder $filterBuilder */ - $filterBuilder = $this->objectManager->get(FilterBuilder::class); - $filters = [ - $filterBuilder->setField(OrderInterface::INCREMENT_ID) - ->setValue('100000002') - ->create() - ]; - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilters($filters) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php deleted file mode 100644 index ba4c4efd78f1b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php +++ /dev/null @@ -1,252 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Model; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Simplexml\Element; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class contains tests for Direct Post integration - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class DirectpostTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ZendClientFactory|MockObject - */ - private $httpClientFactory; - - /** - * @var Directpost - */ - private $directPost; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->directPost = $this->objectManager->create(Directpost::class, [ - 'httpClientFactory' => $this->httpClientFactory - ]); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::capture - * @magentoDataFixture Magento/Authorizenet/_files/order.php - */ - public function testCapture() - { - $amount = 120.15; - /** @var Payment $payment */ - $payment = $this->getPayment('100000002'); - $transactionId = '106235225'; - - /** @var ZendClient|MockObject $httpClient */ - $httpClient = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['setUri', 'setConfig', 'setParameterPost', 'setMethod', 'request']) - ->getMock(); - - $this->httpClientFactory->expects(static::once()) - ->method('create') - ->willReturn($httpClient); - - $response = $this->getMockBuilder('Zend_Http_Response') - ->disableOriginalConstructor() - ->setMethods(['getBody']) - ->getMock(); - $response->expects(static::once()) - ->method('getBody') - ->willReturn( - "1(~)1(~)1(~)This transaction has been approved.(~)AWZFTG(~)P(~){$transactionId}(~)100000002(~) - (~)120.15(~)CC(~)prior_auth_capture(~)(~)Anthony(~)Nealy(~)(~)Pearl St(~)Los Angeles(~)California - (~)10020(~)US(~)22-333-44(~)(~)customer@example.com(~)John(~)Doe(~) - (~)Bourne St(~)London(~)(~)DW23W(~)UK(~)0.00(~)(~){$amount}(~)(~) - (~)74B5D54ADFE98093A0FF6446(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)XXXX1111(~)Visa(~)(~)(~)(~)(~) - (~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)" - ); - - $httpClient->expects(static::once()) - ->method('request') - ->willReturn($response); - - $this->directPost->capture($payment, $amount); - - static::assertEquals($transactionId, $payment->getTransactionId()); - static::assertFalse($payment->getIsTransactionClosed()); - static::assertEquals('US', $payment->getOrder()->getBillingAddress()->getCountryId()); - static::assertEquals('UK', $payment->getOrder()->getShippingAddress()->getCountryId()); - } - - /** - * Verifies that order is placed in correct state according the action taken for a transaction that - * triggered one or more of the Advanced Fraud Detection Suite filters. - * - * @param string $filterAction - * @param string $orderId - * @param string $expectedOrderState - * - * @magentoConfigFixture current_store payment/authorizenet_directpost/trans_md5 TestHash - * @magentoConfigFixture current_store payment/authorizenet_directpost/login TestLogin - * @magentoDataFixture Magento/Authorizenet/_files/order.php - * @dataProvider fdsFilterActionDataProvider - */ - public function testProcessWithFdsFilterActionReportOnly($filterAction, $orderId, $expectedOrderState) - { - $responseBody = $this->getSuccessResponse($orderId); - $transactionService = $this->getTransactionService($filterAction); - $this->objectManager->addSharedInstance($transactionService, TransactionService::class); - - $this->directPost->process($responseBody); - - /** @var Payment $payment */ - $payment = $this->getPayment($orderId); - $this->objectManager->removeSharedInstance(TransactionService::class); - - static::assertEquals($expectedOrderState, $payment->getOrder()->getState()); - } - - /** - * @return array - */ - public function fdsFilterActionDataProvider() - { - return [ - [ - 'filter_action' => 'authAndHold', - 'order_id' => '100000003', - 'expected_order_state' => Order::STATE_PAYMENT_REVIEW - ], - [ - 'filter_action' => 'report', - 'order_id' => '100000004', - 'expected_order_state' => Order::STATE_COMPLETE - ], - ]; - } - - /** - * @param string $orderId - * @return array - */ - private function getSuccessResponse($orderId) - { - return [ - 'x_response_code' => '1', - 'x_response_reason_code' => '1', - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'YWO2E2', - 'x_trans_id' => '40004862720', - 'x_method' => 'CC', - 'x_card_type' => 'Visa', - 'x_account_number' => 'XXXX1111', - 'x_first_name' => 'John', - 'x_last_name' => 'Smith', - 'x_company' => 'CompanyName', - 'x_address' => 'Green str, 67', - 'x_city' => 'CityM', - 'x_state' => 'Alabama', - 'x_zip' => '93930', - 'x_country' => 'US', - 'x_phone' => '3468676', - 'x_fax' => '04040404', - 'x_email' => 'user_1@example.com', - 'x_invoice_num' => $orderId, - 'x_description' => '', - 'x_type' => 'auth_only', - 'x_cust_id' => '', - 'x_ship_to_first_name' => 'John', - 'x_ship_to_last_name' => 'Smith', - 'x_ship_to_company' => 'CompanyName', - 'x_ship_to_address' => 'Green str, 67', - 'x_ship_to_city' => 'CityM', - 'x_ship_to_state' => 'Alabama', - 'x_ship_to_zip' => '93930', - 'x_ship_to_country' => 'US', - 'x_amount' => '120.15', - 'x_tax' => '0.00', - 'x_duty' => '0.00', - 'x_freight' => '5.00', - 'x_tax_exempt' => 'FALSE', - 'x_po_num' => '', - 'x_MD5_Hash' => 'C1CC5AB9D6F0481E240AD74DFF624584', - 'x_SHA2_Hash' => '', - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => '2', - 'x_test_request' => 'false', - 'controller_action_name' => 'directpost_payment', - 'is_secure' => '1', - ]; - } - - /** - * Get order payment. - * - * @param string $orderId - * @return Payment - */ - private function getPayment($orderId) - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $orderId) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - $order = array_pop($orders); - return $order->getPayment(); - } - - /** - * Returns TransactionService mocked object with authorize predefined response. - * - * @param string $filterAction - * @return TransactionService|MockObject - */ - private function getTransactionService($filterAction) - { - $response = str_replace( - '{filterAction}', - $filterAction, - file_get_contents(__DIR__ . '/../_files/transaction_details.xml') - ); - - $transactionService = $this->getMockBuilder(TransactionService::class) - ->disableOriginalConstructor() - ->getMock(); - $transactionService->method('getTransactionDetails') - ->willReturn( - new Element($response) - ); - - return $transactionService; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php deleted file mode 100644 index b8d632f3a87af..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -$amount = 120.15; - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment - ->setMethod('authorizenet_directpost') - ->setAnetTransType('AUTH_ONLY') - ->setBaseAmountAuthorized($amount) - ->setPoNumber('10101200'); - -/** @var Address\ $billingAddress */ -$billingAddress = $objectManager->create(Address::class, [ - 'data' => [ - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'customer@example.com', - 'street' => 'Pearl St', - 'city' => 'Los Angeles', - 'region' => 'CA', - 'postcode' => '10020', - 'country_id' => 'US', - 'telephone' => '22-333-44', - 'address_type' => 'billing' - ] -]); - -$shippingAddress = $objectManager->create(Address::class, [ - 'data' => [ - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'customer@example.com', - 'street' => 'Bourne St', - 'city' => 'London', - 'postcode' => 'DW23W', - 'country_id' => 'UK', - 'telephone' => '22-333-44', - 'address_type' => 'billing' - ] -]); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setQuoteId(2) - ->setIncrementId('100000002') - ->setBaseGrandTotal($amount) - ->setBaseCurrencyCode('USD') - ->setBaseTaxAmount($amount) - ->setBaseShippingAmount($amount) - ->setCustomerEmail('customer@example.com') - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setPayment($payment); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -$clonedOrder = clone $order; -$clonedOrder->setIncrementId('100000003') - ->setId(null) - ->setBillingAddress($billingAddress->setId(null)) - ->setShippingAddress($shippingAddress->setId(null)) - ->setPayment($payment->setId(null)); -$orderRepository->save($clonedOrder); - -$clonedOrder = clone $order; -$clonedOrder->setIncrementId('100000004') - ->setId(null) - ->setBillingAddress($billingAddress->setId(null)) - ->setShippingAddress($shippingAddress->setId(null)) - ->setPayment($payment->setId(null)); -$orderRepository->save($clonedOrder); diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml deleted file mode 100644 index 98b9f258b0625..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<getTransactionDetailsResponse> - <messages> - <resultCode>Ok</resultCode> - <message> - <code>I00001</code> - <text>Successful.</text> - </message> - </messages> - <transaction> - <transId>40004862720</transId> - <submitTimeUTC>2017-06-12T13:33:10.1Z</submitTimeUTC> - <submitTimeLocal>2017-06-12T06:33:10.1</submitTimeLocal> - <transactionType>authOnlyTransaction</transactionType> - <transactionStatus>authorizedPendingCapture</transactionStatus> - <responseCode>1</responseCode> - <responseReasonCode>1</responseReasonCode> - <responseReasonDescription>Approval</responseReasonDescription> - <authCode>YWO2E2</authCode> - <AVSResponse>Y</AVSResponse> - <cardCodeResponse>P</cardCodeResponse> - <FDSFilterAction>{filterAction}</FDSFilterAction> - <FDSFilters> - <FDSFilter> - <name>Amount Filter</name> - <action>{filterAction}</action> - </FDSFilter> - </FDSFilters> - <order> - <invoiceNumber>100000002</invoiceNumber> - </order> - <authAmount>120.15</authAmount> - <settleAmount>120.15</settleAmount> - <shipping> - <amount>5.00</amount> - </shipping> - <taxExempt>false</taxExempt> - <payment> - <creditCard> - <cardNumber>XXXX1111</cardNumber> - <expirationDate>XXXX</expirationDate> - <cardType>Visa</cardType> - </creditCard> - </payment> - <customer> - <email>user_1@example.com</email> - </customer> - <billTo> - <firstName>John</firstName> - <lastName>Smith</lastName> - <company>CompanyName</company> - <address>Green str, 67</address> - <city>CityM</city> - <state>Alabama</state> - <zip>93930</zip> - <country>US</country> - <phoneNumber>3468676</phoneNumber> - <faxNumber>04040404</faxNumber> - </billTo> - <shipTo> - <firstName>John</firstName> - <lastName>Smith</lastName> - <company>CompanyName</company> - <address>Green str, 67</address> - <city>CityM</city> - <state>Alabama</state> - <zip>93930</zip> - <country>US</country> - </shipTo> - <recurringBilling>false</recurringBilling> - <customerIP>195.14.124.5</customerIP> - <product>Card Not Present</product> - <marketType>eCommerce</marketType> - </transaction> -</getTransactionDetailsResponse> diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php deleted file mode 100644 index 0b1e8196ef007..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\AuthorizenetAcceptjs\Gateway\Config; -use Magento\Sales\Api\InvoiceRepositoryInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\OrderRepository; -use Magento\Sales\Model\Service\InvoiceService; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -// phpcs:ignore Magento2.Security.IncludeFile.FoundIncludeFile -$order = include __DIR__ . '/../_files/full_order.php'; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var Payment $payment */ -$payment = $order->getPayment(); -$payment->setMethod(Config::METHOD); -$payment->setAuthorizationTransaction(false); -$payment->setParentTransactionId(4321); - -/** @var OrderRepository $orderRepo */ -$orderRepo = $objectManager->get(OrderRepository::class); -$orderRepo->save($order); - -/** @var InvoiceService $invoiceService */ -$invoiceService = $objectManager->get(InvoiceService::class); -$invoice = $invoiceService->prepareInvoice($order); -$invoice->setIncrementId('100000001'); -$invoice->register(); - -/** @var InvoiceRepositoryInterface $invoiceRepository */ -$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); -$invoice = $invoiceRepository->save($invoice); - - -/** @var \Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory */ -$creditmemoFactory = $objectManager->get(\Magento\Sales\Model\Order\CreditmemoFactory::class); -$creditmemo = $creditmemoFactory->createByInvoice($invoice, $invoice->getData()); -$creditmemo->setOrder($order); -$creditmemo->setState(Magento\Sales\Model\Order\Creditmemo::STATE_OPEN); -$creditmemo->setIncrementId('100000001'); - -/** @var \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository */ -$creditmemoRepository = $objectManager->get(\Magento\Sales\Api\CreditmemoRepositoryInterface::class); -$creditmemoRepository->save($creditmemo); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); -$transactionCapture = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(4321) - ->build(Transaction::TYPE_CAPTURE); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); -$transactionRepository->save($transactionCapture); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php deleted file mode 100644 index 1a2cb2532fe52..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\ObjectManager; - -$objectManager = ObjectManager::getInstance(); - -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); -$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000001') - ->create(); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$items = $orderRepository->getList($searchCriteria) - ->getItems(); - -foreach ($items as $item) { - $orderRepository->delete($item); -} - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php deleted file mode 100644 index b1d0521c9c610..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item as OrderItem; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping'); - -/** @var OrderItem $orderItem */ -$orderItem = $objectManager->create(OrderItem::class); -$orderItem->setProductId($product->getId()) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType('simple'); - -require __DIR__ . '/payment.php'; - -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setSubtotal($product->getPrice() * 2) - ->setBaseSubtotal($product->getPrice() * 2) - ->setCustomerEmail('admin@example.com') - ->setCustomerIsGuest(true) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setStoreId( - $objectManager->get(StoreManagerInterface::class)->getStore() - ->getId() - ) - ->addItem($orderItem) - ->setPayment($payment); - -$payment->setParentTransactionId(1234); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); - -$transactionAuthorize->setAdditionalInformation('real_transaction_id', '1234'); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php deleted file mode 100644 index 5a65a1fc0d0c7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -require __DIR__ . '/order_captured_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php deleted file mode 100644 index 9bfc863df7de5..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item as OrderItem; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping'); - -/** @var OrderItem $orderItem */ -$orderItem = $objectManager->create(OrderItem::class); -$orderItem->setProductId($product->getId()) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType('simple'); - -require __DIR__ . '/payment.php'; - -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setSubtotal($product->getPrice() * 2) - ->setBaseSubtotal($product->getPrice() * 2) - ->setCustomerEmail('admin@example.com') - ->setCustomerIsGuest(true) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setStoreId( - $objectManager->get(StoreManagerInterface::class)->getStore() - ->getId() - ) - ->addItem($orderItem) - ->setPayment($payment); - -$payment->setParentTransactionId(4321); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); -$transactionCapture = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(4321) - ->build(Transaction::TYPE_CAPTURE); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); -$transactionRepository->save($transactionCapture); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php deleted file mode 100644 index a2da0b639e98d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\ObjectManager; - -$objectManager = ObjectManager::getInstance(); - -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); -$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000002') - ->create(); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$items = $orderRepository->getList($searchCriteria) - ->getItems(); - -foreach ($items as $item) { - $orderRepository->delete($item); -} - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php deleted file mode 100644 index 5b15e356a7d8d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\AuthorizenetAcceptjs\Gateway\Config; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setMethod(Config::METHOD); -$payment->setAuthorizationTransaction(true); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php deleted file mode 100644 index f1458a19012f3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\Area; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Payment\Gateway\Data\PaymentDataObjectFactory; -use Magento\Quote\Model\Quote\PaymentFactory; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Zend_Http_Response; - -abstract class AbstractTest extends TestCase -{ - /** - * @var ObjectManager - */ - protected $objectManager; - - /** - * @var ZendClient|MockObject|InvocationMocker - */ - protected $clientMock; - - /** - * @var PaymentFactory - */ - protected $paymentFactory; - - /** - * @var Zend_Http_Response - */ - protected $responseMock; - - /** - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - $this->objectManager = Bootstrap::getObjectManager(); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - /** @var PaymentDataObjectFactory $paymentFactory */ - $this->paymentFactory = $this->objectManager->get(PaymentDataObjectFactory::class); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - parent::tearDown(); - } - - protected function getOrderWithIncrementId(string $incrementId): Order - { - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $searchCriteria = $this->objectManager->get(SearchCriteriaBuilder::class) - ->addFilter('increment_id', $incrementId) - ->create(); - /** @var Order $order */ - $order = current( - $orderRepository->getList($searchCriteria) - ->getItems() - ); - - return $order; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php deleted file mode 100644 index 394d9de6684c4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class AcceptFdsCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testAcceptFdsCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('accept_fds'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/accept_fds.php'; - $response = include __DIR__ . '/../../_files/response/generic_success.php'; - - $this->clientMock->expects($this->once()) - ->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->expects($this->once()) - ->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php deleted file mode 100644 index 9affd80be0600..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -class AuthorizeCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - */ - public function testAuthorizeCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../_files/full_order.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/authorize.php'; - $response = include __DIR__ . '/../../_files/response/authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php deleted file mode 100644 index aa606a50ae67a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class CancelCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - * @dataProvider aliasesProvider - */ - public function testCancelCommand(string $commandName) - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get($commandName); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/void.php'; - $response = include __DIR__ . '/../../_files/response/void.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - - /** @var Payment $payment */ - - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertArrayNotHasKey('real_transaction_id', $payment->getTransactionAdditionalInfo()); - } - - public function aliasesProvider() - { - return [ - ['cancel'], - ['deny_payment'] - ]; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php deleted file mode 100644 index 1651dfc7db3d9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class FetchTransactionInfoCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionApproved() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_authorized.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertTrue($payment->getIsTransactionApproved()); - $this->assertFalse($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionVoided() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_voided.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertFalse($payment->getIsTransactionApproved()); - $this->assertTrue($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionDenied() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_voided.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertFalse($payment->getIsTransactionApproved()); - $this->assertTrue($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionPending() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_fds_pending.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertNull($payment->getIsTransactionApproved()); - $this->assertNull($payment->getIsTransactionDenied()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php deleted file mode 100644 index 0206ecd6b876b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Api\Data\CreditmemoInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\ResourceModel\Order\Creditmemo\CollectionFactory as CreditmemoCollectionFactory; - -class RefundSettledCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php - */ - public function testRefundSettledCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('refund_settled'); - - $order = $this->getOrderWithIncrementId('100000001'); - $payment = $order->getPayment(); - $payment->setCreditmemo($this->getCreditmemo($order)); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/refund.php'; - $response = include __DIR__ . '/../../_files/response/refund.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertSame('5678', $payment->getTransactionId()); - } - - /** - * Retrieve creditmemo from order. - * - * @param Order $order - * @return CreditmemoInterface - */ - private function getCreditmemo(Order $order): CreditmemoInterface - { - /** @var \Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection $creditMemoCollection */ - $creditMemoCollection = $this->objectManager->create(CreditmemoCollectionFactory::class)->create(); - - /** @var CreditmemoInterface $creditMemo */ - $creditMemo = $creditMemoCollection - ->setOrderFilter($order) - ->setPageSize(1) - ->getFirstItem(); - - return $creditMemo; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php deleted file mode 100644 index 7ae03d36cb752..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -class SaleCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - */ - public function testSaleCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('sale'); - - $order = include __DIR__ . '/../../_files/full_order.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/sale.php'; - $response = include __DIR__ . '/../../_files/response/sale.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertFalse($payment->getData('is_transaction_closed')); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php deleted file mode 100644 index bb0a259b165bf..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class SettleCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testRefundSettledCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('settle'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/settle.php'; - $response = include __DIR__ . '/../../_files/response/settle.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $this->assertTrue($payment->getShouldCloseParentTransaction()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php deleted file mode 100644 index d81cffc413b59..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; - -class TransactionDetailsCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_captured.php - */ - public function testTransactionDetails() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('get_transaction_details'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_settled_capture.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $resultData = $result->get(); - - $this->assertEquals($response, $resultData); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php deleted file mode 100644 index f74f8542bfdc3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class VoidCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testVoidCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('void'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/void.php'; - $response = include __DIR__ . '/../../_files/response/void.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - - /** @var Payment $payment */ - - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertEquals('1234', $payment->getTransactionAdditionalInfo()['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php deleted file mode 100644 index a37f927274242..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway; - -use Magento\Framework\Config\Data; -use Magento\Payment\Model\Method\Adapter; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; - -class ConfigTest extends TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - } - - public function testVerifyConfiguration() - { - /** @var Adapter $paymentAdapter */ - $paymentAdapter = $this->objectManager->get('AuthorizenetAcceptjsFacade'); - - $this->assertEquals('authorizenet_acceptjs', $paymentAdapter->getCode()); - $this->assertTrue($paymentAdapter->canAuthorize()); - $this->assertTrue($paymentAdapter->canCapture()); - $this->assertFalse($paymentAdapter->canCapturePartial()); - $this->assertTrue($paymentAdapter->canRefund()); - $this->assertTrue($paymentAdapter->canUseCheckout()); - $this->assertTrue($paymentAdapter->canVoid()); - $this->assertTrue($paymentAdapter->canUseInternal()); - $this->assertTrue($paymentAdapter->canEdit()); - $this->assertTrue($paymentAdapter->canFetchTransactionInfo()); - - /** @var Data $configReader */ - $configReader = $this->objectManager->get('Magento\Payment\Model\Config\Data'); - $value = $configReader->get('methods/authorizenet_acceptjs/allow_multiple_address'); - - $this->assertSame('0', $value); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php deleted file mode 100644 index 1bee2e95cef7d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Response; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; - -/** - * Test for Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler class. - */ -class CloseTransactionHandlerTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - * - * @return void - */ - public function testTransactionCloseStatus(): void - { - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('settle'); - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/settle.php'; - $response = include __DIR__ . '/../../_files/response/settle.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute(['payment' => $paymentDO]); - - $this->assertFalse($payment->getIsTransactionClosed()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php deleted file mode 100644 index d843de1c2cac0..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'updateHeldTransactionRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => '1234', - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php deleted file mode 100644 index 16debdb2ef820..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php deleted file mode 100644 index 5ed331d076f66..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'refundTransaction', - 'amount' => '100.00', - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => '1111', - 'expirationDate' => 'XXXX' - ] - ], - 'refTransId' => '4321', - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => '1', - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1' - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php deleted file mode 100644 index 4514acbcb6646..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php deleted file mode 100644 index b4fa88cc1e5a9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'priorAuthCaptureTransaction', - 'refTransId' => '1234', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'priorAuthCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php deleted file mode 100644 index 110333866766e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'getTransactionDetailsRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'transId' => '4321' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php deleted file mode 100644 index c3ffdedba6851..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'getTransactionDetailsRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'transId' => '1234' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php deleted file mode 100644 index a1d3dade74ff1..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' =>[ - 'transactionType' => 'voidTransaction', - 'refTransId' => '1234', - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php deleted file mode 100644 index 420d0f55cf34e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Attribute\Source\Status; -use Magento\Catalog\Model\Product\Type; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\TestFramework\Helper\Bootstrap; - -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var $product Product */ -$product = $objectManager->create(Product::class); -$product->isObjectNew(true); -$product->setTypeId(Type::TYPE_SIMPLE) - ->setId(1) - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product') - ->setSku('simple') - ->setPrice(10) - ->setWeight(1) - ->setShortDescription('Short description') - ->setTaxClassId(0) - ->setDescription('Description with <b>html tag</b>') - ->setMetaTitle('meta title') - ->setMetaKeyword('meta keyword') - ->setMetaDescription('meta description') - ->setVisibility(Visibility::VISIBILITY_BOTH) - ->setStatus(Status::STATUS_ENABLED) - ->setStockData( - [ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ] - )->setCanSaveCustomOptions(true) - ->setHasOptions(false); - -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = $objectManager->create(ProductRepositoryInterface::class); -$productRepository->save($product); - - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping') - ->setStreet(['6161 West Centinela Avenue']) - ->setFirstname('John') - ->setLastname('Doe') - ->setShippingMethod('flatrate_flatrate'); - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setAdditionalInformation('ccLast4', '1111'); -$payment->setAdditionalInformation('opaqueDataDescriptor', 'mydescriptor'); -$payment->setAdditionalInformation('opaqueDataValue', 'myvalue'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -/** @var Item $orderItem */ -$orderItem2 = $objectManager->create(Item::class); -$orderItem2->setProductId($product->getId()) - ->setSku('simple2') - ->setName('Simple product') - ->setPrice(100) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000001') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt(date('Y-m-d 00:00:55')) - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setBaseShippingAmount(10) - ->setStoreId(1) - ->addItem($orderItem1) - ->addItem($orderItem2) - ->setQuoteId(1) - ->setPayment($payment); - -return $order; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php deleted file mode 100644 index f80495137ca29..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php deleted file mode 100644 index ea7662e319376..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php deleted file mode 100644 index 536f51d659ad8..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => '', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '', - 'transId' => '5678', - 'refTransID' => '4321', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'transHashSha2' => '78BD31BA5BCDF3C3FA3C8373D8DF80EF07FC7E02C3545FCF18A408E2F76ED4F20D' - . 'FF007221374B576FDD1BFD953B3E5CF37249CEC4C135EEF975F7B478D8452C', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php deleted file mode 100644 index 74a80110adece..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php deleted file mode 100644 index 5e54c30198741..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => '', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '', - 'transId' => '1234', - 'refTransID' => '1234', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'transHashSha2' => '1B22AB4E4DF750CF2E0D1944BB6903537C145545C7313C87B6FD4A6384' - . '709EA2609CE9A9788C128F2F2EAEEE474F6010418904648C6D000BE3AF7BCD98A5AD8F', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php deleted file mode 100644 index 80fd24a5c601a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'authorizedPendingCapture' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php deleted file mode 100644 index 24c9353e4088a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'declined' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php deleted file mode 100644 index de045f30ab22e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'FDSPendingReview' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php deleted file mode 100644 index 5df2f03a943a6..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '4321', - 'transactionType' => 'captureOnlyTransaction', - 'transactionStatus' => 'settledSuccessfully' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php deleted file mode 100644 index 7ee735cd8cf36..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'void' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php deleted file mode 100644 index eb71de4dd9667..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transactionResponse' => [ - 'responseCode' => '1', - 'messages' => [ - 'message' => [ - [ - 'code' => 1 - ] - ] - ], - 'transHashSha2' => '1B22AB4E4DF750CF2E0D1944BB6903537C145545C7313C87B6FD4A6384709E' - . 'A2609CE9A9788C128F2F2EAEEE474F6010418904648C6D000BE3AF7BCD98A5AD8F', - 'transId' => '1234' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php deleted file mode 100644 index ceab22403d987..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'cardholderAuthentication' => [ - 'authenticationIndicator' => '05', - 'cardholderAuthenticationValue' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php deleted file mode 100644 index f96facb19b3b5..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'cardholderAuthentication' => [ - 'authenticationIndicator' => '05', - 'cardholderAuthenticationValue' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php deleted file mode 100644 index 4f50b502e8554..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; - -$order = include __DIR__ . '/../../AuthorizenetAcceptjs/_files/full_order.php'; - -$objectManager = Bootstrap::getObjectManager(); -$cardinalJWT = include __DIR__ . '/response/cardinal_jwt.php'; - -/** @var Payment $payment */ -$payment = $order->getPayment(); -$payment->setAdditionalInformation('cardinalJWT', $cardinalJWT); - -return $order; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php deleted file mode 100644 index c17b14721b157..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php deleted file mode 100644 index 80f42524897f6..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\CardinalCommerce\Model\Config; -use Magento\CardinalCommerce\Model\JwtManagement; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -/** @var JwtManagement $jwtManagment */ -$jwtManagment = $objectManager->get(JwtManagement::class); -/** @var Config $config */ -$config = $objectManager->get(Config::class); -$currentDate = new \DateTime('now', new \DateTimeZone('UTC')); -$response = [ - 'iss' => 'some_api_identifier', - 'iat' => 1559855656, - 'exp' => $currentDate->getTimestamp() + 3600, - 'jti' => '0d695df5-ca06-4f7d-b150-ff169510f6d2', - 'ConsumerSessionId' => '0_9e6a4084-2191-4fd7-9631-19f576375e0a', - 'ReferenceId' => '0_9e6a4084-2191-4fd7-9631-19f576375e0a', - 'aud' => '52efb9cc-843c-4ee9-a38c-107943be6b03', - 'Payload' => [ - 'Validated' => true, - 'Payment' => [ - 'Type' => 'CCA', - 'ProcessorTransactionId' => '4l7xg1WA7CS0YwgPgNZ0', - 'ExtendedData' => [ - 'CAVV' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - 'ECIFlag' => '05', - 'XID' => 'NGw3eGcxV0E3Q1MwWXdnUGdOWjA=', - 'Enrolled' => 'Y', - 'PAResStatus' => 'Y', - 'SignatureVerification' => 'Y', - ], - ], - 'ActionCode' => 'SUCCESS', - 'ErrorNumber' => 0, - 'ErrorDescription' => 'Success', - ], -]; -$cardinalJWT = $jwtManagment->encode($response, $config->getApiKey()); - -return $cardinalJWT; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php deleted file mode 100644 index 561734b089213..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetCardinal\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -/** - * Tests "Authorize" command for Authorize.net payment requests with 3D-Secure. - */ -class AuthorizeCommandTest extends AbstractTest -{ - /** - * Tests Authorize command with enabled 3D secure and valid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWith3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../Fixture/expected_request/authorize.php'; - $response = include __DIR__ . '/../../Fixture/response/authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('P', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } - - /** - * Tests Authorize command with enabled 3D secure and invalid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWithInvalidJwt() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/full_order.php'; - $payment = $order->getPayment(); - $payment->setAdditionalInformation('cardinalJWT', 'Invalid JWT'); - - $paymentDO = $this->paymentFactory->create($payment); - - $this->expectException(LocalizedException::class); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - } - - /** - * Tests Authorize command with disabled 3D secure. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 0 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWithDisabled3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/expected_request/authorize.php'; - $response = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/response/authorize.php'; - $response['transactionResponse']['cavvResultCode'] = '0'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '0', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php deleted file mode 100644 index c22e1fceaa84f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetCardinal\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -/** - * Tests "Sale" command for Authorize.net payment requests with 3D-Secure. - */ -class SaleCommandTest extends AbstractTest -{ - /** - * Tests Sale command with enabled 3D secure and valid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testSaleCommandWith3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('sale'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../Fixture/expected_request/sale.php'; - $response = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/response/sale.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php deleted file mode 100644 index fa3869d49bd2a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Zend_Http_Response; - -/** - * Tests end to end Place Order process for customer via authorizeNet - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class PlaceOrderWithAuthorizeNetTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - /** @var ZendClient|MockObject|InvocationMocker */ - private $clientMock; - - /** @var CustomerTokenServiceInterface */ - private $customerTokenService; - - /** @var Zend_Http_Response */ - protected $responseMock; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Sales/_files/default_rollback.php - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - */ - public function testDispatchToPlaceOrderWithRegisteredCustomer(): void - { - $paymentMethod = 'authorizenet_acceptjs'; - $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$cartId" - payment_method: { - code: "$paymentMethod" - authorizenet_acceptjs: - {opaque_data_descriptor: "mydescriptor", - opaque_data_value: "myvalue", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } - placeOrder(input: {cart_id: "$cartId"}) { - order { - order_number - } - } -} -QUERY; - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $requestHeaders = [ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . $customerToken - ]; - // phpcs:ignore Magento2.Security.IncludeFile - $expectedRequest = include __DIR__ . '/../../../_files/request_authorize_customer.php'; - // phpcs:ignore Magento2.Security.IncludeFile - $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); - - $response = $this->graphQlRequest->send($query, [], '', $requestHeaders); - $responseData = $this->jsonSerializer->unserialize($response->getContent()); - - $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); - $this->assertTrue( - isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) - ); - $this->assertEquals( - $paymentMethod, - $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] - ); - - $this->assertTrue( - isset($responseData['data']['placeOrder']['order']['order_number']) - ); - - $this->assertEquals( - 'test_quote', - $responseData['data']['placeOrder']['order']['order_number'] - ); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php deleted file mode 100644 index b82469c61d288..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\TestCase; - -/** - * Tests SetPaymentMethod mutation for customer via authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase -{ - /** @var \Magento\Framework\ObjectManagerInterface */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var CustomerTokenServiceInterface */ - private $customerTokenService; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - */ - public function testDispatchToSetPaymentMethodWithAuthorizenet(): void - { - $methodCode = 'authorizenet_acceptjs'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$maskedQuoteId" - payment_method: { - code: "$methodCode" - authorizenet_acceptjs: - {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", - opaque_data_value: "abx", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - - $requestHeaders = [ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . $customerToken - ]; - - $response = $this->graphQlRequest->send($query, [], '', $requestHeaders); - - $output = $this->jsonSerializer->unserialize($response->getContent()); - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); - $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; - $this->assertEquals($methodCode, $selectedPaymentMethod['code']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php deleted file mode 100644 index 4946448f91ccc..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Zend_Http_Response; - -/** - * Tests end to end Place Order process for non logged in customer using authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class PlaceOrderWithAuthorizeNetTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - /** @var ZendClient|MockObject|InvocationMocker */ - private $clientMock; - - /** @var Zend_Http_Response */ - protected $responseMock; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Sales/_files/default_rollback.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - */ - public function testDispatchToPlaceAnOrderWithAuthorizenet(): void - { - $paymentMethod = 'authorizenet_acceptjs'; - $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$cartId" - payment_method: { - code: "$paymentMethod" - authorizenet_acceptjs: - {opaque_data_descriptor: "mydescriptor", - opaque_data_value: "myvalue", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } - placeOrder(input: {cart_id: "$cartId"}) { - order { - order_number - } - } -} -QUERY; - - // phpcs:ignore Magento2.Security.IncludeFile - $expectedRequest = include __DIR__ . '/../../../_files/request_authorize.php'; - // phpcs:ignore Magento2.Security.IncludeFile - $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); - - $response = $this->graphQlRequest->send($query); - $responseData = $this->jsonSerializer->unserialize($response->getContent()); - - $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); - $this->assertTrue( - isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) - ); - $this->assertEquals( - $paymentMethod, - $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] - ); - - $this->assertTrue( - isset($responseData['data']['placeOrder']['order']['order_number']) - ); - - $this->assertEquals( - 'test_quote', - $responseData['data']['placeOrder']['order']['order_number'] - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php deleted file mode 100644 index ff526a491b5d7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; - -/** - * Tests SetPaymentMethod mutation for guest via authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - */ - public function testDispatchToSetPaymentMethodWithAuthorizenet(): void - { - $methodCode = 'authorizenet_acceptjs'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$maskedQuoteId" - payment_method: { - code: "$methodCode" - authorizenet_acceptjs: - {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", - opaque_data_value: "abx", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - - $response = $this->graphQlRequest->send($query); - $output = $this->jsonSerializer->unserialize($response->getContent()); - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); - $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; - $this->assertEquals($methodCode, $selectedPaymentMethod['code']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php deleted file mode 100644 index 3646e864ab49e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var CartRepositoryInterface $cartRepository */ -$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); - -$product = $productRepository->get('simple_product'); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$quote->addProduct($product, 4); -$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php deleted file mode 100644 index c91c8081736c4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => 'test_quote', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => null, - 'email' => 'guest@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php deleted file mode 100644 index 0ef173009bd6c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => 'test_quote', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => '1', - 'email' => 'customer@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php deleted file mode 100644 index f80495137ca29..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php deleted file mode 100644 index 4f045c550cd37..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\AddressInterfaceFactory; -use Magento\Quote\Api\BillingAddressManagementInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; - -use Magento\TestFramework\Helper\Bootstrap; - -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var AddressInterfaceFactory $quoteAddressFactory */ -$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var BillingAddressManagementInterface $billingAddressManagement */ -$billingAddressManagement = Bootstrap::getObjectManager()->get(BillingAddressManagementInterface::class); - -$quoteAddressData = [ - AddressInterface::KEY_TELEPHONE => 11111111, - AddressInterface::KEY_POSTCODE => 11111, - AddressInterface::KEY_COUNTRY_ID => 'US', - AddressInterface::KEY_CITY => 'Los Angeles', - AddressInterface::KEY_COMPANY => '', - AddressInterface::KEY_STREET => 'street', - AddressInterface::KEY_LASTNAME => 'lastname', - AddressInterface::KEY_FIRSTNAME => 'firstname', - AddressInterface::KEY_REGION_ID => 12, -]; -$quoteAddress = $quoteAddressFactory->create(); -$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$billingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php deleted file mode 100644 index 8837a3cb2397c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\AddressInterfaceFactory; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\Quote\Model\ShippingAddressManagementInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var AddressInterfaceFactory $quoteAddressFactory */ -$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var ShippingAddressManagementInterface $shippingAddressManagement */ -$shippingAddressManagement = Bootstrap::getObjectManager()->get(ShippingAddressManagementInterface::class); - -$quoteAddressData = [ - AddressInterface::KEY_TELEPHONE => 3468676, - AddressInterface::KEY_POSTCODE => '11111', - AddressInterface::KEY_COUNTRY_ID => 'US', - AddressInterface::KEY_CITY => 'Los Angeles', - AddressInterface::KEY_COMPANY => '', - AddressInterface::KEY_STREET => '6161 West Centinela Avenue', - AddressInterface::KEY_LASTNAME => 'Doe', - AddressInterface::KEY_FIRSTNAME => 'John', - AddressInterface::KEY_REGION_ID => 12, -]; -$quoteAddress = $quoteAddressFactory->create(); -$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$shippingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php deleted file mode 100644 index 50ec950853711..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Api\Data\ProductInterfaceFactory; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product\Attribute\Source\Status; -use Magento\Catalog\Model\Product\Type; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Framework\Api\DataObjectHelper; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -/** @var ProductInterfaceFactory $productFactory */ -$productFactory = $objectManager->get(ProductInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = $objectManager->get(ProductRepositoryInterface::class); - -$product = $productFactory->create(); -$productData = [ - ProductInterface::TYPE_ID => Type::TYPE_SIMPLE, - ProductInterface::ATTRIBUTE_SET_ID => 4, - ProductInterface::SKU => 'simple_product', - ProductInterface::NAME => 'Simple Product', - ProductInterface::PRICE => 20, - ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH, - ProductInterface::STATUS => Status::STATUS_ENABLED, -]; -$dataObjectHelper->populateWithArray($product, $productData, ProductInterface::class); -/** Out of interface */ -$product - ->setWebsiteIds([1]) - ->setStockData( - [ - 'qty' => 85.5, - 'is_in_stock' => true, - 'manage_stock' => true, - 'is_qty_decimal' => true - ] - ); -$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php deleted file mode 100644 index 6b37585f8224f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -// phpcs:ignore Magento2.Security.IncludeFile -require __DIR__ . '/../../GraphQl/Catalog/_files/simple_product_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock index d9da3edf3d209..3d293417a22c0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock @@ -3124,79 +3124,6 @@ ], "description": "Authorization module provides access to Magento ACL functionality." }, - { - "name": "magento/module-authorizenet", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet/magento-module-authorizenet-100.3.1.0.zip", - "reference": null, - "shasum": "ab9dc9018f1a5b71cfaa54c1975d6ee6b5c7610c" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-catalog": "103.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, - { - "name": "magento/module-authorizenet-acceptjs", - "version": "100.3.0", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet-acceptjs/magento-module-authorizenet-acceptjs-100.3.0.0.zip", - "reference": null, - "shasum": "16bc1b50a4e7f63bfa3869fa2c3bd4c67cedd8a3" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, { "name": "magento/module-backend", "version": "101.0.1", @@ -9705,8 +9632,6 @@ "magento/module-analytics": "100.3.1", "magento/module-asynchronous-operations": "100.3.1", "magento/module-authorization": "100.3.1", - "magento/module-authorizenet": "100.3.1", - "magento/module-authorizenet-acceptjs": "100.3.0", "magento/module-backend": "101.0.1", "magento/module-backup": "100.3.1", "magento/module-braintree": "100.3.1", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock index d755bfa0479e6..fa4d358ced200 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock @@ -3124,79 +3124,6 @@ ], "description": "Authorization module provides access to Magento ACL functionality." }, - { - "name": "magento/module-authorizenet", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet/magento-module-authorizenet-100.3.1.0.zip", - "reference": null, - "shasum": "ab9dc9018f1a5b71cfaa54c1975d6ee6b5c7610c" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-catalog": "103.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, - { - "name": "magento/module-authorizenet-acceptjs", - "version": "100.3.0", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet-acceptjs/magento-module-authorizenet-acceptjs-100.3.0.0.zip", - "reference": null, - "shasum": "16bc1b50a4e7f63bfa3869fa2c3bd4c67cedd8a3" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, { "name": "magento/module-backend", "version": "101.0.1", @@ -9705,8 +9632,6 @@ "magento/module-analytics": "100.3.1", "magento/module-asynchronous-operations": "100.3.1", "magento/module-authorization": "100.3.1", - "magento/module-authorizenet": "100.3.1", - "magento/module-authorizenet-acceptjs": "100.3.0", "magento/module-backend": "101.0.1", "magento/module-backup": "100.3.1", "magento/module-braintree": "100.3.1", diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php b/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php deleted file mode 100644 index 3043a5eaf2ae1..0000000000000 --- a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\Storage\Writer; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Encryption\EncryptorInterface; -use Magento\AuthorizenetAcceptjs\Gateway\Config; - -$objectManager = Bootstrap::getObjectManager(); -/** @var EncryptorInterface $encryptor */ -$encryptor = $objectManager->get(EncryptorInterface::class); - -/** @var Writer $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$configWriter->save('payment/' . Config::METHOD . '/active', '1'); -$configWriter->save('payment/' . Config::METHOD . '/environment', 'sandbox'); -$configWriter->save('payment/' . Config::METHOD . '/login', $encryptor->encrypt('def_login')); -$configWriter->save('payment/' . Config::METHOD . '/trans_key', $encryptor->encrypt('def_trans_key')); -$configWriter->save('payment/' . Config::METHOD . '/public_client_key', $encryptor->encrypt('def_public_client_key')); -$configWriter->save( - 'payment/' . Config::METHOD . '/trans_signature_key', - $encryptor->encrypt('def_trans_signature_key') -); - -$scopeConfig = $objectManager->get(ScopeConfigInterface::class); -$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php deleted file mode 100644 index b73883f80f333..0000000000000 --- a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\Storage\Writer; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\AuthorizenetAcceptjs\Gateway\Config; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var Writer $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$configWriter->delete('payment/' . Config::METHOD . '/active'); -$configWriter->delete('payment/' . Config::METHOD . '/environment'); -$configWriter->delete('payment/' . Config::METHOD . '/login'); -$configWriter->delete('payment/' . Config::METHOD . '/trans_key'); -$configWriter->delete('payment/' . Config::METHOD . '/public_client_key'); -$configWriter->delete('payment/' . Config::METHOD . '/trans_signature_key'); - -$scopeConfig = $objectManager->get(ScopeConfigInterface::class); -$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php index ff2a2835448c6..1af8d900b23dc 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php @@ -9,7 +9,6 @@ * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ declare(strict_types=1); @@ -26,7 +25,6 @@ $configWriter->save('payment/cashondelivery/active', 1); $configWriter->save('payment/checkmo/active', 1); $configWriter->save('payment/purchaseorder/active', 1); -$configWriter->save('payment/authorizenet_acceptjs/active', 1); $scopeConfig = $objectManager->get(ScopeConfigInterface::class); $scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php index 3be528eb41de8..b3268ecc9ee98 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php @@ -20,4 +20,3 @@ $configWriter->delete('payment/cashondelivery/active'); $configWriter->delete('payment/checkmo/active'); $configWriter->delete('payment/purchaseorder/active'); -$configWriter->delete('payment/authorizenet_acceptjs/active'); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php index 3fb53be2ec400..b120c65d3a759 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php @@ -190,7 +190,6 @@ 'oauth_consumer' => 'Magento\Integration', 'oauth_nonce' => 'Magento\Integration', 'oauth_token' => 'Magento\Integration', - 'authorizenet_debug' => 'Magento\Authorizenet', 'admin_passwords' => 'Magento\User', 'paypal_cert' => 'Magento\Paypal', 'paypal_payment_transaction' => 'Magento\Paypal', diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt index 181ecd43ce564..a143424feb015 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt @@ -2,7 +2,6 @@ app/code/Magento/Customer/view/frontend/web/js/zxcvbn.js app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js app/code/Magento/Tinymce3/view/base/web/tiny_mce/**/*.js -app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js app/code/Magento/Rule/view/adminhtml/web/rules.js app/code/Magento/Sales/view/adminhtml/web/order/create/giftmessage.js diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index af785c28db414..2f4b9e60fa784 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4243,4 +4243,204 @@ ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], ['Magento\Wishlist\Setup\Patch\Schema\AddProductIdConstraint'], + ['Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails'], + ['Magento\Authorizenet\Block\Adminhtml\Order\View\Info\PaymentDetails'], + ['Magento\Authorizenet\Block\Transparent\Iframe'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\AddConfigured'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Cancel'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ConfigureProductToAdd'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ConfigureQuoteItems'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Index'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\LoadBlock'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Place'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ProcessData'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Redirect'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Reorder'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ReturnQuote'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Save'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ShowUpdateResult'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Start'], + ['Magento\Authorizenet\Controller\Directpost\Payment'], + ['Magento\Authorizenet\Controller\Directpost\Payment\BackendResponse'], + ['Magento\Authorizenet\Controller\Directpost\Payment\Place'], + ['Magento\Authorizenet\Controller\Directpost\Payment\Redirect'], + ['Magento\Authorizenet\Controller\Directpost\Payment\Response'], + ['Magento\Authorizenet\Controller\Directpost\Payment\ReturnQuote'], + ['Magento\Authorizenet\Helper\Backend\Data'], + ['Magento\Authorizenet\Helper\Data'], + ['Magento\Authorizenet\Helper\DataFactory'], + ['Magento\Authorizenet\Model\Authorizenet'], + ['Magento\Authorizenet\Model\Debug'], + ['Magento\Authorizenet\Model\Directpost'], + ['Magento\Authorizenet\Model\Directpost\Request'], + ['Magento\Authorizenet\Model\Directpost\Request\Factory'], + ['Magento\Authorizenet\Model\Directpost\Response'], + ['Magento\Authorizenet\Model\Directpost\Response\Factory'], + ['Magento\Authorizenet\Model\Directpost\Session'], + ['Magento\Authorizenet\Model\Request'], + ['Magento\Authorizenet\Model\Request\Factory'], + ['Magento\Authorizenet\Model\ResourceModel\Debug'], + ['Magento\Authorizenet\Model\ResourceModel\Debug\Collection'], + ['Magento\Authorizenet\Model\Response'], + ['Magento\Authorizenet\Model\Response\Factory'], + ['Magento\Authorizenet\Model\Source\Cctype'], + ['Magento\Authorizenet\Model\Source\PaymentAction'], + ['Magento\Authorizenet\Model\TransactionService'], + ['Magento\Authorizenet\Observer\AddFieldsToResponseObserver'], + ['Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver'], + ['Magento\Authorizenet\Observer\UpdateAllEditIncrementsObserver'], + ['Magento\Authorizenet\Test\Unit\Controller\Adminhtml\Authorizenet\Directpost\Payment\RedirectTest'], + ['Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment\PlaceTest'], + ['Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment\RedirectTest'], + ['Magento\Authorizenet\Test\Unit\Helper\Backend\DataTest'], + ['Magento\Authorizenet\Test\Unit\Helper\DataTest'], + ['Magento\Authorizenet\Test\Unit\Model\Directpost\Request\FactoryTest'], + ['Magento\Authorizenet\Test\Unit\Model\Directpost\RequestTest'], + ['Magento\Authorizenet\Test\Unit\Model\Directpost\Response\FactoryTest'], + ['Magento\Authorizenet\Test\Unit\Model\Directpost\ResponseTest'], + ['Magento\Authorizenet\Test\Unit\Model\Directpost\SessionTest'], + ['Magento\Authorizenet\Test\Unit\Model\DirectpostTest'], + ['Magento\Authorizenet\Test\Unit\Model\Request\FactoryTest'], + ['Magento\Authorizenet\Test\Unit\Model\Response\FactoryTest'], + ['Magento\Authorizenet\Test\Unit\Model\TransactionServiceTest'], + ['Magento\Authorizenet\Test\Unit\Observer\AddFieldsToResponseObserverTest'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\PlaceTest'], + ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\PlaceTesting'], + ['Magento\Authorizenet\Controller\Directpost\Payment\BackendResponseTest'], + ['Magento\Authorizenet\Controller\Directpost\Payment\ResponseTest'], + ['Magento\Authorizenet\Controller\Directpost\PaymentTest'], + ['Magento\Authorizenet\Model\Directpost\RequestTest'], + ['Magento\Authorizenet\Model\DirectpostTest'], + ['Magento\AuthorizenetAcceptjs\Block\Form'], + ['Magento\AuthorizenetAcceptjs\Block\Info'], + ['Magento\AuthorizenetAcceptjs\Block\Payment'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\AcceptPaymentStrategyCommand'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\CaptureStrategyCommand'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\FetchTransactionInfoCommand'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\GatewayQueryCommand'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\RefundTransactionStrategyCommand'], + ['Magento\AuthorizenetAcceptjs\Gateway\Config'], + ['Magento\AuthorizenetAcceptjs\Gateway\Http\Client'], + ['Magento\AuthorizenetAcceptjs\Gateway\Http\Payload\Filter\RemoveFieldsFilter'], + ['Magento\AuthorizenetAcceptjs\Gateway\Http\Payload\FilterInterface'], + ['Magento\AuthorizenetAcceptjs\Gateway\Http\TransferFactory'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\AcceptFdsDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\CaptureDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundPaymentDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundReferenceTransactionDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundTransactionTypeDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\RequestTypeBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\SaleDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\StubDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\TransactionDetailsDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Request\VoidDataBuilder'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\ClosePartialTransactionHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentResponseHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentReviewStatusHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionDetailsResponseHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\VoidResponseHandler'], + ['Magento\AuthorizenetAcceptjs\Gateway\SubjectReader'], + ['Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator'], + ['Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator'], + ['Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionResponseValidator'], + ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Cctype'], + ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Environment'], + ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\PaymentAction'], + ['Magento\AuthorizenetAcceptjs\Model\PassthroughDataObject'], + ['Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider'], + ['Magento\AuthorizenetAcceptjs\Observer\DataAssignObserver'], + ['Magento\AuthorizenetAcceptjs\Setup\Patch\Data\CopyCurrentConfig'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\FormTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\InfoTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\PaymentTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\AcceptPaymentStrategyCommandTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\CaptureStrategyCommandTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\FetchTransactionInfoCommandTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\GatewayQueryCommandTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\RefundTransactionStrategyCommandTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\ConfigTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\ClientTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\Payload\Filter\RemoveFieldsFilterTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\TransferFactoryTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AcceptFdsDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AddressDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AmountDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AuthenticationDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AuthorizationDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CaptureDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CustomSettingsBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CustomerDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\OrderDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PassthroughDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PaymentDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PoDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundPaymentDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundReferenceTransactionDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundTransactionTypeDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RequestTypeBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\SaleDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\ShippingDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\SolutionDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\StoreConfigBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\TransactionDetailsDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\VoidDataBuilderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\CloseParentTransactionHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\CloseTransactionHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\PaymentResponseHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\PaymentReviewStatusHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\TransactionDetailsResponseHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\TransactionIdHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\VoidResponseHandlerTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\SubjectReaderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\GeneralResponseValidatorTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\TransactionHashValidatorTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\TransactionResponseValidatorTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Model\Ui\ConfigProviderTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Observer\DataAssignObserverTest'], + ['Magento\AuthorizenetAcceptjs\Test\Unit\Setup\Patch\Data\CopyCurrentConfigTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\AbstractTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\AcceptFdsCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\AuthorizeCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\CancelCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\FetchTransactionInfoCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\RefundSettledCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\SaleCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\SettleCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\TransactionDetailsCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Command\VoidCommandTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\ConfigTest'], + ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandlerTest'], + ['Magento\AuthorizenetCardinal\Gateway\Request\Authorize3DSecureBuilder'], + ['Magento\AuthorizenetCardinal\Gateway\Validator\CavvResponseValidator'], + ['Magento\AuthorizenetCardinal\Model\Checkout\ConfigProvider'], + ['Magento\AuthorizenetCardinal\Model\Config'], + ['Magento\AuthorizenetCardinal\Observer\DataAssignObserver'], + ['Magento\AuthorizenetCardinal\Test\Unit\Observer\DataAssignObserverTest'], + ['Magento\AuthorizenetCardinal\Gateway\Command\AuthorizeCommandTest'], + ['Magento\AuthorizenetCardinal\Gateway\Command\SaleCommandTest'], + ['Magento\AuthorizenetGraphQl\Model\AuthorizenetDataProvider'], + ['Magento\AuthorizenetGraphQl\Model\Resolver\Customer\PlaceOrderWithAuthorizeNetTest'], + ['Magento\AuthorizenetGraphQl\Model\Resolver\Customer\SetAuthorizeNetPaymentMethodOnCartTest'], + ['Magento\AuthorizenetGraphQl\Model\Resolver\Guest\PlaceOrderWithAuthorizeNetTest'], + ['Magento\AuthorizenetGraphQl\Model\Resolver\Guest\SetAuthorizeNetPaymentMethodOnCartTest'], + ['Magento\GraphQl\AuthorizenetAcceptjs\Customer\SetPaymentMethodTest'], + ['Magento\GraphQl\AuthorizenetAcceptjs\Guest\SetPaymentMethodTest'], + ['Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http\MockClient'], + ['Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt index 411f02e2c5930..fc8c0c17447d8 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt @@ -1,5 +1,4 @@ Magento/Adminhtml -Magento/Authorizenet/Model Magento/Backend Magento/Bundle Magento/Catalog/Block/Product/ProductList From ad9f69b2e5171c6be44f7cc82a42191b6242355a Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 4 Mar 2020 11:21:17 +0200 Subject: [PATCH 261/734] MC-31729: Non-cacheable block added to default handle makes every page non-cacheable --- .../Catalog/Model/Layout/DepersonalizePlugin.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 4 ++-- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 4 ++-- .../Model/Layout/DepersonalizePlugin.php | 4 ++-- .../Model/Layout/DepersonalizePluginTest.php | 8 ++++---- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../PageCache/Model/Layout/LayoutPlugin.php | 16 ++++++++-------- .../Model/Layout/DepersonalizePluginTest.php | 4 ++-- .../Test/Unit/Model/Layout/LayoutPluginTest.php | 6 +++--- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 4 ++-- .../Tax/Model/Layout/DepersonalizePlugin.php | 4 ++-- .../Model/Layout/DepersonalizePluginTest.php | 10 +++++----- lib/internal/Magento/Framework/View/Layout.php | 2 +- .../Framework/View/Test/Unit/LayoutTest.php | 5 ++++- 16 files changed, 41 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php index d99dc41bcba12..5edb148e501d5 100644 --- a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php @@ -41,7 +41,7 @@ public function __construct( } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php index cac1d8eb244d9..cf19c4a8868c1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -59,7 +59,7 @@ protected function setUp() } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -71,7 +71,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ diff --git a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php index 4857c14251765..2f39cb6907661 100644 --- a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php @@ -42,7 +42,7 @@ public function __construct( } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 29b47de21b8f8..3d69bc88bb73d 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -59,7 +59,7 @@ protected function setUp() } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -75,7 +75,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ diff --git a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php index 4dcf1a2ca9ba2..66b8f8419deae 100644 --- a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php @@ -79,7 +79,7 @@ public function __construct( } /** - * Retrieves sensitive customer data. + * Retrieve sensitive customer data. * * @param LayoutInterface $subject * @return void @@ -93,7 +93,7 @@ public function beforeGenerateXml(LayoutInterface $subject) } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 245b39c79f6a7..095a83101322f 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -105,7 +105,7 @@ protected function setUp() } /** - * Tests beforeGenerateXml method when depersonalization is needed. + * Test beforeGenerateXml method when depersonalization is needed. * * @return void */ @@ -121,7 +121,7 @@ public function testBeforeGenerateXml(): void } /** - * Tests beforeGenerateXml method when depersonalization is not needed. + * Test beforeGenerateXml method when depersonalization is not needed. * * @return void */ @@ -136,7 +136,7 @@ public function testBeforeGenerateXmlNoDepersonalize(): void } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -168,7 +168,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ diff --git a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php index 3218afabe0511..c3a5a19bc1c2a 100644 --- a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php @@ -50,7 +50,7 @@ public function __construct( } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index c30efd6ae78ae..762f393f2a1b9 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -10,11 +10,11 @@ use Magento\Framework\App\MaintenanceMode; use Magento\Framework\App\ResponseInterface; use Magento\Framework\DataObject\IdentityInterface; -use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Layout; use Magento\PageCache\Model\Config; /** - * Appends cacheable pages response headers. + * Append cacheable pages response headers. */ class LayoutPlugin { @@ -49,14 +49,14 @@ public function __construct( } /** - * Sets appropriate Cache-Control headers. + * Set appropriate Cache-Control headers. * * We have to set public headers in order to tell Varnish and Builtin app that page should be cached * - * @param LayoutInterface $subject + * @param Layout $subject * @return void */ - public function afterGenerateElements(LayoutInterface $subject) + public function afterGenerateElements(Layout $subject) { if ($subject->isCacheable() && !$this->maintenanceMode->isOn() && $this->config->isEnabled()) { $this->response->setPublicHeaders($this->config->getTtl()); @@ -64,13 +64,13 @@ public function afterGenerateElements(LayoutInterface $subject) } /** - * Retrieves all identities from blocks for further cache invalidation. + * Retrieve all identities from blocks for further cache invalidation. * - * @param LayoutInterface $subject + * @param Layout $subject * @param mixed $result * @return mixed */ - public function afterGetOutput(LayoutInterface $subject, $result) + public function afterGetOutput(Layout $subject, $result) { if ($subject->isCacheable() && $this->config->isEnabled()) { $tags = [[]]; diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php index baed50e989e1c..bf10de64ff365 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -69,7 +69,7 @@ protected function setUp() } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -85,7 +85,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php index f3d5c449c654e..d5d6efa5e35ae 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php @@ -12,7 +12,7 @@ use Magento\Framework\App\Response\Http; use Magento\Framework\App\ResponseInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Layout; use Magento\PageCache\Model\Config; use Magento\PageCache\Model\Layout\LayoutPlugin; use Magento\PageCache\Test\Unit\Block\Controller\StubBlock; @@ -35,7 +35,7 @@ class LayoutPluginTest extends TestCase private $responseMock; /** - * @var LayoutInterface|MockObject + * @var Layout|MockObject */ private $layoutMock; @@ -54,7 +54,7 @@ class LayoutPluginTest extends TestCase */ protected function setUp() { - $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->layoutMock = $this->createPartialMock(Layout::class, ['isCacheable', 'getAllBlocks']); $this->responseMock = $this->createMock(Http::class); $this->configMock = $this->createMock(Config::class); $this->maintenanceModeMock = $this->createMock(MaintenanceMode::class); diff --git a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php index 5546cf3ef8fba..16c7be78456d1 100644 --- a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php @@ -39,7 +39,7 @@ public function __construct( } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 633e39fbb63b9..393e2d8ab7799 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -59,7 +59,7 @@ protected function setUp() } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -72,7 +72,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ diff --git a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php index c19580b873d77..6959f0a0d7c7c 100644 --- a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php @@ -55,7 +55,7 @@ public function __construct( } /** - * Resolves sensitive customer data if the depersonalization is needed. + * Resolve sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void @@ -70,7 +70,7 @@ public function beforeGenerateXml(LayoutInterface $subject) } /** - * Changes sensitive customer data if the depersonalization is needed. + * Change sensitive customer data if the depersonalization is needed. * * @param LayoutInterface $subject * @return void diff --git a/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 46a11731006d0..26ceef5f6ac3b 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -69,7 +69,7 @@ protected function setUp() } /** - * Tests beforeGenerateXml method when depersonalization is needed. + * Test beforeGenerateXml method when depersonalization is needed. * * @return void */ @@ -83,7 +83,7 @@ public function testBeforeGenerateXml(): void } /** - * Tests beforeGenerateXml method when depersonalization is not needed. + * Test beforeGenerateXml method when depersonalization is not needed. * * @return void */ @@ -97,7 +97,7 @@ public function testBeforeGenerateXmlNoDepersonalize(): void } /** - * Tests afterGenerateElements method when depersonalization is needed. + * Test afterGenerateElements method when depersonalization is needed. * * @return void */ @@ -111,7 +111,7 @@ public function testAfterGenerateElements(): void } /** - * Tests afterGenerateElements method when depersonalization is not needed. + * Test afterGenerateElements method when depersonalization is not needed. * * @return void */ @@ -125,7 +125,7 @@ public function testAfterGenerateElementsNoDepersonalize(): void } /** - * Tests beforeGenerateXml and afterGenerateElements methods. + * Test beforeGenerateXml and afterGenerateElements methods. * * @return void */ diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index c6058887ee66e..ebefc6200cdfa 100644 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -1103,7 +1103,7 @@ protected function _prepareMessageGroup($messageGroups) } /** - * Checks existed non-cacheable layout elements. + * Check existed non-cacheable layout elements. * * @return bool */ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index 84dd31121a270..b9754150bb92e 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -351,7 +351,10 @@ public function testGetChildBlock(): void ); } - public function testGetChildNonExistBlock() + /** + * @return void + */ + public function testGetChildNonExistBlock(): void { $this->structureMock->expects($this->once()) ->method('getChildId') From 7be7c4992e000faab76367cd65120be4e00f8edb Mon Sep 17 00:00:00 2001 From: Andrii Kalinich <51681435+engcom-Echo@users.noreply.github.com> Date: Wed, 4 Mar 2020 13:06:18 +0200 Subject: [PATCH 262/734] Update app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml Co-Authored-By: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> --- .../Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml index f567239b5f262..32a4ebc2503ad 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml @@ -31,7 +31,7 @@ <!-- Set Magento back to default configuration --> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersDisable.path}} {{CatalogInventoryItemOptionsBackordersDisable.value}}" stepKey="setConfigAllowBackordersFalse"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCache/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> From c92cd5254042d0d1bbdc7f98880ad558b2fa9615 Mon Sep 17 00:00:00 2001 From: Andrii Kalinich <51681435+engcom-Echo@users.noreply.github.com> Date: Wed, 4 Mar 2020 14:43:12 +0200 Subject: [PATCH 263/734] Update AdminBackorderAllowedAddProductToCartTest.xml --- .../Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml index 32a4ebc2503ad..2640ab04023a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml @@ -31,7 +31,7 @@ <!-- Set Magento back to default configuration --> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersDisable.path}} {{CatalogInventoryItemOptionsBackordersDisable.value}}" stepKey="setConfigAllowBackordersFalse"/> - <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCache/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCache"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> From 76e61aecca249855bf9f45c25672bd800121e58e Mon Sep 17 00:00:00 2001 From: Michael Bottens <michael.bottens@blueacorn.com> Date: Wed, 4 Mar 2020 07:57:47 -0500 Subject: [PATCH 264/734] CR fixes --- .../Magento/Wishlist/Block/Share/Email/Items.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Share/Email/Items.php b/app/code/Magento/Wishlist/Block/Share/Email/Items.php index 0887951d180aa..c7ff49943b222 100644 --- a/app/code/Magento/Wishlist/Block/Share/Email/Items.php +++ b/app/code/Magento/Wishlist/Block/Share/Email/Items.php @@ -14,6 +14,7 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Catalog\Model\Product\Image\UrlBuilder; +use Magento\Framework\App\ObjectManager; use Magento\Framework\View\ConfigInterface; use Magento\Wishlist\Model\Item; @@ -23,7 +24,9 @@ */ class Items extends \Magento\Wishlist\Block\AbstractBlock { - /** @var ItemResolverInterface */ + /** + * @var ItemResolverInterface + */ private $itemResolver; /** @@ -32,23 +35,24 @@ class Items extends \Magento\Wishlist\Block\AbstractBlock protected $_template = 'Magento_Wishlist::email/items.phtml'; /** + * Items constructor. * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Framework\App\Http\Context $httpContext - * @param ItemResolverInterface $itemResolver * @param array $data * @param ConfigInterface|null $config * @param UrlBuilder|null $urlBuilder + * @param ItemResolverInterface|null $itemResolver */ public function __construct( \Magento\Catalog\Block\Product\Context $context, \Magento\Framework\App\Http\Context $httpContext, - ItemResolverInterface $itemResolver, array $data = [], ConfigInterface $config = null, - UrlBuilder $urlBuilder = null + UrlBuilder $urlBuilder = null, + ItemResolverInterface $itemResolver = null ) { - $this->itemResolver = $itemResolver; parent::__construct($context, $httpContext, $data, $config, $urlBuilder); + $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -57,7 +61,7 @@ public function __construct( * @param Item $item * @return Product */ - public function getProductForThumbnail(Item $item) : Product + public function getProductForThumbnail(Item $item): Product { return $this->itemResolver->getFinalProduct($item); } From d3d3f2921b7bc68c958cf1cfb2eaec4948420cf0 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Wed, 4 Mar 2020 13:05:26 +0000 Subject: [PATCH 265/734] Processed review comments --- .../Magento/Catalog/Model/Product/Action.php | 10 +- ...ProductProcessUrlRewriteSavingObserver.php | 108 ++-- .../ProductToWebsiteChangeObserver.php | 109 ---- ...ProductProcessUrlRewriteRemovingPlugin.php | 124 +++++ ...uctProcessUrlRewriteSavingObserverTest.php | 227 ++++++-- .../ProductToWebsiteChangeObserverTest.php | 271 ---------- ...uctProcessUrlRewriteRemovingPluginTest.php | 339 ++++++++++++ app/code/Magento/CatalogUrlRewrite/etc/di.xml | 4 + .../Magento/CatalogUrlRewrite/etc/events.xml | 3 - .../UrlRewrite/Model/Storage/DbStorage.php | 29 ++ ...ateURLRewriteWhenCategoryIsDeletedTest.xml | 1 + ...lKeyForStoreViewAndMovingCategory2Test.xml | 2 +- ...rlKeyForStoreViewAndMovingCategoryTest.xml | 2 +- ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 1 + ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 + ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...eUrlRewriteAndAddPermanentRedirectTest.xml | 1 + ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 + ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...eProductURLRewriteAndAddNoRedirectTest.xml | 1 + ...ithCategoryAndAddTemporaryRedirectTest.xml | 1 + ...tUrLRewriteAndAddPermanentRedirectTest.xml | 1 + ...tUrLRewriteAndAddTemporaryRedirectTest.xml | 1 + ...nDeleteCMSPageNoRedirectUrlRewriteTest.xml | 1 + ...CMSPagePermanentRedirectUrlRewriteTest.xml | 1 + ...CMSPageTemporaryRedirectUrlRewriteTest.xml | 1 + .../AdminDeleteCategoryUrlRewriteTest.xml | 1 + ...teCmsPageUrlRewriteWithNoRedirectsTest.xml | 1 + ...ageUrlRewriteWithPermanentRedirectTest.xml | 1 + ...ageUrlRewriteWithTemporaryRedirectTest.xml | 1 + .../Test/AdminDeleteProductUrlRewriteTest.xml | 1 + ...inMarketingUrlRewritesNavigateMenuTest.xml | 1 + ...oryUrlRewriteAndAddAspxRequestPathTest.xml | 1 + ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 1 + ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 + ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...CmsPageRewriteEntityWithNoRedirectTest.xml | 1 + ...eRewriteEntityWithPermanentReirectTest.xml | 1 + ...RewriteEntityWithTemporaryRedirectTest.xml | 1 + ...eCmsPageUrlRewriteAndAddNoRedirectTest.xml | 1 + ...eUrlRewriteAndAddPermanentRedirectTest.xml | 1 + ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 1 + ...uctProcessUrlRewriteSavingObserverTest.php | 486 ++++++++++++++++-- 45 files changed, 1223 insertions(+), 523 deletions(-) delete mode 100644 app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php delete mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php diff --git a/app/code/Magento/Catalog/Model/Product/Action.php b/app/code/Magento/Catalog/Model/Product/Action.php index aba82e458a693..3863cf2457247 100644 --- a/app/code/Magento/Catalog/Model/Product/Action.php +++ b/app/code/Magento/Catalog/Model/Product/Action.php @@ -169,14 +169,6 @@ public function updateWebsites($productIds, $websiteIds, $type) $categoryIndexer->reindexList(array_unique($productIds)); } - //Dispatch event to update Rewrite URLs for new/removed websites - $this->_eventManager->dispatch( - 'catalog_product_to_website_change', - [ - 'products' => $productIds, - 'website_ids' => $websiteIds, - 'action_type' => $type - ] - ); + $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index ded4bfbaa04ed..7cb916b6a6dd8 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -6,15 +6,19 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductRepository; +use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Event\Observer; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; -use Magento\Catalog\Model\Product\Visibility; use Magento\Store\Model\StoreManagerInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Store\Api\StoreWebsiteRelationInterface; +use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\Store\Model\Store; /** * Class ProductProcessUrlRewriteSavingObserver @@ -50,38 +54,61 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $storeWebsiteRelation; + /** + * @var ProductRepository $productRepository + */ + private $productRepository; + + /** + * @var ProductScopeRewriteGenerator + */ + private $productScopeRewriteGenerator; + + /** + * @var DbStorage + */ + private $dbStorage; + /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist - * @param ProductUrlPathGenerator|null $productUrlPathGenerator - * @param StoreManagerInterface|null $storeManager - * @param StoreWebsiteRelationInterface|null $storeWebsiteRelation + * @param ProductUrlPathGenerator $productUrlPathGenerator + * @param StoreManagerInterface $storeManager + * @param StoreWebsiteRelationInterface $storeWebsiteRelation + * @param ProductRepository $productRepository + * @param ProductScopeRewriteGenerator $productScopeRewriteGenerator + * @param DbStorage $dbStorage */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $productUrlPathGenerator = null, - StoreManagerInterface $storeManager = null, - StoreWebsiteRelationInterface $storeWebsiteRelation = null + ProductUrlPathGenerator $productUrlPathGenerator, + StoreManagerInterface $storeManager, + StoreWebsiteRelationInterface $storeWebsiteRelation, + ProductRepository $productRepository, + ProductScopeRewriteGenerator $productScopeRewriteGenerator, + DbStorage $dbStorage ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; - $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() - ->get(ProductUrlPathGenerator::class); - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManagerInterface::class); - $this->storeWebsiteRelation = $storeWebsiteRelation ?: ObjectManager::getInstance() - ->get(StoreWebsiteRelationInterface::class); + $this->productUrlPathGenerator = $productUrlPathGenerator; + $this->storeManager = $storeManager; + $this->storeWebsiteRelation = $storeWebsiteRelation; + $this->productRepository = $productRepository; + $this->productScopeRewriteGenerator = $productScopeRewriteGenerator; + $this->dbStorage = $dbStorage; } /** * Generate urls for UrlRewrite and save it in storage * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return void - * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException + * @throws UrlAlreadyExistsException + * @throws NoSuchEntityException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { /** @var Product $product */ $product = $observer->getEvent()->getProduct(); @@ -91,24 +118,45 @@ public function execute(\Magento\Framework\Event\Observer $observer) || $product->getIsChangedWebsites() || $product->dataHasChangedFor('visibility') ) { - if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { - $product->unsUrlPath(); - $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); + //Refresh rewrite urls + $product->unsUrlPath(); + $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); + if (!empty($this->productUrlRewriteGenerator->generate($product))) { $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + } - //Remove any rewrite URLs for websites the product is not in + $storeIdsToRemove = []; + if ($this->productScopeRewriteGenerator->isGlobalScope($product->getStoreId())) { + //Remove any rewrite URLs for websites the product is not in, or is not visible in. Global Scope. foreach ($this->storeManager->getWebsites() as $website) { $websiteId = $website->getWebsiteId(); - if (!in_array($websiteId, $product->getWebsiteIds())) { - foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($websiteId) as $storeId) { - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::STORE_ID => $storeId - ]); - } + foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($websiteId) as $storeid) { + //Load the product for the store we are processing so we can see if it is visible + $storeProduct = $this->productRepository->getById( + $product->getId(), + false, + $storeid, + true + ); + if (!$storeProduct->isVisibleInSiteVisibility() || + !in_array($websiteId, $product->getWebsiteIds())) { + $storeIdsToRemove[] = $storeid; + }; } } + } else { + //Only remove rewrite for current scope + if (!$product->isVisibleInSiteVisibility() || + !in_array($product->getStoreId(), $product->getStoreIds())) { + $storeIdsToRemove[] = $product->getStoreId(); + } + } + if (count($storeIdsToRemove)) { + $this->dbStorage->deleteEntitiesFromStores( + $storeIdsToRemove, + [$product->getId()], + ProductUrlRewriteGenerator::ENTITY_TYPE + ); } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php deleted file mode 100644 index 45cec45a3c75d..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\CatalogUrlRewrite\Observer; - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product\Visibility; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\Event\ObserverInterface; -use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Store\Api\StoreWebsiteRelationInterface; -use Magento\Framework\App\ObjectManager; - -/** - * Class ProductToWebsiteChangeObserver - * - * Observer to update the Rewrite URLs for a product. - * This observer is triggered by the product_action_attribute.website.update - * consumer in response to Mass Action changes in the Admin Product Grid. - */ -class ProductToWebsiteChangeObserver implements ObserverInterface -{ - /** - * @var ProductUrlRewriteGenerator - */ - protected $productUrlRewriteGenerator; - - /** - * @var UrlPersistInterface - */ - protected $urlPersist; - - /** - * @var ProductRepositoryInterface - */ - protected $productRepository; - - /** - * @var StoreWebsiteRelationInterface - */ - private $storeWebsiteRelation; - - /** - * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param UrlPersistInterface $urlPersist - * @param ProductRepositoryInterface $productRepository - * @param RequestInterface $request - * @param StoreWebsiteRelationInterface $storeWebsiteRelation - * - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function __construct( - ProductUrlRewriteGenerator $productUrlRewriteGenerator, - UrlPersistInterface $urlPersist, - ProductRepositoryInterface $productRepository, - RequestInterface $request, - StoreWebsiteRelationInterface $storeWebsiteRelation = null - ) { - $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->productRepository = $productRepository; - $this->storeWebsiteRelation = $storeWebsiteRelation ?: - ObjectManager::getInstance()->get(StoreWebsiteRelationInterface::class); - } - - /** - * Generate urls for UrlRewrite and save it in storage - * - * @param \Magento\Framework\Event\Observer $observer - * @return void - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - foreach ($observer->getEvent()->getProducts() as $productId) { - /* @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->getById( - $productId, - false, - Store::DEFAULT_STORE_ID, - true - ); - - // Remove the URLs from websites this product no longer belongs to - if ($observer->getEvent()->getActionType() == "remove" && $observer->getEvent()->getWebsiteIds()) { - foreach ($observer->getEvent()->getWebsiteIds() as $webId) { - foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeId) { - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::STORE_ID => $storeId - ]); - } - } - } - - // Refresh all existing URLs for the product - if (!empty($this->productUrlRewriteGenerator->generate($product))) { - if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { - $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); - } - } - } - } -} diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php new file mode 100644 index 0000000000000..1bec43c4fd75b --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Plugin; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Action; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Store\Api\StoreWebsiteRelationInterface; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\UrlRewrite\Model\UrlPersistInterface; + +/** + * Class ProductProcessUrlRewriteRemovingPlugin + * + * Plugin to update the Rewrite URLs for a product. + * This plugin is triggered by the product_action_attribute.website.update + * consumer in response to Mass Action changes in the Admin Product Grid. + */ +class ProductProcessUrlRewriteRemovingPlugin +{ + /** + * @var ProductRepositoryInterface $productRepository + */ + protected $productRepository; + + /** + * @var StoreWebsiteRelationInterface $storeWebsiteRelation + */ + private $storeWebsiteRelation; + + /** + * @var UrlPersistInterface $urlPersist + */ + protected $urlPersist; + + /** + * @var ProductUrlRewriteGenerator $productUrlRewriteGenerator + */ + protected $productUrlRewriteGenerator; + + /** + * @var DbStorage $dbStorage + */ + private $dbStorage; + + /** + * @param ProductRepositoryInterface $productRepository + * @param StoreWebsiteRelationInterface $storeWebsiteRelation + * @param UrlPersistInterface $urlPersist + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param DbStorage $dbStorage + */ + public function __construct( + ProductRepositoryInterface $productRepository, + StoreWebsiteRelationInterface $storeWebsiteRelation, + UrlPersistInterface $urlPersist, + ProductUrlRewriteGenerator $productUrlRewriteGenerator, + DbStorage $dbStorage + ) { + $this->productRepository = $productRepository; + $this->storeWebsiteRelation = $storeWebsiteRelation; + $this->urlPersist = $urlPersist; + $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; + $this->dbStorage = $dbStorage; + } + + /** + * @param Action $subject + * @param null $result + * @param array $productIds + * @param array $websiteIds + * @param string $type + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateWebsites( + Action $subject, + $result, + $productIds, + $websiteIds, + $type + ) { + foreach ($productIds as $productId) { + /* @var Product $product */ + $product = $this->productRepository->getById( + $productId, + false, + Store::DEFAULT_STORE_ID, + true + ); + + // Refresh all existing URLs for the product + if (!empty($this->productUrlRewriteGenerator->generate($product))) { + if ($product->isVisibleInSiteVisibility()) { + $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + } + } + } + + $storeIdsToRemove = []; + // Remove the URLs from websites this product no longer belongs to + if ($type == "remove" && $websiteIds && $productIds) { + foreach ($websiteIds as $webId) { + foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeid) { + $storeIdsToRemove[] = $storeid; + } + } + if (count($storeIdsToRemove)) { + $this->dbStorage->deleteEntitiesFromStores( + $storeIdsToRemove, + $productIds, + ProductUrlRewriteGenerator::ENTITY_TYPE + ); + } + } + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index f68eaa2a02b9f..df1effb2f2eb8 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -6,10 +6,13 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; +use Magento\Catalog\Model\ProductRepository; +use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\StoreWebsiteRelationInterface; use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\Storage\DbStorage; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Catalog\Model\Product; use Magento\Framework\Event; @@ -17,7 +20,8 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\Website; use Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver; -use Magento\Catalog\Model\Product\Visibility; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class ProductProcessUrlRewriteSavingObserverTest @@ -29,63 +33,93 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase +class ProductProcessUrlRewriteSavingObserverTest extends TestCase { /** - * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject + * @var UrlPersistInterface|MockObject */ - protected $urlPersist; + private $urlPersist; /** - * @var Event|\PHPUnit_Framework_MockObject_MockObject + * @var Event|MockObject */ - protected $event; + private $event; /** - * @var Observer|\PHPUnit_Framework_MockObject_MockObject + * @var Observer|MockObject */ - protected $observer; + private $observer; /** - * @var Product|\PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $product; + private $product; /** - * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $productUrlRewriteGenerator; + private $product1; + + /** + * @var Product|MockObject + */ + private $product2; + + /** + * @var Product|MockObject + */ + private $product5; + + /** + * @var ProductUrlRewriteGenerator|MockObject + */ + private $productUrlRewriteGenerator; + + /** + * @var ProductScopeRewriteGenerator|MockObject + */ + private $productScopeRewriteGenerator; /** * @var ObjectManager */ - protected $objectManager; + private $objectManager; /** * @var ProductProcessUrlRewriteSavingObserver */ - protected $model; + private $model; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManager; + private $storeManager; /** - * @var Website|\PHPUnit_Framework_MockObject_MockObject + * @var Website|MockObject */ - protected $website1; + private $website1; /** - * @var Website|\PHPUnit_Framework_MockObject_MockObject + * @var Website|MockObject */ - protected $website2; + private $website2; /** - * @var StoreWebsiteRelationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreWebsiteRelationInterface|MockObject */ private $storeWebsiteRelation; + /** + * @var ProductRepository|MockObject + */ + private $productRepository; + + /** + * @var DbStorage|MockObject + */ + private $dbStorage; + /** * Set up * Website_ID = 1 -> Store_ID = 1 @@ -99,14 +133,39 @@ protected function setUp() [ 'getId', 'dataHasChangedFor', - 'getVisibility', + 'isVisibleInSiteVisibility', 'getIsChangedWebsites', 'getIsChangedCategories', 'getStoreId', 'getWebsiteIds' ] ); - $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); + $this->product1 = $this->createPartialMock( + Product::class, + ['getId', 'isVisibleInSiteVisibility'] + ); + $this->product2 = $this->createPartialMock( + Product::class, + ['getId', 'isVisibleInSiteVisibility'] + ); + $this->product5 = $this->createPartialMock( + Product::class, + ['getId', 'isVisibleInSiteVisibility'] + ); + $this->productRepository = $this->createPartialMock(ProductRepository::class, ['getById']); + $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->product1->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->product2->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->product5->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValueMap([ + [1, false, 0, true, $this->product], + [1, false, 1, true, $this->product1], + [1, false, 2, true, $this->product2], + [1, false, 5, true, $this->product5] + ])); + $this->dbStorage = $this->createPartialMock(DbStorage::class, ['deleteEntitiesFromStores']); $this->event = $this->createPartialMock(Event::class, ['getProduct']); $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); @@ -117,7 +176,20 @@ protected function setUp() ); $this->productUrlRewriteGenerator->expects($this->any()) ->method('generate') - ->will($this->returnValue([3 => 'rewrite'])); + ->will($this->returnValue([1 => 'rewrite'])); + $this->productScopeRewriteGenerator = $this->createPartialMock( + ProductScopeRewriteGenerator::class, + ['isGlobalScope'] + ); + $this->productScopeRewriteGenerator->expects($this->any()) + ->method('isGlobalScope') + ->will($this->returnValueMap([ + [null, true], + [0, true], + [1, false], + [2, false], + [5, false], + ])); $this->objectManager = new ObjectManager($this); $this->storeManager = $this->createMock(StoreManagerInterface::class); $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); @@ -142,7 +214,10 @@ protected function setUp() 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, 'urlPersist' => $this->urlPersist, 'storeManager' => $this->storeManager, - 'storeWebsiteRelation' => $this->storeWebsiteRelation + 'storeWebsiteRelation' => $this->storeWebsiteRelation, + 'productRepository' => $this->productRepository, + 'dbStorage' => $this->dbStorage, + 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator ] ); } @@ -160,10 +235,15 @@ public function urlKeyDataProvider() 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'visibilityResult' => [ + '0' => true, + '1' => true, + '2' => true, + '5' => true + ], + 'productInWebsites' => [1], 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 2, - 'productInWebsites' => [1] + 'expectedRemoves' => [2, 5], ], 'no changes' => [ @@ -171,50 +251,75 @@ public function urlKeyDataProvider() 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'visibilityResult' => [ + '0' => true, + '1' => true, + '2' => true, + '5' => true + ], + 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 0, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2] + 'expectedRemoves' => [], ], 'visibility changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => true, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'visibilityResult' => [ + '0' => true, + '1' => true, + '2' => true, + '5' => true + ], + 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2] + 'expectedRemoves' => [], ], 'websites changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => true, 'isChangedCategories' => false, - 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'visibilityResult' => [ + '0' => true, + '1' => true, + '2' => true, + '5' => true + ], + 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2] + 'expectedRemoves' => [], ], 'categories changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => true, - 'visibilityResult' => Visibility::VISIBILITY_BOTH, + 'visibilityResult' => [ + '0' => true, + '1' => true, + '2' => true, + '5' => true + ], + 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2] + 'expectedRemoves' => [], ], 'url changed invisible' => [ 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, - 'expectedReplaceCount' => 0, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2] + 'visibilityResult' => [ + '0' => false, + '1' => false, + '2' => false, + '5' => false + ], + 'productInWebsites' => [1, 2], + 'expectedReplaceCount' => 1, + 'expectedRemoves' => [1,2,5], ], ]; } @@ -224,10 +329,10 @@ public function urlKeyDataProvider() * @param bool $isChangedVisibility * @param bool $isChangedWebsites * @param bool $isChangedCategories - * @param bool $visibilityResult - * @param int $expectedReplaceCount - * @param int $expectedDeleteCount + * @param array $visibilityResult * @param int $productInWebsites + * @param int $expectedReplaceCount + * @param array $expectedRemoves * * @dataProvider urlKeyDataProvider */ @@ -237,9 +342,9 @@ public function testExecuteUrlKey( $isChangedWebsites, $isChangedCategories, $visibilityResult, + $productInWebsites, $expectedReplaceCount, - $expectedDeleteCount, - $productInWebsites + $expectedRemoves ) { $this->product->expects($this->any())->method('getStoreId')->will( $this->returnValue(Store::DEFAULT_STORE_ID) @@ -266,15 +371,29 @@ public function testExecuteUrlKey( ->will($this->returnValue($isChangedCategories)); $this->product->expects($this->any()) - ->method('getVisibility') - ->will($this->returnValue($visibilityResult)); + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($visibilityResult['0'])); + $this->product1->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($visibilityResult['1'])); + $this->product2->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($visibilityResult['2'])); + $this->product5->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($visibilityResult['5'])); $this->urlPersist->expects($this->exactly($expectedReplaceCount)) ->method('replace') - ->with([3 => 'rewrite']); + ->with([1 => 'rewrite']); - $this->urlPersist->expects($this->exactly($expectedDeleteCount)) - ->method('deleteByData'); + $this->dbStorage->expects($this->any()) + ->method('deleteEntitiesFromStores') + ->with( + $expectedRemoves, + [1], + ProductUrlRewriteGenerator::ENTITY_TYPE + ); $this->model->execute($this->observer); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php deleted file mode 100644 index 04bf14974b6e7..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php +++ /dev/null @@ -1,271 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; - -use Magento\Catalog\Model\Product\Visibility; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Model\Website; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver; -use Magento\Store\Api\StoreWebsiteRelationInterface; - -/** - * Class ProductToWebsiteChangeObserverTest - * - * Tests the ProductToWebsiteChangeObserver to ensure the - * replace method (refresh existing URLs) and deleteByData (remove - * old URLs) are called the correct number of times. - * - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ProductToWebsiteChangeObserverTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $urlPersist; - - /** - * @var Event|\PHPUnit_Framework_MockObject_MockObject - */ - protected $event; - - /** - * @var Observer|\PHPUnit_Framework_MockObject_MockObject - */ - protected $observer; - - /** - * @var Product|\PHPUnit_Framework_MockObject_MockObject - */ - protected $product; - - /** - * @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productRepository; - - /** - * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productUrlRewriteGenerator; - - /** - * @var ObjectManager - */ - protected $objectManager; - - /** - * @var ProductToWebsiteChangeObserver - */ - protected $model; - - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManager; - - /** - * @var Website|\PHPUnit_Framework_MockObject_MockObject - */ - protected $website1; - - /** - * @var Website|\PHPUnit_Framework_MockObject_MockObject - */ - protected $website2; - - /** - * @var StoreWebsiteRelationInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeWebsiteRelation; - - /** - * Set up - * Website_ID = 1 -> Store_ID = 1 - * Website_ID = 2 -> Store_ID = 2 & 5 - */ - protected function setUp() - { - $this->urlPersist = $this->createMock(UrlPersistInterface::class); - $this->productUrlRewriteGenerator = $this->createPartialMock( - ProductUrlRewriteGenerator::class, - ['generate'] - ); - - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); - $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); - $this->storeManager->expects($this->any()) - ->method('getWebsites') - ->will($this->returnValue([$this->website1, $this->website2])); - - $this->storeWebsiteRelation = $this->createPartialMock( - StoreWebsiteRelationInterface::class, - ['getStoreByWebsiteId'] - ); - $this->storeWebsiteRelation->expects($this->any()) - ->method('getStoreByWebsiteId') - ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); - - $this->product = $this->createPartialMock( - Product::class, - ['getId', 'getVisibility', 'getStoreId', 'getWebsiteIds'] - ); - $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); - $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class); - $this->productRepository->expects($this->any())->method('getById')->willReturn($this->product); - $this->event = $this->createPartialMock( - Event::class, - ['getProducts', 'getActionType', 'getWebsiteIds'] - ); - $this->event->expects($this->any())->method('getProducts')->willReturn([$this->product]); - $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); - $this->observer->expects($this->any())->method('getEvent')->willReturn($this->event); - - $this->objectManager = new ObjectManager($this); - $this->model = $this->objectManager->getObject( - ProductToWebsiteChangeObserver::class, - [ - 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'urlPersist' => $this->urlPersist, - 'productRepository' => $this->productRepository, - 'storeWebsiteRelation' => $this->storeWebsiteRelation - ] - ); - } - - /** - * Data provider - * - * @return array - */ - public function urlKeyDataProvider() - { - return [ - 'in_all_no_changes' => [ - 'visibilityResult' => Visibility::VISIBILITY_BOTH, - 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2], - 'actionType' => null, - 'websitesChanged' => [], - 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] - ], - 'add_to_website_from_empty' => [ - 'visibilityResult' => Visibility::VISIBILITY_BOTH, - 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2], - 'actionType' => 'add', - 'websitesChanged' => [1, 2], - 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] - ], - 'add_to_website_existing' => [ - 'visibilityResult' => Visibility::VISIBILITY_BOTH, - 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2], - 'actionType' => 'add', - 'websitesChanged' => [2], - 'rewrites' => [1 => 'url', 2 => 'url', 5 => 'url'] - ], - 'remove_single' => [ - 'visibilityResult' => Visibility::VISIBILITY_BOTH, - 'expectedReplaceCount' => 1, - 'expectedDeleteCount' => 2, - 'productInWebsites' => [1], - 'actionType' => 'remove', - 'websitesChanged' => [2], - 'rewrites' => [1 => 'url'] - ], - 'remove_all' => [ - 'visibilityResult' => Visibility::VISIBILITY_BOTH, - 'expectedReplaceCount' => 0, - 'expectedDeleteCount' => 3, - 'productInWebsites' => [], - 'actionType' => 'remove', - 'websitesChanged' => [1, 2], - 'rewrites' => [] - ], - 'not_visible_add' => [ - 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, - 'expectedReplaceCount' => 0, - 'expectedDeleteCount' => 0, - 'productInWebsites' => [1, 2], - 'actionType' => 'add', - 'websitesChanged' => [1, 2], - 'rewrites' => [] - ], - 'not_visible_remove' => [ - 'visibilityResult' => Visibility::VISIBILITY_NOT_VISIBLE, - 'expectedReplaceCount' => 0, - 'expectedDeleteCount' => 3, - 'productInWebsites' => [], - 'actionType' => 'remove', - 'websitesChanged' => [1, 2], - 'rewrites' => [] - ], - ]; - } - - /** - * @param bool $visibilityResult - * @param int $expectedReplaceCount - * @param int $expectedDeleteCount - * @param int $productInWebsites - * @param string $actionType - * @param array $websitesChanged - * @param array $rewrites - * - * @dataProvider urlKeyDataProvider - */ - public function testExecuteUrlKey( - $visibilityResult, - $expectedReplaceCount, - $expectedDeleteCount, - $productInWebsites, - $actionType, - $websitesChanged, - $rewrites - ) { - $this->product->expects($this->any())->method('getStoreId')->will( - $this->returnValue(Store::DEFAULT_STORE_ID) - ); - $this->product->expects($this->any())->method('getWebsiteIds')->will( - $this->returnValue($productInWebsites) - ); - $this->event->expects($this->any())->method('getActionType')->willReturn($actionType); - $this->event->expects($this->any())->method('getWebsiteIds')->willReturn($websitesChanged); - - $this->productUrlRewriteGenerator->expects($this->any()) - ->method('generate') - ->will($this->returnValue($rewrites)); - - $this->product->expects($this->any()) - ->method('getVisibility') - ->will($this->returnValue($visibilityResult)); - - $this->urlPersist->expects($this->exactly($expectedReplaceCount)) - ->method('replace') - ->with($rewrites); - - $this->urlPersist->expects($this->exactly($expectedDeleteCount)) - ->method('deleteByData'); - - $this->model->execute($this->observer); - } -} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php new file mode 100644 index 0000000000000..1e9d6a911fe14 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php @@ -0,0 +1,339 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogUrlRewrite\Test\Unit\Plugin; + +use Magento\Catalog\Model\Product\Action; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Website; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Plugin\ProductProcessUrlRewriteRemovingPlugin; +use Magento\Store\Api\StoreWebsiteRelationInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class ProductProcessUrlRewriteRemovingPluginTest + * + * Tests the ProductProcessUrlRewriteRemovingPlugin to ensure the + * replace method (refresh existing URLs) and deleteByData (remove + * old URLs) are called the correct number of times. + * + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductProcessUrlRewriteRemovingPluginTest extends TestCase +{ + /** + * @var UrlPersistInterface|MockObject + */ + private $urlPersist; + + /** + * @var Product|MockObject + */ + private $product1; + + /** + * @var Product|MockObject + */ + private $product2; + + /** + * @var Product|MockObject + */ + private $product3; + + /** + * @var ProductRepositoryInterface|MockObject + */ + private $productRepository; + + /** + * @var ProductUrlRewriteGenerator|MockObject + */ + private $productUrlRewriteGenerator; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductProcessUrlRewriteRemovingPlugin + */ + private $plugin; + + /** + * @var Action|MockObject + */ + private $subject; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var Website|MockObject + */ + private $website1; + + /** + * @var Website|MockObject + */ + private $website2; + + /** + * @var StoreWebsiteRelationInterface|MockObject + */ + private $storeWebsiteRelation; + + /** + * @var DbStorage|MockObject + */ + private $dbStorage; + + /** + * Set up + * Website_ID = 1 -> Store_ID = 1 + * Website_ID = 2 -> Store_ID = 2 & 5 + */ + protected function setUp() + { + $this->urlPersist = $this->createMock(UrlPersistInterface::class); + $this->productUrlRewriteGenerator = $this->createPartialMock( + ProductUrlRewriteGenerator::class, + ['generate'] + ); + + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); + $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); + $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); + $this->storeManager->expects($this->any()) + ->method('getWebsites') + ->will($this->returnValue([$this->website1, $this->website2])); + + $this->storeWebsiteRelation = $this->createPartialMock( + StoreWebsiteRelationInterface::class, + ['getStoreByWebsiteId'] + ); + $this->storeWebsiteRelation->expects($this->any()) + ->method('getStoreByWebsiteId') + ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); + + $this->product1 = $this->createPartialMock( + Product::class, + ['getWebsiteIds', 'isVisibleInSiteVisibility'] + ); + $this->product2 = $this->createPartialMock( + Product::class, + ['getWebsiteIds', 'isVisibleInSiteVisibility'] + ); + $this->product3 = $this->createPartialMock( + Product::class, + ['getWebsiteIds', 'isVisibleInSiteVisibility'] + ); + + $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValueMap([ + [1, false, 0, true, $this->product1], + [2, false, 0, true, $this->product2], + [3, false, 0, true, $this->product3] + ])); + + $this->dbStorage = $this->createPartialMock(DbStorage::class, ['deleteEntitiesFromStores']); + + $this->subject = $this->createMock( + Action::class + ); + + $this->objectManager = new ObjectManager($this); + $this->plugin = $this->objectManager->getObject( + ProductProcessUrlRewriteRemovingPlugin::class, + [ + 'productRepository' => $this->productRepository, + 'storeWebsiteRelation' => $this->storeWebsiteRelation, + 'urlPersist' => $this->urlPersist, + 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, + 'dbStorage' => $this->dbStorage + + ] + ); + } + + /** + * Data provider + * + * @return array + */ + public function afterUpdateWebsitesDataProvider() + { + return [ + 'add_new_websites_1' => [ + 'products' => [ + '1' => ['visibilityResult' => true, 'websiteids' => [1]], + '2' => ['visibilityResult' => true, 'websiteids' => [1]], + '3' => ['visibilityResult' => true, 'websiteids' => [1]], + ], + 'productids' => [1,2,3], + 'type' => 'add', + 'websiteids' => [2], + 'expectedReplaceCount' => 3, + 'expectedStoreRemovals' => [], + 'expectedDeleteCount' => 0, + 'rewrites' => [true] + ], + 'add_new_websites_2' => [ + 'products' => [ + '1' => ['visibilityResult' => true, 'websiteids' => [1]], + '2' => ['visibilityResult' => true, 'websiteids' => [1]], + '3' => ['visibilityResult' => true, 'websiteids' => [1]], + ], + 'productids' => [1,2,3], + 'type' => 'add', + 'websiteids' => [2], + 'expectedReplaceCount' => 3, + 'expectedStoreRemovals' => [], + 'expectedDeleteCount' => 0, + 'rewrites' => [true] + ], + 'remove_all' => [ + 'products' => [ + '1' => ['visibilityResult' => true, 'websiteids' => [1,2]], + '2' => ['visibilityResult' => true, 'websiteids' => [1,2]], + '3' => ['visibilityResult' => true, 'websiteids' => [1,2]], + ], + 'productids' => [1,2,3], + 'type' => 'remove', + 'websiteids' => [1,2], + 'expectedReplaceCount' => 0, + 'expectedStoreRemovals' => [1,2,5], + 'expectedDeleteCount' => 1, + 'rewrites' => [] + ], + 'remove_single' => [ + 'products' => [ + '1' => ['visibilityResult' => true, 'websiteids' => [1,2]], + '2' => ['visibilityResult' => true, 'websiteids' => [1,2]], + '3' => ['visibilityResult' => true, 'websiteids' => [1,2]], + ], + 'productids' => [1,2,3], + 'type' => 'remove', + 'websiteids' => [2], + 'expectedReplaceCount' => 0, + 'expectedStoreRemovals' => [2,5], + 'expectedDeleteCount' => 1, + 'rewrites' => [] + ], + 'not_visible_add_1' => [ + 'products' => [ + '1' => ['visibilityResult' => false, 'websiteids' => [1]], + '2' => ['visibilityResult' => false, 'websiteids' => [1]], + '3' => ['visibilityResult' => false, 'websiteids' => [1]], + ], + 'productids' => [1,2,3], + 'type' => 'add', + 'websiteids' => [2], + 'expectedReplaceCount' => 0, + 'expectedStoreRemovals' => [], + 'expectedDeleteCount' => 0, + 'rewrites' => [true] + ], + 'not_visible_add_2' => [ + 'products' => [ + '1' => ['visibilityResult' => false, 'websiteids' => [1]], + '2' => ['visibilityResult' => false, 'websiteids' => [1]], + '3' => ['visibilityResult' => true, 'websiteids' => [1]], + ], + 'productids' => [1,2,3], + 'type' => 'add', + 'websiteids' => [2], + 'expectedReplaceCount' => 1, + 'expectedStoreRemovals' => [], + 'expectedDeleteCount' => 0, + 'rewrites' => [true] + ], + + ]; + } + + /** + * @param array $products + * @param array $productids + * @param string $type + * @param array $websiteids + * @param int $expectedReplaceCount + * @param array $expectedStoreRemovals + * @param int $expectedDeleteCount + * @param array $rewrites + * + * @dataProvider afterUpdateWebsitesDataProvider + */ + public function testAfterUpdateWebsites( + $products, + $productids, + $type, + $websiteids, + $expectedReplaceCount, + $expectedStoreRemovals, + $expectedDeleteCount, + $rewrites + ) { + + $this->productUrlRewriteGenerator->expects($this->any()) + ->method('generate') + ->will($this->returnValue($rewrites)); + + $this->product1->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue($products['1']['websiteids'])); + $this->product2->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue($products['2']['websiteids'])); + $this->product3->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue($products['3']['websiteids'])); + + $this->product1->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($products['1']['visibilityResult'])); + $this->product2->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($products['2']['visibilityResult'])); + $this->product3->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($products['3']['visibilityResult'])); + + $this->urlPersist->expects($this->exactly($expectedReplaceCount)) + ->method('replace') + ->with($rewrites); + + $this->dbStorage->expects($this->exactly($expectedDeleteCount)) + ->method('deleteEntitiesFromStores') + ->with( + $expectedStoreRemovals, + $productids, + ProductUrlRewriteGenerator::ENTITY_TYPE + ); + + $this->plugin->afterUpdateWebsites( + $this->subject, + null, + $productids, + $websiteids, + $type + ); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index 5fb7d33546d60..6efaa5dd8517e 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -70,4 +70,8 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Action"> + <plugin name="process_product_url_rewrites_on_website_change" + type="Magento\CatalogUrlRewrite\Plugin\ProductProcessUrlRewriteRemovingPlugin"/> + </type> </config> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/events.xml index a6e5a5265b6c8..728442acf7a44 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/events.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/events.xml @@ -36,7 +36,4 @@ <event name="catalog_category_move_after"> <observer name="process_url_rewrite_moving" instance="Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteMovingObserver"/> </event> - <event name="catalog_product_to_website_change"> - <observer name="catalog_product_to_website_change" instance="Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver"/> - </event> </config> diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index f0e94e8379ad2..a5d23f99bc0cd 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -375,4 +375,33 @@ public function deleteByData(array $data) $this->prepareSelect($data)->deleteFromSelect($this->resource->getTableName(self::TABLE_NAME)) ); } + + /** + * Function deleteEntitiesFromStores + * + * Deletes multiple URL Rewrites from database + * + * @param array $store_ids + * @param array $entity_ids + * @param int $entity_type + */ + public function deleteEntitiesFromStores($store_ids, $entity_ids, $entity_type) + { + $select = $this->connection->select(); + $select->from($this->resource->getTableName(self::TABLE_NAME)); + + $select->where( + $this->connection->quoteIdentifier( + UrlRewrite::STORE_ID + ) . ' IN (' . $this->connection->quote($store_ids, 'INTEGER') . ')' . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_ID + ) . ' IN (' . $this->connection->quote($entity_ids, 'INTEGER') . ')' . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_TYPE + ) . ' = ' . $this->connection->quote($entity_type) + ); + $select = $select->deleteFromSelect($this->resource->getTableName(self::TABLE_NAME)); + $this->connection->query($select); + } } diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml index 022d28ed692c1..a5473863b9ba9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5342"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml index e14bb5342db91..d26ee18b1c719 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml @@ -16,7 +16,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-25622"/> <group value="catalog"/> - <group value="url_rewrite"/> + <group value="urlRewrite"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 7f9ee3020c388..30eafbbd39a99 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -15,7 +15,7 @@ <description value="DEPRECATED. Check url rewrites in catalog categories after changing url key for store view and moving category"/> <severity value="CRITICAL"/> <testCaseId value="MC-5352"/> - <group value="url_rewrite"/> + <group value="urlRewrite"/> <group value="mtf_migrated"/> <skip> <issueId value="DEPRECATED">Use AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test instead</issueId> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml index 732fc22aaf84a..3216eec1a2196 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5335"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 867b3ee54161c..6ffce05552aae 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5334"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index ab18add56aeb9..7b6c222d65e92 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5336"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml index bd4f7d7a32165..662913adc4e47 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5345"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml index 074140845c8a6..049402aba12c5 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5346"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index cdd7c334e35cd..40fb0b7763723 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5343"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml index 593c4282fc516..7bb97d7258f2f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5344"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml index c51030315b287..dccc98d107581 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5339"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml index d433aa7557094..7edb4ac1eec3f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5338"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index e1ff4f598943e..90ec26c7d1284 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5341"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 3fa4e6b7bad90..1023c7c58c3d7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5340"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml index 1171a5e8b79c3..3f7ce95cb05b4 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml index 566204094cabd..9378d40b5d11c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml index 22cb74bf96ad6..85d1f42449377 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml index ceb71f65e4489..125008e9cf2e0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml @@ -14,6 +14,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml index e2f0d6af0deab..16a6f4b7562d7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -16,6 +16,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-14648"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml index 741be6985d517..ccd6175e7cbe7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <description value="Log in to admin and delete CMS Page URL rewrite with Permanent Redirect"/> <testCaseId value="MC-14649"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml index e3d417f3c1f39..5a873cdb9b5b7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -16,6 +16,7 @@ <testCaseId value="MC-14650"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml index 8e0ea8334a2cc..211a4b244300a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml @@ -14,6 +14,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml index 443307b427b42..18a6ef868ff51 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-14202"/> <group value="menu"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml index fb8f200741c8a..ace9f3f9b0abb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5358"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml index 72180105b38f8..66c1b5ae20103 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5355"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 3fb8e5da39caf..014f5de510bde 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5357"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index bea5c44461a70..794c3991f2b6e 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5356"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml index b2fa13ead1164..90e0e03293556 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml @@ -15,6 +15,7 @@ <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml index 3bf278db8410a..4186bf61d2298 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml @@ -14,6 +14,7 @@ <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> <group value="cMSContent"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml index b00241bc3acac..0057ec91934bf 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml index b799a58ac9e40..6dba13d8f0679 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml index 3cf30444fcaee..dd8c93c50480a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml index b95d1eaa44d7f..dba869196dc13 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index 74f3a60f35cea..b80dc86e48c8e 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,6 +15,7 @@ <testCaseId value="MC-5351"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index c72a58197b1fd..eae90a47cead3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -5,25 +5,46 @@ */ namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\ObjectManagerInterface; use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\Store; +use PHPUnit\Framework\TestCase; /** * @magentoAppArea adminhtml * @magentoDbIsolation disabled */ -class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase +class ProductProcessUrlRewriteSavingObserverTest extends TestCase { - /** @var \Magento\Framework\ObjectManagerInterface */ - protected $objectManager; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var StoreManagerInterface $storeManager + */ + private $storeManager; + + /** + * @var ProductRepositoryInterface $productRepository + */ + private $productRepository; /** * Set up */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); } /** @@ -32,8 +53,8 @@ protected function setUp() */ private function getActualResults(array $filter) { - /** @var \Magento\UrlRewrite\Model\UrlFinderInterface $urlFinder */ - $urlFinder = $this->objectManager->get(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + /** @var UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualResults = []; foreach ($urlFinder->findAllByData($filter) as $url) { $actualResults[] = [ @@ -53,16 +74,14 @@ private function getActualResults(array $filter) */ public function testUrlKeyHasChangedInGlobalContext() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - /** @var \Magento\Catalog\Model\Product $product*/ - $product = $productRepository->get('product1'); + $testStore1 = $this->storeManager->getStore('default'); + $testStore4 = $this->storeManager->getStore('test'); - /** @var StoreManagerInterface $storeManager */ - $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore(0); + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); - $testStore = $storeManager->getStore('test'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', ]; @@ -73,14 +92,14 @@ public function testUrlKeyHasChangedInGlobalContext() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore->getId(), + 'store_id' => $testStore4->getId(), ], ]; $actual = $this->getActualResults($productFilter); @@ -91,7 +110,7 @@ public function testUrlKeyHasChangedInGlobalContext() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $product->save(); + $this->productRepository->save($product); $expected = [ [ @@ -99,28 +118,28 @@ public function testUrlKeyHasChangedInGlobalContext() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), ], [ 'request_path' => "new-url.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore->getId(), + 'store_id' => $testStore4->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => $testStore->getId(), + 'store_id' => $testStore4->getId(), ], ]; @@ -136,16 +155,13 @@ public function testUrlKeyHasChangedInGlobalContext() */ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - /** @var \Magento\Catalog\Model\Product $product*/ - $product = $productRepository->get('product1'); + $testStore1 = $this->storeManager->getStore('default'); + $testStore4 = $this->storeManager->getStore('test'); - /** @var StoreManagerInterface $storeManager */ - $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore(1); + $this->storeManager->setCurrentStore($testStore1); - $testStore = $storeManager->getStore('test'); + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', @@ -154,7 +170,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $product->save(); + $this->productRepository->save($product); $expected = [ [ @@ -162,21 +178,21 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore->getId(), + 'store_id' => $testStore4->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), ], ]; @@ -192,16 +208,13 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() */ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirection() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - /** @var \Magento\Catalog\Model\Product $product*/ - $product = $productRepository->get('product1'); + $testStore1 = $this->storeManager->getStore('default'); + $testStore4 = $this->storeManager->getStore('test'); - /** @var StoreManagerInterface $storeManager */ - $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore(1); + $this->storeManager->setCurrentStore(1); - $testStore = $storeManager->getStore('test'); + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', @@ -210,7 +223,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio $product->setData('save_rewrites_history', false); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $product->save(); + $this->productRepository->save($product); $expected = [ [ @@ -218,17 +231,400 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => 1, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ], + ]; + + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + } + + /** + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAddAndRemoveProductFromWebsite() + { + $testStore1 = $this->storeManager->getStore('default'); + $testStore2 = $this->storeManager->getStore('fixture_second_store'); + $testStore3 = $this->storeManager->getStore('fixture_third_store'); + $testStore4 = $this->storeManager->getStore('test'); + + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); + + $productFilter = [ + UrlRewrite::ENTITY_TYPE => 'product', + ]; + + //Product in 1st website. Should result in being in 1st and 4th stores. + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Add product to websites corresponding to all 4 stores. + //Rewrites should not be present as the product is hidden + //at the global scope. + $product->setWebsiteIds( + array_unique( + [ + $testStore1->getWebsiteId(), + $testStore2->getWebsiteId(), + $testStore3->getWebsiteId(), + $testStore4->getWebsiteId() + ] + ) + ); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore2->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore3->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; + + //Add product to websites corresponding to stores 2 and 3. + $product->setWebsiteIds( + array_unique( + [ + $testStore2->getWebsiteId(), + $testStore3->getWebsiteId(), + ] + ) + ); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore2->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore3->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + } + + /** + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testChangeVisibilityGlobalScope() + { + $testStore1 = $this->storeManager->getStore('default'); + $testStore2 = $this->storeManager->getStore('fixture_second_store'); + $testStore3 = $this->storeManager->getStore('fixture_third_store'); + $testStore4 = $this->storeManager->getStore('test'); + + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); + + $productFilter = [ + UrlRewrite::ENTITY_TYPE => 'product', + ]; + + //Product in 1st website. Should result in being in 1st and 4th stores. + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Set product to be not visible at global scope + $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); + $this->productRepository->save($product); + $expected = []; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Add product to websites corresponding to all 4 stores. + //Rewrites should not be present as the product is hidden + //at the global scope. + $product->setWebsiteIds( + array_unique( + [ + $testStore1->getWebsiteId(), + $testStore2->getWebsiteId(), + $testStore3->getWebsiteId(), + $testStore4->getWebsiteId() + ] + ) + ); + $this->productRepository->save($product); + $expected = []; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Set product to be visible at global scope + $product->setVisibility(Visibility::VISIBILITY_BOTH); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore2->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore3->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + } + + /** + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testChangeVisibilityLocalScope() + { + $testStore1 = $this->storeManager->getStore('default'); + $testStore2 = $this->storeManager->getStore('fixture_second_store'); + $testStore3 = $this->storeManager->getStore('fixture_third_store'); + $testStore4 = $this->storeManager->getStore('test'); + + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + + /** @var Product $product*/ + $product = $this->productRepository->get('product1'); + + $productFilter = [ + UrlRewrite::ENTITY_TYPE => 'product', + ]; + + //Product in 1st website. Should result in being in 1st and 4th stores. + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Set product to be not visible at store 4 scope + //Rewrite should only be present for store 1 + $this->storeManager->setCurrentStore($testStore4); + $product = $this->productRepository->get('product1'); + $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ] + ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + //Add product to websites corresponding to all 4 stores. + //Rewrites should be present for stores 1,2 and 3. + //No rewrites should be present for store 4 as that is not visible + //at local scope. + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + $product = $this->productRepository->get('product1'); + $product->setWebsiteIds( + array_unique( + [ + $testStore1->getWebsiteId(), + $testStore2->getWebsiteId(), + $testStore3->getWebsiteId(), + $testStore4->getWebsiteId() + ] + ) + ); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore->getId(), + 'store_id' => $testStore2->getId(), ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore3->getId(), + ] ]; + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + //Set product to be visible at store 4 scope only + $this->storeManager->setCurrentStore($testStore4); + $product = $this->productRepository->get('product1'); + $product->setVisibility(Visibility::VISIBILITY_BOTH); + $this->productRepository->save($product); + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore1->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore2->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore3->getId(), + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore4->getId(), + ] + ]; $actual = $this->getActualResults($productFilter); foreach ($expected as $row) { $this->assertContains($row, $actual); From 790546250726fe83364a391cf544ae9b89a67742 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Wed, 4 Mar 2020 13:10:38 +0000 Subject: [PATCH 266/734] Added CatalogUrlRewrite MFTF tests to urlRewrite test group --- .../Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml | 1 + .../Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml | 1 + .../Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml | 1 + .../Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml | 1 + 4 files changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index c3c7e3c3281f4..d51c9884ab3fa 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -17,6 +17,7 @@ <testCaseId value="MC-17515"/> <useCaseId value="MAGETWO-69825"/> <group value="CatalogUrlRewrite"/> + <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index cc5f09faca57b..211a1b107b596 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -17,6 +17,7 @@ <testCaseId value="MAGETWO-97224"/> <useCaseId value="MAGETWO-64191"/> <group value="CatalogUrlRewrite"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index c3a358bbbd292..a561b916d4f10 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -15,6 +15,7 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-94934"/> <group value="CatalogUrlRewrite"/> + <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml index 6674c55064169..8bef7a81ebd18 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml @@ -15,6 +15,7 @@ <features value="CatalogUrlRewrite"/> <severity value="MAJOR"/> <group value="CatalogUrlRewrite"/> + <group value="urlRewrite"/> </annotations> <before> <magentoCLI command="config:set catalog/seo/category_url_suffix ''" stepKey="setCategoryUrlSuffix"/> From 98b745f12bb88e712c703e5abcc390a4af26d6fb Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Wed, 4 Mar 2020 14:02:59 +0000 Subject: [PATCH 267/734] Resolved test failures --- .../Plugin/ProductProcessUrlRewriteRemovingPlugin.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php index 1bec43c4fd75b..d536b61bd4a79 100644 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php @@ -28,7 +28,7 @@ class ProductProcessUrlRewriteRemovingPlugin /** * @var ProductRepositoryInterface $productRepository */ - protected $productRepository; + private $productRepository; /** * @var StoreWebsiteRelationInterface $storeWebsiteRelation @@ -38,12 +38,12 @@ class ProductProcessUrlRewriteRemovingPlugin /** * @var UrlPersistInterface $urlPersist */ - protected $urlPersist; + private $urlPersist; /** * @var ProductUrlRewriteGenerator $productUrlRewriteGenerator */ - protected $productUrlRewriteGenerator; + private $productUrlRewriteGenerator; /** * @var DbStorage $dbStorage @@ -72,13 +72,16 @@ public function __construct( } /** + * Function afterUpdateWebsites + * * @param Action $subject - * @param null $result + * @param $result * @param array $productIds * @param array $websiteIds * @param string $type * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function afterUpdateWebsites( Action $subject, From 8f4e576d2a36e76e1ef5fddfda9c70770b77bb0d Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Wed, 4 Mar 2020 14:43:27 +0000 Subject: [PATCH 268/734] Fixed test failure --- .../Plugin/ProductProcessUrlRewriteRemovingPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php index d536b61bd4a79..20d376bd32825 100644 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php @@ -75,7 +75,7 @@ public function __construct( * Function afterUpdateWebsites * * @param Action $subject - * @param $result + * @param void $result * @param array $productIds * @param array $websiteIds * @param string $type From 9c0211605537143b9cde9516a1be4a3173edb0b0 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Wed, 4 Mar 2020 17:58:06 +0200 Subject: [PATCH 269/734] Display category filter item in layered navigation based on the system configuration from admin area --- .../Model/Config/LayerCategoryConfig.php | 82 +++++++++++++++++ .../Catalog/Model/Layer/FilterList.php | 22 ++++- .../Test/Unit/Model/Layer/FilterListTest.php | 88 ++++++++++++++++++- .../Magento/Catalog/etc/adminhtml/system.xml | 6 ++ app/code/Magento/Catalog/etc/config.xml | 3 + 5 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php diff --git a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php new file mode 100644 index 0000000000000..3ee9bd888f568 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Config; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Config for category in the layered navigation + */ +class LayerCategoryConfig +{ + private const XML_PATH_CATALOG_LAYERED_NAVIGATION_DISPLAY_CATEGORY = 'catalog/layered_navigation/display_category'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * LayerCategoryConfig constructor + * + * @param ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager + ) { + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; + } + + /** + * Check if category filter item should be added in the layered navigation + * + * @param string $scopeType + * @param null|int|string $scopeCode + * + * @return bool + */ + public function isCategoryFilterVisibleInLayerNavigation( + $scopeType = ScopeInterface::SCOPE_STORES, + $scopeCode = null + ): bool { + if (!$scopeCode) { + $scopeCode = $this->getStoreId(); + } + + return $this->scopeConfig->isSetFlag( + static::XML_PATH_CATALOG_LAYERED_NAVIGATION_DISPLAY_CATEGORY, + $scopeType, + $scopeCode + ); + } + + /** + * Get the current store ID + * + * @return int|null + */ + protected function getStoreId(): ?int + { + try { + return (int) $this->storeManager->getStore()->getId(); + } catch (NoSuchEntityException $e) { + return null; + } + } +} diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php index b8e9b8ad4aaa5..2f32971c80bed 100644 --- a/app/code/Magento/Catalog/Model/Layer/FilterList.php +++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php @@ -7,6 +7,9 @@ namespace Magento\Catalog\Model\Layer; +use Magento\Catalog\Model\Config\LayerCategoryConfig; +use Magento\Framework\App\ObjectManager; + /** * Layer navigation filters */ @@ -44,18 +47,27 @@ class FilterList */ protected $filters = []; + /** + * @var LayerCategoryConfig|null + */ + private $layerCategoryConfig; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param FilterableAttributeListInterface $filterableAttributes * @param array $filters + * @param LayerCategoryConfig|null $layerCategoryConfig */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, FilterableAttributeListInterface $filterableAttributes, - array $filters = [] + array $filters = [], + LayerCategoryConfig $layerCategoryConfig = null ) { $this->objectManager = $objectManager; $this->filterableAttributes = $filterableAttributes; + $this->layerCategoryConfig = $layerCategoryConfig ?: + ObjectManager::getInstance()->get(LayerCategoryConfig::class); /** Override default filter type models */ $this->filterTypes = array_merge($this->filterTypes, $filters); @@ -70,9 +82,11 @@ public function __construct( public function getFilters(\Magento\Catalog\Model\Layer $layer) { if (!count($this->filters)) { - $this->filters = [ - $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), - ]; + if ($this->layerCategoryConfig->isCategoryFilterVisibleInLayerNavigation()) { + $this->filters = [ + $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), + ]; + } foreach ($this->filterableAttributes->getList() as $attribute) { $this->filters[] = $this->createAttributeFilter($attribute, $layer); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php index 731c5efd99746..92734bae2e444 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php @@ -7,8 +7,13 @@ namespace Magento\Catalog\Test\Unit\Model\Layer; +use Magento\Catalog\Model\Config\LayerCategoryConfig; use \Magento\Catalog\Model\Layer\FilterList; +use PHPUnit\Framework\MockObject\MockObject; +/** + * Filter List Test + */ class FilterListTest extends \PHPUnit\Framework\TestCase { /** @@ -36,6 +41,14 @@ class FilterListTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @var LayerCategoryConfig|MockObject + */ + private $layerCategoryConfigMock; + + /** + * Set Up + */ protected function setUp() { $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); @@ -51,8 +64,14 @@ protected function setUp() ]; $this->layerMock = $this->createMock(\Magento\Catalog\Model\Layer::class); + $this->layerCategoryConfigMock = $this->createMock(LayerCategoryConfig::class); - $this->model = new FilterList($this->objectManagerMock, $this->attributeListMock, $filters); + $this->model = new FilterList( + $this->objectManagerMock, + $this->attributeListMock, + $filters, + $this->layerCategoryConfigMock + ); } /** @@ -90,9 +109,57 @@ public function testGetFilters($method, $value, $expectedClass) ->method('getList') ->will($this->returnValue([$this->attributeMock])); + $this->layerCategoryConfigMock->expects($this->once()) + ->method('isCategoryVisibleInLayer') + ->willReturn(true); + $this->assertEquals(['filter', 'filter'], $this->model->getFilters($this->layerMock)); } + /** + * Test filters list result when category should not be included + * + * @param string $method + * @param string $value + * @param string $expectedClass + * @param array $expectedResult + * + * @dataProvider getFiltersWithoutCategoryDataProvider + * + * @return void + */ + public function testGetFiltersWithoutCategoryFilter( + string $method, + string $value, + string $expectedClass, + array $expectedResult + ): void { + $this->objectManagerMock->expects($this->at(0)) + ->method('create') + ->with( + $expectedClass, + [ + 'data' => ['attribute_model' => $this->attributeMock], + 'layer' => $this->layerMock + ] + ) + ->will($this->returnValue('filter')); + + $this->attributeMock->expects($this->once()) + ->method($method) + ->will($this->returnValue($value)); + + $this->attributeListMock->expects($this->once()) + ->method('getList') + ->will($this->returnValue([$this->attributeMock])); + + $this->layerCategoryConfigMock->expects($this->once()) + ->method('isCategoryVisibleInLayer') + ->willReturn(false); + + $this->assertEquals($expectedResult, $this->model->getFilters($this->layerMock)); + } + /** * @return array */ @@ -116,4 +183,23 @@ public function getFiltersDataProvider() ] ]; } + + /** + * Provides attribute filters without category item + * + * @return array + */ + public function getFiltersWithoutCategoryDataProvider(): array + { + return [ + 'Filters contains only price attribute' => [ + 'method' => 'getFrontendInput', + 'value' => 'price', + 'expectedClass' => 'PriceFilterClass', + 'expectedResult' => [ + 'filter' + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index f59990cdcea96..f548c23b68ce3 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -138,6 +138,12 @@ <hide_in_single_store_mode>1</hide_in_single_store_mode> </field> </group> + <group id="layered_navigation"> + <field id="display_category" translate="label" type="select" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Display Category</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> + </group> <group id="navigation" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Category Top Navigation</label> <field id="max_depth" translate="label" type="text" sortOrder="1" showInDefault="1" canRestore="1"> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 68289904db0cf..e7beb3d083226 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -54,6 +54,9 @@ <time_format>12h</time_format> <forbidden_extensions>php,exe</forbidden_extensions> </custom_options> + <layered_navigation> + <display_category>1</display_category> + </layered_navigation> </catalog> <indexer> <catalog_product_price> From 4acf45408b3b11e021ae474c5e5ef91e1e274159 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 4 Mar 2020 10:00:48 -0600 Subject: [PATCH 270/734] MQE-1960: MFTF 3.0.0 - Update composer.lock --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 5775009bd433e..1789e7501af29 100644 --- a/composer.lock +++ b/composer.lock @@ -7385,12 +7385,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "3a18c6bca1d7fd5a8a62033148968a4888ff8f21" + "reference": "4462b2cf6c978a2554b8f1789c676f562acd261f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/3a18c6bca1d7fd5a8a62033148968a4888ff8f21", - "reference": "3a18c6bca1d7fd5a8a62033148968a4888ff8f21", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4462b2cf6c978a2554b8f1789c676f562acd261f", + "reference": "4462b2cf6c978a2554b8f1789c676f562acd261f", "shasum": "" }, "require": { @@ -7462,7 +7462,7 @@ "magento", "testing" ], - "time": "2020-03-02T19:48:04+00:00" + "time": "2020-03-03T20:05:39+00:00" }, { "name": "mikey179/vfsstream", From 6ef861e2accc2e91db4b0e0e08c3732e0dad7907 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Wed, 4 Mar 2020 11:22:40 -0500 Subject: [PATCH 271/734] use fully qualified class names (magento/magento2#22833: Short-term admin accounts) --- .../Api/Data/UserExpirationInterface.php | 8 +++-- .../Security/Model/Plugin/AdminUserForm.php | 22 ++++++++----- .../Model/Plugin/UserValidationRules.php | 8 +++-- .../Magento/Security/Model/UserExpiration.php | 6 ++-- .../Model/UserExpiration/Validator.php | 14 ++++---- .../Security/Model/UserExpirationManager.php | 33 +++++++++++-------- .../Observer/AdminUserAuthenticateBefore.php | 9 ++--- .../Security/Observer/AfterAdminUserSave.php | 15 +++++---- 8 files changed, 68 insertions(+), 47 deletions(-) diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php index 3a930aeb584ab..e3c89ae253b01 100644 --- a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -8,6 +8,8 @@ namespace Magento\Security\Api\Data; +use \Magento\Security\Api\Data\UserExpirationExtensionInterface; + /** * Interface UserExpirationInterface to be used as a DTO for expires_at property on User model. */ @@ -51,15 +53,15 @@ public function setUserId($userId); /** * Retrieve existing extension attributes object or create a new one. * - * @return \Magento\Security\Api\Data\UserExpirationExtensionInterface|null + * @return UserExpirationExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param \Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes + * @param UserExpirationExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes(\Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes); + public function setExtensionAttributes(UserExpirationExtensionInterface $extensionAttributes); } diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php index ad3c352b7f369..0c024d15ef4ae 100644 --- a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -7,6 +7,10 @@ namespace Magento\Security\Model\Plugin; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Security\Model\ResourceModel\UserExpiration; +use Magento\Security\Model\UserExpirationFactory; + /** * Add the `expires_at` form field to the User main form. */ @@ -14,31 +18,31 @@ class AdminUserForm { /** - * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface + * @var TimezoneInterface */ private $localeDate; /** - * @var \Magento\Security\Model\ResourceModel\UserExpiration + * @var UserExpiration */ private $userExpirationResource; /** - * @var \Magento\Security\Api\Data\UserExpirationInterfaceFactory + * @var UserExpirationFactory */ private $userExpirationFactory; /** * UserForm constructor. * - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory - * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + * @param TimezoneInterface $localeDate + * @param UserExpirationFactory $userExpirationFactory + * @param UserExpiration $userExpirationResource */ public function __construct( - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, - \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + TimezoneInterface $localeDate, + UserExpirationFactory $userExpirationFactory, + UserExpiration $userExpirationResource ) { $this->localeDate = $localeDate; $this->userExpirationResource = $userExpirationResource; diff --git a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php index 00d5bc13b65e8..7fddbb21200f4 100644 --- a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php +++ b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php @@ -7,20 +7,22 @@ namespace Magento\Security\Model\Plugin; +use Magento\Security\Model\UserExpiration\Validator; + /** * \Magento\User\Model\UserValidationRules decorator */ class UserValidationRules { - /**@var \Magento\Security\Model\UserExpiration\Validator */ + /**@var Validator */ private $validator; /** * UserValidationRules constructor. * - * @param \Magento\Security\Model\UserExpiration\Validator $validator + * @param Validator $validator */ - public function __construct(\Magento\Security\Model\UserExpiration\Validator $validator) + public function __construct(Validator $validator) { $this->validator = $validator; } diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php index 51a41b5c0a5be..e6c711b7ac049 100644 --- a/app/code/Magento/Security/Model/UserExpiration.php +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -7,12 +7,14 @@ namespace Magento\Security\Model; +use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\Security\Api\Data\UserExpirationExtensionInterface; use Magento\Security\Api\Data\UserExpirationInterface; /** * Admin User Expiration model. */ -class UserExpiration extends \Magento\Framework\Model\AbstractExtensibleModel implements UserExpirationInterface +class UserExpiration extends AbstractExtensibleModel implements UserExpirationInterface { /** @@ -78,7 +80,7 @@ public function getExtensionAttributes() /** * @inheritDoc */ - public function setExtensionAttributes(\Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes) + public function setExtensionAttributes(UserExpirationExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php index 46633db260692..62dbd7852ff33 100644 --- a/app/code/Magento/Security/Model/UserExpiration/Validator.php +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -7,6 +7,8 @@ namespace Magento\Security\Model\UserExpiration; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\Validator\AbstractValidator; /** @@ -15,21 +17,21 @@ class Validator extends AbstractValidator { - /**@var \Magento\Framework\Stdlib\DateTime\TimezoneInterface */ + /**@var TimezoneInterface */ private $timezone; - /**@var \Magento\Framework\Stdlib\DateTime\DateTime */ + /**@var DateTime */ private $dateTime; /** * Validator constructor. * - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + * @param TimezoneInterface $timezone + * @param DateTime $dateTime */ public function __construct( - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + TimezoneInterface $timezone, + DateTime $dateTime ) { $this->timezone = $timezone; $this->dateTime = $dateTime; diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php index b9645ba52ff67..fe6b87de5a8ec 100644 --- a/app/code/Magento/Security/Model/UserExpirationManager.php +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -7,7 +7,12 @@ namespace Magento\Security\Model; +use Magento\Backend\Model\Auth\Session; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory as AdminSessionCollectionFactory; use Magento\Security\Model\ResourceModel\UserExpiration\Collection as ExpiredUsersCollection; +use Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory as UserExpirationCollectionFactory; +use Magento\User\Model\ResourceModel\User\CollectionFactory as UserCollectionFactory; /** * Class to handle admin user expirations. Temporary admin users can be created with an expiration @@ -19,7 +24,7 @@ class UserExpirationManager { /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ private $dateTime; @@ -34,7 +39,7 @@ class UserExpirationManager private $adminSessionInfoCollectionFactory; /** - * @var \Magento\Backend\Model\Auth\Session + * @var Session */ private $authSession; @@ -44,27 +49,27 @@ class UserExpirationManager private $userExpirationCollectionFactory; /** - * @var \Magento\User\Model\ResourceModel\User\CollectionFactory + * @var UserCollectionFactory */ private $userCollectionFactory; /** * UserExpirationManager constructor. * - * @param \Magento\Backend\Model\Auth\Session $authSession + * @param Session $authSession * @param ConfigInterface $securityConfig - * @param ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory - * @param ResourceModel\UserExpiration\CollectionFactory $userExpirationCollectionFactory - * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + * @param AdminSessionCollectionFactory $adminSessionInfoCollectionFactory + * @param UserExpirationCollectionFactory $userExpirationCollectionFactory + * @param UserCollectionFactory $userCollectionFactory + * @param DateTime $dateTime */ public function __construct( - \Magento\Backend\Model\Auth\Session $authSession, - \Magento\Security\Model\ConfigInterface $securityConfig, - \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory, - \Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory $userExpirationCollectionFactory, - \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory, - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime + Session $authSession, + ConfigInterface $securityConfig, + AdminSessionCollectionFactory $adminSessionInfoCollectionFactory, + UserExpirationCollectionFactory $userExpirationCollectionFactory, + UserCollectionFactory $userCollectionFactory, + DateTime $dateTime ) { $this->dateTime = $dateTime; $this->securityConfig = $securityConfig; diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php index 3c07285afcb32..2d0f7bc0f0ac0 100644 --- a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -11,6 +11,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\Plugin\AuthenticationException; use Magento\Security\Model\UserExpirationManager; +use Magento\User\Model\UserFactory; /** * Check for expired users. @@ -23,7 +24,7 @@ class AdminUserAuthenticateBefore implements ObserverInterface private $userExpirationManager; /** - * @var \Magento\User\Model\UserFactory + * @var UserFactory */ private $userFactory; @@ -31,11 +32,11 @@ class AdminUserAuthenticateBefore implements ObserverInterface * AdminUserAuthenticateBefore constructor. * * @param UserExpirationManager $userExpirationManager - * @param \Magento\User\Model\UserFactory $userFactory + * @param UserFactory $userFactory */ public function __construct( - \Magento\Security\Model\UserExpirationManager $userExpirationManager, - \Magento\User\Model\UserFactory $userFactory + UserExpirationManager $userExpirationManager, + UserFactory $userFactory ) { $this->userExpirationManager = $userExpirationManager; $this->userFactory = $userFactory; diff --git a/app/code/Magento/Security/Observer/AfterAdminUserSave.php b/app/code/Magento/Security/Observer/AfterAdminUserSave.php index 247e88a720ceb..d11c1bfdcdf17 100644 --- a/app/code/Magento/Security/Observer/AfterAdminUserSave.php +++ b/app/code/Magento/Security/Observer/AfterAdminUserSave.php @@ -9,6 +9,8 @@ use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Security\Model\ResourceModel\UserExpiration as UserExpirationResource; +use Magento\Security\Model\UserExpirationFactory; /** * Save UserExpiration on admin user record. @@ -16,24 +18,24 @@ class AfterAdminUserSave implements ObserverInterface { /** - * @var \Magento\Security\Model\UserExpirationFactory + * @var UserExpirationFactory */ private $userExpirationFactory; /** - * @var \Magento\Security\Model\ResourceModel\UserExpiration + * @var UserExpirationResource */ private $userExpirationResource; /** * AfterAdminUserSave constructor. * - * @param \Magento\Security\Model\UserExpirationFactory $userExpirationFactory - * @param \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + * @param UserExpirationFactory $userExpirationFactory + * @param UserExpirationResource $userExpirationResource */ public function __construct( - \Magento\Security\Model\UserExpirationFactory $userExpirationFactory, - \Magento\Security\Model\ResourceModel\UserExpiration $userExpirationResource + UserExpirationFactory $userExpirationFactory, + UserExpirationResource $userExpirationResource ) { $this->userExpirationFactory = $userExpirationFactory; @@ -45,6 +47,7 @@ public function __construct( * * @param Observer $observer * @return void + * @throws \Magento\Framework\Exception\AlreadyExistsException */ public function execute(Observer $observer) { From 2619c78466015857a37c421110e69dd80156633d Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Thu, 5 Mar 2020 09:21:00 +0200 Subject: [PATCH 272/734] Fixing failed tests --- app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php | 2 +- .../Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php index 3ee9bd888f568..50cf9f39f26ba 100644 --- a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php +++ b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php @@ -71,7 +71,7 @@ public function isCategoryFilterVisibleInLayerNavigation( * * @return int|null */ - protected function getStoreId(): ?int + private function getStoreId(): ?int { try { return (int) $this->storeManager->getStore()->getId(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php index 92734bae2e444..2b579ecea6b83 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php @@ -13,6 +13,8 @@ /** * Filter List Test + * + * Check whenever the given filters list matches the expected result */ class FilterListTest extends \PHPUnit\Framework\TestCase { From d48cc2f8d08adb30a954ea12a03a58abacb23000 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Thu, 5 Mar 2020 11:51:08 +0200 Subject: [PATCH 273/734] improvement: cover Chart with integration test --- .../Backend/Model/Dashboard/ChartTest.php | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php new file mode 100644 index 0000000000000..a6fa0da1f6568 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model; + +use Magento\Backend\Model\Dashboard\Chart; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify chart data by different period. + * + * @magentoAppArea adminhtml + */ +class ChartTest extends TestCase +{ + /** + * @var Chart + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->model = Bootstrap::getObjectManager()->create(Chart::class); + } + + /** + * Verify getByPeriod with all types of period + * + * @magentoDataFixture Magento/Sales/_files/order_list_with_invoice.php + * @dataProvider getChartDataProvider + * @return void + */ + public function testGetByPeriodWithParam(int $expectedDataQty, string $period, string $chartParam): void + { + $this->assertCount($expectedDataQty, $this->model->getByPeriod($period, $chartParam)); + } + + /** + * Expected chart data + * + * @return array + */ + public function getChartDataProvider(): array + { + return [ + [ + 24, + '24h', + 'quantity' + ], + [ + 8, + '7d', + 'quantity' + ], + [ + 6, + '1m', + 'quantity' + ], + [ + 16, + '1y', + 'quantity' + ], + [ + 28, + '2y', + 'quantity' + ] + ]; + } +} From e45bb9694a4cc56b8d8ef06f66bdbb6db7dab05b Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Thu, 5 Mar 2020 12:32:05 +0200 Subject: [PATCH 274/734] fix incorrect namespace --- .../testsuite/Magento/Backend/Model/Dashboard/ChartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php index a6fa0da1f6568..fbca1b8af7e0e 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Backend\Model; +namespace Magento\Backend\Model\Dashboard; use Magento\Backend\Model\Dashboard\Chart; use Magento\TestFramework\Helper\Bootstrap; From 385df13013e83d0c4064c678d99b2607dc838186 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 5 Mar 2020 11:21:31 +0000 Subject: [PATCH 275/734] Moved DeleteEntitiesFromStores into own class. --- ...ProductProcessUrlRewriteSavingObserver.php | 25 ++++--- ...ProductProcessUrlRewriteRemovingPlugin.php | 16 ++--- .../Storage/DeleteEntitiesFromStores.php | 68 +++++++++++++++++++ 3 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 7cb916b6a6dd8..3671a25a2d9c0 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -13,12 +13,11 @@ use Magento\Framework\Event\Observer; use Magento\Framework\Exception\NoSuchEntityException; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; +use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\StoreWebsiteRelationInterface; -use Magento\UrlRewrite\Model\Storage\DbStorage; -use Magento\Store\Model\Store; /** * Class ProductProcessUrlRewriteSavingObserver @@ -30,17 +29,17 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { /** - * @var ProductUrlRewriteGenerator + * @var ProductUrlRewriteGenerator $productUrlRewriteGenerator */ private $productUrlRewriteGenerator; /** - * @var UrlPersistInterface + * @var UrlPersistInterface $urlPersist */ private $urlPersist; /** - * @var ProductUrlPathGenerator + * @var ProductUrlPathGenerator $productUrlPathGenerator */ private $productUrlPathGenerator; @@ -50,7 +49,7 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface private $storeManager; /** - * @var StoreWebsiteRelationInterface + * @var StoreWebsiteRelationInterface $storeWebsiteRelation */ private $storeWebsiteRelation; @@ -60,14 +59,14 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface private $productRepository; /** - * @var ProductScopeRewriteGenerator + * @var ProductScopeRewriteGenerator $productScopeRewriteGenerator */ private $productScopeRewriteGenerator; /** - * @var DbStorage + * @var DeleteEntitiesFromStores $deleteEntitiesFromStores */ - private $dbStorage; + private $deleteEntitiesFromStores; /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator @@ -77,7 +76,7 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface * @param StoreWebsiteRelationInterface $storeWebsiteRelation * @param ProductRepository $productRepository * @param ProductScopeRewriteGenerator $productScopeRewriteGenerator - * @param DbStorage $dbStorage + * @param DeleteEntitiesFromStores $deleteEntitiesFromStores */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, @@ -87,7 +86,7 @@ public function __construct( StoreWebsiteRelationInterface $storeWebsiteRelation, ProductRepository $productRepository, ProductScopeRewriteGenerator $productScopeRewriteGenerator, - DbStorage $dbStorage + DeleteEntitiesFromStores $deleteEntitiesFromStores ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; @@ -96,7 +95,7 @@ public function __construct( $this->storeWebsiteRelation = $storeWebsiteRelation; $this->productRepository = $productRepository; $this->productScopeRewriteGenerator = $productScopeRewriteGenerator; - $this->dbStorage = $dbStorage; + $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; } /** @@ -152,7 +151,7 @@ public function execute(Observer $observer) } } if (count($storeIdsToRemove)) { - $this->dbStorage->deleteEntitiesFromStores( + $this->deleteEntitiesFromStores->execute( $storeIdsToRemove, [$product->getId()], ProductUrlRewriteGenerator::ENTITY_TYPE diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php index 20d376bd32825..3b4929c25a1cd 100644 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php @@ -13,7 +13,7 @@ use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Store\Api\StoreWebsiteRelationInterface; use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; /** @@ -46,29 +46,29 @@ class ProductProcessUrlRewriteRemovingPlugin private $productUrlRewriteGenerator; /** - * @var DbStorage $dbStorage + * @var DeleteEntitiesFromStores $deleteEntitiesFromStores */ - private $dbStorage; + private $deleteEntitiesFromStores; /** * @param ProductRepositoryInterface $productRepository * @param StoreWebsiteRelationInterface $storeWebsiteRelation * @param UrlPersistInterface $urlPersist * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param DbStorage $dbStorage + * @param DeleteEntitiesFromStores $deleteEntitiesFromStores */ public function __construct( ProductRepositoryInterface $productRepository, StoreWebsiteRelationInterface $storeWebsiteRelation, UrlPersistInterface $urlPersist, ProductUrlRewriteGenerator $productUrlRewriteGenerator, - DbStorage $dbStorage + DeleteEntitiesFromStores $deleteEntitiesFromStores ) { $this->productRepository = $productRepository; $this->storeWebsiteRelation = $storeWebsiteRelation; $this->urlPersist = $urlPersist; $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; - $this->dbStorage = $dbStorage; + $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; } /** @@ -109,14 +109,14 @@ public function afterUpdateWebsites( $storeIdsToRemove = []; // Remove the URLs from websites this product no longer belongs to - if ($type == "remove" && $websiteIds && $productIds) { + if ($type === "remove" && $websiteIds && $productIds) { foreach ($websiteIds as $webId) { foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeid) { $storeIdsToRemove[] = $storeid; } } if (count($storeIdsToRemove)) { - $this->dbStorage->deleteEntitiesFromStores( + $this->deleteEntitiesFromStores->execute( $storeIdsToRemove, $productIds, ProductUrlRewriteGenerator::ENTITY_TYPE diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php b/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php new file mode 100644 index 0000000000000..8f56a382fc888 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Model\Storage; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * Class DeleteEntitiesFromStores + * + * Deletes multiple URL Rewrites from database + */ +class DeleteEntitiesFromStores +{ + /** + * @var AdapterInterface $connection + */ + private $connection; + + /** + * @var Resource $resource + */ + private $resource; + + /** + * @param ResourceConnection $resource + */ + public function __construct( + ResourceConnection $resource + ) { + $this->connection = $resource->getConnection(); + $this->resource = $resource; + } + + /** + * Function execute + * + * Deletes multiple URL Rewrites from database + * + * @param array $storeIds + * @param array $entityIds + * @param int $entityType + */ + public function execute($storeIds, $entityIds, $entityType) + { + $select = $this->connection->select(); + $select->from($this->resource->getTableName(DbStorage::TABLE_NAME)); + + $select->where( + $this->connection->quoteIdentifier( + UrlRewrite::STORE_ID + ) . ' IN (' . $this->connection->quote($storeIds, 'INTEGER') . ')' . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_ID + ) . ' IN (' . $this->connection->quote($entityIds, 'INTEGER') . ')' . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_TYPE + ) . ' = ' . $this->connection->quote($entityType) + ); + $select = $select->deleteFromSelect($this->resource->getTableName(DbStorage::TABLE_NAME)); + $this->connection->query($select); + } +} From 9e4f01d84fd27605953d8633680d00fbe8590d35 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 5 Mar 2020 11:24:38 +0000 Subject: [PATCH 276/734] Removed replaced function from DbStorage, now implemented in own class. --- .../UrlRewrite/Model/Storage/DbStorage.php | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index a5d23f99bc0cd..f0e94e8379ad2 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -375,33 +375,4 @@ public function deleteByData(array $data) $this->prepareSelect($data)->deleteFromSelect($this->resource->getTableName(self::TABLE_NAME)) ); } - - /** - * Function deleteEntitiesFromStores - * - * Deletes multiple URL Rewrites from database - * - * @param array $store_ids - * @param array $entity_ids - * @param int $entity_type - */ - public function deleteEntitiesFromStores($store_ids, $entity_ids, $entity_type) - { - $select = $this->connection->select(); - $select->from($this->resource->getTableName(self::TABLE_NAME)); - - $select->where( - $this->connection->quoteIdentifier( - UrlRewrite::STORE_ID - ) . ' IN (' . $this->connection->quote($store_ids, 'INTEGER') . ')' . - ' AND ' . $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_ID - ) . ' IN (' . $this->connection->quote($entity_ids, 'INTEGER') . ')' . - ' AND ' . $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_TYPE - ) . ' = ' . $this->connection->quote($entity_type) - ); - $select = $select->deleteFromSelect($this->resource->getTableName(self::TABLE_NAME)); - $this->connection->query($select); - } } From a4700f56d0d9eee961f2cab4404af7295e5db6c7 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 5 Mar 2020 11:36:39 +0000 Subject: [PATCH 277/734] Updated Unit Tests for new Class --- ...oductProcessUrlRewriteSavingObserverTest.php | 17 ++++++++++------- ...oductProcessUrlRewriteRemovingPluginTest.php | 17 ++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index df1effb2f2eb8..e561c26f98ee6 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -12,7 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\StoreWebsiteRelationInterface; use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Catalog\Model\Product; use Magento\Framework\Event; @@ -116,9 +116,9 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase private $productRepository; /** - * @var DbStorage|MockObject + * @var DeleteEntitiesFromStores|MockObject */ - private $dbStorage; + private $deleteEntitiesFromStores; /** * Set up @@ -165,7 +165,10 @@ protected function setUp() [1, false, 2, true, $this->product2], [1, false, 5, true, $this->product5] ])); - $this->dbStorage = $this->createPartialMock(DbStorage::class, ['deleteEntitiesFromStores']); + $this->deleteEntitiesFromStores = $this->createPartialMock( + DeleteEntitiesFromStores::class, + ['execute'] + ); $this->event = $this->createPartialMock(Event::class, ['getProduct']); $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); @@ -216,7 +219,7 @@ protected function setUp() 'storeManager' => $this->storeManager, 'storeWebsiteRelation' => $this->storeWebsiteRelation, 'productRepository' => $this->productRepository, - 'dbStorage' => $this->dbStorage, + 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores, 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator ] ); @@ -387,8 +390,8 @@ public function testExecuteUrlKey( ->method('replace') ->with([1 => 'rewrite']); - $this->dbStorage->expects($this->any()) - ->method('deleteEntitiesFromStores') + $this->deleteEntitiesFromStores->expects($this->any()) + ->method('execute') ->with( $expectedRemoves, [1], diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php index 1e9d6a911fe14..16375da5fdf72 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php @@ -8,7 +8,7 @@ use Magento\Catalog\Model\Product\Action; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\UrlRewrite\Model\Storage\DbStorage; +use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\Website; @@ -97,9 +97,9 @@ class ProductProcessUrlRewriteRemovingPluginTest extends TestCase private $storeWebsiteRelation; /** - * @var DbStorage|MockObject + * @var DeleteEntitiesFromStores|MockObject */ - private $dbStorage; + private $deleteEntitiesFromStores; /** * Set up @@ -153,7 +153,10 @@ protected function setUp() [3, false, 0, true, $this->product3] ])); - $this->dbStorage = $this->createPartialMock(DbStorage::class, ['deleteEntitiesFromStores']); + $this->deleteEntitiesFromStores = $this->createPartialMock( + DeleteEntitiesFromStores::class, + ['execute'] + ); $this->subject = $this->createMock( Action::class @@ -167,7 +170,7 @@ protected function setUp() 'storeWebsiteRelation' => $this->storeWebsiteRelation, 'urlPersist' => $this->urlPersist, 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'dbStorage' => $this->dbStorage + 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores ] ); @@ -320,8 +323,8 @@ public function testAfterUpdateWebsites( ->method('replace') ->with($rewrites); - $this->dbStorage->expects($this->exactly($expectedDeleteCount)) - ->method('deleteEntitiesFromStores') + $this->deleteEntitiesFromStores->expects($this->exactly($expectedDeleteCount)) + ->method('execute') ->with( $expectedStoreRemovals, $productids, From 2b2858e71d4b676d82bdda9f826dbe2177517a24 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Thu, 5 Mar 2020 13:43:29 +0200 Subject: [PATCH 278/734] Added adjustments as per review --- .../Catalog/Model/Config/LayerCategoryConfig.php | 12 +++++------- app/code/Magento/Catalog/Model/Layer/FilterList.php | 11 +++++------ app/code/Magento/Catalog/etc/adminhtml/system.xml | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php index 50cf9f39f26ba..0e8565e3b25d1 100644 --- a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php +++ b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php @@ -69,14 +69,12 @@ public function isCategoryFilterVisibleInLayerNavigation( /** * Get the current store ID * - * @return int|null + * @return int + * + * @throws NoSuchEntityException */ - private function getStoreId(): ?int + private function getStoreId(): int { - try { - return (int) $this->storeManager->getStore()->getId(); - } catch (NoSuchEntityException $e) { - return null; - } + return (int) $this->storeManager->getStore()->getId(); } } diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php index 2f32971c80bed..a7eba474c58d8 100644 --- a/app/code/Magento/Catalog/Model/Layer/FilterList.php +++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php @@ -48,26 +48,25 @@ class FilterList protected $filters = []; /** - * @var LayerCategoryConfig|null + * @var LayerCategoryConfig */ private $layerCategoryConfig; /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param FilterableAttributeListInterface $filterableAttributes + * @param LayerCategoryConfig $layerCategoryConfig * @param array $filters - * @param LayerCategoryConfig|null $layerCategoryConfig */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, FilterableAttributeListInterface $filterableAttributes, - array $filters = [], - LayerCategoryConfig $layerCategoryConfig = null + LayerCategoryConfig $layerCategoryConfig, + array $filters = [] ) { $this->objectManager = $objectManager; $this->filterableAttributes = $filterableAttributes; - $this->layerCategoryConfig = $layerCategoryConfig ?: - ObjectManager::getInstance()->get(LayerCategoryConfig::class); + $this->layerCategoryConfig = $layerCategoryConfig; /** Override default filter type models */ $this->filterTypes = array_merge($this->filterTypes, $filters); diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index f548c23b68ce3..30a8ec8a81ec5 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -140,7 +140,7 @@ </group> <group id="layered_navigation"> <field id="display_category" translate="label" type="select" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> - <label>Display Category</label> + <label>Display Category Filter</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> From 52db731a99cc7db1d1ad4efe87dcfb141118658f Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 5 Mar 2020 15:08:35 +0200 Subject: [PATCH 279/734] MC-29052: Magento\FunctionalTestingFramework.functional.AdminTaxReportGridTest fails randomly --- .../Section/AdminOrderItemsOrderedSection.xml | 1 - .../Section/AdminOrderItemsOrderedSection.xml | 14 ++ .../Test/AdminCheckingTaxReportGridTest.xml | 2 +- .../Test/Mftf/Test/AdminTaxReportGridTest.xml | 225 ------------------ 4 files changed, 15 insertions(+), 227 deletions(-) create mode 100644 app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml delete mode 100644 app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml index 94af3c79c8e02..a2c82de60a78e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -20,7 +20,6 @@ <element name="itemTaxPercent" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-tax-percent" parameterized="true"/> <element name="itemDiscountAmount" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-discont .price" parameterized="true"/> <element name="itemTotal" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-total .price" parameterized="true"/> - <element name="itemTaxAmountByProductName" type="text" selector="//table[contains(@class,'edit-order-table')]//div[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'col-tax-amount')]//span" parameterized="true"/> <element name="productNameColumn" type="text" selector=".edit-order-table .col-product .product-title"/> <element name="productNameOptions" type="text" selector=".edit-order-table .col-product .item-options"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml new file mode 100644 index 0000000000000..969b59af2a3c3 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderItemsOrderedSection"> + <element name="itemTaxAmountByProductName" type="text" selector="//table[contains(@class,'edit-order-table')]//div[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'col-tax-amount')]//span" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml index 9b1a0ca2a9892..b8e0881c4431f 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -16,7 +16,7 @@ <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> <severity value="MAJOR"/> <testCaseId value="MC-6230"/> - <useCaseId value="MAGETWO-91521"/> + <useCaseId value="MC-25815"/> <group value="Tax"/> </annotations> <before> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml deleted file mode 100644 index 1c23f455d0ad0..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ /dev/null @@ -1,225 +0,0 @@ -<?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="AdminTaxReportGridTest"> - <annotations> - <features value="Tax"/> - <stories value="MAGETWO-91521: Reports / Sales / Tax report show incorrect amount"/> - <title value="DEPRECATED Checking Tax Report grid"/> - <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94338"/> - <group value="Tax"/> - <skip> - <issueId value="DEPRECATED">Use AdminCheckingTaxReportGridTest instead.</issueId> - </skip> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Go to tax rule page --> - <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule1"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxWithZipCode"/> - </actionGroup> - - <actionGroup ref="addProductTaxClass" stepKey="addProductTaxClass"> - <argument name="prodTaxClassName" value="TaxClasses1"/> - </actionGroup> - - <click stepKey="disableDefaultProdTaxClass" selector="{{AdminTaxRulesSection.defaultTaxClass}}"/> - <waitForPageLoad stepKey="waitForTaxRulePage"/> - <click stepKey="clickSave" selector="{{AdminTaxRulesSection.saveRule}}"/> - <waitForPageLoad stepKey="waitForNewTaxRuleCreated"/> - - <!-- Go to tax rule page to create second Tax Rule--> - <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> - <fillField stepKey="fillSecondRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule2"/> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleSecondTaxWithZipCode"/> - </actionGroup> - - <actionGroup ref="addProductTaxClass" stepKey="addSecondProductTaxClass"> - <argument name="prodTaxClassName" value="TaxClasses2"/> - </actionGroup> - - <click stepKey="disableSecondProdTaxClass" selector="{{AdminTaxRulesSection.defaultTaxClass}}"/> - <waitForPageLoad stepKey="waitForTaxRulePage2"/> - <click stepKey="clickSaveBtn" selector="{{AdminTaxRulesSection.saveRule}}"/> - <waitForPageLoad stepKey="waitForSecondTaxRuleCreated"/> - - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="firstProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <createData entity="_defaultProduct" stepKey="secondProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - - <!--Open Created products. In Tax Class select new created Product Tax classes.--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openFirstProductForEdit"/> - <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForFirstProduct" userInput="TaxClasses1"/> - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveFirstProduct"/> - <waitForPageLoad stepKey="waitForFirstProductSaved"/> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="againGoToProductIndex"/> - <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSecondProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterSecondProductGridBySku"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openSecondProductForEdit"/> - <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForSecondProduct" userInput="TaxClasses2"/> - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveSecondProduct"/> - <waitForPageLoad stepKey="waitForSecondProductSaved"/> - - <!--Create an order with these 2 products in that zip code.--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> - <waitForPageLoad stepKey="waitForIndexPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> - <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> - <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> - <waitForPageLoad stepKey="waitForPage" time="60"/> - <!--Check if order can be submitted without the required fields including email address--> - <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="seeNewOrderPageTitle"/> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder" after="scrollToTopOfOrderFormPage"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToOrder" after="addFirstProductToOrder"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - - <!--Fill customer group and customer email--> - <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup" after="addSecondProductToOrder"/> - <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> - - <!--Fill customer address information--> - <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillCustomerEmail"> - <argument name="customer" value="Simple_US_Customer"/> - <argument name="address" value="US_Address_TX"/> - </actionGroup> - - <!-- Select shipping --> - <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" after="selectFlatRateShipping" stepKey="selectCheckMoneyPayment"/> - <!--Submit Order and verify information--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" after="selectCheckMoneyPayment" stepKey="clickSubmitOrder"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> - - <!--Create Invoice and Shipment for this Order.--> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> - <waitForPageLoad stepKey="waitForInvoicePageOpened"/> - - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl" after="clickShipAction"/> - <!--Submit Shipment--> - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment" after="seeOrderShipmentUrl"/> - <waitForPageLoad stepKey="waitForShipmentSaved"/> - - <!--Go to "Reports" -> "Sales" -> "Tax"--> - <amOnPage url="/admin/reports/report_sales/tax/" stepKey="navigateToReportsTaxPage"/> - <waitForPageLoad stepKey="waitForReportsTaxPageLoad"/> - - <!--click "here" to refresh last day's statistics --> - <click stepKey="clickRefrashStatisticsHere" selector="{{AdminTaxReportsSection.refreshStatistics}}"/> - <waitForPageLoad stepKey="waitForRefresh"/> - - <!--Select Dates--> - <fillField selector="{{AdminTaxReportsSection.fromDate}}" userInput="05/16/2018" stepKey="fillDateFrom"/> - <click selector="{{AdminTaxReportsSection.toDate}}" stepKey="clickDateTo"/> - <click selector="{{AdminTaxReportsSection.goTodayButton}}" stepKey="clickGoTodayDate"/> - <!--Click "Show report" in the upper right corner.--> - <click selector="{{AdminTaxReportsSection.showReportButton}}" stepKey="clickShowReportButton"/> - <waitForPageLoad time="60" stepKey="waitForReload"/> - <!--Tax Report Grid displays Tax amount in rows. "Total" and "Subtotal" is a sum of all tax amounts--> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Texas-0.125')}}" stepKey="amountOfFirstTaxRate"/> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Texas-7.25')}}" stepKey="amountOfSecondTaxRate"/> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Subtotal')}}" stepKey="amountOfSubtotalTaxRate"/> - <assertEquals stepKey="assertSubtotalFirstField"> - <expectedResult type="string">$0.15</expectedResult> - <actualResult type="variable">amountOfFirstTaxRate</actualResult> - </assertEquals> - - <assertEquals stepKey="assertSubtotalSecondField"> - <expectedResult type="string">$8.92</expectedResult> - <actualResult type="variable">amountOfSecondTaxRate</actualResult> - </assertEquals> - - <assertEquals stepKey="assertSubtotalField"> - <expectedResult type="string">$9.07</expectedResult> - <actualResult type="variable">amountOfSubtotalTaxRate</actualResult> - </assertEquals> - - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="TaxRule1"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteSecondRule"> - <argument name="name" value="TaxRule2"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxWithZipCode.state}}-{{SimpleTaxWithZipCode.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleSecondTaxWithZipCode.state}}-{{SimpleSecondTaxWithZipCode.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> - <argument name="taxClassName" value="TaxClasses1"/> - </actionGroup> - - <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteSecondProductTaxClass"> - <argument name="taxClassName" value="TaxClasses2"/> - </actionGroup> - - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - </test> -</tests> From 820d3a4d7b76c7eb185babef252378ab8383f5de Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Thu, 5 Mar 2020 17:05:19 +0200 Subject: [PATCH 280/734] improve widget name --- .../Magento/Backend/view/adminhtml/web/js/dashboard/totals.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js index 18953140e5b62..2a696fe7bb38c 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js @@ -11,7 +11,7 @@ define([ ], function ($) { 'use strict'; - $.widget('mage.graph', { + $.widget('mage.dashboardTotals', { options: { updateUrl: '', periodSelect: null @@ -56,5 +56,5 @@ define([ } }); - return $.mage.graph; + return $.mage.dashboardTotals; }); From d65e609e12f139bd3a2d69697c1bbad361404b60 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Thu, 5 Mar 2020 11:39:26 -0500 Subject: [PATCH 281/734] fix static test failure (magento/magento2#22833: Short-term admin accounts) --- app/code/Magento/Security/Api/Data/UserExpirationInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php index e3c89ae253b01..63cf2b7099c03 100644 --- a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -53,7 +53,7 @@ public function setUserId($userId); /** * Retrieve existing extension attributes object or create a new one. * - * @return UserExpirationExtensionInterface|null + * @return UserExpirationExtensionInterface */ public function getExtensionAttributes(); From 9c38473fbeb417e72615fba7687abb92c4fb0873 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 5 Mar 2020 11:13:27 -0600 Subject: [PATCH 282/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index ef327b5078e93..986d9c5e9eb24 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "522d676db5baf5864a824409c54948fc", + "content-hash": "3a25d06e6a86adbba9b2f2dfe202d69e", "packages": [ { "name": "aws/aws-sdk-php", @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "4462b2cf6c978a2554b8f1789c676f562acd261f" + "reference": "32e76001ac157f2953fedfed1a3ed32020ecf6ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4462b2cf6c978a2554b8f1789c676f562acd261f", - "reference": "4462b2cf6c978a2554b8f1789c676f562acd261f", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/32e76001ac157f2953fedfed1a3ed32020ecf6ec", + "reference": "32e76001ac157f2953fedfed1a3ed32020ecf6ec", "shasum": "" }, "require": { @@ -7575,7 +7575,7 @@ "magento", "testing" ], - "time": "2020-03-03T20:05:39+00:00" + "time": "2020-03-05T17:05:08+00:00" }, { "name": "mikey179/vfsstream", From c8cfb5ba4877e8c9eba533a3979693e5d92590f9 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Thu, 5 Mar 2020 12:28:40 -0500 Subject: [PATCH 283/734] fix static test failure (magento/magento2#22833: Short-term admin accounts) --- .../Magento/Security/Api/Data/UserExpirationInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php index 63cf2b7099c03..4f602c8b58a87 100644 --- a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -53,14 +53,14 @@ public function setUserId($userId); /** * Retrieve existing extension attributes object or create a new one. * - * @return UserExpirationExtensionInterface + * @return \Magento\Security\Api\Data\UserExpirationExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param UserExpirationExtensionInterface $extensionAttributes + * @param \Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes(UserExpirationExtensionInterface $extensionAttributes); From c0937333ce178e50fada4a27a64477e98b2c121d Mon Sep 17 00:00:00 2001 From: Anton Kaplya <a.kaplya@gmail.com> Date: Thu, 5 Mar 2020 13:23:28 -0600 Subject: [PATCH 284/734] Revert "Make sure image metadata is not fetched from remote storage every time" --- .../Product/Helper/Form/Gallery/Content.php | 82 +- .../Magento/Catalog/Block/Product/Gallery.php | 51 +- .../Adminhtml/Product/Gallery/Upload.php | 20 +- app/code/Magento/Catalog/Helper/Image.php | 8 +- .../Source/Web/CatalogMediaUrlFormat.php | 2 +- .../Magento/Catalog/Model/ImageUploader.php | 15 - app/code/Magento/Catalog/Model/Product.php | 4 - .../Model/Product/Gallery/CreateHandler.php | 40 +- .../Magento/Catalog/Model/Product/Image.php | 69 +- .../ResourceModel/Product/Collection.php | 40 +- .../Model/ResourceModel/Product/Gallery.php | 70 +- .../Model/View/Asset/Image/Context.php | 5 +- .../Config/CatalogClone/Media/ImageTest.php | 153 +++ .../ResourceModel/Product/CollectionTest.php | 40 + .../ResourceModel/Product/GalleryTest.php | 198 ++- app/code/Magento/Catalog/etc/db_schema.xml | 1 - .../Catalog/etc/db_schema_whitelist.json | 3 +- .../Form/Modifier/Data/AssociatedProducts.php | 3 +- .../Console/Command/ImagesResizeCommand.php | 3 +- .../MediaStorage/Service/ImageResize.php | 18 +- .../Test/Unit/Service/ImageResizeTest.php | 60 +- app/code/Magento/MediaStorage/etc/di.xml | 7 - .../Adminhtml/Wysiwyg/Files/ContentTest.php | 220 ++++ app/etc/di.xml | 9 - composer.json | 9 +- composer.lock | 1139 ++++++++--------- .../Catalog/Model/ImageUploaderTest.php | 165 +++ .../Model/Product/Gallery/ReadHandlerTest.php | 6 +- .../SimplePolicyHeaderRendererTest.php | 4 - .../Php/_files/phpstan/blacklist/common.txt | 2 - .../HTTP/PhpEnvironment/Response.php | 9 - .../AdapterFactoryInterface.php | 25 - .../Storage/AdapterFactory/AwsS3Factory.php | 31 - .../Storage/AdapterFactory/AzureFactory.php | 32 - .../Storage/AdapterFactory/LocalFactory.php | 31 - .../Storage/FileNotFoundException.php | 17 - .../InvalidStorageConfigurationException.php | 14 - .../Magento/Framework/Storage/README.md | 121 -- .../Storage/RootViolationException.php | 17 - .../Magento/Framework/Storage/Storage.php | 77 -- .../Storage/StorageAdapterProvider.php | 63 - .../Framework/Storage/StorageInterface.php | 57 - .../Framework/Storage/StorageProvider.php | 102 -- .../Storage/UnsupportedStorageException.php | 13 - .../Test/Unit/Module/I18n/ContextTest.php | 153 +++ 45 files changed, 1637 insertions(+), 1571 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php create mode 100644 app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php delete mode 100644 lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php delete mode 100644 lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php delete mode 100644 lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php delete mode 100644 lib/internal/Magento/Framework/Storage/AdapterFactory/LocalFactory.php delete mode 100644 lib/internal/Magento/Framework/Storage/FileNotFoundException.php delete mode 100644 lib/internal/Magento/Framework/Storage/InvalidStorageConfigurationException.php delete mode 100644 lib/internal/Magento/Framework/Storage/README.md delete mode 100644 lib/internal/Magento/Framework/Storage/RootViolationException.php delete mode 100644 lib/internal/Magento/Framework/Storage/Storage.php delete mode 100644 lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php delete mode 100644 lib/internal/Magento/Framework/Storage/StorageInterface.php delete mode 100644 lib/internal/Magento/Framework/Storage/StorageProvider.php delete mode 100644 lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index 4cf67858fe287..8e6011c09a27f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -13,20 +13,16 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery; -use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; +use Magento\Framework\App\ObjectManager; use Magento\Backend\Block\Media\Uploader; +use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\FileSystemException; -use Magento\Framework\Storage\FileNotFoundException; -use Magento\Framework\Storage\StorageProvider; -use Magento\Framework\View\Element\AbstractBlock; +use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; use Magento\MediaStorage\Helper\File\Storage\Database; /** * Block for gallery content. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Content extends \Magento\Backend\Block\Widget { @@ -59,21 +55,11 @@ class Content extends \Magento\Backend\Block\Widget * @var Database */ private $fileStorageDatabase; - /** - * @var StorageProvider - */ - private $storageProvider; - - /** - * @var \Magento\Framework\Filesystem\Directory\ReadInterface - */ - private $mediaDirectory; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig - * @param StorageProvider $storageProvider * @param array $data * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider * @param Database $fileStorageDatabase @@ -82,7 +68,6 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, - StorageProvider $storageProvider, array $data = [], ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null, Database $fileStorageDatabase = null @@ -90,12 +75,10 @@ public function __construct( $this->_jsonEncoder = $jsonEncoder; $this->_mediaConfig = $mediaConfig; parent::__construct($context, $data); - $this->mediaDirectory = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA); $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class); $this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class); - $this->storageProvider = $storageProvider; } /** @@ -174,49 +157,10 @@ public function getAddImagesButton() ); } - /** - * Sync images to database - * - * @param string $fileName - */ - private function syncImageToDatabase(string $fileName): void - { - if ($this->fileStorageDatabase->checkDbUsage() && - !$this->mediaDirectory->isFile($this->_mediaConfig->getMediaPath($fileName)) - ) { - $this->fileStorageDatabase->saveFileToFilesystem( - $this->_mediaConfig->getMediaPath($fileName) - ); - } - } - - /** - * Returns file metadata as an associative array - * - * @param string $fileName - * @return array - * @throws FileNotFoundException - */ - private function getFileMetadata(string $fileName): array - { - $metadata = []; - try { - $info = $this->storageProvider->get('media') - ->getMetadata($this->_mediaConfig->getMediaPath($fileName)); - $metadata['size'] = $info['size']; - } catch (FileSystemException $e) { - $metadata['url'] = $this->getImageHelper()->getDefaultPlaceholderUrl('small_image'); - $metadata['size'] = 0; - $this->_logger->warning($e); - } - return $metadata; - } - /** * Returns image json * * @return string - * @throws FileNotFoundException */ public function getImagesJson() { @@ -226,14 +170,24 @@ public function getImagesJson() is_array($value['images']) && count($value['images']) ) { + $mediaDir = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA); $images = $this->sortImagesByPosition($value['images']); foreach ($images as &$image) { $image['url'] = $this->_mediaConfig->getMediaUrl($image['file']); - $this->syncImageToDatabase($image['file']); - if (isset($image['image_metadata']) && is_array($image['image_metadata'])) { - $image = array_replace_recursive($image, $image['image_metadata']); - } else { - $image = array_replace_recursive($image, $this->getFileMetadata($image['file'])); + if ($this->fileStorageDatabase->checkDbUsage() && + !$mediaDir->isFile($this->_mediaConfig->getMediaPath($image['file'])) + ) { + $this->fileStorageDatabase->saveFileToFilesystem( + $this->_mediaConfig->getMediaPath($image['file']) + ); + } + try { + $fileHandler = $mediaDir->stat($this->_mediaConfig->getMediaPath($image['file'])); + $image['size'] = $fileHandler['size']; + } catch (FileSystemException $e) { + $image['url'] = $this->getImageHelper()->getDefaultPlaceholderUrl('small_image'); + $image['size'] = 0; + $this->_logger->warning($e); } } return $this->_jsonEncoder->encode($images); diff --git a/app/code/Magento/Catalog/Block/Product/Gallery.php b/app/code/Magento/Catalog/Block/Product/Gallery.php index 2e9dcd1fe6952..54f848a92e958 100644 --- a/app/code/Magento/Catalog/Block/Product/Gallery.php +++ b/app/code/Magento/Catalog/Block/Product/Gallery.php @@ -11,13 +11,9 @@ */ namespace Magento\Catalog\Block\Product; -use Magento\Framework\Storage\FileNotFoundException; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Media\Config; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Data\Collection; -use Magento\Framework\Registry; -use Magento\Framework\Storage\StorageProvider; /** * Product gallery block @@ -30,37 +26,22 @@ class Gallery extends \Magento\Framework\View\Element\Template /** * Core registry * - * @var Registry + * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; - /** - * @var StorageProvider - */ - private $storageProvider; - /** - * @var Config - */ - private $mediaConfig; - /** * @param \Magento\Framework\View\Element\Template\Context $context - * @param Registry $registry + * @param \Magento\Framework\Registry $registry * @param array $data - * @param StorageProvider $storageProvider - * @param Config $mediaConfig */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, - Registry $registry, - array $data = [], - StorageProvider $storageProvider = null, - Config $mediaConfig = null + \Magento\Framework\Registry $registry, + array $data = [] ) { $this->_coreRegistry = $registry; parent::__construct($context, $data); - $this->storageProvider = $storageProvider ?? ObjectManager::getInstance()->get(StorageProvider::class); - $this->mediaConfig = $mediaConfig ?? ObjectManager::getInstance()->get(Config::class); } /** @@ -140,24 +121,16 @@ public function getImageFile() */ public function getImageWidth() { - $file = $this->getCurrentImage()->getFile(); - if (!$file) { - return false; - } - $productMediaFile = $this->mediaConfig->getMediaPath($file); - - $mediaStorage = $this->storageProvider->get('media'); - if ($mediaStorage->has($productMediaFile)) { - try { - $meta = $mediaStorage->getMetadata($productMediaFile); - $size = $meta['size']; - if ($size > 600) { + $file = $this->getCurrentImage()->getPath(); + + if ($this->_filesystem->getDirectoryRead(DirectoryList::MEDIA)->isFile($file)) { + $size = getimagesize($file); + if (isset($size[0])) { + if ($size[0] > 600) { return 600; } else { - return (int) $size; + return (int) $size[0]; } - } catch (FileNotFoundException $e) { - return false; } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index fda3d0abced7f..3e7cc3ee962b9 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Storage\StorageProvider; +use Magento\Framework\Exception\LocalizedException; /** * Upload product image action controller @@ -52,15 +52,9 @@ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterf */ private $productMediaConfig; - /** - * @var StorageProvider - */ - private $storageProvider; - /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory - * @param StorageProvider $storageProvider * @param \Magento\Framework\Image\AdapterFactory $adapterFactory * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Catalog\Model\Product\Media\Config $productMediaConfig @@ -68,7 +62,6 @@ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterf public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory, - StorageProvider $storageProvider, \Magento\Framework\Image\AdapterFactory $adapterFactory = null, \Magento\Framework\Filesystem $filesystem = null, \Magento\Catalog\Model\Product\Media\Config $productMediaConfig = null @@ -81,7 +74,6 @@ public function __construct( ->get(\Magento\Framework\Filesystem::class); $this->productMediaConfig = $productMediaConfig ?: ObjectManager::getInstance() ->get(\Magento\Catalog\Model\Product\Media\Config::class); - $this->storageProvider = $storageProvider; } /** @@ -92,7 +84,6 @@ public function __construct( public function execute() { try { - /** @var \Magento\MediaStorage\Model\File\Uploader $uploader */ $uploader = $this->_objectManager->create( \Magento\MediaStorage\Model\File\Uploader::class, ['fileId' => 'image'] @@ -102,18 +93,11 @@ public function execute() $uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile'); $uploader->setAllowRenameFiles(true); $uploader->setFilesDispersion(true); - $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); - $baseImagePath = $this->productMediaConfig->getBaseTmpMediaPath(); $result = $uploader->save( - $mediaDirectory->getAbsolutePath($baseImagePath) + $mediaDirectory->getAbsolutePath($this->productMediaConfig->getBaseTmpMediaPath()) ); - $origFile = $this->productMediaConfig->getTmpMediaPath($result['file']); - $storage = $this->storageProvider->get('media'); - $content = $mediaDirectory->readFile($origFile); - $storage->put($origFile, $content); - $this->_eventManager->dispatch( 'catalog_product_gallery_upload_image_after', ['result' => $result, 'action' => $this] diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index f220fa0ef0444..5b0aa0c496ecd 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -13,7 +13,7 @@ use Magento\Framework\View\Element\Block\ArgumentInterface; /** - * Catalog image helper + * Catalog image helper. * * @api * @SuppressWarnings(PHPMD.TooManyFields) @@ -166,8 +166,7 @@ public function __construct( $this->_assetRepo = $assetRepo; $this->viewConfig = $viewConfig; $this->viewAssetPlaceholderFactory = $placeholderFactory - ?: ObjectManager::getInstance() - ->get(PlaceholderFactory::class); + ?: ObjectManager::getInstance()->get(PlaceholderFactory::class); $this->mediaConfig = $mediaConfig ?: ObjectManager::getInstance()->get(CatalogMediaConfig::class); } @@ -574,9 +573,6 @@ public function save() * Return resized product image information * * @return array - * @deprecated Magento is not responsible for image resizing anymore. This method works with local filesystem only. - * Service that provides resized images should guarantee that the image sizes correspond to requested ones. - * Use `getWidth()` and `getHeight()` instead. */ public function getResizedImageInfo() { diff --git a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php b/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php index 0ceeeb596655d..f24044fc92c95 100644 --- a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php +++ b/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php @@ -24,7 +24,7 @@ public function toOptionArray() 'value' => CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS, 'label' => __('Image optimization based on query parameters') ], - ['value' => CatalogMediaConfig::HASH, 'label' => __('Legacy mode (unique hash per image variant)')] + ['value' => CatalogMediaConfig::HASH, 'label' => __('Unique hash per image variant (Legacy mode)')] ]; } } diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index d333ea589b997..0c3e008fa8bb5 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -6,7 +6,6 @@ namespace Magento\Catalog\Model; use Magento\Framework\File\Uploader; -use Magento\Framework\Storage\StorageProvider; /** * Catalog image uploader @@ -74,11 +73,6 @@ class ImageUploader */ private $allowedMimeTypes; - /** - * @var StorageProvider - */ - private $storageProvider; - /** * ImageUploader constructor * @@ -90,9 +84,7 @@ class ImageUploader * @param string $baseTmpPath * @param string $basePath * @param string[] $allowedExtensions - * @param StorageProvider $storageProvider * @param string[] $allowedMimeTypes - * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase, @@ -103,7 +95,6 @@ public function __construct( $baseTmpPath, $basePath, $allowedExtensions, - StorageProvider $storageProvider, $allowedMimeTypes = [] ) { $this->coreFileStorageDatabase = $coreFileStorageDatabase; @@ -115,7 +106,6 @@ public function __construct( $this->basePath = $basePath; $this->allowedExtensions = $allowedExtensions; $this->allowedMimeTypes = $allowedMimeTypes; - $this->storageProvider = $storageProvider; } /** @@ -230,11 +220,6 @@ public function moveFileFromTmp($imageName, $returnRelativePath = false) $baseTmpImagePath, $baseImagePath ); - - $storage = $this->storageProvider->get('media'); - $content = $this->mediaDirectory->readFile($baseImagePath); - $storage->put($baseImagePath, $content); - } catch (\Exception $e) { throw new \Magento\Framework\Exception\LocalizedException( __('Something went wrong while saving the file(s).') diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index c5dce0df14755..a9907c1661bd8 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -1540,11 +1540,7 @@ public function getMediaGalleryImages() } $image['url'] = $this->getMediaConfig()->getMediaUrl($image['file']); $image['id'] = $image['value_id']; - - // @deprecated 'path' should not be used - // The file can be absent in local filesystem if remote storage is used $image['path'] = $directory->getAbsolutePath($this->getMediaConfig()->getMediaPath($image['file'])); - $images->addItem(new \Magento\Framework\DataObject($image)); } $this->setData('media_gallery_images', $images); diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index e56fb8e59d0e9..225a3a4c44a9b 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -11,7 +11,6 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\Operation\ExtensionInterface; -use Magento\Framework\Storage\StorageProvider; use Magento\MediaStorage\Model\File\Uploader as FileUploader; use Magento\Store\Model\StoreManagerInterface; @@ -89,10 +88,6 @@ class CreateHandler implements ExtensionInterface * @var \Magento\Store\Model\StoreManagerInterface */ private $storeManager; - /** - * @var StorageProvider - */ - private $storageProvider; /** * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool @@ -103,7 +98,6 @@ class CreateHandler implements ExtensionInterface * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager - * @param StorageProvider $storageProvider * @throws \Magento\Framework\Exception\FileSystemException */ public function __construct( @@ -114,8 +108,7 @@ public function __construct( \Magento\Catalog\Model\Product\Media\Config $mediaConfig, \Magento\Framework\Filesystem $filesystem, \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb, - \Magento\Store\Model\StoreManagerInterface $storeManager = null, - StorageProvider $storageProvider = null + \Magento\Store\Model\StoreManagerInterface $storeManager = null ) { $this->metadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $this->attributeRepository = $attributeRepository; @@ -125,7 +118,6 @@ public function __construct( $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->fileStorageDb = $fileStorageDb; $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); - $this->storageProvider = $storageProvider ?: ObjectManager::getInstance()->get(StorageProvider::class); } /** @@ -253,6 +245,7 @@ public function getAttribute() 'media_gallery' ); } + return $this->attribute; } @@ -297,8 +290,7 @@ protected function processNewAndExistingImages($product, array &$images) $data['position'] = isset($image['position']) ? (int)$image['position'] : 0; $data['disabled'] = isset($image['disabled']) ? (int)$image['disabled'] : 0; $data['store_id'] = (int)$product->getStoreId(); - $stat = $this->mediaDirectory->stat($this->mediaConfig->getMediaPath($image['file'])); - $data['image_metadata']['size'] = $stat['size']; + $data[$this->metadata->getLinkField()] = (int)$product->getData($this->metadata->getLinkField()); $this->resourceModel->insertGalleryValueInStore($data); @@ -374,20 +366,20 @@ protected function moveImageFromTmp($file) $file = $this->getFilenameFromTmp($this->getSafeFilename($file)); $destinationFile = $this->getUniqueFileName($file); - $tmpMediaPath = $this->mediaConfig->getTmpMediaPath($file); - $mediaPath = $this->mediaConfig->getMediaPath($destinationFile); - $this->mediaDirectory->renameFile( - $tmpMediaPath, - $mediaPath - ); - $this->fileStorageDb->renameFile( - $this->mediaConfig->getTmpMediaShortUrl($file), - $this->mediaConfig->getMediaShortUrl($destinationFile) - ); + if ($this->fileStorageDb->checkDbUsage()) { + $this->fileStorageDb->renameFile( + $this->mediaConfig->getTmpMediaShortUrl($file), + $this->mediaConfig->getMediaShortUrl($destinationFile) + ); - $storage = $this->storageProvider->get('media'); - $content = $this->mediaDirectory->readFile($mediaPath); - $storage->put($mediaPath, $content); + $this->mediaDirectory->delete($this->mediaConfig->getTmpMediaPath($file)); + $this->mediaDirectory->delete($this->mediaConfig->getMediaPath($destinationFile)); + } else { + $this->mediaDirectory->renameFile( + $this->mediaConfig->getTmpMediaPath($file), + $this->mediaConfig->getMediaPath($destinationFile) + ); + } return str_replace('\\', '/', $destinationFile); } diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 6a0032ca694a5..7c2a53768fd47 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -14,8 +14,7 @@ use Magento\Framework\Image as MagentoImage; use Magento\Framework\Serialize\SerializerInterface; use Magento\Catalog\Model\Product\Image\ParamsBuilder; -use Magento\Framework\Storage\StorageInterface; -use Magento\Framework\Storage\StorageProvider; +use Magento\Framework\Filesystem\Driver\File as FilesystemDriver; /** * Image operations @@ -204,9 +203,9 @@ class Image extends \Magento\Framework\Model\AbstractModel private $serializer; /** - * @var StorageInterface + * @var FilesystemDriver */ - private $storage; + private $filesystemDriver; /** * Constructor @@ -223,12 +222,12 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param ImageFactory $viewAssetImageFactory * @param PlaceholderFactory $viewAssetPlaceholderFactory * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param StorageProvider $storageProvider * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param SerializerInterface $serializer * @param ParamsBuilder $paramsBuilder + * @param FilesystemDriver $filesystemDriver * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedLocalVariable) @@ -246,30 +245,27 @@ public function __construct( ImageFactory $viewAssetImageFactory, PlaceholderFactory $viewAssetPlaceholderFactory, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - StorageProvider $storageProvider, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], SerializerInterface $serializer = null, - ParamsBuilder $paramsBuilder = null + ParamsBuilder $paramsBuilder = null, + FilesystemDriver $filesystemDriver = null ) { $this->_storeManager = $storeManager; $this->_catalogProductMediaConfig = $catalogProductMediaConfig; - - $this->_mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->_coreFileStorageDatabase = $coreFileStorageDatabase; - $this->_imageFactory = $imageFactory; - $this->viewAssetImageFactory = $viewAssetImageFactory; - - $this->storage = $storageProvider->get('media'); - parent::__construct($context, $registry, $resource, $resourceCollection, $data); + $this->_mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->_imageFactory = $imageFactory; $this->_assetRepo = $assetRepo; $this->_viewFileSystem = $viewFileSystem; $this->_scopeConfig = $scopeConfig; + $this->viewAssetImageFactory = $viewAssetImageFactory; $this->viewAssetPlaceholderFactory = $viewAssetPlaceholderFactory; $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->paramsBuilder = $paramsBuilder ?: ObjectManager::getInstance()->get(ParamsBuilder::class); + $this->filesystemDriver = $filesystemDriver ?: ObjectManager::getInstance()->get(FilesystemDriver::class); } /** @@ -437,20 +433,19 @@ public function setBaseFile($file) { $this->_isBaseFilePlaceholder = false; - if ($file == 'no_selection' || empty($file)) { + $this->imageAsset = $this->viewAssetImageFactory->create( + [ + 'miscParams' => $this->getMiscParams(), + 'filePath' => $file, + ] + ); + if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile())) { $this->_isBaseFilePlaceholder = true; $this->imageAsset = $this->viewAssetPlaceholderFactory->create( [ 'type' => $this->getDestinationSubdir(), ] ); - } else { - $this->imageAsset = $this->viewAssetImageFactory->create( - [ - 'miscParams' => $this->getMiscParams(), - 'filePath' => $file, - ] - ); } $this->_baseFile = $this->imageAsset->getSourceFile(); @@ -682,7 +677,12 @@ public function getDestinationSubdir() public function isCached() { $path = $this->imageAsset->getPath(); - return is_array($this->loadImageInfoFromCache($path)) || $this->_mediaDirectory->isExist($path); + try { + $isCached = is_array($this->loadImageInfoFromCache($path)) || $this->filesystemDriver->isExists($path); + } catch (FileSystemException $e) { + $isCached = false; + } + return $isCached; } /** @@ -854,20 +854,35 @@ public function clearCache() { $directory = $this->_catalogProductMediaConfig->getBaseMediaPath() . '/cache'; $this->_mediaDirectory->delete($directory); - $this->storage->deleteDir($directory); $this->_coreFileStorageDatabase->deleteFolder($this->_mediaDirectory->getAbsolutePath($directory)); $this->clearImageInfoFromCache(); } + /** + * First check this file on FS + * + * If it doesn't exist - try to download it from DB + * + * @param string $filename + * @return bool + */ + protected function _fileExists($filename) + { + if ($this->_mediaDirectory->isFile($filename)) { + return true; + } else { + return $this->_coreFileStorageDatabase->saveFileToFilesystem( + $this->_mediaDirectory->getAbsolutePath($filename) + ); + } + } + /** * Return resized product image information * * @return array * @throws NotLoadInfoImageException - * @deprecated Magento is not responsible for image resizing anymore. This method works with local filesystem only. - * Service that provides resized images should guarantee that the image sizes correspond to requested ones. - * Use `getWidth()` and `getHeight()` instead. */ public function getResizedImageInfo() { diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 12e3c6b53d701..14daac0147abf 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -12,7 +12,6 @@ use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; -use Magento\Catalog\Model\ResourceModel\Category; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; @@ -24,6 +23,7 @@ use Magento\Framework\Indexer\DimensionFactory; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\Store; +use Magento\Catalog\Model\ResourceModel\Category; /** * Product collection @@ -2337,35 +2337,49 @@ public function addPriceDataFieldFilter($comparisonFormat, $fields) * @SuppressWarnings(PHPMD.NPathComplexity) * @since 101.0.1 * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Zend_Db_Statement_Exception */ public function addMediaGalleryData() { if ($this->getFlag('media_gallery_added')) { return $this; } + if (!$this->getSize()) { return $this; } - if (!$this->isLoaded()) { - $this->load(); - } - $records = $this->getMediaGalleryResource()->getMediaRecords( - $this->getStoreId(), - $this->getLoadedIds() - ); + + $items = $this->getItems(); + $linkField = $this->getProductEntityMetadata()->getLinkField(); + + $select = $this->getMediaGalleryResource() + ->createBatchBaseSelect( + $this->getStoreId(), + $this->getAttribute('media_gallery')->getAttributeId() + )->reset( + Select::ORDER // we don't care what order is in current scenario + )->where( + 'entity.' . $linkField . ' IN (?)', + array_map( + function ($item) use ($linkField) { + return (int) $item->getOrigData($linkField); + }, + $items + ) + ); + $mediaGalleries = []; - foreach ($records as $record) { - $mediaGalleries[$record['entity_id']][] = $record; + foreach ($this->getConnection()->fetchAll($select) as $row) { + $mediaGalleries[$row[$linkField]][] = $row; } - foreach ($this->getItems() as $item) { + foreach ($items as $item) { $this->getGalleryReadHandler() ->addMediaDataToProduct( $item, - $mediaGalleries[$item->getId()] ?? [] + $mediaGalleries[$item->getOrigData($linkField)] ?? [] ); } + $this->setFlag('media_gallery_added', true); return $this; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index 6acda0e574828..a9741cd8e1ec7 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -6,8 +6,6 @@ namespace Magento\Catalog\Model\ResourceModel\Product; -use Magento\Framework\DB\Select; -use Magento\Framework\DB\Sql\ColumnValueExpression; use Magento\Store\Model\Store; /** @@ -35,12 +33,9 @@ class Gallery extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $metadata; /** - * Gallery constructor. - * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param string $connectionName - * @throws \Exception */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -50,6 +45,7 @@ public function __construct( $this->metadata = $metadataPool->getMetadata( \Magento\Catalog\Api\Data\ProductInterface::class ); + parent::__construct($context, $connectionName); } @@ -126,14 +122,19 @@ public function loadDataFromTableByValueId( * @param int $attributeId * @return array * @since 101.0.0 - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Zend_Db_Statement_Exception */ - public function loadProductGalleryByAttributeId($product, $attributeId = null) + public function loadProductGalleryByAttributeId($product, $attributeId) { - $result = $this->getMediaRecords($product->getStoreId(), [$product->getId()], true); + $select = $this->createBaseLoadSelect( + $product->getData($this->metadata->getLinkField()), + $product->getStoreId(), + $attributeId + ); + + $result = $this->getConnection()->fetchAll($select); + $this->removeDuplicates($result); + return $result; } @@ -143,7 +144,6 @@ public function loadProductGalleryByAttributeId($product, $attributeId = null) * @param int $entityId * @param int $storeId * @param int $attributeId - * @deprecated Misleading method, methods relies on autoincrement field instead of entity ID * @return \Magento\Framework\DB\Select * @throws \Magento\Framework\Exception\LocalizedException * @since 101.0.0 @@ -159,35 +159,6 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId) return $select; } - /** - * Returns media entries from database - * - * @param int $storeId - * @param array $entityIds - * @param bool $preserveSortOrder - * @return array - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Zend_Db_Statement_Exception - */ - public function getMediaRecords(int $storeId, array $entityIds, bool $preserveSortOrder = false) : array - { - $output = []; - $select = $this->createBatchBaseSelect($storeId) - ->where('cpe.entity_id IN (?)', $entityIds); - if (!$preserveSortOrder) { - // due to performance consideration it is better to do not use sorting for this query - $select->reset(Select::ORDER); - } - $cursor = $this->getConnection()->query($select); - while ($row = $cursor->fetch()) { - if (!empty($row['image_metadata'])) { - $row['image_metadata'] = $this->getSerializer()->unserialize($row['image_metadata']); - } - $output[] = $row; - } - return $output; - } - /** * Create batch base select * @@ -195,10 +166,9 @@ public function getMediaRecords(int $storeId, array $entityIds, bool $preserveSo * @param int $attributeId * @return \Magento\Framework\DB\Select * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) Media gallery doesn't support other attributes than media_galley * @since 101.0.1 */ - public function createBatchBaseSelect($storeId, $attributeId = null) + public function createBatchBaseSelect($storeId, $attributeId) { $linkField = $this->metadata->getLinkField(); @@ -221,10 +191,6 @@ public function createBatchBaseSelect($storeId, $attributeId = null) ['entity' => $this->getTable(self::GALLERY_VALUE_TO_ENTITY_TABLE)], $mainTableAlias . '.value_id = entity.value_id', [$linkField] - )->joinInner( - ['cpe' => $this->getTable('catalog_product_entity')], - sprintf('cpe.%1$s = entity.%1$s', $linkField), - ['entity_id' => 'cpe.entity_id'] )->joinLeft( ['value' => $this->getTable(self::GALLERY_VALUE_TABLE)], implode( @@ -253,15 +219,16 @@ public function createBatchBaseSelect($storeId, $attributeId = null) 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'), 'label_default' => 'default_value.label', 'position_default' => 'default_value.position', - 'disabled_default' => 'default_value.disabled', - 'image_metadata' => new ColumnValueExpression( - 'JSON_MERGE_PATCH(default_value.image_metadata, value.image_metadata)' - ) + 'disabled_default' => 'default_value.disabled' ])->where( + $mainTableAlias . '.attribute_id = ?', + $attributeId + )->where( $mainTableAlias . '.disabled = 0' )->order( $positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC ); + return $select; } @@ -390,9 +357,6 @@ public function insertGalleryValueInStore($data) $this->getTable(self::GALLERY_VALUE_TABLE) ); - if (!empty($data['image_metadata'])) { - $data['image_metadata'] = $this->getSerializer()->serialize($data['image_metadata']); - } $this->getConnection()->insert( $this->getTable(self::GALLERY_VALUE_TABLE), $data diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php index ead68c897f95f..49d150a31750c 100644 --- a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php +++ b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php @@ -47,10 +47,11 @@ public function __construct( $this->mediaConfig = $mediaConfig; $this->filesystem = $filesystem; $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->mediaDirectory->create($this->mediaConfig->getBaseMediaPath()); } /** - * @inheritdoc + * {@inheritdoc} */ public function getPath() { @@ -58,7 +59,7 @@ public function getPath() } /** - * @inheritdoc + * {@inheritdoc} */ public function getBaseUrl() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php new file mode 100644 index 0000000000000..23f0aec5b69a2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Config\CatalogClone\Media; + +use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Tests \Magento\Catalog\Model\Config\CatalogClone\Media\Image. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ImageTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\Config\CatalogClone\Media\Image + */ + private $model; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavConfig; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $attributeCollectionFactory; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeCollection; + + /** + * @var \Magento\Eav\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject + */ + private $attribute; + + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->attributeCollection = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->attributeCollectionFactory = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class + ) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeCollectionFactory->expects($this->any())->method('create')->will( + $this->returnValue($this->attributeCollection) + ); + + $this->attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->escaperMock = $this->getMockBuilder( + \Magento\Framework\Escaper::class + ) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); + + $helper = new ObjectManager($this); + $this->model = $helper->getObject( + \Magento\Catalog\Model\Config\CatalogClone\Media\Image::class, + [ + 'eavConfig' => $this->eavConfig, + 'attributeCollectionFactory' => $this->attributeCollectionFactory, + 'escaper' => $this->escaperMock, + ] + ); + } + + /** + * @param string $actualLabel + * @param string $expectedLabel + * @return void + * + * @dataProvider getPrefixesDataProvider + */ + public function testGetPrefixes(string $actualLabel, string $expectedLabel): void + { + $entityTypeId = 3; + /** @var \Magento\Eav\Model\Entity\Type|\PHPUnit_Framework_MockObject_MockObject $entityType */ + $entityType = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $entityType->expects($this->once())->method('getId')->willReturn($entityTypeId); + + /** @var AbstractFrontend|\PHPUnit_Framework_MockObject_MockObject $frontend */ + $frontend = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend::class) + ->setMethods(['getLabel']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $frontend->expects($this->once())->method('getLabel')->willReturn($actualLabel); + + $this->attributeCollection->expects($this->once())->method('setEntityTypeFilter')->with($entityTypeId); + $this->attributeCollection->expects($this->once())->method('setFrontendInputTypeFilter')->with('media_image'); + + $this->attribute->expects($this->once())->method('getAttributeCode')->willReturn('attributeCode'); + $this->attribute->expects($this->once())->method('getFrontend')->willReturn($frontend); + + $this->attributeCollection->expects($this->any())->method('getIterator') + ->willReturn(new \ArrayIterator([$this->attribute])); + + $this->eavConfig->expects($this->any())->method('getEntityType')->with(Product::ENTITY) + ->willReturn($entityType); + + $this->escaperMock->expects($this->once())->method('escapeHtml')->with($actualLabel) + ->willReturn($expectedLabel); + + $this->assertEquals([['field' => 'attributeCode_', 'label' => $expectedLabel]], $this->model->getPrefixes()); + } + + /** + * @return array + */ + public function getPrefixesDataProvider(): array + { + return [ + [ + 'actual_label' => 'testLabel', + 'expected_label' => 'testLabel', + ], + [ + 'actual_label' => '<media-image-attributelabel', + 'expected_label' => '<media-image-attributelabel', + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index b49decf96452d..0316b2e374d2f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -230,6 +230,46 @@ public function testAddProductCategoriesFilter() $this->collection->addCategoriesFilter([$conditionType => $values]); } + public function testAddMediaGalleryData() + { + $attributeId = 42; + $rowId = 4; + $linkField = 'row_id'; + $mediaGalleriesMock = [[$linkField => $rowId]]; + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getOrigData']) + ->getMock(); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->collection->addItem($itemMock); + $this->galleryResourceMock->expects($this->once())->method('createBatchBaseSelect')->willReturn($selectMock); + $attributeMock->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->entityMock->expects($this->once())->method('getAttribute')->willReturn($attributeMock); + $itemMock->expects($this->atLeastOnce())->method('getOrigData')->willReturn($rowId); + $selectMock->expects($this->once())->method('reset')->with(Select::ORDER)->willReturnSelf(); + $selectMock->expects($this->once())->method('where')->with('entity.' . $linkField . ' IN (?)', [$rowId]) + ->willReturnSelf(); + $this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadataMock); + $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkField); + + $this->connectionMock->expects($this->once())->method('fetchOne')->with($selectMock)->willReturn(42); + $this->connectionMock->expects($this->once())->method('fetchAll')->with($selectMock)->willReturn( + [['row_id' => $rowId]] + ); + $this->galleryReadHandlerMock->expects($this->once())->method('addMediaDataToProduct') + ->with($itemMock, $mediaGalleriesMock); + + $this->assertSame($this->collection, $this->collection->addMediaGalleryData()); + } + /** * Test addTierPriceDataByGroupId method. * diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php index 43c1abc91a1a9..47ef3c999125f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; -use Magento\Framework\DB\Sql\ColumnValueExpression; - /** * Unit test for product media gallery resource. */ @@ -282,4 +280,200 @@ public function testBindValueToEntityRecordExists() $entityId = 1; $this->resource->bindValueToEntity($valueId, $entityId); } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testLoadGallery() + { + $productId = 5; + $storeId = 1; + $attributeId = 6; + $getTableReturnValue = 'table'; + $quoteInfoReturnValue = + 'main.value_id = value.value_id AND value.store_id = ' . $storeId + . ' AND value.entity_id = entity.entity_id'; + $quoteDefaultInfoReturnValue = + 'main.value_id = default_value.value_id AND default_value.store_id = 0' + . ' AND default_value.entity_id = entity.entity_id'; + + $positionCheckSql = 'testchecksql'; + $resultRow = [ + [ + 'value_id' => '1', + 'file' => '/d/o/download_7.jpg', + 'label' => null, + 'position' => '1', + 'disabled' => '0', + 'label_default' => null, + 'position_default' => '1', + 'disabled_default' => '0', + ], + ]; + + $this->connection->expects($this->once())->method('getCheckSql')->with( + 'value.position IS NULL', + 'default_value.position', + 'value.position' + )->will($this->returnValue($positionCheckSql)); + $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select)); + $this->select->expects($this->at(0))->method('from')->with( + [ + 'main' => $getTableReturnValue, + ], + [ + 'value_id', + 'file' => 'value', + 'media_type' + ] + )->willReturnSelf(); + $this->select->expects($this->at(1))->method('joinInner')->with( + ['entity' => $getTableReturnValue], + 'main.value_id = entity.value_id', + ['entity_id'] + )->willReturnSelf(); + $this->product->expects($this->at(0))->method('getData') + ->with('entity_id')->willReturn($productId); + $this->product->expects($this->at(1))->method('getStoreId')->will($this->returnValue($storeId)); + $this->connection->expects($this->exactly(2))->method('quoteInto')->withConsecutive( + ['value.store_id = ?'], + ['default_value.store_id = ?'] + )->willReturnOnConsecutiveCalls( + 'value.store_id = ' . $storeId, + 'default_value.store_id = ' . 0 + ); + $this->connection->expects($this->any())->method('getIfNullSql')->will( + $this->returnValueMap([ + [ + '`value`.`label`', + '`default_value`.`label`', + 'IFNULL(`value`.`label`, `default_value`.`label`)' + ], + [ + '`value`.`position`', + '`default_value`.`position`', + 'IFNULL(`value`.`position`, `default_value`.`position`)' + ], + [ + '`value`.`disabled`', + '`default_value`.`disabled`', + 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)' + ] + ]) + ); + $this->select->expects($this->at(2))->method('joinLeft')->with( + ['value' => $getTableReturnValue], + $quoteInfoReturnValue, + [] + )->willReturnSelf(); + $this->select->expects($this->at(3))->method('joinLeft')->with( + ['default_value' => $getTableReturnValue], + $quoteDefaultInfoReturnValue, + [] + )->willReturnSelf(); + $this->select->expects($this->at(4))->method('columns')->with([ + 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)', + 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)', + 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)', + 'label_default' => 'default_value.label', + 'position_default' => 'default_value.position', + 'disabled_default' => 'default_value.disabled' + ])->willReturnSelf(); + $this->select->expects($this->at(5))->method('where')->with( + 'main.attribute_id = ?', + $attributeId + )->willReturnSelf(); + $this->select->expects($this->at(6))->method('where') + ->with('main.disabled = 0')->willReturnSelf(); + $this->select->expects($this->at(8))->method('where') + ->with('entity.entity_id = ?', $productId) + ->willReturnSelf(); + $this->select->expects($this->once())->method('order') + ->with($positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('fetchAll') + ->with($this->select) + ->willReturn($resultRow); + + $this->assertEquals($resultRow, $this->resource->loadProductGalleryByAttributeId($this->product, $attributeId)); + } + + public function testInsertGalleryValueInStore() + { + $data = [ + 'value_id' => '8', + 'store_id' => 0, + 'provider' => '', + 'url' => 'https://www.youtube.com/watch?v=abcdfghijk', + 'title' => 'New Title', + 'description' => 'New Description', + 'metadata' => 'New metadata', + ]; + + $this->connection->expects($this->once())->method('describeTable')->willReturn($this->fields); + $this->connection->expects($this->any())->method('prepareColumnValue')->willReturnOnConsecutiveCalls( + '8', + 0, + '', + 'https://www.youtube.com/watch?v=abcdfghijk', + 'New Title', + 'New Description', + 'New metadata' + ); + + $this->resource->insertGalleryValueInStore($data); + } + + public function testDeleteGalleryValueInStore() + { + $valueId = 4; + $entityId = 6; + $storeId = 1; + + $this->connection->expects($this->exactly(3))->method('quoteInto')->withConsecutive( + ['value_id = ?', (int)$valueId], + ['entity_id = ?', (int)$entityId], + ['store_id = ?', (int)$storeId] + )->willReturnOnConsecutiveCalls( + 'value_id = ' . $valueId, + 'entity_id = ' . $entityId, + 'store_id = ' . $storeId + ); + + $this->connection->expects($this->once())->method('delete')->with( + 'table', + 'value_id = 4 AND entity_id = 6 AND store_id = 1' + )->willReturnSelf(); + + $this->resource->deleteGalleryValueInStore($valueId, $entityId, $storeId); + } + + public function testCountImageUses() + { + $results = [ + [ + 'value_id' => '1', + 'attribute_id' => 90, + 'value' => '/d/o/download_7.jpg', + 'media_type' => 'image', + 'disabled' => '0', + ], + ]; + + $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select)); + $this->select->expects($this->at(0))->method('from')->with( + [ + 'main' => 'table', + ], + '*' + )->willReturnSelf(); + $this->select->expects($this->at(1))->method('where')->with( + 'value = ?', + 1 + )->willReturnSelf(); + $this->connection->expects($this->once())->method('fetchAll') + ->with($this->select) + ->willReturn($results); + $this->assertEquals($this->resource->countImageUses(1), count($results)); + } } diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 9f43c8a69b5e5..d5b318f671726 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -813,7 +813,6 @@ default="0" comment="Is Disabled"/> <column xsi:type="int" name="record_id" padding="10" unsigned="true" nullable="false" identity="true" comment="Record ID"/> - <column xsi:type="json" name="image_metadata" comment="Image metadata"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="record_id"/> </constraint> diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index a9b5dd2084c35..d4bd6927d4345 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -479,8 +479,7 @@ "label": true, "position": true, "disabled": true, - "record_id": true, - "image_metadata": true + "record_id": true }, "index": { "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php index 034f68bdab9d4..ec69baeb92cb9 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php @@ -314,8 +314,7 @@ protected function prepareVariations() 'canEdit' => 0, 'newProduct' => 0, 'attributes' => $this->getTextAttributes($variationOptions), - 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image') - ->getUrl(), + 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image')->getUrl(), '__disableTmpl' => true ]; $productIds[] = $product->getId(); diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index 526774a3e6bcd..d592a004e111a 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -86,7 +86,8 @@ protected function configure() $this->setName('catalog:images:resize') ->setDescription( 'Creates resized product images ' . - '(Deprecated: see https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options' + '(Not relevant when image resizing is offloaded from Magento. ' . + 'See https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options )' ) ->setDefinition($this->getOptionsList()); } diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index 145fcd1b85f4f..d061ddbd3dc46 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -19,7 +19,6 @@ use Magento\Framework\Image\Factory as ImageFactory; use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; use Magento\Framework\App\State; -use Magento\Framework\Storage\StorageProvider; use Magento\Framework\View\ConfigInterface as ViewConfig; use \Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Store\Model\StoreManagerInterface; @@ -100,11 +99,6 @@ class ImageResize */ private $storeManager; - /** - * @var StorageProvider - */ - private $storageProvider; - /** * @param State $appState * @param MediaConfig $imageConfig @@ -118,7 +112,6 @@ class ImageResize * @param Filesystem $filesystem * @param Database $fileStorageDatabase * @param StoreManagerInterface $storeManager - * @param StorageProvider $storageProvider * @throws \Magento\Framework\Exception\FileSystemException * @internal param ProductImage $gallery * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -135,8 +128,7 @@ public function __construct( Collection $themeCollection, Filesystem $filesystem, Database $fileStorageDatabase = null, - StoreManagerInterface $storeManager = null, - StorageProvider $storageProvider = null + StoreManagerInterface $storeManager = null ) { $this->appState = $appState; $this->imageConfig = $imageConfig; @@ -152,7 +144,6 @@ public function __construct( $this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class); $this->storeManager = $storeManager ?? ObjectManager::getInstance()->get(StoreManagerInterface::class); - $this->storageProvider = $storageProvider ?? ObjectManager::getInstance()->get(StorageProvider::class); } /** @@ -346,15 +337,10 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->save($imageAsset->getPath()); - $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); if ($this->fileStorageDatabase->checkDbUsage()) { + $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); $this->fileStorageDatabase->saveFile($mediastoragefilename); } - - $this->storageProvider->get('media')->put( - $mediastoragefilename, - $this->mediaDirectory->readFile($mediastoragefilename) - ); } /** diff --git a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php index 74913b444e63a..f0e1efa7806e4 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php @@ -6,27 +6,25 @@ namespace Magento\MediaStorage\Test\Unit\Service; use Magento\Catalog\Model\Product\Image\ParamsBuilder; -use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; -use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; -use Magento\Catalog\Model\View\Asset\Image as AssetImage; use Magento\Catalog\Model\View\Asset\ImageFactory as AssetImageFactory; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\State; -use Magento\Framework\Config\View; +use Magento\Catalog\Model\View\Asset\Image as AssetImage; use Magento\Framework\DataObject; use Magento\Framework\Filesystem; -use Magento\Framework\Image; use Magento\Framework\Image\Factory as ImageFactory; -use Magento\Framework\Storage\StorageInterface; +use Magento\Framework\Image; +use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; +use Magento\Framework\App\State; use Magento\Framework\View\ConfigInterface as ViewConfig; -use Magento\MediaStorage\Helper\File\Storage\Database; -use Magento\MediaStorage\Service\ImageResize; +use Magento\Framework\Config\View; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Store\Model\StoreManagerInterface; use Magento\Theme\Model\Config\Customization as ThemeCustomizationConfig; use Magento\Theme\Model\ResourceModel\Theme\Collection; +use Magento\MediaStorage\Helper\File\Storage\Database; +use Magento\Framework\App\Filesystem\DirectoryList; /** - * Class ImageResizeTest test for \Magento\MediaStorage\Service\ImageResize + * Class ImageResizeTest * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -34,7 +32,7 @@ class ImageResizeTest extends \PHPUnit\Framework\TestCase { /** - * @var ImageResize + * @var \Magento\MediaStorage\Service\ImageResize */ protected $service; @@ -122,17 +120,11 @@ class ImageResizeTest extends \PHPUnit\Framework\TestCase * @var string */ private $testfilepath; - /** * @var \PHPUnit\Framework\MockObject\MockObject|StoreManagerInterface */ private $storeManager; - /** - * @var StorageInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storage; - /** * @inheritDoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -157,16 +149,10 @@ protected function setUp() $this->filesystemMock = $this->createMock(Filesystem::class); $this->databaseMock = $this->createMock(Database::class); $this->storeManager = $this->getMockForAbstractClass(StoreManagerInterface::class); - $storageProvider = $this->createMock(\Magento\Framework\Storage\StorageProvider::class); - $this->storage = $this->getMockForAbstractClass(StorageInterface::class); - $storageProvider->expects($this->any()) - ->method('get') - ->with('media') - ->willReturn($this->storage); $this->mediaDirectoryMock = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() - ->setMethods(['getAbsolutePath','isFile','getRelativePath', 'readFile']) + ->setMethods(['getAbsolutePath','isFile','getRelativePath']) ->getMock(); $this->filesystemMock->expects($this->any()) @@ -215,7 +201,8 @@ protected function setUp() $this->viewMock->expects($this->any()) ->method('getMediaEntities') ->willReturn( - ['product_small_image' => [ + ['product_small_image' => + [ 'type' => 'small_image', 'width' => 75, 'height' => 75 @@ -236,7 +223,7 @@ protected function setUp() ->method('getStores') ->willReturn([$store]); - $this->service = new ImageResize( + $this->service = new \Magento\MediaStorage\Service\ImageResize( $this->appStateMock, $this->imageConfigMock, $this->productImageMock, @@ -248,8 +235,7 @@ protected function setUp() $this->themeCollectionMock, $this->filesystemMock, $this->databaseMock, - $this->storeManager, - $storageProvider + $this->storeManager ); } @@ -292,14 +278,6 @@ function () { ->method('saveFile') ->with($this->testfilepath); - $this->mediaDirectoryMock->expects($this->any()) - ->method('readFile') - ->with($this->testfilepath) - ->willReturn('image data'); - $this->storage->expects($this->once()) - ->method('put') - ->with($this->testfilepath, 'image data'); - $generator = $this->service->resizeFromThemes(['test-theme']); while ($generator->valid()) { $generator->next(); @@ -338,14 +316,6 @@ public function testResizeFromImageNameMediaStorageDatabase() ->method('saveFile') ->with($this->testfilepath); - $this->mediaDirectoryMock->expects($this->any()) - ->method('readFile') - ->with($this->testfilepath) - ->willReturn('image data'); - $this->storage->expects($this->once()) - ->method('put') - ->with($this->testfilepath, 'image data'); - $this->service->resizeFromImageName($this->testfilename); } } diff --git a/app/code/Magento/MediaStorage/etc/di.xml b/app/code/Magento/MediaStorage/etc/di.xml index 061c3be1bbe4a..5cdcbb3b2b9a9 100644 --- a/app/code/Magento/MediaStorage/etc/di.xml +++ b/app/code/Magento/MediaStorage/etc/di.xml @@ -31,11 +31,4 @@ <argument name="imageResizeScheduler" xsi:type="object">Magento\MediaStorage\Service\ImageResizeScheduler\Proxy</argument> </arguments> </type> - <type name="Magento\Framework\Storage\StorageProvider"> - <arguments> - <argument name="storage" xsi:type="array"> - <item name="media" xsi:type="string">pub/media</item> - </argument> - </arguments> - </type> </config> diff --git a/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php b/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php new file mode 100644 index 0000000000000..7fe3b25cf97b2 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php @@ -0,0 +1,220 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Test\Unit\Block\Adminhtml\Wysiwyg\Files; + +use Magento\Theme\Model\Wysiwyg\Storage; + +class ContentTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Backend\Model\Url|PHPUnit_Framework_MockObject_MockObject + */ + protected $_urlBuilder; + + /** + * @var \Magento\Theme\Helper\Storage|PHPUnit_Framework_MockObject_MockObject + */ + protected $_helperStorage; + + /** + * @var \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content|PHPUnit_Framework_MockObject_MockObject + */ + protected $_filesContent; + + /** + * @var \Magento\Framework\App\RequestInterface|PHPUnit_Framework_MockObject_MockObject + */ + protected $_request; + + protected function setUp() + { + $this->_helperStorage = $this->createMock(\Magento\Theme\Helper\Storage::class); + $this->_urlBuilder = $this->createMock(\Magento\Backend\Model\Url::class); + $this->_request = $this->createMock(\Magento\Framework\App\RequestInterface::class); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $constructArguments = $objectManagerHelper->getConstructArguments( + \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content::class, + [ + 'urlBuilder' => $this->_urlBuilder, + 'request' => $this->_request, + 'storageHelper' => $this->_helperStorage + ] + ); + $this->_filesContent = $objectManagerHelper->getObject( + \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content::class, + $constructArguments + ); + } + + /** + * @dataProvider requestParamsProvider + * @param array $requestParams + */ + public function testGetNewFolderUrl($requestParams) + { + $expectedUrl = 'some_url'; + + $this->_helperStorage->expects( + $this->once() + )->method( + 'getRequestParams' + )->will( + $this->returnValue($requestParams) + ); + + $this->_urlBuilder->expects( + $this->once() + )->method( + 'getUrl' + )->with( + 'adminhtml/*/newFolder', + $requestParams + )->will( + $this->returnValue($expectedUrl) + ); + + $this->assertEquals($expectedUrl, $this->_filesContent->getNewfolderUrl()); + } + + /** + * @dataProvider requestParamsProvider + * @param array $requestParams + */ + public function testGetDeleteFilesUrl($requestParams) + { + $expectedUrl = 'some_url'; + + $this->_helperStorage->expects( + $this->once() + )->method( + 'getRequestParams' + )->will( + $this->returnValue($requestParams) + ); + + $this->_urlBuilder->expects( + $this->once() + )->method( + 'getUrl' + )->with( + 'adminhtml/*/deleteFiles', + $requestParams + )->will( + $this->returnValue($expectedUrl) + ); + + $this->assertEquals($expectedUrl, $this->_filesContent->getDeleteFilesUrl()); + } + + /** + * @dataProvider requestParamsProvider + * @param array $requestParams + */ + public function testGetOnInsertUrl($requestParams) + { + $expectedUrl = 'some_url'; + + $this->_helperStorage->expects( + $this->once() + )->method( + 'getRequestParams' + )->will( + $this->returnValue($requestParams) + ); + + $this->_urlBuilder->expects( + $this->once() + )->method( + 'getUrl' + )->with( + 'adminhtml/*/onInsert', + $requestParams + )->will( + $this->returnValue($expectedUrl) + ); + + $this->assertEquals($expectedUrl, $this->_filesContent->getOnInsertUrl()); + } + + /** + * Data provider for requestParams + * @return array + */ + public function requestParamsProvider() + { + return [ + [ + 'requestParams' => [ + \Magento\Theme\Helper\Storage::PARAM_THEME_ID => 1, + \Magento\Theme\Helper\Storage::PARAM_CONTENT_TYPE => Storage::TYPE_IMAGE, + \Magento\Theme\Helper\Storage::PARAM_NODE => 'root', + ] + ] + ]; + } + + public function testGetTargetElementId() + { + $expectedRequest = 'some_request'; + + $this->_request->expects( + $this->once() + )->method( + 'getParam' + )->with( + 'target_element_id' + )->will( + $this->returnValue($expectedRequest) + ); + + $this->assertEquals($expectedRequest, $this->_filesContent->getTargetElementId()); + } + + public function testGetContentsUrl() + { + $expectedUrl = 'some_url'; + + $expectedRequest = 'some_request'; + + $requestParams = [ + \Magento\Theme\Helper\Storage::PARAM_THEME_ID => 1, + \Magento\Theme\Helper\Storage::PARAM_CONTENT_TYPE => Storage::TYPE_IMAGE, + \Magento\Theme\Helper\Storage::PARAM_NODE => 'root', + ]; + + $this->_urlBuilder->expects( + $this->once() + )->method( + 'getUrl' + )->with( + 'adminhtml/*/contents', + ['type' => $expectedRequest] + $requestParams + )->will( + $this->returnValue($expectedUrl) + ); + + $this->_request->expects( + $this->once() + )->method( + 'getParam' + )->with( + 'type' + )->will( + $this->returnValue($expectedRequest) + ); + + $this->_helperStorage->expects( + $this->once() + )->method( + 'getRequestParams' + )->will( + $this->returnValue($requestParams) + ); + + $this->assertEquals($expectedUrl, $this->_filesContent->getContentsUrl()); + } +} diff --git a/app/etc/di.xml b/app/etc/di.xml index 85d05b6be38fa..a11b8fd5a2506 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1814,13 +1814,4 @@ <argument name="cache" xsi:type="object">configured_block_cache</argument> </arguments> </type> - <type name="Magento\Framework\Storage\StorageAdapterProvider"> - <arguments> - <argument name="config" xsi:type="array"> - <item name="local" xsi:type="string">Magento\Framework\Storage\AdapterFactory\LocalFactory</item> - <item name="aws_s3" xsi:type="string">Magento\Framework\Storage\AdapterFactory\AwsS3Factory</item> - <item name="ms_azure" xsi:type="string">Magento\Framework\Storage\AdapterFactory\AzureFactory</item> - </argument> - </arguments> - </type> </config> diff --git a/composer.json b/composer.json index ac005f9da6a1e..db34b0a9c2fd0 100644 --- a/composer.json +++ b/composer.json @@ -35,14 +35,11 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", - "guzzlehttp/guzzle": "^6.3.3", - "league/flysystem": "^1.0", - "league/flysystem-aws-s3-v3": "^1.0", - "league/flysystem-azure-blob-storage": "^0.1.6", "magento/composer": "1.6.x-dev", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", + "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", @@ -55,7 +52,6 @@ "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", - "wikimedia/less.php": "~1.8.0", "zendframework/zend-captcha": "^2.7.1", "zendframework/zend-code": "~3.3.0", "zendframework/zend-config": "^2.6.0", @@ -83,7 +79,8 @@ "zendframework/zend-text": "^2.6.0", "zendframework/zend-uri": "^2.5.1", "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.11.2" + "zendframework/zend-view": "~2.11.2", + "guzzlehttp/guzzle": "^6.3.3" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", diff --git a/composer.lock b/composer.lock index 1236d2b0ae82f..144614ba2279d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,95 +1,11 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "522d676db5baf5864a824409c54948fc", + "content-hash": "d9bed7b45c83f9133bdec76acac8b796", "packages": [ - { - "name": "aws/aws-sdk-php", - "version": "3.133.24", - "source": { - "type": "git", - "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/726426e1514be5220d55ecf02eb1f938a3b4a105", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-simplexml": "*", - "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "^2.5", - "php": ">=5.5" - }, - "require-dev": { - "andrewsville/php-token-reflection": "^1.4", - "aws/aws-php-sns-message-validator": "~1.0", - "behat/behat": "~3.0", - "doctrine/cache": "~1.4", - "ext-dom": "*", - "ext-openssl": "*", - "ext-pcntl": "*", - "ext-sockets": "*", - "nette/neon": "^2.3", - "phpunit/phpunit": "^4.8.35|^5.4.3", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3" - }, - "suggest": { - "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", - "doctrine/cache": "To use the DoctrineCacheAdapter", - "ext-curl": "To send requests using cURL", - "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", - "ext-sockets": "To use client-side monitoring" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Aws\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" - } - ], - "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", - "keywords": [ - "amazon", - "aws", - "cloud", - "dynamodb", - "ec2", - "glacier", - "s3", - "sdk" - ], - "time": "2020-02-27T19:13:45+00:00" - }, { "name": "braintree/braintree_php", "version": "3.35.0", @@ -341,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.9.3", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b" + "reference": "7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/1291a16ce3f48bfdeca39d64fca4875098af4d7b", - "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b", + "url": "https://api.github.com/repos/composer/composer/zipball/7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb", + "reference": "7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb", "shasum": "" }, "require": { @@ -417,7 +333,7 @@ "dependency", "package" ], - "time": "2020-02-04T11:58:49+00:00" + "time": "2020-01-14T15:30:32+00:00" }, { "name": "composer/semver", @@ -482,16 +398,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.3", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" + "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", + "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", "shasum": "" }, "require": { @@ -538,7 +454,7 @@ "spdx", "validator" ], - "time": "2020-02-14T07:44:31+00:00" + "time": "2019-07-29T10:31:59+00:00" }, { "name": "composer/xdebug-handler", @@ -1034,178 +950,6 @@ ], "time": "2019-09-25T14:49:45+00:00" }, - { - "name": "league/flysystem", - "version": "1.0.64", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "d13c43dbd4b791f815215959105a008515d1a2e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d13c43dbd4b791f815215959105a008515d1a2e0", - "reference": "d13c43dbd4b791f815215959105a008515d1a2e0", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "php": ">=5.5.9" - }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" - }, - "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.26" - }, - "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Flysystem\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Filesystem abstraction: Many filesystems, one API.", - "keywords": [ - "Cloud Files", - "WebDAV", - "abstraction", - "aws", - "cloud", - "copy.com", - "dropbox", - "file systems", - "files", - "filesystem", - "filesystems", - "ftp", - "rackspace", - "remote", - "s3", - "sftp", - "storage" - ], - "time": "2020-02-05T18:14:17+00:00" - }, - { - "name": "league/flysystem-aws-s3-v3", - "version": "1.0.24", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570", - "shasum": "" - }, - "require": { - "aws/aws-sdk-php": "^3.0.0", - "league/flysystem": "^1.0.40", - "php": ">=5.5.0" - }, - "require-dev": { - "henrikbjorn/phpspec-code-coverage": "~1.0.1", - "phpspec/phpspec": "^2.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Flysystem\\AwsS3v3\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Flysystem adapter for the AWS S3 SDK v3.x", - "time": "2020-02-23T13:31:58+00:00" - }, - { - "name": "league/flysystem-azure-blob-storage", - "version": "0.1.6", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem-azure-blob-storage.git", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-azure-blob-storage/zipball/97215345f3c42679299ba556a4d16d4847ee7f6d", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.5", - "league/flysystem": "^1.0", - "microsoft/azure-storage-blob": "^1.1", - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\Flysystem\\AzureBlobStorage\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "time": "2019-06-07T20:42:16+00:00" - }, { "name": "magento/composer", "version": "1.6.x-dev", @@ -1368,94 +1112,6 @@ ], "time": "2019-11-26T15:09:40+00:00" }, - { - "name": "microsoft/azure-storage-blob", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/Azure/azure-storage-blob-php.git", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-blob-php/zipball/6a333cd28a3742c3e99e79042dc6510f9f917919", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919", - "shasum": "" - }, - "require": { - "microsoft/azure-storage-common": "~1.4", - "php": ">=5.6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "MicrosoftAzure\\Storage\\Blob\\": "src/Blob" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } - ], - "description": "This project provides a set of PHP client libraries that make it easy to access Microsoft Azure Storage Blob APIs.", - "keywords": [ - "azure", - "blob", - "php", - "sdk", - "storage" - ], - "time": "2020-01-02T07:18:59+00:00" - }, - { - "name": "microsoft/azure-storage-common", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/Azure/azure-storage-common-php.git", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-common-php/zipball/be4df800761d0d0fa91a9460c7f42517197d57a0", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "~6.0", - "php": ">=5.6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "MicrosoftAzure\\Storage\\Common\\": "src/Common" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } - ], - "description": "This project provides a set of common code shared by Azure Storage Blob, Table, Queue and File PHP client libraries.", - "keywords": [ - "azure", - "common", - "php", - "sdk", - "storage" - ], - "time": "2020-01-02T07:15:54+00:00" - }, { "name": "monolog/monolog", "version": "1.25.3", @@ -1534,63 +1190,6 @@ ], "time": "2019-12-20T14:15:16+00:00" }, - { - "name": "mtdowling/jmespath.php", - "version": "2.5.0", - "source": { - "type": "git", - "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "52168cb9472de06979613d365c7f1ab8798be895" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", - "reference": "52168cb9472de06979613d365c7f1ab8798be895", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/polyfill-mbstring": "^1.4" - }, - "require-dev": { - "composer/xdebug-handler": "^1.2", - "phpunit/phpunit": "^4.8.36|^7.5.15" - }, - "bin": [ - "bin/jp.php" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - }, - "autoload": { - "psr-4": { - "JmesPath\\": "src/" - }, - "files": [ - "src/JmesPath.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Declaratively specify how to extract elements from a JSON document", - "keywords": [ - "json", - "jsonpath" - ], - "time": "2019-12-30T18:03:34+00:00" - }, { "name": "paragonie/random_compat", "version": "v9.99.99", @@ -1916,16 +1515,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.25", + "version": "2.0.23", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0" + "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c18159618ed7cd7ff721ac1a8fec7860a475d2f0", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c78eb5058d5bb1a183133c36d4ba5b6675dfa099", + "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099", "shasum": "" }, "require": { @@ -2004,7 +1603,7 @@ "x.509", "x509" ], - "time": "2020-02-25T04:16:50+00:00" + "time": "2019-09-17T03:41:22+00:00" }, { "name": "psr/container", @@ -2371,16 +1970,16 @@ }, { "name": "seld/phar-utils", - "version": "1.1.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" + "reference": "84715761c35808076b00908a20317a3a8a67d17e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/84715761c35808076b00908a20317a3a8a67d17e", + "reference": "84715761c35808076b00908a20317a3a8a67d17e", "shasum": "" }, "require": { @@ -2409,22 +2008,22 @@ ], "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "phar" + "phra" ], - "time": "2020-02-14T15:25:33+00:00" + "time": "2020-01-13T10:41:09+00:00" }, { "name": "symfony/console", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656" + "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f512001679f37e6a042b51897ed24a2f05eba656", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656", + "url": "https://api.github.com/repos/symfony/console/zipball/e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", + "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", "shasum": "" }, "require": { @@ -2487,11 +2086,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2020-01-25T12:44:29+00:00" + "time": "2020-01-10T21:54:01+00:00" }, { "name": "symfony/css-selector", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2544,7 +2143,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2672,7 +2271,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2722,7 +2321,7 @@ }, { "name": "symfony/finder", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2771,16 +2370,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", "shasum": "" }, "require": { @@ -2792,7 +2391,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2825,20 +2424,20 @@ "polyfill", "portable" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" + "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", + "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", "shasum": "" }, "require": { @@ -2850,7 +2449,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2884,20 +2483,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-11-27T14:18:11+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" + "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/4b0e2222c55a25b4541305a053013d5647d3a25f", + "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f", "shasum": "" }, "require": { @@ -2906,7 +2505,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2942,11 +2541,11 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-11-27T16:25:15+00:00" }, { "name": "symfony/process", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -5499,16 +5098,16 @@ }, { "name": "allure-framework/allure-php-api", - "version": "1.1.7", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/allure-framework/allure-php-commons.git", - "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18" + "reference": "2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", - "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", + "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f", + "reference": "2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f", "shasum": "" }, "require": { @@ -5548,7 +5147,7 @@ "php", "report" ], - "time": "2020-02-05T16:43:19+00:00" + "time": "2020-01-09T10:26:09+00:00" }, { "name": "allure-framework/allure-phpunit", @@ -5600,18 +5199,102 @@ ], "time": "2017-11-03T13:08:21+00:00" }, + { + "name": "aws/aws-sdk-php", + "version": "3.133.8", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", + "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2020-02-05T19:12:47+00:00" + }, { "name": "behat/gherkin", - "version": "v4.6.1", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759" + "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/25bdcaf37898b4a939fa3031d5d753ced97e4759", - "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", + "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", "shasum": "" }, "require": { @@ -5657,7 +5340,7 @@ "gherkin", "parser" ], - "time": "2020-02-27T11:29:57+00:00" + "time": "2019-01-16T14:22:17+00:00" }, { "name": "cache/cache", @@ -5921,31 +5604,31 @@ }, { "name": "consolidation/annotated-command", - "version": "4.1.0", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178" + "reference": "512a2e54c98f3af377589de76c43b24652bcb789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/33e472d3cceb0f22a527d13ccfa3f76c4d21c178", - "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", + "reference": "512a2e54c98f3af377589de76c43b24652bcb789", "shasum": "" }, "require": { - "consolidation/output-formatters": "^4.1", - "php": ">=7.1.3", - "psr/log": "^1|^2", - "symfony/console": "^4|^5", - "symfony/event-dispatcher": "^4|^5", - "symfony/finder": "^4|^5" + "consolidation/output-formatters": "^3.4", + "php": ">=5.4.5", + "psr/log": "^1", + "symfony/console": "^2.8|^3|^4", + "symfony/event-dispatcher": "^2.5|^3|^4", + "symfony/finder": "^2.5|^3|^4" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3" + "squizlabs/php_codesniffer": "^2.7" }, "type": "library", "extra": { @@ -5953,11 +5636,48 @@ "symfony4": { "require": { "symfony/console": "^4.0" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + }, + "scenario-options": { + "create-lockfile": "false" + } + }, + "phpunit4": { + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } } } }, "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -5976,7 +5696,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2020-02-07T03:35:30+00:00" + "time": "2019-03-08T16:55:03+00:00" }, { "name": "consolidation/config", @@ -6061,33 +5781,74 @@ }, { "name": "consolidation/log", - "version": "2.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff" + "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/446f804476db4f73957fa4bcb66ab2facf5397ff", - "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff", + "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", + "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", "shasum": "" }, "require": { "php": ">=5.4.5", "psr/log": "^1.0", - "symfony/console": "^4|^5" + "symfony/console": "^2.8|^3|^4" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3" + "squizlabs/php_codesniffer": "^2" }, "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require": { + "symfony/console": "^4.0" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + }, + "phpunit4": { + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + } + }, "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -6106,35 +5867,34 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2020-02-07T01:22:27+00:00" + "time": "2019-01-01T17:30:51+00:00" }, { "name": "consolidation/output-formatters", - "version": "4.1.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "eae721c3a916707c40d4390efbf48d4c799709cc" + "reference": "99ec998ffb697e0eada5aacf81feebfb13023605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/eae721c3a916707c40d4390efbf48d4c799709cc", - "reference": "eae721c3a916707c40d4390efbf48d4c799709cc", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/99ec998ffb697e0eada5aacf81feebfb13023605", + "reference": "99ec998ffb697e0eada5aacf81feebfb13023605", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0", - "php": ">=7.1.3", - "symfony/console": "^4|^5", - "symfony/finder": "^4|^5" + "php": ">=5.4.0", + "symfony/console": "^2.8|^3|^4", + "symfony/finder": "^2.5|^3|^4" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3", - "symfony/var-dumper": "^4", - "symfony/yaml": "^4", + "phpunit/phpunit": "^5.7.27", + "squizlabs/php_codesniffer": "^2.7", + "symfony/var-dumper": "^2.8|^3|^4", "victorjonsson/markdowndocs": "^1.3" }, "suggest": { @@ -6146,11 +5906,50 @@ "symfony4": { "require": { "symfony/console": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^6" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony3": { + "require": { + "symfony/console": "^3.4", + "symfony/finder": "^3.4", + "symfony/var-dumper": "^3.4" + }, + "config": { + "platform": { + "php": "5.6.32" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + }, + "scenario-options": { + "create-lockfile": "false" } } }, "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -6169,30 +5968,30 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2020-02-07T03:22:30+00:00" + "time": "2019-05-30T23:16:01+00:00" }, { "name": "consolidation/robo", - "version": "1.4.12", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "eb45606f498b3426b9a98b7c85e300666a968e51" + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/eb45606f498b3426b9a98b7c85e300666a968e51", - "reference": "eb45606f498b3426b9a98b7c85e300666a968e51", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.11.0|^4.1", - "consolidation/config": "^1.2.1", - "consolidation/log": "^1.1.1|^2", - "consolidation/output-formatters": "^3.1.13|^4.1", - "consolidation/self-update": "^1.1.5", - "grasmash/yaml-expander": "^1.4", - "league/container": "^2.4.1", + "consolidation/annotated-command": "^2.11.0", + "consolidation/config": "^1.2", + "consolidation/log": "~1", + "consolidation/output-formatters": "^3.1.13", + "consolidation/self-update": "^1", + "grasmash/yaml-expander": "^1.3", + "league/container": "^2.2", "php": ">=5.5.0", "symfony/console": "^2.8|^3|^4", "symfony/event-dispatcher": "^2.5|^3|^4", @@ -6204,13 +6003,20 @@ "codegyre/robo": "< 1.0" }, "require-dev": { + "codeception/aspect-mock": "^1|^2.1.1", + "codeception/base": "^2.3.7", + "codeception/verify": "^0.3.2", "g1a/composer-test-scenarios": "^3", + "goaop/framework": "~2.1.2", + "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", - "patchwork/jsqueeze": "^2", + "nikic/php-parser": "^3.1.5", + "patchwork/jsqueeze": "~2", "pear/archive_tar": "^1.4.4", "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5.7.27", - "squizlabs/php_codesniffer": "^3" + "phpunit/php-code-coverage": "~2|~4", + "sebastian/comparator": "^1.2.4", + "squizlabs/php_codesniffer": "^2.8" }, "suggest": { "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", @@ -6238,11 +6044,8 @@ "require": { "symfony/console": "^2.8" }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, "remove": [ - "php-coveralls/php-coveralls" + "goaop/framework" ], "config": { "platform": { @@ -6255,7 +6058,7 @@ } }, "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -6274,7 +6077,7 @@ } ], "description": "Modern task runner", - "time": "2020-02-18T17:31:26+00:00" + "time": "2019-10-29T15:50:02+00:00" }, { "name": "consolidation/self-update", @@ -7239,16 +7042,16 @@ }, { "name": "jms/serializer", - "version": "1.14.1", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0" + "reference": "ee96d57024af9a7716d56fcbe3aa94b3d030f3ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ba908d278fff27ec01fb4349f372634ffcd697c0", - "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ee96d57024af9a7716d56fcbe3aa94b3d030f3ca", + "reference": "ee96d57024af9a7716d56fcbe3aa94b3d030f3ca", "shasum": "" }, "require": { @@ -7301,13 +7104,13 @@ "MIT" ], "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, { "name": "Asmir Mustafic", "email": "goetas@gmail.com" + }, + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" } ], "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", @@ -7319,7 +7122,7 @@ "serialization", "xml" ], - "time": "2020-02-22T20:59:37+00:00" + "time": "2019-04-17T08:12:16+00:00" }, { "name": "league/container", @@ -7386,6 +7189,90 @@ ], "time": "2017-05-10T09:20:27+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.63", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8132daec326565036bc8e8d1876f77ec183a7bd6", + "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.10" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2020-01-04T16:30:31+00:00" + }, { "name": "lusitanian/oauth", "version": "v0.8.11", @@ -7623,6 +7510,63 @@ "homepage": "http://vfs.bovigo.org/", "time": "2019-10-30T15:31:00+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" + }, + "require-dev": { + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2019-12-30T18:03:34+00:00" + }, { "name": "mustache/mustache", "version": "v2.13.0", @@ -7912,16 +7856,16 @@ }, { "name": "php-webdriver/webdriver", - "version": "1.8.1", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "262ea0d209c292e0330be1041424887bbbffef04" + "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/262ea0d209c292e0330be1041424887bbbffef04", - "reference": "262ea0d209c292e0330be1041424887bbbffef04", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", + "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", "shasum": "" }, "require": { @@ -7953,12 +7897,12 @@ } }, "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - }, "files": [ "lib/Exception/TimeoutException.php" - ] + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7973,7 +7917,7 @@ "selenium", "webdriver" ], - "time": "2020-02-17T08:14:38+00:00" + "time": "2020-02-10T15:04:25+00:00" }, { "name": "phpcollection/phpcollection", @@ -8135,38 +8079,41 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "doctrine/instantiator": "^1.0.5", + "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "phpDocumentor\\Reflection\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -8177,14 +8124,10 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -8421,16 +8364,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.11", + "version": "0.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b" + "reference": "07fa7958027fd98c567099bbcda5d6a0f2ec5197" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ca5f2b7cf81c6d8fba74f9576970399c5817e03b", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/07fa7958027fd98c567099bbcda5d6a0f2ec5197", + "reference": "07fa7958027fd98c567099bbcda5d6a0f2ec5197", "shasum": "" }, "require": { @@ -8456,7 +8399,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-02-16T14:00:29+00:00" + "time": "2020-01-20T21:59:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -9650,7 +9593,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -9709,7 +9652,7 @@ }, { "name": "symfony/config", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -9773,16 +9716,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a" + "reference": "6faf589e1f6af78692aed3ab6b3c336c58d5d83c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ec60a7d12f5e8ab0f99456adce724717d9c1784a", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6faf589e1f6af78692aed3ab6b3c336c58d5d83c", + "reference": "6faf589e1f6af78692aed3ab6b3c336c58d5d83c", "shasum": "" }, "require": { @@ -9842,11 +9785,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:49:27+00:00" + "time": "2020-01-21T07:39:36+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -9907,16 +9850,16 @@ }, { "name": "symfony/http-foundation", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5" + "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/491a20dfa87e0b3990170593bc2de0bb34d828a5", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c33998709f3fe9b8e27e0277535b07fbf6fde37a", + "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a", "shasum": "" }, "require": { @@ -9958,11 +9901,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:11:17+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "symfony/mime", - "version": "v5.0.4", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", @@ -10024,7 +9967,7 @@ }, { "name": "symfony/options-resolver", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -10078,22 +10021,22 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a" + "reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a", - "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6f9c239e61e1b0c9229a28ff89a812dc449c3d46", + "reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46", "shasum": "" }, "require": { "php": ">=5.3.3", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.10" + "symfony/polyfill-php72": "^1.9" }, "suggest": { "ext-intl": "For best performance" @@ -10101,7 +10044,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -10136,20 +10079,20 @@ "portable", "shim" ], - "time": "2020-01-17T12:01:36+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "419c4940024c30ccc033650373a1fe13890d3255" + "reference": "af23c7bb26a73b850840823662dda371484926c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/419c4940024c30ccc033650373a1fe13890d3255", - "reference": "419c4940024c30ccc033650373a1fe13890d3255", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/af23c7bb26a73b850840823662dda371484926c4", + "reference": "af23c7bb26a73b850840823662dda371484926c4", "shasum": "" }, "require": { @@ -10159,7 +10102,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -10195,20 +10138,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.14.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" + "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038", + "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038", "shasum": "" }, "require": { @@ -10217,7 +10160,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -10250,11 +10193,11 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -10304,7 +10247,7 @@ }, { "name": "symfony/yaml", - "version": "v4.4.4", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -10494,16 +10437,16 @@ }, { "name": "webmozart/assert", - "version": "1.7.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { @@ -10538,7 +10481,7 @@ "check", "validate" ], - "time": "2020-02-14T12:15:55+00:00" + "time": "2019-11-24T13:36:37+00:00" }, { "name": "weew/helpers-array", diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php new file mode 100644 index 0000000000000..569cf2357675c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php @@ -0,0 +1,165 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model; + +use Magento\Framework\App\Filesystem\DirectoryList; + +/** + * Tests for the \Magento\Catalog\Model\ImageUploader class + */ +class ImageUploaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var \Magento\Catalog\Model\ImageUploader + */ + private $imageUploader; + + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @var \Magento\Framework\Filesystem\Directory\WriteInterface + */ + private $mediaDirectory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Framework\Filesystem $filesystem */ + $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + /** @var $uploader \Magento\MediaStorage\Model\File\Uploader */ + $this->imageUploader = $this->objectManager->create( + \Magento\Catalog\Model\ImageUploader::class, + [ + 'baseTmpPath' => 'catalog/tmp/category', + 'basePath' => 'catalog/category', + 'allowedExtensions' => ['jpg', 'jpeg', 'gif', 'png'], + 'allowedMimeTypes' => ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'] + ] + ); + } + + /** + * @return void + */ + public function testSaveFileToTmpDir(): void + { + $fileName = 'magento_small_image.jpg'; + $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); + $fixtureDir = realpath(__DIR__ . '/../_files'); + $filePath = $tmpDirectory->getAbsolutePath($fileName); + copy($fixtureDir . DIRECTORY_SEPARATOR . $fileName, $filePath); + + $_FILES['image'] = [ + 'name' => $fileName, + 'type' => 'image/jpeg', + 'tmp_name' => $filePath, + 'error' => 0, + 'size' => 12500, + ]; + + $this->imageUploader->saveFileToTmpDir('image'); + $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; + $this->assertTrue(is_file($this->mediaDirectory->getAbsolutePath($filePath))); + } + + /** + * Test that method rename files when move it with the same name into base directory. + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/catalog_category_image.php + * @magentoDataFixture Magento/Catalog/_files/catalog_tmp_category_image.php + */ + public function testMoveFileFromTmp(): void + { + $expectedFilePath = $this->imageUploader->getBasePath() . DIRECTORY_SEPARATOR . 'magento_small_image_1.jpg'; + + $this->assertFileNotExists($this->mediaDirectory->getAbsolutePath($expectedFilePath)); + + $this->imageUploader->moveFileFromTmp('magento_small_image.jpg'); + + $this->assertFileExists($this->mediaDirectory->getAbsolutePath($expectedFilePath)); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage File validation failed. + * @return void + */ + public function testSaveFileToTmpDirWithWrongExtension(): void + { + $fileName = 'text.txt'; + $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); + $filePath = $tmpDirectory->getAbsolutePath($fileName); + $file = fopen($filePath, "wb"); + fwrite($file, 'just a text'); + + $_FILES['image'] = [ + 'name' => $fileName, + 'type' => 'text/plain', + 'tmp_name' => $filePath, + 'error' => 0, + 'size' => 12500, + ]; + + $this->imageUploader->saveFileToTmpDir('image'); + $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; + $this->assertFalse(is_file($this->mediaDirectory->getAbsolutePath($filePath))); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage File validation failed. + * @return void + */ + public function testSaveFileToTmpDirWithWrongFile(): void + { + $fileName = 'file.gif'; + $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); + $filePath = $tmpDirectory->getAbsolutePath($fileName); + $file = fopen($filePath, "wb"); + fwrite($file, 'just a text'); + + $_FILES['image'] = [ + 'name' => $fileName, + 'type' => 'image/gif', + 'tmp_name' => $filePath, + 'error' => 0, + 'size' => 12500, + ]; + + $this->imageUploader->saveFileToTmpDir('image'); + $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; + $this->assertFalse(is_file($this->mediaDirectory->getAbsolutePath($filePath))); + } + + /** + * @inheritdoc + */ + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Framework\Filesystem::class + ); + /** @var \Magento\Framework\Filesystem\Directory\WriteInterface $mediaDirectory */ + $mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $mediaDirectory->delete('tmp'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php index f348372f2029a..89b91ab57e51a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php @@ -338,7 +338,11 @@ private function getProductInstance(?int $storeId = null): ProductInterface { /** @var ProductInterface $product */ $product = $this->productFactory->create(); - $product->setId($this->getProduct()->getId()); + $product->setData( + $this->productLinkField, + $this->getProduct()->getData($this->productLinkField) + ); + if ($storeId) { $product->setStoreId($storeId); } diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php index 12ed71b708b88..93e7833038a42 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -58,8 +58,6 @@ public function testRenderRestrictMode(): void foreach ($header as $item) { $contentSecurityPolicyContent[] = $item->getFieldValue(); } - } else { - $contentSecurityPolicyContent = [$header->getFieldValue()]; } $this->assertEquals(['default-src https://magento.com \'self\';'], $contentSecurityPolicyContent); } @@ -86,8 +84,6 @@ public function testRenderRestrictWithReportingMode(): void foreach ($header as $item) { $contentSecurityPolicyContent[] = $item->getFieldValue(); } - } else { - $contentSecurityPolicyContent = [$header->getFieldValue()]; } $this->assertEquals( ['default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;'], diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt index 6a7c814f50524..f54defbd57604 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -14,5 +14,3 @@ dev/tests/api-functional/testsuite/Magento/Customer/Api/AddressRepositoryTest.ph dev/tests/api-functional/testsuite/Magento/Framework/Model/Entity/HydratorTest.php dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php dev/tests/api-functional/testsuite/Magento/Integration/Model/CustomerTokenServiceTest.php -lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php -lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index 2adff8162e5a3..dc3e63fcc7df8 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -19,11 +19,6 @@ class Response extends \Zend\Http\PhpEnvironment\Response implements \Magento\Fr */ protected $isRedirect = false; - /** - * @var bool - */ - private $headersSent; - /** * @inheritdoc */ @@ -33,10 +28,6 @@ public function getHeader($name) $headers = $this->getHeaders(); if ($headers->has($name)) { $header = $headers->get($name); - // zend-http >= 2.10.11 can return \ArrayIterator instead of a single Header - if ($header instanceof \ArrayIterator) { - $header = $header->current(); - } } return $header; } diff --git a/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php b/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php deleted file mode 100644 index 56794bbd29fcf..0000000000000 --- a/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage\AdapterFactory; - -use League\Flysystem\AdapterInterface; - -/** - * Storage adapter factory - * - * A storage adapter should have a factory implementing this interface in order to be supported by Magento. - * A new factory should be registered in \Magento\Framework\Storage\StorageProvider::$storageAdapters via di.xml. - */ -interface AdapterFactoryInterface -{ - /** - * Create instance of a storage adapter - * - * @param array $options - * @return AdapterInterface - */ - public function create(array $options): AdapterInterface; -} diff --git a/lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php b/lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php deleted file mode 100644 index 719c85b6f7f9d..0000000000000 --- a/lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage\AdapterFactory; - -use Aws\S3\S3Client; -use League\Flysystem\AdapterInterface; -use League\Flysystem\AwsS3v3\AwsS3Adapter; -use Magento\Framework\Storage\InvalidStorageConfigurationException; - -/** - * Factory for AWS S3 storage adapter - */ -class AwsS3Factory implements AdapterFactoryInterface -{ - /** - * @inheritdoc - */ - public function create(array $options): AdapterInterface - { - if (empty($options['client']) || empty($options['bucket'])) { - throw new InvalidStorageConfigurationException( - "Can't create AWS S3 adapter: required 'client' and/or 'bucket' options are absent" - ); - } - $client = new S3Client($options['client']); - return new AwsS3Adapter($client, $options['bucket'], $options['prefix'] ?? ''); - } -} diff --git a/lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php b/lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php deleted file mode 100644 index 1d548151cb95a..0000000000000 --- a/lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage\AdapterFactory; - -use League\Flysystem\AdapterInterface; -use League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter; -use Magento\Framework\Storage\InvalidStorageConfigurationException; -use MicrosoftAzure\Storage\Blob\BlobRestProxy; - -/** - * Factory for Azure storage adapter - */ -class AzureFactory implements AdapterFactoryInterface -{ - /** - * @inheritdoc - */ - public function create(array $options): AdapterInterface - { - if (empty($options['connection_string']) || empty($options['container_name'])) { - throw new InvalidStorageConfigurationException( - "Can't create Azure Blob storage adapter: " . - "required 'connection_string' and/or 'container_name' options are absent" - ); - } - $client = BlobRestProxy::createBlobService($options['connection_string']); - return new AzureBlobStorageAdapter($client, $options['container_name'], $options['prefix'] ?? null); - } -} diff --git a/lib/internal/Magento/Framework/Storage/AdapterFactory/LocalFactory.php b/lib/internal/Magento/Framework/Storage/AdapterFactory/LocalFactory.php deleted file mode 100644 index edf6535f372f9..0000000000000 --- a/lib/internal/Magento/Framework/Storage/AdapterFactory/LocalFactory.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage\AdapterFactory; - -use League\Flysystem\Adapter\Local; -use League\Flysystem\AdapterInterface; -use Magento\Framework\Storage\InvalidStorageConfigurationException; - -/** - * Factory for local filesystem storage adapter - */ -class LocalFactory implements AdapterFactoryInterface -{ - public const ADAPTER_NAME = 'local'; - - /** - * @inheritdoc - */ - public function create(array $options): AdapterInterface - { - if (empty($options['root'])) { - throw new InvalidStorageConfigurationException( - "Can't create local filesystem storage adapter: required 'root' option is absent" - ); - } - return new Local($options['root']); - } -} diff --git a/lib/internal/Magento/Framework/Storage/FileNotFoundException.php b/lib/internal/Magento/Framework/Storage/FileNotFoundException.php deleted file mode 100644 index 540fc332130e8..0000000000000 --- a/lib/internal/Magento/Framework/Storage/FileNotFoundException.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Framework\Storage; - -/** - * Exception: FileNotFoundException - * - * Exception to be thrown when the a requested file does not exists - */ -class FileNotFoundException extends \RuntimeException -{ -} diff --git a/lib/internal/Magento/Framework/Storage/InvalidStorageConfigurationException.php b/lib/internal/Magento/Framework/Storage/InvalidStorageConfigurationException.php deleted file mode 100644 index 6f388103bfe21..0000000000000 --- a/lib/internal/Magento/Framework/Storage/InvalidStorageConfigurationException.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Framework\Storage; - -/** - * Exception to be thrown in a case when storage is configured incorrectly - */ -class InvalidStorageConfigurationException extends \RuntimeException -{ -} diff --git a/lib/internal/Magento/Framework/Storage/README.md b/lib/internal/Magento/Framework/Storage/README.md deleted file mode 100644 index 4cd276df8808f..0000000000000 --- a/lib/internal/Magento/Framework/Storage/README.md +++ /dev/null @@ -1,121 +0,0 @@ -The Storage library provides abstraction over different file storage providers. - -## Usage - -A module that needs file storage, it can be configured via `\Magento\Framework\Storage\StorageProvider` in `di.xml`: - -```xml -<type name="Magento\Framework\Storage\StorageProvider"> - <arguments> - <argument name="storage" xsi:type="array"> - <item name="storage-name" xsi:type="string">default/location</item> - </argument> - </arguments> -</type> -``` - -`default/location` is a default path in local filesystem, relative to Magento root. - -Now, in a PHP class that uses the declared storage, use the same `\Magento\Framework\Storage\StorageProvider` to get it: - -```php -/** - * @var \Magento\Framework\Storage\StorageProvider - */ -private $storageProvider; - -public function doSomething() -{ - $storage = $this->storageProvider->get('storage-name') - $storage->put('path.txt', $content); -} -``` - -## Configuring Storage - -A storage can be configured in `env.php`: - -```php -'storage' => [ - 'storage-name' => [ - 'adapter' => 'aws_s3', - 'options' => [ - 'client' => [ - 'credentials' => [ - 'key' => '<key>', - 'secret' => '<secret>' - ], - 'region' => '<region>', - 'version' => 'latest', - ], - 'bucket' => '<bucket>', - ], - ], - 'media' => [ - // this is default configuration, so it doesn't need to be configured explicitly like so - 'adapter' => 'local', - 'options' => [ - 'root' => 'pub/media' - ] - ] -] -``` - -Different providers have different `options` available for configuration. -Under the hood, Magento Storage relies on [Flysystem](https://github.com/thephpleague/flysystem) library, so`options` might reflect options required by a corresponding storage adapter implemented for Flysystem. - -## Storage Providers - -By default, Magento Storage provides support for the following storage providers: - -* Local filesystem (based on `\League\Flysystem\Adapter\Local`) - * Adapter name: `local` - * Options: - ```php - [ - 'root' => 'path/relative/to/magento/root' - ] - ``` -* AWS S3 V3 (based on `\League\Flysystem\AwsS3v3\AwsS3Adapter`) - * Adapter name: `aws_s3` - * Options: - ```php - [ - 'client' => [ - 'credentials' => [ - 'key' => '<key>', - 'secret' => '<secret>' - ], - 'region' => '<region>', - 'version' => 'latest', - ], - 'bucket' => '<bucket>', - 'prefix' => '<prefix>', - ] - ``` -* Azure Blob storage (based on `\League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter`) - * Adapter name: `ms_azure` - * Options: - ```php - [ - 'connection_string' => '<connection-string>', - 'container_name' => '<container-name>', - 'prefix' => '<prefix>', - ] - ``` - -Additional adapters can be added by: -1. Creating an adapter factory implementing `\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface` -2. Registering the factory in `Magento\Framework\Storage\StorageProvider` via `di.xml`: - ```xml - <type name="Magento\Framework\Storage\StorageProvider"> - <arguments> - <argument name="storageAdapters" xsi:type="array"> - <item name="custom_adapter" xsi:type="string">My\Storage\AdapterFactory</item> - </argument> - </arguments> - </type> - ``` - -The factory is registered as a "string" (name of the class). -That's because in most cases only a few adapters will be really created for a single application, and we don't want to create unnecessary factory instances. diff --git a/lib/internal/Magento/Framework/Storage/RootViolationException.php b/lib/internal/Magento/Framework/Storage/RootViolationException.php deleted file mode 100644 index 3ea41bfb57073..0000000000000 --- a/lib/internal/Magento/Framework/Storage/RootViolationException.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Framework\Storage; - -/** - * Exception: RootViolationException - * - * Exception to be thrown when the a directory root not specified - */ -class RootViolationException extends \RuntimeException -{ -} diff --git a/lib/internal/Magento/Framework/Storage/Storage.php b/lib/internal/Magento/Framework/Storage/Storage.php deleted file mode 100644 index fd0eaac008368..0000000000000 --- a/lib/internal/Magento/Framework/Storage/Storage.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage; - -use League\Flysystem\Filesystem; - -/** - * File storage abstraction - */ -class Storage implements StorageInterface -{ - - /** - * @var Filesystem - */ - private $filesystem; - - /** - * Storage constructor. - * - * @param Filesystem $filesystem - */ - public function __construct(Filesystem $filesystem) - { - $this->filesystem = $filesystem; - } - - /** - * @inheritDoc - */ - public function put($path, $contents, array $config = []): bool - { - return $this->filesystem->put($path, $contents, $config); - } - - /** - * @inheritDoc - */ - public function deleteDir($dirname): bool - { - try { - $result = $this->filesystem->deleteDir($dirname); - } catch (\League\Flysystem\RootViolationException $exception) { - throw new \Magento\Framework\Storage\RootViolationException($exception->getMessage()); - } - return $result; - } - - /** - * @inheritDoc - */ - public function getMetadata($path): ?array - { - try { - $metadata = $this->filesystem->getMetadata($path); - } catch (\League\Flysystem\FileNotFoundException $exception) { - throw new \Magento\Framework\Storage\FileNotFoundException( - $exception->getMessage() - ); - } - if ($metadata === false) { - $metadata = null; - } - return $metadata; - } - - /** - * @inheritDoc - */ - public function has($path): bool - { - return $this->filesystem->has($path); - } -} diff --git a/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php b/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php deleted file mode 100644 index bcd5fe806c0c3..0000000000000 --- a/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage; - -use League\Flysystem\AdapterInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface; - -/** - * Provider of storage adapters based on storage name - */ -class StorageAdapterProvider -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var array - */ - private $config; - - /** - * Constructor - * - * @param ObjectManagerInterface $objectManager - * @param array $config - */ - public function __construct(ObjectManagerInterface $objectManager, array $config) - { - $this->objectManager = $objectManager; - $this->config = $config; - } - - /** - * Create storage adapter based on its name with provided options - * - * @param string $adapterName - * @param array $options - * @return AdapterInterface|null - */ - public function create(string $adapterName, array $options) :? AdapterInterface - { - if (!isset($this->config[$adapterName])) { - throw new InvalidStorageConfigurationException( - "Configured adapter '$adapterName' is not supported" - ); - } - $adapterFactoryClass = $this->config[$adapterName]; - $adapterFactory = $this->objectManager->get($adapterFactoryClass); - if (!$adapterFactory instanceof AdapterFactoryInterface) { - throw new InvalidStorageConfigurationException( - "Configured storage adapter factory '$adapterFactory' must implement " . - "'\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface'" - ); - } - return $adapterFactory->create($options); - } -} diff --git a/lib/internal/Magento/Framework/Storage/StorageInterface.php b/lib/internal/Magento/Framework/Storage/StorageInterface.php deleted file mode 100644 index 4cf965b2287cd..0000000000000 --- a/lib/internal/Magento/Framework/Storage/StorageInterface.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage; - -/** - * Storage interface to be used by client code to manipulate objects in the storage - * - * Retrieve a real instance of storage via $storageProvider->get('<your-storage-name>'), - * where $storageProvider is an instance of \Magento\Framework\Storage\StorageProvider - */ -interface StorageInterface -{ - /** - * Create a file or update if exists. - * - * @param string $path The path to the file. - * @param string $contents The file contents. - * @param array $config An optional configuration array. - * - * @return bool True on success, false on failure. - */ - public function put($path, $contents, array $config = []): bool; - - /** - * Delete a directory. - * - * @param string $dirname - * - * @throws RootViolationException Thrown if $dirname is empty. - * - * @return bool True on success, false on failure. - */ - public function deleteDir($dirname): bool; - - /** - * Get a file's metadata. - * - * @param string $path The path to the file. - * - * @throws FileNotFoundException - * - * @return array|false The file metadata or false on failure. - */ - public function getMetadata($path): ?array; - - /** - * Check whether a file exists. - * - * @param string $path - * - * @return bool - */ - public function has($path): bool; -} diff --git a/lib/internal/Magento/Framework/Storage/StorageProvider.php b/lib/internal/Magento/Framework/Storage/StorageProvider.php deleted file mode 100644 index bad0813f33002..0000000000000 --- a/lib/internal/Magento/Framework/Storage/StorageProvider.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage; - -use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\Storage\AdapterFactory\LocalFactory; -use Magento\Framework\Storage\StorageFactory; -use League\Flysystem\FilesystemFactory; - -/** - * Main entry point for accessing file storage - * - * See README.md for usage details - */ -class StorageProvider -{ - private $storageConfig = []; - - private $storage = []; - - /** - * @var StorageFactory - */ - private $storageFactory; - - /** - * @var StorageAdapterProvider - */ - private $adapterProvider; - - /** - * @var FilesystemFactory - */ - private $filesystemFactory; - - /** - * StorageProvider constructor. - * @param StorageAdapterProvider $adapterProvider - * @param \Magento\Framework\Storage\StorageFactory $storageFactory - * @param array $storage - * @param DeploymentConfig $envConfig - * @param FilesystemFactory $filesystemFactory - * @throws \Magento\Framework\Exception\FileSystemException - * @throws \Magento\Framework\Exception\RuntimeException - */ - public function __construct( - StorageAdapterProvider $adapterProvider, - StorageFactory $storageFactory, - array $storage, - DeploymentConfig $envConfig, - FilesystemFactory $filesystemFactory - ) { - foreach ($storage as $storageName => $localPath) { - $this->storageConfig[$storageName] = [ - 'adapter' => LocalFactory::ADAPTER_NAME, - 'options' => [ - 'root' => BP . '/' . $localPath, - ], - ]; - $envStorageConfig = $envConfig->get('storage/' . $storageName); - if ($envStorageConfig) { - $this->storageConfig[$storageName] = array_replace( - $this->storageConfig[$storageName], - $envStorageConfig - ); - } - } - $this->filesystemFactory = $filesystemFactory; - $this->storageFactory = $storageFactory; - $this->adapterProvider = $adapterProvider; - } - - /** - * Get storage by its name - * - * @param string $storageName - * @return StorageInterface - */ - public function get(string $storageName): StorageInterface - { - if (!isset($this->storage[$storageName])) { - if (isset($this->storageConfig[$storageName])) { - $config = $this->storageConfig[$storageName]; - if (empty($config['adapter']) || empty($config['options'])) { - throw new InvalidStorageConfigurationException( - "Incorrect configuration for storage '$storageName': required field " . - "'adapter' and/or 'options' is not defined" - ); - } - $adapter = $this->adapterProvider->create($config['adapter'], $config['options']); - $filesystem = $this->filesystemFactory->create(['adapter' => $adapter]); - $this->storage[$storageName] = $this->storageFactory->create(['filesystem' => $filesystem]); - } else { - throw new UnsupportedStorageException("No storage with name '$storageName' is declared"); - } - } - return $this->storage[$storageName]; - } -} diff --git a/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php b/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php deleted file mode 100644 index 8267fea85319e..0000000000000 --- a/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Storage; - -/** - * Exception to be thrown when unsupported (undeclared) storage is requested - */ -class UnsupportedStorageException extends \RuntimeException -{ -} diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php new file mode 100644 index 0000000000000..1917d7aef5d13 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Setup\Test\Unit\Module\I18n; + +use Magento\Framework\Component\ComponentRegistrar; +use \Magento\Setup\Module\I18n\Context; + +class ContextTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Setup\Module\I18n\Context + */ + protected $context; + + /** + * @var \Magento\Framework\Component\ComponentRegistrar|\PHPUnit_Framework_MockObject_MockObject + */ + protected $componentRegistrar; + + protected function setUp() + { + $this->componentRegistrar = $this->createMock(\Magento\Framework\Component\ComponentRegistrar::class); + } + + /** + * @param array $context + * @param string $path + * @param array $pathValues + * @dataProvider dataProviderContextByPath + */ + public function testGetContextByPath($context, $path, $pathValues) + { + $this->componentRegistrar->expects($this->any()) + ->method('getPaths') + ->willReturnMap($pathValues); + $this->context = new Context($this->componentRegistrar); + $this->assertEquals($context, $this->context->getContextByPath($path)); + } + + /** + * @return array + */ + public function dataProviderContextByPath() + { + return [ + [ + [Context::CONTEXT_TYPE_MODULE, 'Magento_Module'], + '/app/code/Magento/Module/Block/Test.php', + [ + [Context::CONTEXT_TYPE_MODULE, ['Magento_Module' => '/app/code/Magento/Module']], + [Context::CONTEXT_TYPE_THEME, []], + ] + ], + [ + [Context::CONTEXT_TYPE_THEME, 'frontend/Some/theme'], + '/app/design/area/theme/test.phtml', + [ + [Context::CONTEXT_TYPE_MODULE, []], + [Context::CONTEXT_TYPE_THEME, ['frontend/Some/theme' => '/app/design/area/theme']], + ] + ], + [ + [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], + '/lib/web/module/test.phtml', + [ + [Context::CONTEXT_TYPE_MODULE, []], + [Context::CONTEXT_TYPE_THEME, []], + ] + ], + ]; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid path given: "invalid_path". + */ + public function testGetContextByPathWithInvalidPath() + { + $this->componentRegistrar->expects($this->any()) + ->method('getPaths') + ->willReturnMap([ + [ComponentRegistrar::MODULE, ['/path/to/module']], + [ComponentRegistrar::THEME, ['/path/to/theme']] + ]); + $this->context = new Context($this->componentRegistrar); + $this->context->getContextByPath('invalid_path'); + } + + /** + * @param string $path + * @param array $context + * @param array $registrar + * @dataProvider dataProviderPathToLocaleDirectoryByContext + */ + public function testBuildPathToLocaleDirectoryByContext($path, $context, $registrar) + { + $paths = []; + foreach ($registrar as $module) { + $paths[$module[1]] = $module[2]; + } + $this->componentRegistrar->expects($this->any()) + ->method('getPath') + ->willReturnMap($registrar); + $this->context = new Context($this->componentRegistrar); + $this->assertEquals($path, $this->context->buildPathToLocaleDirectoryByContext($context[0], $context[1])); + } + + /** + * @return array + */ + public function dataProviderPathToLocaleDirectoryByContext() + { + return [ + [ + BP . '/app/code/Magento/Module/i18n/', + [Context::CONTEXT_TYPE_MODULE, 'Magento_Module'], + [[ComponentRegistrar::MODULE, 'Magento_Module', BP . '/app/code/Magento/Module']] + ], + [ + BP . '/app/design/frontend/Magento/luma/i18n/', + [Context::CONTEXT_TYPE_THEME, 'frontend/Magento/luma'], + [[ComponentRegistrar::THEME, 'frontend/Magento/luma', BP . '/app/design/frontend/Magento/luma']] + ], + + [ + null, + [Context::CONTEXT_TYPE_MODULE, 'Unregistered_Module'], + [[ComponentRegistrar::MODULE, 'Unregistered_Module', null]] + ], + [ + null, + [Context::CONTEXT_TYPE_THEME, 'frontend/Magento/unregistered'], + [[ComponentRegistrar::THEME, 'frontend/Magento/unregistered', null]] + ], + [BP . '/lib/web/i18n/', [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], []], + ]; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid context given: "invalid_type". + */ + public function testBuildPathToLocaleDirectoryByContextWithInvalidType() + { + $this->componentRegistrar->expects($this->never()) + ->method('getPath'); + $this->context = new Context($this->componentRegistrar); + $this->context->buildPathToLocaleDirectoryByContext('invalid_type', 'Magento_Module'); + } +} From 04e1bb0a74b3d6c17714f4e9d1c80320d5adf5a1 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 6 Mar 2020 12:59:55 +0200 Subject: [PATCH 285/734] MC-24245: [MFTF Test] Product is deleted from cart if not included in shared catalog --- ...ProductAbsentOnCategoryPageActionGroup.xml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..f98229f8aaada --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.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="AssertStorefrontProductAbsentOnCategoryPageActionGroup"> + <annotations> + <description>Navigate to category page and verify product is absent.</description> + </annotations> + <arguments> + <argument name="categoryUrlKey" defaultValue="{{_defaultCategory.url_key}}"/> + <argument name="productName" defaultValue="{{SimpleProduct.name}}"/> + </arguments> + + <amOnPage url="{{StorefrontCategoryPage.url(categoryUrlKey)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <dontSee selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{productName}}" stepKey="assertProductIsNotPresent"/> + </actionGroup> +</actionGroups> From 6f1e2e7b0481933f35f4e6def35b63ad2b3ffc8e Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Fri, 6 Mar 2020 13:02:32 +0200 Subject: [PATCH 286/734] MC-32126: Admin: Create/update/delete customer --- .../Controller/Adminhtml/Index/DeleteTest.php | 104 ++++ .../Controller/Adminhtml/Index/SaveTest.php | 584 ++++++++++++++++++ .../Adminhtml/Index/ValidateTest.php | 123 ++++ .../Controller/Adminhtml/IndexTest.php | 347 +---------- .../AccountManagement/CreateAccountTest.php | 398 +++++++++++- .../Model/AccountManagement/ValidateTest.php | 113 ++++ .../Customer/Model/AccountManagementTest.php | 330 ---------- 7 files changed, 1327 insertions(+), 672 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php new file mode 100644 index 0000000000000..1f40c459c43e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for delete customer via backend/customer/index/delete controller. + * + * @magentoAppArea adminhtml + */ +class DeleteTest extends AbstractBackendController +{ + /** @var FormKey */ + private $formKey; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->formKey = $this->_objectManager->get(FormKey::class); + } + + /** + * Delete customer + * + * @dataProvider deleteCustomerProvider + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * + * @param array $paramsData + * @param string $expected + * @return void + */ + public function testDeleteCustomer(array $paramsData, array $expected): void + { + $this->dispatchCustomerDelete($paramsData); + + $this->assertRedirect($this->stringContains('customer/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__(...$expected['message'])]), + $expected['message_type'] + ); + } + + /** + * Delete customer provider + * + * @return array + */ + public function deleteCustomerProvider(): array + { + return [ + 'delete_customer_success' => [ + 'params_data' => [ + 'id' => 1, + ], + 'expected' => [ + 'message' => ['You deleted the customer.'], + 'message_type' => MessageInterface::TYPE_SUCCESS, + ], + ], + 'not_existing_customer_error' => [ + 'params_data' => [ + 'id' => 2, + ], + 'expected' => [ + 'message' => [ + 'No such entity with %fieldName = %fieldValue', + [ + 'fieldName' => 'customerId', + 'fieldValue' => '2', + ], + ], + 'message_type' => MessageInterface::TYPE_ERROR, + ], + ], + ]; + } + + /** + * Delete customer using backend/customer/index/delete action. + * + * @param array $params + * @return void + */ + private function dispatchCustomerDelete(array $params): void + { + $params['form_key'] = $this->formKey->getFormKey(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setParams($params); + $this->dispatch('backend/customer/index/delete'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php new file mode 100644 index 0000000000000..aed6003ea76ac --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php @@ -0,0 +1,584 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerNameGenerationInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Data\Customer as CustomerData; +use Magento\Customer\Model\EmailNotification; +use Magento\Framework\App\Area; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for save customer via backend/customer/index/save controller. + * + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SaveTest extends AbstractBackendController +{ + /** + * Base controller URL + * + * @var string + */ + private $_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /**@var CustomerNameGenerationInterface */ + private $customerViewHelper; + + /** @var SubscriberFactory */ + private $subscriberFactory; + + /** @var Session */ + private $session; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerViewHelper = $this->_objectManager->get(CustomerNameGenerationInterface::class); + $this->subscriberFactory = $this->_objectManager->get(SubscriberFactory::class); + $this->session = $this->_objectManager->get(Session::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + } + + /** + * Create customer + * + * @dataProvider createCustomerProvider + * @magentoDbIsolation enabled + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testCreateCustomer(array $postData, array $expectedData): void + { + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->assertCustomerData( + $postData['customer'][CustomerData::EMAIL], + (int)$postData['customer'][CustomerData::WEBSITE_ID], + $expectedData + ); + } + + /** + * Create customer provider + * + * @return array + */ + public function createCustomerProvider(): array + { + $defaultCustomerData = $this->getDefaultCustomerData(); + $expectedCustomerData = $this->getExpectedCustomerData($defaultCustomerData); + return [ + "fill_all_fields" => [ + 'post_data' => $defaultCustomerData, + 'expected_data' => $expectedCustomerData + ], + 'only_require_fields' => [ + 'post_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '0', + CustomerData::PREFIX => '', + CustomerData::MIDDLENAME => '', + CustomerData::SUFFIX => '', + CustomerData::DOB => '', + CustomerData::TAXVAT => '', + CustomerData::GENDER => '', + ], + ] + ), + 'expected_data' => array_replace_recursive( + $expectedCustomerData, + [ + 'customer' => [ + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '0', + CustomerData::PREFIX => '', + CustomerData::MIDDLENAME => '', + CustomerData::SUFFIX => '', + CustomerData::DOB => '', + CustomerData::TAXVAT => '', + CustomerData::GENDER => '0', + ], + ] + ), + ], + ]; + } + + /** + * Create customer with exceptions + * + * @dataProvider createCustomerErrorsProvider + * @magentoDbIsolation enabled + * + * @param array $postData + * @param array $expectedData + * @param array $expectedMessage + * @return void + */ + public function testCreateCustomerErrors(array $postData, array $expectedData, array $expectedMessage): void + { + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo($expectedMessage), + MessageInterface::TYPE_ERROR + ); + $this->assertNotEmpty($this->session->getCustomerFormData()); + $this->assertArraySubset($expectedData, $this->session->getCustomerFormData()); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + } + + /** + * Create customer errors provider + * + * @return array + */ + public function createCustomerErrorsProvider(): array + { + $defaultCustomerData = $this->getDefaultCustomerData(); + return [ + 'without_some_require_fields' => [ + 'post_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::FIRSTNAME => '', + CustomerData::LASTNAME => '', + ], + ] + ), + 'expected_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::FIRSTNAME => '', + CustomerData::LASTNAME => '', + CustomerData::DOB => '2000-01-01', + ], + ] + ), + 'expected_message' => [ + (string)__('"%1" is a required value.', 'First Name'), + (string)__('"%1" is a required value.', 'Last Name'), + ], + ], + 'with_empty_post_data' => [ + 'post_data' => [], + 'expected_data' => [], + 'expected_message' => [ + (string)__('The customer email is missing. Enter and try again.'), + ], + ], + 'with_invalid_form_data' => [ + 'post_data' => [ + 'account' => [ + 'middlename' => 'test middlename', + 'group_id' => 1, + ], + ], + 'expected_data' => [ + 'account' => [ + 'middlename' => 'test middlename', + 'group_id' => 1, + ], + ], + 'expected_message' => [ + (string)__('The customer email is missing. Enter and try again.'), + ], + ] + ]; + } + + /** + * Update customer with subscription and redirect to edit page. + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testUpdateCustomer(): void + { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->getById(1); + $secondStore = $this->storeManager->getStore('fixturestore'); + $postData = $expectedData = [ + 'customer' => [ + CustomerData::FIRSTNAME => 'Jane', + CustomerData::MIDDLENAME => 'Mdl', + CustomerData::LASTNAME => 'Doe', + ], + 'subscription_status' => [$customerData->getWebsiteId() => '1'], + 'subscription_store' => [$customerData->getWebsiteId() => $secondStore->getId()], + ]; + $postData['customer']['entity_id'] = $customerData->getId(); + $params = ['back' => true]; + + $this->dispatchCustomerSave($postData, $params); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringStartsWith( + $this->_baseControllerUrl . 'edit/id/' . $customerData->getId() + )); + $this->assertCustomerData($customerData->getEmail(), (int)$customerData->getWebsiteId(), $expectedData); + $this->assertCustomerSubscription( + (int)$customerData->getId(), + (int)$customerData->getWebsiteId(), + Subscriber::STATUS_SUBSCRIBED, + (int)$secondStore->getId() + ); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * @return void + */ + public function testExistingCustomerUnsubscribeNewsletter(): void + { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->getById(1); + /** @var Store $defaultStore */ + $defaultStore = $this->storeManager->getWebsite()->getDefaultStore(); + $postData = [ + 'customer' => [ + 'entity_id' => $customerData->getId(), + CustomerData::EMAIL => 'customer@example.com', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::LASTNAME => 'test lastname', + 'sendemail_store_id' => '1' + ], + 'subscription_status' => [$customerData->getWebsiteId() => '0'], + 'subscription_store' => [$customerData->getWebsiteId() => $defaultStore->getId()], + ]; + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->assertCustomerSubscription( + (int)$customerData->getId(), + (int)$customerData->getWebsiteId(), + Subscriber::STATUS_UNSUBSCRIBED, + (int)$defaultStore->getId() + ); + } + + /** + * Ensure that an email is sent during save action + * + * @magentoConfigFixture current_store customer/account_information/change_email_template change_email_template + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * @return void + */ + public function testExistingCustomerChangeEmail(): void + { + $customerId = 1; + $newEmail = 'newcustomer@example.com'; + $transportBuilderMock = $this->prepareEmailMock( + 2, + 'change_email_template', + [ + 'name' => 'CustomerSupport', + 'email' => 'support@example.com', + ], + $customerId, + $newEmail + ); + $this->addEmailMockToClass($transportBuilderMock, EmailNotification::class); + $postData = [ + 'customer' => [ + 'entity_id' => $customerId, + CustomerData::WEBSITE_ID => '1', + CustomerData::GROUP_ID => '1', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::MIDDLENAME => 'test middlename', + CustomerData::LASTNAME => 'test lastname', + CustomerData::EMAIL => $newEmail, + 'new_password' => 'auto', + 'sendemail_store_id' => '1', + 'sendemail' => '1', + CustomerData::CREATED_AT => '2000-01-01 00:00:00', + CustomerData::DEFAULT_SHIPPING => '_item1', + CustomerData::DEFAULT_BILLING => '1' + ] + ]; + $this->dispatchCustomerSave($postData); + + /** + * Check that no errors were generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * @return void + */ + public function testCreateSameEmailFormatDateError(): void + { + $postData = [ + 'customer' => [ + CustomerData::WEBSITE_ID => '1', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::MIDDLENAME => 'test middlename', + CustomerData::LASTNAME => 'test lastname', + CustomerData::EMAIL => 'customer@example.com', + CustomerData::DOB => '12/3/1996', + ], + ]; + $postFormatted = array_replace_recursive( + $postData, + [ + 'customer' => [ + CustomerData::DOB => '1996-12-03', + ], + ] + ); + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([ + (string)__('A customer with the same email address already exists in an associated website.'), + ]), + MessageInterface::TYPE_ERROR + ); + $this->assertArraySubset( + $postFormatted, + $this->session->getCustomerFormData(), + true, + 'Customer form data should be formatted' + ); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + } + + /** + * Default values for customer creation + * + * @return array + */ + private function getDefaultCustomerData(): array + { + return [ + 'customer' => [ + CustomerData::WEBSITE_ID => '1', + CustomerData::GROUP_ID => '1', + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '1', + CustomerData::PREFIX => 'Mr.', + CustomerData::FIRSTNAME => 'Jane', + CustomerData::MIDDLENAME => 'Mdl', + CustomerData::LASTNAME => 'Doe', + CustomerData::SUFFIX => 'Esq.', + CustomerData::EMAIL => 'janedoe' . uniqid() . '@example.com', + CustomerData::DOB => '01/01/2000', + CustomerData::TAXVAT => '121212', + CustomerData::GENDER => 'Male', + 'sendemail_store_id' => '1', + ] + ]; + } + + /** + * Expected values for customer creation + * + * @param array $defaultCustomerData + * @return array + */ + private function getExpectedCustomerData(array $defaultCustomerData): array + { + unset($defaultCustomerData['customer']['sendemail_store_id']); + return array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::DOB => '2000-01-01', + CustomerData::GENDER => '0', + CustomerData::STORE_ID => 1, + CustomerData::CREATED_IN => 'Default Store View', + ], + ] + ); + } + + /** + * Create or update customer using backend/customer/index/save action. + * + * @param array $postData + * @param array $params + * @return void + */ + private function dispatchCustomerSave(array $postData, array $params = []): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + if (!empty($params)) { + $this->getRequest()->setParams($params); + } + $this->dispatch('backend/customer/index/save'); + } + + /** + * Check that customer parameters match expected values. + * + * @param string $customerEmail + * @param int $customerWebsiteId + * @param array $expectedData + * @return void + */ + private function assertCustomerData( + string $customerEmail, + int $customerWebsiteId, + array $expectedData + ): void { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->get($customerEmail, $customerWebsiteId); + $actualCustomerArray = $customerData->__toArray(); + foreach ($expectedData['customer'] as $key => $expectedValue) { + $this->assertEquals( + $expectedValue, + $actualCustomerArray[$key], + "Invalid expected value for $key field." + ); + } + } + + /** + * Check that customer subscription status match expected status. + * + * @param int $customerId + * @param int $websiteId + * @param int $expectedStatus + * @param int $expectedStoreId + * @return void + */ + private function assertCustomerSubscription( + int $customerId, + int $websiteId, + int $expectedStatus, + int $expectedStoreId + ): void { + $subscriber = $this->subscriberFactory->create(); + $subscriber->loadByCustomer($customerId, $websiteId); + $this->assertNotEmpty($subscriber->getId()); + $this->assertEquals($expectedStatus, $subscriber->getStatus()); + $this->assertEquals($expectedStoreId, $subscriber->getStoreId()); + } + + /** + * Prepare email mock to test emails. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @param int $occurrenceNumber + * @param string $templateId + * @param array $sender + * @param int $customerId + * @param string|null $newEmail + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function prepareEmailMock( + int $occurrenceNumber, + string $templateId, + array $sender, + int $customerId, + $newEmail = null + ) : \PHPUnit_Framework_MockObject_MockObject { + $area = Area::AREA_FRONTEND; + $customer = $this->customerRepository->getById($customerId); + $storeId = $customer->getStoreId(); + $name = $this->customerViewHelper->getCustomerName($customer); + + $transportMock = $this->getMockBuilder(TransportInterface::class) + ->setMethods(['sendMessage']) + ->getMockForAbstractClass(); + $transportMock->expects($this->exactly($occurrenceNumber)) + ->method('sendMessage'); + $transportBuilderMock = $this->getMockBuilder(TransportBuilder::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'addTo', + 'setFrom', + 'setTemplateIdentifier', + 'setTemplateVars', + 'setTemplateOptions', + 'getTransport', + ] + ) + ->getMock(); + $transportBuilderMock->method('setTemplateIdentifier') + ->with($templateId) + ->willReturnSelf(); + $transportBuilderMock->method('setTemplateOptions') + ->with(['area' => $area, 'store' => $storeId]) + ->willReturnSelf(); + $transportBuilderMock->method('setTemplateVars') + ->willReturnSelf(); + $transportBuilderMock->method('setFrom') + ->with($sender) + ->willReturnSelf(); + $transportBuilderMock->method('addTo') + ->with($this->logicalOr($customer->getEmail(), $newEmail), $name) + ->willReturnSelf(); + $transportBuilderMock->expects($this->exactly($occurrenceNumber)) + ->method('getTransport') + ->willReturn($transportMock); + + return $transportBuilderMock; + } + + /** + * Add email mock to class + * + * @param \PHPUnit_Framework_MockObject_MockObject $transportBuilderMock + * @param string $className + * @return void + */ + private function addEmailMockToClass( + \PHPUnit_Framework_MockObject_MockObject $transportBuilderMock, + $className + ): void { + $mocked = $this->_objectManager->create( + $className, + ['transportBuilder' => $transportBuilderMock] + ); + $this->_objectManager->addSharedInstance( + $mocked, + $className + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php new file mode 100644 index 0000000000000..d42132823d3da --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for validation customer via backend/customer/index/validate controller. + * + * @magentoAppArea adminhtml + */ +class ValidateTest extends AbstractBackendController +{ + /** @var SerializerInterface */ + private $jsonSerializer; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->jsonSerializer = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * Validate customer with exception + * + * @magentoDbIsolation enabled + * @return void + */ + public function testValidateCustomerErrors(): void + { + $postData = [ + 'customer' => [], + ]; + $attributeEmptyMessage = 'The "%1" attribute value is empty. Set the attribute and try again.'; + $expectedErrors = [ + 'error' => true, + 'messages' => [ + (string)__($attributeEmptyMessage, 'First Name'), + (string)__($attributeEmptyMessage, 'Last Name'), + (string)__($attributeEmptyMessage, 'Email'), + ], + ]; + + $this->dispatchCustomerValidate($postData); + $errors = $this->jsonSerializer->unserialize($this->getResponse()->getBody()); + $this->assertEquals($expectedErrors, $errors); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @return void + */ + public function testValidateCustomerWithAddressSuccess(): void + { + $customerData = [ + 'customer' => [ + 'entity_id' => '1', + 'middlename' => 'new middlename', + 'group_id' => 1, + 'website_id' => 1, + 'firstname' => 'new firstname', + 'lastname' => 'new lastname', + 'email' => 'example@domain.com', + 'default_shipping' => '_item1', + 'new_password' => 'auto', + 'sendemail_store_id' => '1', + 'sendemail' => '1', + ], + 'address' => [ + '_item1' => [ + 'firstname' => 'update firstname', + 'lastname' => 'update lastname', + 'street' => ['update street'], + 'city' => 'update city', + 'country_id' => 'US', + 'region_id' => 10, + 'postcode' => '01001', + 'telephone' => '+7000000001', + ], + '_template_' => [ + 'firstname' => '', + 'lastname' => '', + 'street' => [], + 'city' => '', + 'country_id' => 'US', + 'postcode' => '', + 'telephone' => '', + ], + ], + ]; + $this->dispatchCustomerValidate($customerData); + + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $errors = $this->jsonSerializer->unserialize($this->getResponse()->getBody()); + $this->assertEquals(['error' => 0], $errors); + } + + /** + * Validate customer using backend/customer/index/validate action. + * + * @param array $postData + * @return void + */ + private function dispatchCustomerValidate(array $postData): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/customer/index/validate'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 4a7cc7591f7aa..756270344d720 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -6,19 +6,19 @@ namespace Magento\Customer\Controller\Adminhtml; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Backend\Model\Session; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Helper\View; use Magento\Customer\Model\EmailNotification; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; /** * @magentoAppArea adminhtml * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class IndexTest extends AbstractBackendController { /** * Base controller URL @@ -30,21 +30,9 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle /** @var CustomerRepositoryInterface */ protected $customerRepository; - /** @var AddressRepositoryInterface */ - protected $addressRepository; - - /** @var AccountManagementInterface */ - protected $accountManagement; - - /** @var \Magento\Framework\Data\Form\FormKey */ - protected $formKey; - - /**@var \Magento\Customer\Helper\View */ + /** @var View */ protected $customerViewHelper; - /** @var \Magento\TestFramework\ObjectManager */ - protected $objectManager; - /** * @inheritDoc */ @@ -52,23 +40,8 @@ protected function setUp() { parent::setUp(); $this->_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; - $this->customerRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\CustomerRepositoryInterface::class - ); - $this->addressRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AddressRepositoryInterface::class - ); - $this->accountManagement = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AccountManagementInterface::class - ); - $this->formKey = Bootstrap::getObjectManager()->get( - \Magento\Framework\Data\Form\FormKey::class - ); - - $this->objectManager = Bootstrap::getObjectManager(); - $this->customerViewHelper = $this->objectManager->get( - \Magento\Customer\Helper\View::class - ); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerViewHelper = $this->_objectManager->get(View::class); } /** @@ -79,144 +52,12 @@ protected function tearDown() /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + $this->_objectManager->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithEmptyPostData() - { - $this->getRequest()->setPostValue([])->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithInvalidFormData() - { - $post = ['account' => ['middlename' => 'test middlename', 'group_id' => 1]]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - /** @var \Magento\Backend\Model\Session $session */ - $session = $this->objectManager->get(\Magento\Backend\Model\Session::class); - /** - * Check that customer data were set to session - */ - $this->assertNotEmpty($session->getCustomerFormData()); - $this->assertArraySubset($post, $session->getCustomerFormData()); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); - } - - /** - * @magentoDataFixture Magento/Newsletter/_files/subscribers.php - */ - public function testSaveActionExistingCustomerUnsubscribeNewsletter() - { - $customerId = 1; - $websiteId = 1; - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(1, $subscriber->getStatus()); - - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'email' => 'customer@example.com', - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'sendemail_store_id' => 1 - ], - 'subscription_status' => [$websiteId => '0'] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(3, $subscriber->getStatus()); - - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->equalTo(['You saved the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); - } - - /** - * Ensure that an email is sent during save action - * - * @magentoConfigFixture current_store customer/account_information/change_email_template change_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionExistingCustomerChangeEmail() - { - $customerId = 1; - $newEmail = 'newcustomer@example.com'; - $transportBuilderMock = $this->prepareEmailMock( - 2, - 'change_email_template', - [ - 'name' => 'CustomerSupport', - 'email' => 'support@example.com', - ], - $customerId, - $newEmail - ); - $this->addEmailMockToClass($transportBuilderMock, EmailNotification::class); - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => $newEmail, - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - 'created_at' => '2000-01-01 00:00:00', - 'default_shipping' => '_item1', - 'default_billing' => 1, - ] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->_objectManager->get(Session::class)->getMessages(true); } /** @@ -265,83 +106,6 @@ public function testInlineEditChangeEmail() $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreException() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'password' => 'password', - ], - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - $this->assertArraySubset( - $post, - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreExceptionFormatFormData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '12/3/1996', - ], - ]; - $postCustomerFormatted = [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '1996-12-03', - ]; - - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - - $customerFormData = Bootstrap::getObjectManager() - ->get(\Magento\Backend\Model\Session::class) - ->getCustomerFormData(); - $this->assertEquals( - $postCustomerFormatted, - $customerFormData['customer'], - 'Customer form data should be formatted' - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -390,42 +154,6 @@ public function te1stNewActionWithCustomerData() $this->testNewAction(); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testDeleteAction() - { - $this->getRequest()->setParam('id', 1); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['You deleted the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testNotExistingCustomerDeleteAction() - { - $this->getRequest()->setParam('id', 2); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['No such entity with customerId = 2']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -437,63 +165,6 @@ public function testCartAction() $this->assertContains('<div id="customer_cart_grid1"', $body); } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressSuccess() - { - $customerData = [ - 'customer' => [ - 'entity_id' => '1', - 'middlename' => 'new middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'new firstname', - 'lastname' => 'new lastname', - 'email' => 'example@domain.com', - 'default_shipping' => '_item1', - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - ], - 'address' => [ - '_item1' => [ - 'firstname' => 'update firstname', - 'lastname' => 'update lastname', - 'street' => ['update street'], - 'city' => 'update city', - 'country_id' => 'US', - 'region_id' => 10, - 'postcode' => '01001', - 'telephone' => '+7000000001', - ], - '_template_' => [ - 'firstname' => '', - 'lastname' => '', - 'street' => [], - 'city' => '', - 'country_id' => 'US', - 'postcode' => '', - 'telephone' => '', - ], - ], - ]; - /** - * set customer data - */ - $this->getRequest()->setParams($customerData)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/validate'); - $body = $this->getResponse()->getBody(); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - $this->assertEquals('{"error":0}', $body); - } - /** * @magentoDbIsolation enabled */ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php index 03473e9247c51..8be3dfc10d86e 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php @@ -8,22 +8,31 @@ namespace Magento\Customer\Model\AccountManagement; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Math\Random; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Validator\Exception; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; use PHPUnit\Framework\TestCase; /** * Tests for customer creation via customer account management service. * - * @magentoAppArea frontend * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CreateAccountTest extends TestCase { @@ -56,6 +65,41 @@ class CreateAccountTest extends TestCase 'lastname' => 'Last name', ]; + /** + * @var TransportBuilderMock + */ + private $transportBuilderMock; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var ExtensibleDataObjectConverter + */ + private $extensibleDataObjectConverter; + + /** + * @var CustomerFactory + */ + private $customerModelFactory; + + /** + * @var Random + */ + private $random; + + /** + * @var EncryptorInterface + */ + private $encryptor; + /** * @inheritdoc */ @@ -65,6 +109,13 @@ protected function setUp() $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class); $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $this->extensibleDataObjectConverter = $this->objectManager->get(ExtensibleDataObjectConverter::class); + $this->customerModelFactory = $this->objectManager->get(CustomerFactory::class); + $this->random = $this->objectManager->get(Random::class); + $this->encryptor = $this->objectManager->get(EncryptorInterface::class); parent::setUp(); } @@ -82,9 +133,7 @@ public function testCreateAccountWithInvalidFields( string $errorType, array $errorMessage ): void { - $data = array_merge($this->defaultCustomerData, $customerData); - $customerEntity = $this->customerFactory->create(); - $this->dataObjectHelper->populateWithArray($customerEntity, $data, CustomerInterface::class); + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData); $this->expectException($errorType); $this->expectExceptionMessage((string)__(...$errorMessage)); $this->accountManagement->createAccount($customerEntity, $password); @@ -156,7 +205,300 @@ public function createInvalidAccountDataProvider(): array 'The password can\'t be the same as the email address. Create a new password and try again.', ], ], + 'send_email_store_id_not_match_website' => [ + 'customer_data' => [ + CustomerInterface::WEBSITE_ID => 1, + CustomerInterface::STORE_ID => 5, + ], + 'password' => '_aPassword1', + 'error_type' => LocalizedException::class, + 'error_message' => [ + 'The store view is not in the associated website.', + ], + ], + ]; + } + + /** + * Assert that when you create customer account via admin, link with "set password" is send to customer email. + * + * @return void + */ + public function testSendEmailWithSetPasswordLink(): void + { + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData); + $newCustomerEntity = $this->accountManagement->createAccount($customerEntity); + $mailTemplate = $this->transportBuilderMock->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//a[contains(@href, 'customer/account/createPassword/?id=%s')]", $newCustomerEntity->getId()), + $mailTemplate + ), + 'Password creation link was not found.' + ); + } + + /** + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @return void + */ + public function testCreateCustomerOnSecondWebsite(): void + { + $customerData = [ + CustomerInterface::WEBSITE_ID => $this->storeManager->getWebsite('test')->getId(), + CustomerInterface::STORE_ID => $this->storeManager->getStore('fixture_third_store')->getId(), + ]; + $expectedCustomerData = array_merge($this->defaultCustomerData, $customerData); + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData); + $savedCustomerEntity = $this->accountManagement->createAccount($customerEntity); + + $this->assertNotNull($savedCustomerEntity->getId()); + $this->assertCustomerData($savedCustomerEntity, $expectedCustomerData); + } + + /** + * @return void + */ + public function testCreateNewCustomerWithPasswordHash(): void + { + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::STORE_ID => 1, + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $newCustomerEntity = $this->populateCustomerEntity($customerData); + $password = $this->random->getRandomString(8); + $passwordHash = $this->encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $newCustomerEntity, + $passwordHash + ); + $this->assertNotNull($savedCustomer->getId()); + $this->assertCustomerData($savedCustomer, $expectedCustomerData); + $this->assertEmpty($savedCustomer->getSuffix()); + $this->assertEquals( + $savedCustomer->getId(), + $this->accountManagement->authenticate($customerData[CustomerInterface::EMAIL], $password)->getId() + ); + } + + /** + * Customer has two addresses one of it is allowed in website and second is not + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Store/_files/websites_different_countries.php + * @magentoConfigFixture fixture_second_store_store general/country/allow UA + * @return void + */ + public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry(): void + { + $customerId = 1; + $allowedCountryIdForSecondWebsite = 'UA'; + $store = $this->storeManager->getStore('fixture_second_store'); + $customerData = $this->customerRepository->getById($customerId); + $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) + ->setRegionId(null); + $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); + $password = $this->random->getRandomString(8); + $passwordHash = $this->encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $customerData, + $passwordHash + ); + $this->assertCount( + 1, + $savedCustomer->getAddresses(), + 'The wrong address quantity was saved' + ); + $this->assertSame( + 'UA', + $savedCustomer->getAddresses()[0]->getCountryId(), + 'The address with the disallowed country was saved' + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testCreateNoExistingCustomer(): void + { + $existingCustId = 1; + $existingCustomer = $this->customerRepository->getById($existingCustId); + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'savecustomer@example.com', + CustomerInterface::FIRSTNAME => 'Firstsave', + CustomerInterface::LASTNAME => 'Lastsave', + CustomerInterface::ID => null, ]; + unset($expectedCustomerData[CustomerInterface::ID]); + $customerEntity = $this->populateCustomerEntity($existingCustomer->__toArray(), $customerData); + + $customerAfter = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); + $this->assertGreaterThan(0, $customerAfter->getId()); + $this->assertCustomerData($customerAfter, $expectedCustomerData); + $this->accountManagement->authenticate( + $customerAfter->getEmail(), + '_aPassword1' + ); + $attributesBefore = $this->extensibleDataObjectConverter->toFlatArray( + $existingCustomer, + [], + CustomerInterface::class + ); + $attributesAfter = $this->extensibleDataObjectConverter->toFlatArray( + $customerAfter, + [], + CustomerInterface::class + ); + // ignore 'updated_at' + unset($attributesBefore['updated_at']); + unset($attributesAfter['updated_at']); + $inBeforeOnly = array_diff_assoc($attributesBefore, $attributesAfter); + $inAfterOnly = array_diff_assoc($attributesAfter, $attributesBefore); + $expectedInBefore = [ + 'email', + 'firstname', + 'id', + 'lastname', + ]; + sort($expectedInBefore); + $actualInBeforeOnly = array_keys($inBeforeOnly); + sort($actualInBeforeOnly); + $this->assertEquals($expectedInBefore, $actualInBeforeOnly); + $expectedInAfter = [ + 'created_in', + 'email', + 'firstname', + 'id', + 'lastname', + ]; + $actualInAfterOnly = array_keys($inAfterOnly); + foreach ($expectedInAfter as $item) { + $this->assertContains($item, $actualInAfterOnly); + } + } + + /** + * @return void + */ + public function testCreateCustomerInServiceVsInModel(): void + { + $password = '_aPassword1'; + $firstCustomerData = $secondCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $secondCustomerData[CustomerInterface::EMAIL] = 'email2@example.com'; + + /** @var Customer $customerModel */ + $customerModel = $this->customerModelFactory->create(); + $customerModel->setData($firstCustomerData)->setPassword($password); + $customerModel->save(); + /** @var Customer $customerModel */ + $savedModel = $this->customerModelFactory->create()->load($customerModel->getId()); + $dataInModel = $savedModel->getData(); + $newCustomerEntity = $this->populateCustomerEntity($secondCustomerData); + + $customerData = $this->accountManagement->createAccount($newCustomerEntity, $password); + $this->assertNotNull($customerData->getId()); + $savedCustomer = $this->customerRepository->getById($customerData->getId()); + + /** @var SimpleDataObjectConverter $simpleDataObjectConverter */ + $simpleDataObjectConverter = $this->objectManager->get(SimpleDataObjectConverter::class); + + $dataInService = $simpleDataObjectConverter->toFlatArray( + $savedCustomer, + CustomerInterface::class + ); + $expectedDifferences = [ + 'created_at', + 'updated_at', + 'email', + 'is_active', + 'entity_id', + 'entity_type_id', + 'password_hash', + 'attribute_set_id', + 'disable_auto_group_change', + 'confirmation', + 'reward_update_notification', + 'reward_warning_notification', + ]; + foreach ($dataInModel as $key => $value) { + if (!in_array($key, $expectedDifferences)) { + if ($value === null) { + $this->assertArrayNotHasKey($key, $dataInService); + } elseif (isset($dataInService[$key])) { + $this->assertEquals($value, $dataInService[$key], 'Failed asserting value for ' . $key); + } + } + } + $this->assertEquals($secondCustomerData[CustomerInterface::EMAIL], $dataInService['email']); + $this->assertArrayNotHasKey('is_active', $dataInService); + $this->assertArrayNotHasKey('password_hash', $dataInService); + } + + /** + * @return void + */ + public function testCreateNewCustomer(): void + { + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::STORE_ID => 1, + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $newCustomerEntity = $this->populateCustomerEntity($customerData); + + $savedCustomer = $this->accountManagement->createAccount($newCustomerEntity, '_aPassword1'); + $this->assertNotNull($savedCustomer->getId()); + $this->assertCustomerData($savedCustomer, $expectedCustomerData); + $this->assertEmpty($savedCustomer->getSuffix()); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testCreateNewCustomerFromClone(): void + { + $existingCustId = 1; + $existingCustomer = $this->customerRepository->getById($existingCustId); + $customerEntity = $this->customerFactory->create(); + $this->dataObjectHelper->mergeDataObjects( + CustomerInterface::class, + $customerEntity, + $existingCustomer + ); + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'savecustomer@example.com', + CustomerInterface::FIRSTNAME => 'Firstsave', + CustomerInterface::LASTNAME => 'Lastsave', + CustomerInterface::ID => null, + ]; + unset($expectedCustomerData[CustomerInterface::ID]); + $customerEntity = $this->populateCustomerEntity($customerData, [], $customerEntity); + + $customer = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); + $this->assertNotEmpty($customer->getId()); + $this->assertCustomerData($customer, $expectedCustomerData); + $this->accountManagement->authenticate( + $customer->getEmail(), + '_aPassword1', + true + ); } /** @@ -174,4 +516,52 @@ private function getRandomNumericString(int $length): string return $string; } + + /** + * Fill in customer entity using array of customer data and additional customer data. + * + * @param array $customerData + * @param array $additionalCustomerData + * @param CustomerInterface|null $customerEntity + * @return CustomerInterface + */ + private function populateCustomerEntity( + array $customerData, + array $additionalCustomerData = [], + ?CustomerInterface $customerEntity = null + ): CustomerInterface { + $customerEntity = $customerEntity ?? $this->customerFactory->create(); + $customerData = array_merge( + $customerData, + $additionalCustomerData + ); + $this->dataObjectHelper->populateWithArray( + $customerEntity, + $customerData, + CustomerInterface::class + ); + + return $customerEntity; + } + + /** + * Check that customer parameters match expected values. + * + * @param CustomerInterface $customer + * @param array $expectedData + * return void + */ + private function assertCustomerData( + CustomerInterface $customer, + array $expectedData + ): void { + $actualCustomerArray = $customer->__toArray(); + foreach ($expectedData as $key => $expectedValue) { + $this->assertEquals( + $expectedValue, + $actualCustomerArray[$key], + "Invalid expected value for $key field." + ); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php new file mode 100644 index 0000000000000..8daa310d6dc03 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer validation via customer account management service. + * + * @magentoDbIsolation enabled + */ +class ValidateTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var CustomerInterfaceFactory */ + private $customerFactory; + + /** @var DataObjectHelper */ + private $dataObjectHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class); + $this->dataObjectHelper = $this->objectManager->get(DataObjectHelper::class); + parent::setUp(); + } + + /** + * Validate customer fields. + * + * @dataProvider validateFieldsProvider + * + * @param array $customerData + * @param array $expectedResults + * @return void + */ + public function testValidateFields( + array $customerData, + array $expectedResults + ): void { + $customerEntity = $this->customerFactory->create(); + $this->dataObjectHelper->populateWithArray( + $customerEntity, + $customerData, + CustomerInterface::class + ); + $validationResults = $this->accountManagement->validate($customerEntity); + $this->assertEquals( + $expectedResults, + [ + 'valid' => $validationResults->isValid(), + 'messages' => $validationResults->getMessages(), + ] + ); + } + + /** + * @return array + */ + public function validateFieldsProvider(): array + { + $attributeEmptyMessage = 'The "%1" attribute value is empty. Set the attribute and try again.'; + return [ + 'without_required_fields' => [ + 'customer_data' => [], + 'expectedResults' => [ + 'valid' => false, + 'messages' => [ + (string)__($attributeEmptyMessage, 'Associate to Website'), + (string)__($attributeEmptyMessage, 'Group'), + (string)__($attributeEmptyMessage, 'First Name'), + (string)__($attributeEmptyMessage, 'Last Name'), + (string)__($attributeEmptyMessage, 'Email'), + ], + ], + ], + 'with_required_fields' => [ + 'customer_data' => [ + CustomerInterface::WEBSITE_ID => 1, + CustomerInterface::GROUP_ID => 1, + CustomerInterface::FIRSTNAME => 'Jane', + CustomerInterface::LASTNAME => 'Doe', + CustomerInterface::EMAIL => 'janedoe' . uniqid() . '@example.com', + ], + 'expectedResults' => [ + 'valid' => true, + 'messages' => [], + ], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 754c949747d61..e41d7d9a441c3 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -8,13 +8,11 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\State\ExpiredException; use Magento\Framework\Reflection\DataObjectProcessor; -use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -30,9 +28,6 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var AccountManagementInterface */ private $accountManagement; - /** @var CustomerRepositoryInterface */ - private $customerRepository; - /** @var AddressRepositoryInterface needed to setup tests */ private $addressRepository; @@ -45,18 +40,9 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory */ private $addressFactory; - /** @var \Magento\Customer\Api\Data\CustomerInterfaceFactory */ - private $customerFactory; - /** @var DataObjectProcessor */ private $dataProcessor; - /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ - private $extensibleDataObjectConverter; - - /** @var StoreManagerInterface */ - private $storeManager; - /** @var \Magento\Framework\Api\DataObjectHelper */ protected $dataObjectHelper; @@ -65,16 +51,10 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); $this->accountManagement = $this->objectManager ->create(\Magento\Customer\Api\AccountManagementInterface::class); - $this->customerRepository = $this->objectManager - ->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); $this->addressRepository = $this->objectManager->create(\Magento\Customer\Api\AddressRepositoryInterface::class); $this->addressFactory = $this->objectManager->create(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); - $this->customerFactory = $this->objectManager->create( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class - ); - $this->dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); $regionFactory = $this->objectManager->create(\Magento\Customer\Api\Data\RegionInterfaceFactory::class); $address = $this->addressFactory->create(); @@ -115,12 +95,6 @@ protected function setUp() $this->dataProcessor = $this->objectManager ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); - - $this->extensibleDataObjectConverter = $this->objectManager - ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class); - - $this->storeManager = $this->objectManager - ->create(StoreManagerInterface::class); } /** @@ -620,310 +594,6 @@ public function testResendConfirmationNotNeeded() $this->accountManagement->resendConfirmation('customer@example.com', 1); } - /** - * @magentoDbIsolation enabled - */ - public function testCreateCustomerException() - { - $customerEntity = $this->customerFactory->create(); - - try { - $this->accountManagement->createAccount($customerEntity); - $this->fail('Expected exception not thrown'); - } catch (InputException $ie) { - $this->assertEquals('The customer email is missing. Enter and try again.', $ie->getMessage()); - } - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDbIsolation enabled - */ - public function testCreateNonexistingCustomer() - { - $existingCustId = 1; - $existingCustomer = $this->customerRepository->getById($existingCustId); - - $email = 'savecustomer@example.com'; - $firstName = 'Firstsave'; - $lastName = 'Lastsave'; - $customerData = array_merge( - $existingCustomer->__toArray(), - [ - 'email' => $email, - 'firstname' => $firstName, - 'lastname' => $lastName, - 'id' => null - ] - ); - $customerEntity = $this->customerFactory->create(); - $this->dataObjectHelper->populateWithArray( - $customerEntity, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - - $customerAfter = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); - $this->assertGreaterThan(0, $customerAfter->getId()); - $this->assertEquals($email, $customerAfter->getEmail()); - $this->assertEquals($firstName, $customerAfter->getFirstname()); - $this->assertEquals($lastName, $customerAfter->getLastname()); - $this->accountManagement->authenticate( - $customerAfter->getEmail(), - '_aPassword1' - ); - $attributesBefore = $this->extensibleDataObjectConverter->toFlatArray( - $existingCustomer, - [], - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $attributesAfter = $this->extensibleDataObjectConverter->toFlatArray( - $customerAfter, - [], - \Magento\Customer\Api\Data\CustomerInterface::class - ); - // ignore 'updated_at' - unset($attributesBefore['updated_at']); - unset($attributesAfter['updated_at']); - $inBeforeOnly = array_diff_assoc($attributesBefore, $attributesAfter); - $inAfterOnly = array_diff_assoc($attributesAfter, $attributesBefore); - $expectedInBefore = [ - 'email', - 'firstname', - 'id', - 'lastname', - ]; - sort($expectedInBefore); - $actualInBeforeOnly = array_keys($inBeforeOnly); - sort($actualInBeforeOnly); - $this->assertEquals($expectedInBefore, $actualInBeforeOnly); - $expectedInAfter = [ - 'created_in', - 'email', - 'firstname', - 'id', - 'lastname', - ]; - $actualInAfterOnly = array_keys($inAfterOnly); - foreach ($expectedInAfter as $item) { - $this->assertContains($item, $actualInAfterOnly); - } - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateCustomerInServiceVsInModel() - { - $email = 'email@example.com'; - $email2 = 'email2@example.com'; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - $password = '_aPassword1'; - - /** @var \Magento\Customer\Model\Customer $customerModel */ - $customerModel = $this->objectManager->create(\Magento\Customer\Model\CustomerFactory::class)->create(); - $customerModel->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId) - ->setPassword($password); - $customerModel->save(); - /** @var \Magento\Customer\Model\Customer $customerModel */ - $savedModel = $this->objectManager - ->create(\Magento\Customer\Model\CustomerFactory::class) - ->create() - ->load($customerModel->getId()); - $dataInModel = $savedModel->getData(); - - $newCustomerEntity = $this->customerFactory->create() - ->setEmail($email2) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - $customerData = $this->accountManagement->createAccount($newCustomerEntity, $password); - $this->assertNotNull($customerData->getId()); - $savedCustomer = $this->customerRepository->getById($customerData->getId()); - - /** @var \Magento\Framework\Api\SimpleDataObjectConverter $simpleDataObjectConverter */ - $simpleDataObjectConverter = Bootstrap::getObjectManager() - ->get(\Magento\Framework\Api\SimpleDataObjectConverter::class); - - $dataInService = $simpleDataObjectConverter->toFlatArray( - $savedCustomer, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $expectedDifferences = [ - 'created_at', - 'updated_at', - 'email', - 'is_active', - 'entity_id', - 'entity_type_id', - 'password_hash', - 'attribute_set_id', - 'disable_auto_group_change', - 'confirmation', - 'reward_update_notification', - 'reward_warning_notification', - ]; - foreach ($dataInModel as $key => $value) { - if (!in_array($key, $expectedDifferences)) { - if ($value === null) { - $this->assertArrayNotHasKey($key, $dataInService); - } else { - if (isset($dataInService[$key])) { - $this->assertEquals($value, $dataInService[$key], 'Failed asserting value for ' . $key); - } - } - } - } - $this->assertEquals($email2, $dataInService['email']); - $this->assertArrayNotHasKey('is_active', $dataInService); - $this->assertArrayNotHasKey('password_hash', $dataInService); - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateNewCustomer() - { - $email = 'email@example.com'; - $storeId = 1; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - - $newCustomerEntity = $this->customerFactory->create() - ->setStoreId($storeId) - ->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - $savedCustomer = $this->accountManagement->createAccount($newCustomerEntity, '_aPassword1'); - $this->assertNotNull($savedCustomer->getId()); - $this->assertEquals($email, $savedCustomer->getEmail()); - $this->assertEquals($storeId, $savedCustomer->getStoreId()); - $this->assertEquals($firstname, $savedCustomer->getFirstname()); - $this->assertEquals($lastname, $savedCustomer->getLastname()); - $this->assertEquals($groupId, $savedCustomer->getGroupId()); - $this->assertTrue(!$savedCustomer->getSuffix()); - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateNewCustomerWithPasswordHash() - { - $email = 'email@example.com'; - $storeId = 1; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - - $newCustomerEntity = $this->customerFactory->create() - ->setStoreId($storeId) - ->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - /** @var \Magento\Framework\Math\Random $mathRandom */ - $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); - /** @var \Magento\Framework\Encryption\EncryptorInterface $encryptor */ - $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); - $passwordHash = $encryptor->getHash($password, true); - $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( - $newCustomerEntity, - $passwordHash - ); - $this->assertNotNull($savedCustomer->getId()); - $this->assertEquals($email, $savedCustomer->getEmail()); - $this->assertEquals($storeId, $savedCustomer->getStoreId()); - $this->assertEquals($firstname, $savedCustomer->getFirstname()); - $this->assertEquals($lastname, $savedCustomer->getLastname()); - $this->assertEquals($groupId, $savedCustomer->getGroupId()); - $this->assertTrue(!$savedCustomer->getSuffix()); - $this->assertEquals( - $savedCustomer->getId(), - $this->accountManagement->authenticate($email, $password)->getId() - ); - } - - /** - * Customer has two addresses one of it is allowed in website and second is not - * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php - * @magentoDataFixture Magento/Store/_files/websites_different_countries.php - * @magentoConfigFixture fixture_second_store_store general/country/allow UA - * @return void - */ - public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() - { - $customerId = 1; - $allowedCountryIdForSecondWebsite = 'UA'; - $store = $this->storeManager->getStore('fixture_second_store'); - $customerData = $this->customerRepository->getById($customerId); - $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) - ->setRegionId(null); - $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); - $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); - /** @var \Magento\Framework\Math\Random $mathRandom */ - $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); - $passwordHash = $encryptor->getHash($password, true); - $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( - $customerData, - $passwordHash - ); - $this->assertCount( - 1, - $savedCustomer->getAddresses(), - 'The wrong address quantity was saved' - ); - $this->assertSame( - 'UA', - $savedCustomer->getAddresses()[0]->getCountryId(), - 'The address with the disallowed country was saved' - ); - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testCreateNewCustomerFromClone() - { - $email = 'savecustomer@example.com'; - $firstName = 'Firstsave'; - $lastname = 'Lastsave'; - - $existingCustId = 1; - $existingCustomer = $this->customerRepository->getById($existingCustId); - $customerEntity = $this->customerFactory->create(); - $this->dataObjectHelper->mergeDataObjects( - \Magento\Customer\Api\Data\CustomerInterface::class, - $customerEntity, - $existingCustomer - ); - $customerEntity->setEmail($email) - ->setFirstname($firstName) - ->setLastname($lastname) - ->setId(null); - - $customer = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); - $this->assertNotEmpty($customer->getId()); - $this->assertEquals($email, $customer->getEmail()); - $this->assertEquals($firstName, $customer->getFirstname()); - $this->assertEquals($lastname, $customer->getLastname()); - $this->accountManagement->authenticate( - $customer->getEmail(), - '_aPassword1', - true - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ From eac1a534568c49b3eaee622d183ac7cddb1f0ae5 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Fri, 6 Mar 2020 14:15:10 +0200 Subject: [PATCH 287/734] MC-32130: Admin: Reset customer password on edit customer page in admin --- .../AccountManagement/ResetPasswordTest.php | 147 ++++++++++++++++++ .../Customer/Model/AccountManagementTest.php | 67 -------- 2 files changed, 147 insertions(+), 67 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php new file mode 100644 index 0000000000000..012838ebdf697 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\AccountManagement; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer password reset via customer account management service. + * + * @magentoDbIsolation enabled + */ +class ResetPasswordTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var TransportBuilderMock*/ + private $transportBuilderMock; + + /** @var CustomerRegistry */ + private $customerRegistry; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + parent::setUp(); + } + + /** + * Assert that when you reset customer password via admin, link with "Set a New Password" is send to customer email. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendEmailWithSetNewPasswordLink(): void + { + $this->accountManagement->initiatePasswordReset( + 'customer@example.com', + AccountManagement::EMAIL_REMINDER, + 1 + ); + $customerSecure = $this->customerRegistry->retrieveSecureData(1); + $mailTemplate = $this->transportBuilderMock->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + '//a[contains(@href, \'customer/account/createPassword/?id=%1$d&token=%2$s\')]', + $customerSecure->getId(), + $customerSecure->getRpToken() + ), + $mailTemplate + ), + 'Reset password creation link was not found.' + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendPasswordResetLink(): void + { + $email = 'customer@example.com'; + $websiteId = (int)$this->storeManager->getWebsite('base')->getId(); + + $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET, $websiteId); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendPasswordResetLinkDefaultWebsite(): void + { + $email = 'customer@example.com'; + + $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); + } + + /** + * @magentoAppArea frontend + * @dataProvider passwordResetErrorsProvider + * @magentoDataFixture Magento/Customer/_files/customer.php + * @param string $email + * @param int|null $websiteId + * @return void + */ + public function testPasswordResetErrors(string $email, ?int $websiteId = null): void + { + $websiteId = $websiteId ?? (int)$this->storeManager->getWebsite('base')->getId(); + $this->expectExceptionObject( + NoSuchEntityException::doubleField('email', $email, 'websiteId', $websiteId) + ); + $this->accountManagement->initiatePasswordReset( + $email, + AccountManagement::EMAIL_RESET, + $websiteId + ); + } + + /** + * @return array + */ + public function passwordResetErrorsProvider(): array + { + return [ + 'wrong_email' => [ + 'email' => 'foo@example.com', + ], + 'wrong_website_id' => [ + 'email' => 'customer@example.com', + 'website_id' => 0, + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 754c949747d61..9d78a6827f7ad 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -387,73 +387,6 @@ public function testValidateResetPasswordLinkTokenAmbiguous() $this->accountManagement->validateResetPasswordLinkToken(null, $token); } - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLink() - { - $email = 'customer@example.com'; - - $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET, 1); - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLinkDefaultWebsite() - { - $email = 'customer@example.com'; - - $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * - */ - public function testSendPasswordResetLinkBadEmailOrWebsite() - { - $email = 'foo@example.com'; - - try { - $this->accountManagement->initiatePasswordReset( - $email, - AccountManagement::EMAIL_RESET, - 0 - ); - $this->fail('Expected exception not thrown.'); - } catch (NoSuchEntityException $e) { - $expectedParams = [ - 'fieldName' => 'email', - 'fieldValue' => $email, - 'field2Name' => 'websiteId', - 'field2Value' => 0, - ]; - $this->assertEquals($expectedParams, $e->getParameters()); - } - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLinkBadEmailDefaultWebsite() - { - $email = 'foo@example.com'; - - try { - $this->accountManagement->initiatePasswordReset( - $email, - AccountManagement::EMAIL_RESET - ); - $this->fail('Expected exception not thrown.'); - } catch (NoSuchEntityException $nsee) { - // App area is frontend, so we expect websiteId of 1. - $this->assertEquals('No such entity with email = foo@example.com, websiteId = 1', $nsee->getMessage()); - } - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ From 2134efd809aab5a9b58e90be395993359de784ef Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 6 Mar 2020 15:41:03 +0200 Subject: [PATCH 288/734] MC-31838: Product in Websites checkbox of New Product page isn't automatically checked for restricted admin user --- .../Product/Form/Modifier/WebsitesTest.php | 19 ++++++++---- .../Product/Form/Modifier/Websites.php | 30 +++++++++++++++++-- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php index 829dc4824416d..2f8545c56e71e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php @@ -6,17 +6,16 @@ namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Websites; -use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\Store\Api\GroupRepositoryInterface; use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\Group; +use Magento\Store\Model\Store as StoreView; use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\Website; -use Magento\Store\Model\Store as StoreView; -use Magento\Store\Model\Group; /** - * Class WebsitesTest + * Class WebsitesTest test the meta data and website data for different websites * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -111,7 +110,7 @@ protected function setUp() ->method('getWebsiteIds') ->willReturn($this->assignedWebsites); $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->setMethods(['isSingleStoreMode', 'getWesites']) + ->setMethods(['isSingleStoreMode', 'getWebsites']) ->getMockForAbstractClass(); $this->storeManagerMock->method('getWebsites') ->willReturn([$this->websiteMock, $this->secondWebsiteMock]); @@ -182,6 +181,14 @@ public function testModifyMeta() $this->assertTrue(isset($meta['websites']['children'][self::SECOND_WEBSITE_ID])); $this->assertTrue(isset($meta['websites']['children'][self::WEBSITE_ID])); $this->assertTrue(isset($meta['websites']['children']['copy_to_stores.' . self::WEBSITE_ID])); + $this->assertEquals( + $meta['websites']['children'][self::SECOND_WEBSITE_ID]['arguments']['data']['config']['value'], + (string) self::SECOND_WEBSITE_ID + ); + $this->assertEquals( + $meta['websites']['children'][self::WEBSITE_ID]['arguments']['data']['config']['value'], + "0" + ); } /** diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php index b9d8fc56a91d9..de204b312a3fd 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php @@ -6,12 +6,12 @@ namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\Store\Api\GroupRepositoryInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Ui\Component\Form; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\DynamicRows; +use Magento\Ui\Component\Form; /** * Class Websites customizes websites panel @@ -211,6 +211,30 @@ protected function getFieldsForFieldset() } } + $children = $this->setDefaultWebsiteIdIfNoneAreSelected($children); + return $children; + } + + /** + * Set default website id if none are selected + * + * @param array $children + * @return array + */ + private function setDefaultWebsiteIdIfNoneAreSelected(array $children):array + { + $websitesList = $this->getWebsitesList(); + $defaultSelectedWebsite = false; + foreach ($websitesList as $website) { + if ($children[$website['id']]['arguments']['data']['config']['value']) { + $defaultSelectedWebsite = true; + break; + } + } + if (count($websitesList) === 1 && !$defaultSelectedWebsite) { + $website = reset($websitesList); + $children[$website['id']]['arguments']['data']['config']['value'] = (string)$website['id']; + } return $children; } From c1c1dfee2ef9928010195473b4a72e1bf3299756 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Fri, 6 Mar 2020 16:33:38 +0200 Subject: [PATCH 289/734] MC-32126: Admin: Create/update/delete customer --- .../Controller/Adminhtml/Index/SaveTest.php | 18 +++++++++--------- .../Controller/Adminhtml/IndexTest.php | 19 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php index aed6003ea76ac..f532e2fcb7182 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php @@ -36,7 +36,7 @@ class SaveTest extends AbstractBackendController * * @var string */ - private $_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; + private $baseControllerUrl = 'backend/customer/index/'; /** @var CustomerRepositoryInterface */ private $customerRepository; @@ -83,7 +83,7 @@ public function testCreateCustomer(array $postData, array $expectedData): void $this->equalTo([(string)__('You saved the customer.')]), MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); $this->assertCustomerData( $postData['customer'][CustomerData::EMAIL], (int)$postData['customer'][CustomerData::WEBSITE_ID], @@ -158,7 +158,7 @@ public function testCreateCustomerErrors(array $postData, array $expectedData, a ); $this->assertNotEmpty($this->session->getCustomerFormData()); $this->assertArraySubset($expectedData, $this->session->getCustomerFormData()); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'new/key/')); } /** @@ -251,8 +251,8 @@ public function testUpdateCustomer(): void $this->equalTo([(string)__('You saved the customer.')]), MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith( - $this->_baseControllerUrl . 'edit/id/' . $customerData->getId() + $this->assertRedirect($this->stringContains( + $this->baseControllerUrl . 'edit/id/' . $customerData->getId() )); $this->assertCustomerData($customerData->getEmail(), (int)$customerData->getWebsiteId(), $expectedData); $this->assertCustomerSubscription( @@ -289,7 +289,7 @@ public function testExistingCustomerUnsubscribeNewsletter(): void $this->equalTo([(string)__('You saved the customer.')]), MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); $this->assertCustomerSubscription( (int)$customerData->getId(), (int)$customerData->getWebsiteId(), @@ -344,7 +344,7 @@ public function testExistingCustomerChangeEmail(): void * Check that no errors were generated and set to session */ $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); } /** @@ -384,7 +384,7 @@ public function testCreateSameEmailFormatDateError(): void true, 'Customer form data should be formatted' ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'new/key/')); } /** @@ -449,7 +449,7 @@ private function dispatchCustomerSave(array $postData, array $params = []): void if (!empty($params)) { $this->getRequest()->setParams($params); } - $this->dispatch('backend/customer/index/save'); + $this->dispatch($this->baseControllerUrl . 'save'); } /** diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 756270344d720..23ea8011e9bc9 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -7,8 +7,8 @@ namespace Magento\Customer\Controller\Adminhtml; use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerNameGenerationInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Helper\View; use Magento\Customer\Model\EmailNotification; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; @@ -25,13 +25,13 @@ class IndexTest extends AbstractBackendController * * @var string */ - protected $_baseControllerUrl; + private $baseControllerUrl = 'backend/customer/index/'; /** @var CustomerRepositoryInterface */ - protected $customerRepository; + private $customerRepository; - /** @var View */ - protected $customerViewHelper; + /** @var CustomerNameGenerationInterface */ + private $customerViewHelper; /** * @inheritDoc @@ -39,9 +39,8 @@ class IndexTest extends AbstractBackendController protected function setUp() { parent::setUp(); - $this->_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); - $this->customerViewHelper = $this->_objectManager->get(View::class); + $this->customerViewHelper = $this->_objectManager->get(CustomerNameGenerationInterface::class); } /** @@ -173,7 +172,7 @@ public function testResetPasswordActionNoCustomerId() // No customer ID in post, will just get redirected to base $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -185,7 +184,7 @@ public function testResetPasswordActionBadCustomerId() $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->getRequest()->setPostValue(['customer_id' => '789']); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -200,7 +199,7 @@ public function testResetPasswordActionSuccess() $this->equalTo(['The customer will receive an email with a link to reset password.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'edit')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'edit')); } /** From c340802bb4acd0e0dd1868a56a0f31f51f213fc8 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Fri, 6 Mar 2020 14:56:43 +0000 Subject: [PATCH 290/734] Optimised code. Processed review comments. --- ...ProductProcessUrlRewriteSavingObserver.php | 80 ++-- ...ProductProcessUrlRewriteRemovingPlugin.php | 13 +- ...uctProcessUrlRewriteSavingObserverTest.php | 408 ++++++++++++------ .../Storage/DeleteEntitiesFromStores.php | 4 +- ...uctProcessUrlRewriteSavingObserverTest.php | 9 +- 5 files changed, 323 insertions(+), 191 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 3671a25a2d9c0..fddc0e49c6e2a 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -5,19 +5,19 @@ */ namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\Event\Observer; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Api\StoreWebsiteRelationInterface; /** * Class ProductProcessUrlRewriteSavingObserver @@ -29,73 +29,65 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { /** - * @var ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @var ProductUrlRewriteGenerator */ private $productUrlRewriteGenerator; /** - * @var UrlPersistInterface $urlPersist + * @var UrlPersistInterface */ private $urlPersist; /** - * @var ProductUrlPathGenerator $productUrlPathGenerator + * @var ProductUrlPathGenerator */ private $productUrlPathGenerator; /** - * @var StoreManagerInterface $storeManager + * @var StoreManagerInterface */ private $storeManager; /** - * @var StoreWebsiteRelationInterface $storeWebsiteRelation + * @var ProductScopeRewriteGenerator */ - private $storeWebsiteRelation; - - /** - * @var ProductRepository $productRepository - */ - private $productRepository; + private $productScopeRewriteGenerator; /** - * @var ProductScopeRewriteGenerator $productScopeRewriteGenerator + * @var DeleteEntitiesFromStores */ - private $productScopeRewriteGenerator; + private $deleteEntitiesFromStores; /** - * @var DeleteEntitiesFromStores $deleteEntitiesFromStores + * @var Collection */ - private $deleteEntitiesFromStores; + private $productCollection; /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist * @param ProductUrlPathGenerator $productUrlPathGenerator * @param StoreManagerInterface $storeManager - * @param StoreWebsiteRelationInterface $storeWebsiteRelation - * @param ProductRepository $productRepository * @param ProductScopeRewriteGenerator $productScopeRewriteGenerator * @param DeleteEntitiesFromStores $deleteEntitiesFromStores + * @param Collection $productCollection */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, ProductUrlPathGenerator $productUrlPathGenerator, StoreManagerInterface $storeManager, - StoreWebsiteRelationInterface $storeWebsiteRelation, - ProductRepository $productRepository, ProductScopeRewriteGenerator $productScopeRewriteGenerator, - DeleteEntitiesFromStores $deleteEntitiesFromStores + DeleteEntitiesFromStores $deleteEntitiesFromStores, + Collection $productCollection ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; $this->productUrlPathGenerator = $productUrlPathGenerator; $this->storeManager = $storeManager; - $this->storeWebsiteRelation = $storeWebsiteRelation; - $this->productRepository = $productRepository; $this->productScopeRewriteGenerator = $productScopeRewriteGenerator; $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; + $this->productCollection = $productCollection; } /** @@ -104,8 +96,6 @@ public function __construct( * @param Observer $observer * @return void * @throws UrlAlreadyExistsException - * @throws NoSuchEntityException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(Observer $observer) { @@ -125,29 +115,31 @@ public function execute(Observer $observer) } $storeIdsToRemove = []; + $productWebsiteMap = array_flip($product->getWebsiteIds()); + $storeVisibilities = $this->productCollection->getAllAttributeValues(ProductInterface::VISIBILITY); if ($this->productScopeRewriteGenerator->isGlobalScope($product->getStoreId())) { //Remove any rewrite URLs for websites the product is not in, or is not visible in. Global Scope. - foreach ($this->storeManager->getWebsites() as $website) { - $websiteId = $website->getWebsiteId(); - foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($websiteId) as $storeid) { - //Load the product for the store we are processing so we can see if it is visible - $storeProduct = $this->productRepository->getById( - $product->getId(), - false, - $storeid, - true - ); - if (!$storeProduct->isVisibleInSiteVisibility() || - !in_array($websiteId, $product->getWebsiteIds())) { - $storeIdsToRemove[] = $storeid; - }; + foreach ($this->storeManager->getStores() as $store) { + $websiteId = $store->getWebsiteId(); + $storeId = $store->getStoreId(); + if (!isset($productWebsiteMap[$websiteId])) { + $storeIdsToRemove[] = $storeId; + continue; + } + //Check the visibility of the product in each store. + if (isset($storeVisibilities[$product->getId()][$storeId]) + && ($storeVisibilities[$product->getId()][$storeId] === Visibility::VISIBILITY_NOT_VISIBLE)) { + $storeIdsToRemove[] = $storeId; } } } else { //Only remove rewrite for current scope - if (!$product->isVisibleInSiteVisibility() || - !in_array($product->getStoreId(), $product->getStoreIds())) { - $storeIdsToRemove[] = $product->getStoreId(); + $websiteId = $product->getStore()->getWebsiteId(); + $storeId = $product->getStoreId(); + if (!isset($productWebsiteMap[$websiteId]) || + (isset($storeVisibilities[$product->getId()][$storeId]) + && ($storeVisibilities[$product->getId()][$storeId] === Visibility::VISIBILITY_NOT_VISIBLE))) { + $storeIdsToRemove[] = $storeId; } } if (count($storeIdsToRemove)) { diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php index 3b4929c25a1cd..629649897b9de 100644 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php @@ -26,27 +26,27 @@ class ProductProcessUrlRewriteRemovingPlugin { /** - * @var ProductRepositoryInterface $productRepository + * @var ProductRepositoryInterface */ private $productRepository; /** - * @var StoreWebsiteRelationInterface $storeWebsiteRelation + * @var StoreWebsiteRelationInterface */ private $storeWebsiteRelation; /** - * @var UrlPersistInterface $urlPersist + * @var UrlPersistInterface */ private $urlPersist; /** - * @var ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @var ProductUrlRewriteGenerator */ private $productUrlRewriteGenerator; /** - * @var DeleteEntitiesFromStores $deleteEntitiesFromStores + * @var DeleteEntitiesFromStores */ private $deleteEntitiesFromStores; @@ -108,7 +108,8 @@ public function afterUpdateWebsites( } $storeIdsToRemove = []; - // Remove the URLs from websites this product no longer belongs to + // Remove the URLs for products in $productIds array + // from all stores that belong to websites in $websiteIds array if ($type === "remove" && $websiteIds && $productIds) { foreach ($websiteIds as $webId) { foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeid) { diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index e561c26f98ee6..c2dcb1195bbce 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -6,12 +6,13 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; -use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\StoreWebsiteRelationInterface; use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Catalog\Model\Product; @@ -55,21 +56,6 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase */ private $product; - /** - * @var Product|MockObject - */ - private $product1; - - /** - * @var Product|MockObject - */ - private $product2; - - /** - * @var Product|MockObject - */ - private $product5; - /** * @var ProductUrlRewriteGenerator|MockObject */ @@ -96,14 +82,14 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase private $storeManager; /** - * @var Website|MockObject + * @var array */ - private $website1; + private $websites; /** - * @var Website|MockObject + * @var array */ - private $website2; + private $stores; /** * @var StoreWebsiteRelationInterface|MockObject @@ -111,75 +97,59 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase private $storeWebsiteRelation; /** - * @var ProductRepository|MockObject + * @var DeleteEntitiesFromStores|MockObject */ - private $productRepository; + private $deleteEntitiesFromStores; /** - * @var DeleteEntitiesFromStores|MockObject + * @var Collection|MockObject */ - private $deleteEntitiesFromStores; + private $productCollection; /** * Set up + * Website_ID = 0 -> Store_ID = 0 * Website_ID = 1 -> Store_ID = 1 * Website_ID = 2 -> Store_ID = 2 & 5 */ protected function setUp() { + $this->objectManager = new ObjectManager($this); + $this->urlPersist = $this->createMock(UrlPersistInterface::class); - $this->product = $this->createPartialMock( - Product::class, - [ - 'getId', - 'dataHasChangedFor', - 'isVisibleInSiteVisibility', - 'getIsChangedWebsites', - 'getIsChangedCategories', - 'getStoreId', - 'getWebsiteIds' - ] - ); - $this->product1 = $this->createPartialMock( - Product::class, - ['getId', 'isVisibleInSiteVisibility'] - ); - $this->product2 = $this->createPartialMock( - Product::class, - ['getId', 'isVisibleInSiteVisibility'] - ); - $this->product5 = $this->createPartialMock( - Product::class, - ['getId', 'isVisibleInSiteVisibility'] + + $this->websites[0] = $this->initialiseWebsite(0); + $this->websites[1] = $this->initialiseWebsite(1); + $this->websites[2] = $this->initialiseWebsite(2); + + $this->stores[0] = $this->initialiseStore(0, 0); + $this->stores[1] = $this->initialiseStore(1, 1); + $this->stores[2] = $this->initialiseStore(2, 2); + $this->stores[5] = $this->initialiseStore(5, 2); + + $this->product = $this->initialiseProduct($this->stores[0], 0); + + $this->productCollection = $this->createPartialMock(Collection::class, + ['getAllAttributeValues'] ); - $this->productRepository = $this->createPartialMock(ProductRepository::class, ['getById']); - $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->product1->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->product2->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->product5->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->productRepository->expects($this->any()) - ->method('getById') - ->will($this->returnValueMap([ - [1, false, 0, true, $this->product], - [1, false, 1, true, $this->product1], - [1, false, 2, true, $this->product2], - [1, false, 5, true, $this->product5] - ])); + $this->deleteEntitiesFromStores = $this->createPartialMock( DeleteEntitiesFromStores::class, ['execute'] ); + $this->event = $this->createPartialMock(Event::class, ['getProduct']); - $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); + $this->event->expects($this->any()) + ->method('getProduct') + ->willReturn($this->product); + $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); $this->observer->expects($this->any())->method('getEvent')->willReturn($this->event); + $this->productUrlRewriteGenerator = $this->createPartialMock( ProductUrlRewriteGenerator::class, ['generate'] ); - $this->productUrlRewriteGenerator->expects($this->any()) - ->method('generate') - ->will($this->returnValue([1 => 'rewrite'])); $this->productScopeRewriteGenerator = $this->createPartialMock( ProductScopeRewriteGenerator::class, ['isGlobalScope'] @@ -193,15 +163,16 @@ protected function setUp() [2, false], [5, false], ])); - $this->objectManager = new ObjectManager($this); - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); - $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); + + $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->storeManager->expects($this->any()) ->method('getWebsites') - ->will($this->returnValue([$this->website1, $this->website2])); + ->will($this->returnValue([$this->websites[1], $this->websites[2]])); + $this->storeManager->expects($this->any()) + ->method('getStores') + ->will($this->returnValue([$this->stores[1], $this->stores[2], $this->stores[5]])); $this->storeWebsiteRelation = $this->createPartialMock( StoreWebsiteRelationInterface::class, @@ -218,11 +189,64 @@ protected function setUp() 'urlPersist' => $this->urlPersist, 'storeManager' => $this->storeManager, 'storeWebsiteRelation' => $this->storeWebsiteRelation, - 'productRepository' => $this->productRepository, 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores, - 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator + 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator, + 'productCollection' => $this->productCollection + ] + ); + } + + /** + * Initialise product for test + * + * @param $store + * @param $storeId + * @return MockObject + */ + public function initialiseProduct($store, $storeId) + { + $product = $this->createPartialMock( + Product::class, + [ + 'getId', + 'dataHasChangedFor', + 'isVisibleInSiteVisibility', + 'getIsChangedWebsites', + 'getIsChangedCategories', + 'getStoreId', + 'getWebsiteIds', + 'getStore' ] ); + $product->expects($this->any())->method('getId')->will($this->returnValue(1)); + return $product; + } + + /** + * Initialise website for test + * + * @param $websiteId + * @return MockObject + */ + public function initialiseWebsite($websiteId) + { + $website = $this->createPartialMock(Website::class, ['getWebsiteId']); + $website->expects($this->any())->method('getWebsiteId')->willReturn($websiteId); + return $website; + } + + /** + * Initialise store for test + * + * @param $storeId + * @return mixed + */ + public function initialiseStore($storeId, $websiteId) + { + $store = $this->createPartialMock(Store::class, ['getStoreId','getWebsiteId']); + $store->expects($this->any())->method('getStoreId')->willReturn($storeId); + $store->expects($this->any())->method('getWebsiteId')->willReturn($websiteId); + return $store; } /** @@ -233,129 +257,244 @@ protected function setUp() public function urlKeyDataProvider() { return [ - 'url changed' => [ + //url has changed, so we would expect to see a replace issued + //and the urls removed from the stores the product is not in + //i.e stores belonging to website 2 + 'global_scope_url_changed' => [ + 'productScope' => 0, 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => [ - '0' => true, - '1' => true, - '2' => true, - '5' => true + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_BOTH, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], ], 'productInWebsites' => [1], 'expectedReplaceCount' => 1, 'expectedRemoves' => [2, 5], ], - 'no changes' => [ + //Nothing has changed, so no replaces or removes + 'global_scope_no_changes' => [ + 'productScope' => 0, 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => [ - '0' => true, - '1' => true, - '2' => true, - '5' => true + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_BOTH, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], ], - 'productInWebsites' => [1, 2], + 'productInWebsites' => [1], 'expectedReplaceCount' => 0, 'expectedRemoves' => [], ], - 'visibility changed' => [ + //Product passed in had global scope set, but the visibility + //at local scope for store 2 is false. Expect to see refresh + //of urls and removal from store 2 + 'global_scope_visibility_changed_local' => [ + 'productScope' => 0, 'isChangedUrlKey' => false, 'isChangedVisibility' => true, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => [ - '0' => true, - '1' => true, - '2' => true, - '5' => true + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], ], 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, - 'expectedRemoves' => [], + 'expectedRemoves' => [2], ], - 'websites changed' => [ + //Product passed in had global scope set, but the visibility + //for all stores is false. Expect to see removal from stores 1,2 and 5 + 'global_scope_visibility_changed_global' => [ + 'productScope' => 0, + 'isChangedUrlKey' => false, + 'isChangedVisibility' => true, + 'isChangedWebsites' => false, + 'isChangedCategories' => false, + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + ], + ], + 'productInWebsites' => [1, 2], + 'expectedReplaceCount' => 0, + 'expectedRemoves' => [1, 2, 5], + ], + //Product has changed websites. Now in websites 1 and 2 + //We would expect to see a replace but no removals as the + //product is in all stores + 'global_scope_websites_changed' => [ + 'productScope' => 0, 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => true, 'isChangedCategories' => false, - 'visibilityResult' => [ - '0' => true, - '1' => true, - '2' => true, - '5' => true + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_BOTH, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], ], 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, 'expectedRemoves' => [], ], - 'categories changed' => [ + //Global scope, all visible, categories changed. + //Expect to see replace and no removals. + 'global_scope_categories_changed' => [ + 'productScope' => 0, 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => true, - 'visibilityResult' => [ - '0' => true, - '1' => true, - '2' => true, - '5' => true + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_BOTH, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], ], 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, 'expectedRemoves' => [], ], - 'url changed invisible' => [ + //Global scope, url key has changed but products are + //invisible in all stores, therefore remove any urls if + //they exist. + 'global_scope_url_changed_invisible' => [ + 'productScope' => 0, 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibilityResult' => [ - '0' => false, - '1' => false, - '2' => false, - '5' => false + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + ], ], 'productInWebsites' => [1, 2], 'expectedReplaceCount' => 1, - 'expectedRemoves' => [1,2,5], + 'expectedRemoves' => [1, 2, 5], + ], + //local scope tests should only adjust URLs for local scope + //Even if there are changes to the same product in other stores + //they should be ignored. Here product in store 2 has been set + //visible. Do not expect to see any removals for the other stores. + 'local_scope_visibility_changed_local_1' => [ + 'productScope' => 2, + 'isChangedUrlKey' => false, + 'isChangedVisibility' => true, + 'isChangedWebsites' => false, + 'isChangedCategories' => false, + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 2 => Product\Visibility::VISIBILITY_BOTH, + 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + ], + ], + 'productInWebsites' => [1, 2], + 'expectedReplaceCount' => 1, + 'expectedRemoves' => [], + ], + //Local scope, so only expecting to operate on store 2. + //Product has been set invisible, removal expected. + 'local_scope_visibility_changed_local_2' => [ + 'productScope' => 2, + 'isChangedUrlKey' => false, + 'isChangedVisibility' => true, + 'isChangedWebsites' => false, + 'isChangedCategories' => false, + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_BOTH, + 1 => Product\Visibility::VISIBILITY_BOTH, + 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 5 => Product\Visibility::VISIBILITY_BOTH, + ], + ], + 'productInWebsites' => [1, 2], + 'expectedReplaceCount' => 0, + 'expectedRemoves' => [2], + ], + //Local scope, so only operate on store 5. + //Visibility is false, so see only removal from + //store 5. + 'local_scope_visibility_changed_global' => [ + 'productScope' => 5, + 'isChangedUrlKey' => false, + 'isChangedVisibility' => true, + 'isChangedWebsites' => false, + 'isChangedCategories' => false, + 'visibility' => [ + 1 => [ + 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, + ], + ], + 'productInWebsites' => [1, 2], + 'expectedReplaceCount' => 0, + 'expectedRemoves' => [5], ], ]; } /** + * @param int $productScope * @param bool $isChangedUrlKey * @param bool $isChangedVisibility * @param bool $isChangedWebsites * @param bool $isChangedCategories - * @param array $visibilityResult + * @param array $visibility * @param int $productInWebsites * @param int $expectedReplaceCount * @param array $expectedRemoves * * @dataProvider urlKeyDataProvider + * @throws UrlAlreadyExistsException */ public function testExecuteUrlKey( + $productScope, $isChangedUrlKey, $isChangedVisibility, $isChangedWebsites, $isChangedCategories, - $visibilityResult, + $visibility, $productInWebsites, $expectedReplaceCount, $expectedRemoves ) { - $this->product->expects($this->any())->method('getStoreId')->will( - $this->returnValue(Store::DEFAULT_STORE_ID) - ); - $this->product->expects($this->any())->method('getWebsiteIds')->will( - $this->returnValue($productInWebsites) - ); - + $this->product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue($productInWebsites)); $this->product->expects($this->any()) ->method('dataHasChangedFor') ->will($this->returnValueMap( @@ -364,7 +503,6 @@ public function testExecuteUrlKey( ['url_key', $isChangedUrlKey] ] )); - $this->product->expects($this->any()) ->method('getIsChangedWebsites') ->will($this->returnValue($isChangedWebsites)); @@ -372,23 +510,23 @@ public function testExecuteUrlKey( $this->product->expects($this->any()) ->method('getIsChangedCategories') ->will($this->returnValue($isChangedCategories)); - $this->product->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($visibilityResult['0'])); - $this->product1->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($visibilityResult['1'])); - $this->product2->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($visibilityResult['2'])); - $this->product5->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($visibilityResult['5'])); + ->method('getStoreId') + ->willReturn($productScope); + $this->product->expects($this->any()) + ->method('getStore') + ->willReturn($this->stores[$productScope]); + $this->productCollection->expects($this->any()) + ->method('getAllAttributeValues') + ->will($this->returnValue($visibility)); + + $this->productUrlRewriteGenerator->expects($this->any()) + ->method('generate') + ->will($this->returnValue($expectedReplaceCount > 0 ? ['test'] : [])); $this->urlPersist->expects($this->exactly($expectedReplaceCount)) ->method('replace') - ->with([1 => 'rewrite']); + ->with($expectedReplaceCount > 0 ? ['test'] : []); $this->deleteEntitiesFromStores->expects($this->any()) ->method('execute') diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php b/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php index 8f56a382fc888..98ea968751182 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php @@ -18,12 +18,12 @@ class DeleteEntitiesFromStores { /** - * @var AdapterInterface $connection + * @var AdapterInterface */ private $connection; /** - * @var Resource $resource + * @var Resource */ private $resource; diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index eae90a47cead3..76508f2066b54 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -28,12 +28,12 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase private $objectManager; /** - * @var StoreManagerInterface $storeManager + * @var StoreManagerInterface */ private $storeManager; /** - * @var ProductRepositoryInterface $productRepository + * @var ProductRepositoryInterface */ private $productRepository; @@ -372,7 +372,6 @@ public function testAddAndRemoveProductFromWebsite() /** * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php - * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testChangeVisibilityGlobalScope() @@ -414,6 +413,7 @@ public function testChangeVisibilityGlobalScope() } //Set product to be not visible at global scope + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); $this->productRepository->save($product); $expected = []; @@ -425,6 +425,7 @@ public function testChangeVisibilityGlobalScope() //Add product to websites corresponding to all 4 stores. //Rewrites should not be present as the product is hidden //at the global scope. + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); $product->setWebsiteIds( array_unique( [ @@ -443,6 +444,7 @@ public function testChangeVisibilityGlobalScope() } //Set product to be visible at global scope + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); $product->setVisibility(Visibility::VISIBILITY_BOTH); $this->productRepository->save($product); $expected = [ @@ -484,7 +486,6 @@ public function testChangeVisibilityGlobalScope() /** * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php - * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testChangeVisibilityLocalScope() From 7b2b5e3bd03b203cdac261a1100f2a3f9fa1aecf Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Fri, 6 Mar 2020 15:48:34 +0000 Subject: [PATCH 291/734] Fixed static test failures --- .../ProductProcessUrlRewriteSavingObserver.php | 1 + .../ProductProcessUrlRewriteSavingObserverTest.php | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index fddc0e49c6e2a..d97553f83f473 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -96,6 +96,7 @@ public function __construct( * @param Observer $observer * @return void * @throws UrlAlreadyExistsException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(Observer $observer) { diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index c2dcb1195bbce..2912fcb1b7fad 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -127,9 +127,10 @@ protected function setUp() $this->stores[2] = $this->initialiseStore(2, 2); $this->stores[5] = $this->initialiseStore(5, 2); - $this->product = $this->initialiseProduct($this->stores[0], 0); + $this->product = $this->initialiseProduct(); - $this->productCollection = $this->createPartialMock(Collection::class, + $this->productCollection = $this->createPartialMock( + Collection::class, ['getAllAttributeValues'] ); @@ -199,11 +200,9 @@ protected function setUp() /** * Initialise product for test * - * @param $store - * @param $storeId * @return MockObject */ - public function initialiseProduct($store, $storeId) + public function initialiseProduct() { $product = $this->createPartialMock( Product::class, @@ -253,6 +252,7 @@ public function initialiseStore($storeId, $websiteId) * Data provider * * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function urlKeyDataProvider() { @@ -477,9 +477,9 @@ public function urlKeyDataProvider() * @param int $productInWebsites * @param int $expectedReplaceCount * @param array $expectedRemoves + * @throws UrlAlreadyExistsException * * @dataProvider urlKeyDataProvider - * @throws UrlAlreadyExistsException */ public function testExecuteUrlKey( $productScope, From f1eeb34abe9d5a1688ef0b7bc11e34092e585d7c Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 6 Mar 2020 10:24:45 -0600 Subject: [PATCH 292/734] MQE-1799: Throw exception during generation when leaving out .url for amOnPage --- .../Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml | 2 +- .../Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml | 2 +- .../ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml | 2 +- .../UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml | 2 +- app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml | 2 +- .../Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml index 61646f8b30230..2e61424b29a56 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCreditMemoViewPage" url="sales/order_creditmemo/view/creditmemo_id/{{memoId}}/" area="admin" module="Magento_Sales"> + <page name="AdminCreditMemoViewPage" url="sales/order_creditmemo/view/creditmemo_id/{{memoId}}/" parameterized="true" area="admin" module="Magento_Sales"> <section name="AdminCreditMemoViewItemsSection"/> <section name="AdminCreditMemoViewTotalSection"/> </page> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml index 3a03b4accdfcf..5dcb97692d047 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml @@ -21,7 +21,7 @@ <argument name="description" type="string"/> </arguments> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad" after="openUrlRewriteEditPage"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustonUrlRewrite"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectCustom"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml index 8c578d4c79470..1bb3e80aa06b7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml @@ -21,7 +21,7 @@ <argument name="description" type="string"/> </arguments> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustonUrlRewrite"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml index e0e8df47852d6..9b247af64eef1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin Add URL Rewrite edit page</description> </annotations> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml index b43e0e05ad55d..754381007221f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}/" area="admin" module="Magento_UrlRewrite"> + <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}" parameterized="true" area="admin" module="Magento_UrlRewrite"> <section name="AdminUrlRewriteEditSection"/> </page> </pages> diff --git a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml index 793b763f0fc15..fd4d38aeb9bd1 100644 --- a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml +++ b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}/" area="admin" module="Magento_Catalog"> + <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}" parameterized="true" area="admin" module="Magento_Catalog"> <section name="AdminProductAddFPTValueSection"/> </page> </pages> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml index 6d6151648c5ee..f2fa689546ef2 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="StorefrontCustomerWishlistSharePage" url="/wishlist/index/share/wishlist_id/{{wishlistId}}/" area="storefront" module="Magento_Wishlist"> + <page name="StorefrontCustomerWishlistSharePage" url="/wishlist/index/share/wishlist_id/{{wishlistId}}/" parameterized="true" area="storefront" module="Magento_Wishlist"> <section name="StorefrontCustomerWishlistShareSection"/> </page> </pages> From bc9c6161b93ee1bf4b896be4b6a4a26f826cca75 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 5 Mar 2020 16:55:52 -0600 Subject: [PATCH 293/734] MC-31986: Add support for ES 7 to 2.4-develop --- .../Mftf/Data/ProductAttributeOptionData.xml | 24 + .../Catalog/Test/Mftf/Data/StoreLabelData.xml | 12 + ...dminProductMultiselectAttributeSection.xml | 14 + ...tProductsDisplayUsingElasticSearchTest.xml | 2 +- .../Config/Elasticsearch5/TestConnection.php | 3 +- .../System/Config/TestConnection.php | 31 -- .../CategoryFieldsProvider.php | 15 +- .../Adapter/DataMapper/ProductDataMapper.php | 15 +- .../FieldMapper/ProductFieldMapper.php | 74 +-- .../Model/Client/Elasticsearch.php | 2 + .../Elasticsearch5/SearchAdapter/Adapter.php | 23 +- .../SearchAdapter/Aggregation/Interval.php | 14 +- .../CategoryFieldsProvider.php | 103 ----- .../BatchDataMapper/DataMapperFactory.php | 2 +- .../BatchDataMapper/DataMapperResolver.php | 2 +- .../BatchDataMapper/PriceFieldsProvider.php | 17 +- .../Adapter/DataMapper/ProductDataMapper.php | 19 - .../Model/Adapter/Elasticsearch.php | 19 +- .../Product/FieldProvider/DynamicField.php | 20 +- .../FieldName/Resolver/CategoryName.php | 11 +- .../FieldName/Resolver/Position.php | 11 +- .../FieldName/Resolver/Price.php | 12 +- .../Product/FieldProvider/StaticField.php | 8 +- .../FieldMapper/ProductFieldMapper.php | 17 - .../Model/Adapter/Index/IndexNameResolver.php | 4 +- .../Magento/Elasticsearch/Model/Config.php | 15 +- .../Model/DataProvider/Base}/Suggestions.php | 4 +- .../Model/DataProvider/Suggestions.php | 11 +- .../SearchAdapter/Aggregation/Builder.php | 15 +- .../SearchAdapter/Aggregation/Interval.php | 287 ------------ .../SearchAdapter/ConnectionManager.php | 4 +- .../SearchAdapter/DocumentFactory.php | 16 +- .../SearchAdapter/Filter/Builder.php | 59 ++- .../SearchAdapter/Filter/Builder/Term.php | 8 +- .../Elasticsearch/SearchAdapter/Mapper.php | 64 --- .../SearchAdapter/Query/Builder/Match.php | 54 +-- .../AssertSearchResultActionGroup.xml | 26 ++ .../ModifyCustomAttributeValueActionGroup.xml | 25 + .../Test/Mftf/Data/ConfigData.xml | 4 +- .../Suite/SearchEngineElasticsearchSuite.xml | 0 ...oductQuickSearchUsingElasticSearchTest.xml | 9 +- ...DecimalAttributeUsingElasticSearchTest.xml | 99 ++++ .../DataMapper/ProductDataMapperTest.php | 400 ---------------- .../Unit/Model/Adapter/ElasticsearchTest.php | 11 +- .../Adapter/Index/IndexNameResolverTest.php | 4 +- .../DataProvider/Base}/SuggestionsTest.php | 4 +- .../Model/DataProvider/SuggestionsTest.php | 5 +- .../Unit/Model/Indexer/IndexerHandlerTest.php | 10 +- .../Test/Unit/SearchAdapter/AdapterTest.php | 173 ------- .../Aggregation/IntervalTest.php | 358 --------------- .../SearchAdapter/ConnectionManagerTest.php | 4 +- .../Dynamic/DataProviderTest.php | 6 +- .../Test/Unit/SearchAdapter/MapperTest.php | 202 -------- app/code/Magento/Elasticsearch/composer.json | 2 +- .../Elasticsearch/etc/adminhtml/system.xml | 62 --- app/code/Magento/Elasticsearch/etc/config.xml | 12 +- app/code/Magento/Elasticsearch/etc/di.xml | 49 +- .../Elasticsearch/etc/search_engine.xml | 3 - ...frontElasticSearchForChineseLocaleTest.xml | 7 +- .../Unit/Model/Client/ElasticsearchTest.php | 4 +- app/code/Magento/Elasticsearch6/composer.json | 3 +- app/code/Magento/Elasticsearch6/etc/di.xml | 4 +- .../System/Config/TestConnection.php | 33 ++ app/code/Magento/Elasticsearch7/LICENSE.txt | 48 ++ .../Magento/Elasticsearch7/LICENSE_AFL.txt | 48 ++ .../FieldName/Resolver/DefaultResolver.php | 50 ++ .../Model/Client/Elasticsearch.php | 177 +++---- app/code/Magento/Elasticsearch7/README.md | 2 + .../SearchAdapter/Adapter.php | 73 ++- .../Elasticsearch7/SearchAdapter/Mapper.php | 44 ++ .../Unit}/Model/Client/ElasticsearchTest.php | 184 ++++---- app/code/Magento/Elasticsearch7/composer.json | 29 ++ .../Elasticsearch7/etc/adminhtml/system.xml | 85 ++++ .../Magento/Elasticsearch7/etc/config.xml | 20 + app/code/Magento/Elasticsearch7/etc/di.xml | 222 +++++++++ .../Magento/Elasticsearch7/etc/module.xml | 17 + .../Elasticsearch7/etc/search_engine.xml | 12 + .../Magento/Elasticsearch7/registration.php | 12 + composer.json | 3 +- composer.lock | 430 +++++++++--------- .../Model/ElasticsearchVersionChecker.php | 39 ++ .../Block/Product/ListProduct/SortingTest.php | 4 + .../Controller/Advanced/ResultTest.php | 4 +- .../SearchAdapter/AdapterTest.php | 5 +- .../Model/Client/ElasticsearchTest.php | 35 +- .../Model/Indexer/IndexHandlerTest.php | 36 +- .../Model/Indexer/ReindexAllTest.php | 37 +- .../SearchAdapter/AdapterTest.php | 67 ++- .../Controller/Advanced/ResultTest.php | 37 -- .../Controller/Result/IndexTest.php | 37 -- .../fulltext/Action/DataProviderTest.php | 32 -- .../Search/AttributeSearchWeightTest.php | 4 +- .../Model/QuickSearchTest.php | 69 --- .../Controller/QuickSearchTest.php | 9 +- .../Elasticsearch6/_files/full_reindex.php | 13 + .../testFromCreateProject/composer.lock | 6 +- .../_files/testSkeleton/composer.lock | 6 +- .../Formatters/FilteredErrorFormatter.php | 1 + .../Test/Legacy/_files/obsolete_classes.php | 7 + 99 files changed, 1717 insertions(+), 2717 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml delete mode 100644 app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php rename app/code/Magento/{Elasticsearch6/Model/DataProvider => Elasticsearch/Model/DataProvider/Base}/Suggestions.php (98%) delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php create mode 100644 app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml create mode 100644 app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Test/Mftf/Data/ConfigData.xml (75%) rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml (100%) create mode 100644 app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml delete mode 100644 app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php rename app/code/Magento/{Elasticsearch6/Test/Unit/Model/DataProvider => Elasticsearch/Test/Unit/Model/DataProvider/Base}/SuggestionsTest.php (97%) delete mode 100644 app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php delete mode 100644 app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php delete mode 100644 app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php create mode 100644 app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php create mode 100644 app/code/Magento/Elasticsearch7/LICENSE.txt create mode 100644 app/code/Magento/Elasticsearch7/LICENSE_AFL.txt create mode 100644 app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php rename app/code/Magento/{Elasticsearch => Elasticsearch7}/Model/Client/Elasticsearch.php (60%) create mode 100644 app/code/Magento/Elasticsearch7/README.md rename app/code/Magento/{Elasticsearch => Elasticsearch7}/SearchAdapter/Adapter.php (53%) create mode 100644 app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php rename app/code/Magento/{Elasticsearch/Test/Unit/Elasticsearch5 => Elasticsearch7/Test/Unit}/Model/Client/ElasticsearchTest.php (93%) create mode 100644 app/code/Magento/Elasticsearch7/composer.json create mode 100644 app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml create mode 100644 app/code/Magento/Elasticsearch7/etc/config.xml create mode 100644 app/code/Magento/Elasticsearch7/etc/di.xml create mode 100644 app/code/Magento/Elasticsearch7/etc/module.xml create mode 100644 app/code/Magento/Elasticsearch7/etc/search_engine.xml create mode 100644 app/code/Magento/Elasticsearch7/registration.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php delete mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index a8646a58ae39c..34fee2f3445b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -101,4 +101,28 @@ <entity name="ProductAttributeOptionTwoForExportImport" extends="productAttributeOption2" type="ProductAttributeOption"> <data key="label">option2</data> </entity> + <entity name="ProductAttributeOption10" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">3.5</data> + <data key="value" unique="suffix">3.5</data> + <data key="is_default">false</data> + <data key="sort_order">1</data> + <requiredEntity type="StoreLabel">Option12Store1</requiredEntity> + </entity> + <entity name="ProductAttributeOption11" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">10.12</data> + <data key="value" unique="suffix">10.12</data> + <data key="is_default">false</data> + <data key="sort_order">2</data> + <requiredEntity type="StoreLabel">Option13Store1</requiredEntity> + </entity> + <entity name="ProductAttributeOption12" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">36</data> + <data key="value" unique="suffix">36</data> + <data key="is_default">false</data> + <data key="sort_order">3</data> + <requiredEntity type="StoreLabel">Option14Store1</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index dcd7fde92283c..2ae473cde5108 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -80,4 +80,16 @@ <data key="store_id">1</data> <data key="label">Blue</data> </entity> + <entity name="Option12Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">3.5</data> + </entity> + <entity name="Option13Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">10.12</data> + </entity> + <entity name="Option14Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">36</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml new file mode 100644 index 0000000000000..66af84b08df69 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductMultiselectAttributeSection"> + <element name="option" type="text" selector="//option[contains(@data-title,'{{value}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml index fe37f2110acc9..9690246bbe68c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml @@ -127,7 +127,7 @@ </createData> <!--Enable ElasticSearch as search engine.--> - <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="enableElasticSearchAsSearchEngine"/> + <magentoCLI command="config:set {{SearchEngineElasticsearchConfigData.path}} {{SearchEngineElasticsearchConfigData.value}}" stepKey="enableElasticSearchAsSearchEngine"/> <magentoCLI command="indexer:reindex" stepKey="performReindexAfterElasticSearchEnable"/> <magentoCLI command="cache:flush" stepKey="cleanCacheAfterElasticSearchEnable"/> diff --git a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php index aa40928ce2b16..2b2da7522dfa6 100644 --- a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php +++ b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php @@ -8,11 +8,12 @@ /** * Elasticsearch 5x test connection block * @codeCoverageIgnore + * @deprecated because of EOL for Elasticsearch5 */ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { /** - * {@inheritdoc} + * @inheritdoc */ protected function _getFieldMapping() { diff --git a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php deleted file mode 100644 index 5dc4476794da7..0000000000000 --- a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Block\Adminhtml\System\Config; - -/** - * Elasticsearch test connection block - * @codeCoverageIgnore - */ -class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection -{ - /** - * {@inheritdoc} - */ - protected function _getFieldMapping() - { - $fields = [ - 'engine' => 'catalog_search_engine', - 'hostname' => 'catalog_search_elasticsearch_server_hostname', - 'port' => 'catalog_search_elasticsearch_server_port', - 'index' => 'catalog_search_elasticsearch_index_prefix', - 'enableAuth' => 'catalog_search_elasticsearch_enable_auth', - 'username' => 'catalog_search_elasticsearch_username', - 'password' => 'catalog_search_elasticsearch_password', - 'timeout' => 'catalog_search_elasticsearch_server_timeout', - ]; - return array_merge(parent::_getFieldMapping(), $fields); - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php index eb7874a936140..bd62a7372579c 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php @@ -9,7 +9,6 @@ use Magento\Elasticsearch\Model\ResourceModel\Index; use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; /** @@ -34,19 +33,17 @@ class CategoryFieldsProvider implements AdditionalFieldsProviderInterface /** * @param Index $resourceIndex - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param ResolverInterface $fieldNameResolver */ public function __construct( Index $resourceIndex, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null + AttributeProvider $attributeAdapterProvider, + ResolverInterface $fieldNameResolver ) { $this->resourceIndex = $resourceIndex; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldNameResolver = $fieldNameResolver; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php index f0b7380397235..91bde497c612b 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php @@ -16,7 +16,6 @@ use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; use Magento\Elasticsearch\Model\Adapter\FieldType\Date as DateFieldType; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; /** @@ -103,8 +102,8 @@ class ProductDataMapper implements DataMapperInterface * @param FieldMapperInterface $fieldMapper * @param StoreManagerInterface $storeManager * @param DateFieldType $dateFieldType - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param ResolverInterface $fieldNameResolver */ public function __construct( Builder $builder, @@ -113,8 +112,8 @@ public function __construct( FieldMapperInterface $fieldMapper, StoreManagerInterface $storeManager, DateFieldType $dateFieldType, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null + AttributeProvider $attributeAdapterProvider, + ResolverInterface $fieldNameResolver ) { $this->builder = $builder; $this->attributeContainer = $attributeContainer; @@ -122,10 +121,8 @@ public function __construct( $this->fieldMapper = $fieldMapper; $this->storeManager = $storeManager; $this->dateFieldType = $dateFieldType; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldNameResolver = $fieldNameResolver; $this->mediaGalleryRoles = [ self::MEDIA_ROLE_IMAGE, diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index 5aea87e5e6ae1..fd989050deb2d 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -6,52 +6,16 @@ namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper; -use Magento\Eav\Model\Config; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType; -use Magento\Framework\Registry; -use Magento\Store\Model\StoreManagerInterface as StoreManager; -use \Magento\Customer\Model\Session as CustomerSession; /** - * Class ProductFieldMapper + * Class ProductFieldMapper provides field name by attribute code and retrieve all attribute types */ class ProductFieldMapper implements FieldMapperInterface { - /** - * @deprecated - * @var Config - */ - protected $eavConfig; - - /** - * @deprecated - * @var FieldType - */ - protected $fieldType; - - /** - * @deprecated - * @var CustomerSession - */ - protected $customerSession; - - /** - * @deprecated - * @var StoreManager - */ - protected $storeManager; - - /** - * @deprecated - * @var Registry - */ - protected $coreRegistry; - /** * @var AttributeProvider */ @@ -68,36 +32,18 @@ class ProductFieldMapper implements FieldMapperInterface private $fieldProvider; /** - * @param Config $eavConfig - * @param FieldType $fieldType - * @param CustomerSession $customerSession - * @param StoreManager $storeManager - * @param Registry $coreRegistry - * @param ResolverInterface|null $fieldNameResolver - * @param AttributeProvider|null $attributeAdapterProvider - * @param FieldProviderInterface|null $fieldProvider + * @param ResolverInterface $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param FieldProviderInterface $fieldProvider */ public function __construct( - Config $eavConfig, - FieldType $fieldType, - CustomerSession $customerSession, - StoreManager $storeManager, - Registry $coreRegistry, - ResolverInterface $fieldNameResolver = null, - AttributeProvider $attributeAdapterProvider = null, - FieldProviderInterface $fieldProvider = null + ResolverInterface $fieldNameResolver, + AttributeProvider $attributeAdapterProvider, + FieldProviderInterface $fieldProvider ) { - $this->eavConfig = $eavConfig; - $this->fieldType = $fieldType; - $this->customerSession = $customerSession; - $this->storeManager = $storeManager; - $this->coreRegistry = $coreRegistry; - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldProvider = $fieldProvider ?: ObjectManager::getInstance() - ->get(FieldProviderInterface::class); + $this->fieldNameResolver = $fieldNameResolver; + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldProvider = $fieldProvider; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index ddf79f413df37..bd9a380230652 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -10,6 +10,8 @@ /** * Elasticsearch client + * + * @deprecated the Elasticsearch 5 doesn't supported due to EOL */ class Elasticsearch implements ClientInterface { diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php index 0ae347d5791ad..eeb11a8b0a074 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php @@ -5,14 +5,14 @@ */ namespace Magento\Elasticsearch\Elasticsearch5\SearchAdapter; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\AdapterInterface; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Search\Response\QueryResponse; use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use \Magento\Elasticsearch\SearchAdapter\ResponseFactory; +use Magento\Elasticsearch\SearchAdapter\ResponseFactory; use Psr\Log\LoggerInterface; +use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; /** * Elasticsearch Search Adapter @@ -24,27 +24,27 @@ class Adapter implements AdapterInterface * * @var Mapper */ - protected $mapper; + private $mapper; /** * Response Factory * * @var ResponseFactory */ - protected $responseFactory; + private $responseFactory; /** * @var ConnectionManager */ - protected $connectionManager; + private $connectionManager; /** * @var AggregationBuilder */ - protected $aggregationBuilder; + private $aggregationBuilder; /** - * @var \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory + * @var QueryContainerFactory */ private $queryContainerFactory; @@ -79,7 +79,7 @@ class Adapter implements AdapterInterface * @param Mapper $mapper * @param ResponseFactory $responseFactory * @param AggregationBuilder $aggregationBuilder - * @param \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory $queryContainerFactory + * @param QueryContainerFactory $queryContainerFactory * @param LoggerInterface $logger */ public function __construct( @@ -87,16 +87,15 @@ public function __construct( Mapper $mapper, ResponseFactory $responseFactory, AggregationBuilder $aggregationBuilder, - \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory $queryContainerFactory, - LoggerInterface $logger = null + QueryContainerFactory $queryContainerFactory, + LoggerInterface $logger ) { $this->connectionManager = $connectionManager; $this->mapper = $mapper; $this->responseFactory = $responseFactory; $this->aggregationBuilder = $aggregationBuilder; $this->queryContainerFactory = $queryContainerFactory; - $this->logger = $logger ?: ObjectManager::getInstance() - ->get(LoggerInterface::class); + $this->logger = $logger; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php index a1fcbeb061481..c1170a14d6970 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php @@ -87,7 +87,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function load($limit, $offset = null, $lower = null, $upper = null) { @@ -116,7 +116,7 @@ public function load($limit, $offset = null, $lower = null, $upper = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function loadPrevious($data, $index, $lower = null) { @@ -141,11 +141,15 @@ public function loadPrevious($data, $index, $lower = null) return false; } + if (is_array($offset)) { + $offset = $offset['value']; + } + return $this->load($index - $offset + 1, $offset - 1, $lower); } /** - * {@inheritdoc} + * @inheritdoc */ public function loadNext($data, $rightIndex, $upper = null) { @@ -166,6 +170,10 @@ public function loadNext($data, $rightIndex, $upper = null) return false; } + if (is_array($offset)) { + $offset = $offset['value']; + } + $from = ['gte' => $data - self::DELTA]; if ($upper !== null) { $to = ['lt' => $data - self::DELTA]; diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php deleted file mode 100644 index 0e130c24e79d3..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper; - -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; - -/** - * Provide data mapping for categories fields - */ -class CategoryFieldsProvider implements AdditionalFieldsProviderInterface -{ - /** - * @var Index - */ - private $resourceIndex; - - /** - * @var AttributeProvider - */ - private $attributeAdapterProvider; - - /** - * @var ResolverInterface - */ - private $fieldNameResolver; - - /** - * @param Index $resourceIndex - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver - */ - public function __construct( - Index $resourceIndex, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null - ) { - $this->resourceIndex = $resourceIndex; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); - } - - /** - * @inheritdoc - */ - public function getFields(array $productIds, $storeId) - { - $categoryData = $this->resourceIndex->getFullCategoryProductIndexData($storeId, $productIds); - - $fields = []; - foreach ($productIds as $productId) { - $fields[$productId] = $this->getProductCategoryData($productId, $categoryData); - } - - return $fields; - } - - /** - * Prepare category index data for product - * - * @param int $productId - * @param array $categoryIndexData - * @return array - */ - private function getProductCategoryData($productId, array $categoryIndexData) - { - $result = []; - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - $categoryIds = array_column($indexData, 'id'); - - if (count($categoryIds)) { - $result = ['category_ids' => implode(' ', $categoryIds)]; - $positionAttribute = $this->attributeAdapterProvider->getByAttributeCode('position'); - $categoryNameAttribute = $this->attributeAdapterProvider->getByAttributeCode('category_name'); - foreach ($indexData as $data) { - $categoryPositionKey = $this->fieldNameResolver->getFieldName( - $positionAttribute, - ['categoryId' => $data['id']] - ); - $categoryNameKey = $this->fieldNameResolver->getFieldName( - $categoryNameAttribute, - ['categoryId' => $data['id']] - ); - $result[$categoryPositionKey] = $data['position']; - $result[$categoryNameKey] = $data['name']; - } - } - } - - return $result; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php index 29bdb036e206d..212c0f7b7af9b 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php @@ -11,7 +11,7 @@ use Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface; /** - * Data mapper factory + * Data mapper factory uses to create appropriate mapper class */ class DataMapperFactory { diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php index fd7a64eb0c9b7..b0a5b805e387f 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php @@ -34,7 +34,7 @@ public function __construct(DataMapperFactory $dataMapperFactory) } /** - * {@inheritdoc} + * @inheritdoc */ public function map(array $documentData, $storeId, array $context = []) { diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php index 56c84593256be..f03a7e67212e3 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php @@ -11,7 +11,6 @@ use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; /** @@ -48,23 +47,21 @@ class PriceFieldsProvider implements AdditionalFieldsProviderInterface * @param Index $resourceIndex * @param DataProvider $dataProvider * @param StoreManagerInterface $storeManager - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param ResolverInterface $fieldNameResolver */ public function __construct( Index $resourceIndex, DataProvider $dataProvider, StoreManagerInterface $storeManager, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null + AttributeProvider $attributeAdapterProvider, + ResolverInterface $fieldNameResolver ) { $this->resourceIndex = $resourceIndex; $this->dataProvider = $dataProvider; $this->storeManager = $storeManager; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldNameResolver = $fieldNameResolver; } /** @@ -73,7 +70,7 @@ public function __construct( public function getFields(array $productIds, $storeId) { $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); - + $priceData = $this->dataProvider->getSearchableAttribute('price') ? $this->resourceIndex->getPriceIndexData($productIds, $storeId) : []; diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php deleted file mode 100644 index 24b740b554fcb..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\DataMapper; - -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper as ElasticSearch5ProductDataMapper; - -/** - * Don't use this product data mapper class. - * - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class ProductDataMapper extends ElasticSearch5ProductDataMapper implements DataMapperInterface -{ -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index fa193d86c03c7..0640b61f9551e 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -6,8 +6,6 @@ namespace Magento\Elasticsearch\Model\Adapter; -use Magento\Framework\App\ObjectManager; - /** * Elasticsearch adapter * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -53,7 +51,7 @@ class Elasticsearch protected $clientConfig; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface */ protected $client; @@ -78,17 +76,17 @@ class Elasticsearch private $batchDocumentDataMapper; /** - * Constructor for Elasticsearch adapter. + * Elasticsearch constructor. * * @param \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager * @param DataMapperInterface $documentDataMapper * @param FieldMapperInterface $fieldMapper * @param \Magento\Elasticsearch\Model\Config $clientConfig - * @param \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder + * @param Index\BuilderInterface $indexBuilder * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver - * @param array $options + * @param Index\IndexNameResolver $indexNameResolver * @param BatchDataMapperInterface $batchDocumentDataMapper + * @param array $options * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( @@ -99,8 +97,8 @@ public function __construct( \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder, \Psr\Log\LoggerInterface $logger, \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver, - $options = [], - BatchDataMapperInterface $batchDocumentDataMapper = null + BatchDataMapperInterface $batchDocumentDataMapper, + $options = [] ) { $this->connectionManager = $connectionManager; $this->documentDataMapper = $documentDataMapper; @@ -109,8 +107,7 @@ public function __construct( $this->indexBuilder = $indexBuilder; $this->logger = $logger; $this->indexNameResolver = $indexNameResolver; - $this->batchDocumentDataMapper = $batchDocumentDataMapper ?: - ObjectManager::getInstance()->get(BatchDataMapperInterface::class); + $this->batchDocumentDataMapper = $batchDocumentDataMapper; try { $this->client = $this->connectionManager->getConnection($options); diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php index 76bc7a15e47a7..8dfe34d765e4e 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php @@ -7,7 +7,6 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider; -use Magento\Catalog\Api\CategoryListInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; @@ -19,21 +18,12 @@ as FieldNameResolver; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Catalog\Model\ResourceModel\Category\Collection; -use Magento\Framework\App\ObjectManager; /** * Provide dynamic fields for product. */ class DynamicField implements FieldProviderInterface { - /** - * Category list. - * - * @deprecated - * @var CategoryListInterface - */ - private $categoryList; - /** * Category collection. * @@ -80,30 +70,26 @@ class DynamicField implements FieldProviderInterface * @param IndexTypeConverterInterface $indexTypeConverter * @param GroupRepositoryInterface $groupRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param CategoryListInterface $categoryList * @param FieldNameResolver $fieldNameResolver * @param AttributeProvider $attributeAdapterProvider - * @param Collection|null $categoryCollection + * @param Collection $categoryCollection */ public function __construct( FieldTypeConverterInterface $fieldTypeConverter, IndexTypeConverterInterface $indexTypeConverter, GroupRepositoryInterface $groupRepository, SearchCriteriaBuilder $searchCriteriaBuilder, - CategoryListInterface $categoryList, FieldNameResolver $fieldNameResolver, AttributeProvider $attributeAdapterProvider, - Collection $categoryCollection = null + Collection $categoryCollection ) { $this->groupRepository = $groupRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->fieldTypeConverter = $fieldTypeConverter; $this->indexTypeConverter = $indexTypeConverter; - $this->categoryList = $categoryList; $this->fieldNameResolver = $fieldNameResolver; $this->attributeAdapterProvider = $attributeAdapterProvider; - $this->categoryCollection = $categoryCollection ?: - ObjectManager::getInstance()->get(Collection::class); + $this->categoryCollection = $categoryCollection; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php index 5824aca6cdd54..3ba2143f0e761 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php @@ -11,7 +11,6 @@ use Magento\Framework\Registry; use Magento\Store\Model\StoreManagerInterface as StoreManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; /** * Resolver field name for Category name attribute. @@ -33,13 +32,11 @@ class CategoryName implements ResolverInterface * @param Registry $coreRegistry */ public function __construct( - StoreManager $storeManager = null, - Registry $coreRegistry = null + StoreManager $storeManager, + Registry $coreRegistry ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->coreRegistry = $coreRegistry ?: ObjectManager::getInstance() - ->get(Registry::class); + $this->storeManager = $storeManager; + $this->coreRegistry = $coreRegistry; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php index 044d5d8da9a6c..a67153c6669b2 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php @@ -11,7 +11,6 @@ use Magento\Framework\Registry; use Magento\Store\Model\StoreManagerInterface as StoreManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; /** * Resolver field name for position attribute. @@ -33,13 +32,11 @@ class Position implements ResolverInterface * @param Registry $coreRegistry */ public function __construct( - StoreManager $storeManager = null, - Registry $coreRegistry = null + StoreManager $storeManager, + Registry $coreRegistry ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->coreRegistry = $coreRegistry ?: ObjectManager::getInstance() - ->get(Registry::class); + $this->storeManager = $storeManager; + $this->coreRegistry = $coreRegistry; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php index 12e53ca2bd714..ddf1552e4a202 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php @@ -7,7 +7,6 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; -use Magento\Framework\App\ObjectManager; use Magento\Customer\Model\Session as CustomerSession; use Magento\Store\Model\StoreManagerInterface as StoreManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; @@ -15,6 +14,7 @@ /** * Resolver field name for price attribute. + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Price implements ResolverInterface { @@ -33,13 +33,11 @@ class Price implements ResolverInterface * @param StoreManager $storeManager */ public function __construct( - CustomerSession $customerSession = null, - StoreManager $storeManager = null + CustomerSession $customerSession, + StoreManager $storeManager ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->customerSession = $customerSession ?: ObjectManager::getInstance() - ->get(CustomerSession::class); + $this->storeManager = $storeManager; + $this->customerSession = $customerSession; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php index 7a5d6fcdcc1b1..f032e561d8738 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php @@ -7,7 +7,6 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider; -use Magento\Framework\App\ObjectManager; use Magento\Eav\Model\Config; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; @@ -74,7 +73,7 @@ class StaticField implements FieldProviderInterface * @param FieldTypeResolver $fieldTypeResolver * @param FieldIndexResolver $fieldIndexResolver * @param AttributeProvider $attributeAdapterProvider - * @param FieldName\ResolverInterface|null $fieldNameResolver + * @param FieldName\ResolverInterface $fieldNameResolver * @param array $excludedAttributes */ public function __construct( @@ -84,7 +83,7 @@ public function __construct( FieldTypeResolver $fieldTypeResolver, FieldIndexResolver $fieldIndexResolver, AttributeProvider $attributeAdapterProvider, - FieldName\ResolverInterface $fieldNameResolver = null, + FieldName\ResolverInterface $fieldNameResolver, array $excludedAttributes = [] ) { $this->eavConfig = $eavConfig; @@ -93,8 +92,7 @@ public function __construct( $this->fieldTypeResolver = $fieldTypeResolver; $this->fieldIndexResolver = $fieldIndexResolver; $this->attributeAdapterProvider = $attributeAdapterProvider; - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(FieldName\ResolverInterface::class); + $this->fieldNameResolver = $fieldNameResolver; $this->excludedAttributes = $excludedAttributes; } diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php deleted file mode 100644 index 657605bbd019b..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; - -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper - as Elasticsearch5ProductFieldMapper; - -/** - * Class ProductFieldMapper - */ -class ProductFieldMapper extends Elasticsearch5ProductFieldMapper implements FieldMapperInterface -{ -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php index f69f7001d5bce..40d3ac95cf49c 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php @@ -6,7 +6,7 @@ namespace Magento\Elasticsearch\Model\Adapter\Index; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Elasticsearch\Model\Config; use Psr\Log\LoggerInterface; @@ -14,7 +14,7 @@ use Magento\CatalogSearch\Model\Indexer\Fulltext; /** - * Index name resolver + * Index name resolver for Elasticsearch * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index 0bf23f318c3bd..962c19595c4c0 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -7,11 +7,9 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Search\EngineResolverInterface; -use Magento\Search\Model\EngineResolver; use Magento\Store\Model\ScopeInterface; use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; use Magento\AdvancedSearch\Model\Client\ClientResolver; -use Magento\Framework\App\ObjectManager; /** * Elasticsearch config model @@ -69,22 +67,23 @@ class Config implements ClientOptionsInterface private $engineList; /** + * Config constructor. * @param ScopeConfigInterface $scopeConfig - * @param ClientResolver|null $clientResolver - * @param EngineResolverInterface|null $engineResolver + * @param ClientResolver $clientResolver + * @param EngineResolverInterface $engineResolver * @param string|null $prefix * @param array $engineList */ public function __construct( ScopeConfigInterface $scopeConfig, - ClientResolver $clientResolver = null, - EngineResolverInterface $engineResolver = null, + ClientResolver $clientResolver, + EngineResolverInterface $engineResolver, $prefix = null, $engineList = [] ) { $this->scopeConfig = $scopeConfig; - $this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class); - $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + $this->clientResolver = $clientResolver; + $this->engineResolver = $engineResolver; $this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine(); $this->engineList = $engineList; } diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php similarity index 98% rename from app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php rename to app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php index d05471734bb8f..8364b6c116b7d 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch6\Model\DataProvider; +namespace Magento\Elasticsearch\Model\DataProvider\Base; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Store\Model\ScopeInterface; @@ -17,7 +17,7 @@ use Magento\Store\Model\StoreManagerInterface as StoreManager; /** - * Class Suggestions + * Default implementation to provide suggestions mechanism for Elasticsearch */ class Suggestions implements SuggestedQueriesInterface { diff --git a/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php index c4fab39dfde61..e0afdeb2ffb27 100644 --- a/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php @@ -16,24 +16,27 @@ use Magento\Store\Model\StoreManagerInterface as StoreManager; /** - * Class Suggestions + * The implementation to provide suggestions mechanism for Elasticsearch5 + * + * @deprecated because of EOL for Elasticsearch5 + * @see \Magento\Elasticsearch\Model\DataProvider\Base\Suggestions */ class Suggestions implements SuggestedQueriesInterface { /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT */ const CONFIG_SUGGESTION_COUNT = 'catalog/search/search_suggestion_count'; /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED */ const CONFIG_SUGGESTION_COUNT_RESULTS_ENABLED = 'catalog/search/search_suggestion_count_results_enabled'; /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_ENABLED */ const CONFIG_SUGGESTION_ENABLED = 'catalog/search/search_suggestion_enabled'; diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php index 1e9b60da74a5b..9f7faf4aa13fa 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php @@ -7,22 +7,24 @@ namespace Magento\Elasticsearch\SearchAdapter\Aggregation; use Magento\Elasticsearch\SearchAdapter\QueryContainer; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Search\Dynamic\DataProviderInterface; use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder\BucketBuilderInterface; +/** + * Builder class for Elasticsearch + */ class Builder { /** * @var DataProviderInterface[] */ - protected $dataProviderContainer; + private $dataProviderContainer; /** * @var BucketBuilderInterface[] */ - protected $aggregationContainer; + private $aggregationContainer; /** * @var DataProviderFactory @@ -37,12 +39,12 @@ class Builder /** * @param DataProviderInterface[] $dataProviderContainer * @param BucketBuilderInterface[] $aggregationContainer - * @param DataProviderFactory|null $dataProviderFactory + * @param DataProviderFactory $dataProviderFactory */ public function __construct( array $dataProviderContainer, array $aggregationContainer, - DataProviderFactory $dataProviderFactory = null + DataProviderFactory $dataProviderFactory ) { $this->dataProviderContainer = array_map( function (DataProviderInterface $dataProvider) { @@ -56,8 +58,7 @@ function (BucketBuilderInterface $bucketBuilder) { }, $aggregationContainer ); - $this->dataProviderFactory = $dataProviderFactory - ?: ObjectManager::getInstance()->get(DataProviderFactory::class); + $this->dataProviderFactory = $dataProviderFactory; } /** diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php deleted file mode 100644 index 33ab1a4071560..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php +++ /dev/null @@ -1,287 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\SearchAdapter\Aggregation; - -use Magento\Framework\Search\Dynamic\IntervalInterface; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Model\Config; -use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; -use Magento\CatalogSearch\Model\Indexer\Fulltext; - -class Interval implements IntervalInterface -{ - /** - * Minimal possible value - */ - const DELTA = 0.005; - - /** - * @var ConnectionManager - */ - protected $connectionManager; - - /** - * @var FieldMapperInterface - */ - protected $fieldMapper; - - /** - * @var Config - */ - protected $clientConfig; - - /** - * @var string - */ - private $fieldName; - - /** - * @var string - */ - private $storeId; - - /** - * @var array - */ - private $entityIds; - - /** - * @var SearchIndexNameResolver - */ - private $searchIndexNameResolver; - - /** - * @param ConnectionManager $connectionManager - * @param FieldMapperInterface $fieldMapper - * @param Config $clientConfig - * @param SearchIndexNameResolver $searchIndexNameResolver - * @param string $fieldName - * @param string $storeId - * @param array $entityIds - */ - public function __construct( - ConnectionManager $connectionManager, - FieldMapperInterface $fieldMapper, - Config $clientConfig, - SearchIndexNameResolver $searchIndexNameResolver, - $fieldName, - $storeId, - $entityIds - ) { - $this->connectionManager = $connectionManager; - $this->fieldMapper = $fieldMapper; - $this->clientConfig = $clientConfig; - $this->fieldName = $fieldName; - $this->storeId = $storeId; - $this->entityIds = $entityIds; - $this->searchIndexNameResolver = $searchIndexNameResolver; - } - - /** - * {@inheritdoc} - */ - public function load($limit, $offset = null, $lower = null, $upper = null) - { - $from = $to = []; - if ($lower) { - $from = ['gte' => $lower - self::DELTA]; - } - if ($upper) { - $to = ['lt' => $upper - self::DELTA]; - } - - $requestQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'body' => [ - 'fields' => [ - '_id', - $this->fieldName, - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - 'size' => $limit, - ], - ]; - if ($offset) { - $requestQuery['body']['from'] = $offset; - } - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - return $this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName); - } - - /** - * {@inheritdoc} - */ - public function loadPrevious($data, $index, $lower = null) - { - if ($lower) { - $from = ['gte' => $lower - self::DELTA]; - } - if ($data) { - $to = ['lt' => $data - self::DELTA]; - } - - $requestQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'search_type' => 'count', - 'body' => [ - 'fields' => [ - '_id' - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - ], - ]; - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - $offset = $queryResult['hits']['total']; - if (!$offset) { - return false; - } - - return $this->load($index - $offset + 1, $offset - 1, $lower); - } - - /** - * {@inheritdoc} - */ - public function loadNext($data, $rightIndex, $upper = null) - { - $from = ['gt' => $data + self::DELTA]; - $to = ['lt' => $data - self::DELTA]; - - $requestCountQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'search_type' => 'count', - 'body' => [ - 'fields' => [ - '_id' - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - ], - ]; - $queryCountResult = $this->connectionManager->getConnection() - ->query($requestCountQuery); - - $offset = $queryCountResult['hits']['total']; - if (!$offset) { - return false; - } - - $from = ['gte' => $data - self::DELTA]; - if ($upper !== null) { - $to = ['lt' => $data - self::DELTA]; - } - - $requestQuery = $requestCountQuery; - $requestCountQuery['body']['query']['filtered']['filter']['bool']['must']['range'] = - [$this->fieldName => array_merge($from, $to)]; - - $requestCountQuery['body']['from'] = $offset - 1; - $requestCountQuery['body']['size'] = $rightIndex - $offset + 1; - - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - return array_reverse($this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName)); - } - - /** - * @param array $hits - * @param string $fieldName - * - * @return float[] - */ - private function arrayValuesToFloat($hits, $fieldName) - { - $returnPrices = []; - foreach ($hits as $hit) { - $returnPrices[] = (float) $hit['fields'][$fieldName][0]; - } - - return $returnPrices; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php b/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php index 77c42077323d4..57973b7fb92e4 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php @@ -7,10 +7,12 @@ use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; use Magento\AdvancedSearch\Model\Client\ClientFactoryInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch; +use Magento\AdvancedSearch\Model\Client\ClientInterface as Elasticsearch; use Psr\Log\LoggerInterface; /** + * Class provides interface for Elasticsearch connection + * * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php b/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php index 83652e08d4246..3372a2c26ae0e 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php @@ -5,7 +5,6 @@ */ namespace Magento\Elasticsearch\SearchAdapter; -use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Search\EntityMetadata; use Magento\Framework\Api\AttributeInterface; use Magento\Framework\Api\AttributeValue; @@ -14,33 +13,22 @@ use Magento\Framework\Api\Search\DocumentInterface; /** - * Document Factory + * Document Factory to create Search Document instance * @api * @since 100.1.0 */ class DocumentFactory { - /** - * Object Manager instance - * - * @var ObjectManagerInterface - * @deprecated 100.1.0 - * @since 100.1.0 - */ - protected $objectManager; - /** * @var EntityMetadata */ private $entityMetadata; /** - * @param ObjectManagerInterface $objectManager * @param EntityMetadata $entityMetadata */ - public function __construct(ObjectManagerInterface $objectManager, EntityMetadata $entityMetadata) + public function __construct(EntityMetadata $entityMetadata) { - $this->objectManager = $objectManager; $this->entityMetadata = $entityMetadata; } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php index 8efc342d26452..db6bb3cc7c413 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php @@ -12,32 +12,15 @@ use Magento\Elasticsearch\SearchAdapter\Filter\Builder\Term; use Magento\Elasticsearch\SearchAdapter\Filter\Builder\Wildcard; +/** + * Class Builder to build Elasticsearch filter + */ class Builder implements BuilderInterface { - /** - * Text flag for Elasticsearch filter query condition types - * - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_MUST - */ - const QUERY_CONDITION_MUST = 'must'; - - /** - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_SHOULD - */ - const QUERY_CONDITION_SHOULD = 'should'; - - /** - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT - */ - const QUERY_CONDITION_MUST_NOT = 'must_not'; - /** * @var FilterInterface[] */ - protected $filters; + private $filters; /** * @param Range $range @@ -57,7 +40,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function build(RequestFilterInterface $filter, $conditionType) { @@ -65,11 +48,13 @@ public function build(RequestFilterInterface $filter, $conditionType) } /** + * Processes filter object + * * @param RequestFilterInterface $filter * @param string $conditionType * @return array */ - protected function processFilter(RequestFilterInterface $filter, $conditionType) + private function processFilter(RequestFilterInterface $filter, $conditionType) { if (RequestFilterInterface::TYPE_BOOL == $filter->getType()) { $query = $this->processBoolFilter($filter, $this->isNegation($conditionType)); @@ -88,6 +73,8 @@ protected function processFilter(RequestFilterInterface $filter, $conditionType) } /** + * Processes filter + * * @param RequestFilterInterface|BoolExpression $filter * @param bool $isNegation * @return array @@ -96,12 +83,12 @@ protected function processBoolFilter(RequestFilterInterface $filter, $isNegation { $must = $this->buildFilters( $filter->getMust(), - $this->mapConditionType(self::QUERY_CONDITION_MUST, $isNegation) + $this->mapConditionType(BuilderInterface::FILTER_QUERY_CONDITION_MUST, $isNegation) ); - $should = $this->buildFilters($filter->getShould(), self::QUERY_CONDITION_SHOULD); + $should = $this->buildFilters($filter->getShould(), BuilderInterface::FILTER_QUERY_CONDITION_SHOULD); $mustNot = $this->buildFilters( $filter->getMustNot(), - $this->mapConditionType(self::QUERY_CONDITION_MUST_NOT, $isNegation) + $this->mapConditionType(BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT, $isNegation) ); $queries = [ @@ -116,6 +103,8 @@ protected function processBoolFilter(RequestFilterInterface $filter, $isNegation } /** + * Build filters + * * @param RequestFilterInterface[] $filters * @param string $conditionType * @return string @@ -126,25 +115,31 @@ private function buildFilters(array $filters, $conditionType) foreach ($filters as $filter) { $filterQuery = $this->processFilter($filter, $conditionType); if (isset($filterQuery['bool'][$conditionType])) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $queries['bool'][$conditionType] = array_merge( isset($queries['bool'][$conditionType]) ? $queries['bool'][$conditionType] : [], $filterQuery['bool'][$conditionType] ); } } + return $queries; } /** + * Returns is condition type navigation + * * @param string $conditionType * @return bool */ - protected function isNegation($conditionType) + private function isNegation($conditionType) { - return self::QUERY_CONDITION_MUST_NOT === $conditionType; + return BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT === $conditionType; } /** + * Maps condition type + * * @param string $conditionType * @param bool $isNegation * @return string @@ -152,10 +147,10 @@ protected function isNegation($conditionType) private function mapConditionType($conditionType, $isNegation) { if ($isNegation) { - if ($conditionType == self::QUERY_CONDITION_MUST) { - $conditionType = self::QUERY_CONDITION_MUST_NOT; - } elseif ($conditionType == self::QUERY_CONDITION_MUST_NOT) { - $conditionType = self::QUERY_CONDITION_MUST; + if ($conditionType == BuilderInterface::FILTER_QUERY_CONDITION_MUST) { + $conditionType = BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT; + } elseif ($conditionType == BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT) { + $conditionType = BuilderInterface::FILTER_QUERY_CONDITION_MUST; } } return $conditionType; diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php index d88c7e53d813a..be6ccce4a6b8c 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php @@ -6,7 +6,6 @@ namespace Magento\Elasticsearch\SearchAdapter\Filter\Builder; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Filter\Term as TermFilterRequest; use Magento\Framework\Search\Request\FilterInterface as RequestFilterInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; @@ -21,7 +20,7 @@ class Term implements FilterInterface /** * @var FieldMapperInterface */ - protected $fieldMapper; + private $fieldMapper; /** * @var AttributeProvider @@ -41,12 +40,11 @@ class Term implements FilterInterface */ public function __construct( FieldMapperInterface $fieldMapper, - AttributeProvider $attributeAdapterProvider = null, + AttributeProvider $attributeAdapterProvider, array $integerTypeAttributes = [] ) { $this->fieldMapper = $fieldMapper; - $this->attributeAdapterProvider = $attributeAdapterProvider - ?? ObjectManager::getInstance()->get(AttributeProvider::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; $this->integerTypeAttributes = array_merge($this->integerTypeAttributes, $integerTypeAttributes); } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php b/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php deleted file mode 100644 index fd6ae3424b9da..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\SearchAdapter; - -use Magento\Framework\Search\RequestInterface; -use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; -use Magento\Framework\Search\Request\Query\BoolExpression as BoolQuery; -use Magento\Framework\Search\Request\Query\Filter as FilterQuery; -use Magento\Framework\Search\Request\Query\Match as MatchQuery; -use Magento\Elasticsearch\SearchAdapter\Query\Builder as QueryBuilder; -use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; -use Magento\Elasticsearch\SearchAdapter\Filter\Builder as FilterBuilder; -use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper as Elasticsearch5Mapper; - -/** - * Mapper class - * @api - * @since 100.1.0 - */ -class Mapper extends Elasticsearch5Mapper -{ - /** - * @param QueryBuilder $queryBuilder - * @param MatchQueryBuilder $matchQueryBuilder - * @param FilterBuilder $filterBuilder - */ - public function __construct( - QueryBuilder $queryBuilder, - MatchQueryBuilder $matchQueryBuilder, - FilterBuilder $filterBuilder - ) { - $this->queryBuilder = $queryBuilder; - $this->matchQueryBuilder = $matchQueryBuilder; - $this->filterBuilder = $filterBuilder; - } - - /** - * Build adapter dependent query - * - * @param RequestInterface $request - * @return array - * @since 100.1.0 - */ - public function buildQuery(RequestInterface $request) - { - $searchQuery = $this->queryBuilder->initQuery($request); - $searchQuery['body']['query'] = array_merge( - $searchQuery['body']['query'], - $this->processQuery( - $request->getQuery(), - [], - BoolQuery::QUERY_CONDITION_MUST - ) - ); - - $searchQuery['body']['query']['bool']['minimum_should_match'] = 1; - - $searchQuery = $this->queryBuilder->initAggregations($request, $searchQuery); - return $searchQuery; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index 8a44b58d35fb8..1012918b15a8b 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -9,7 +9,6 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Query\BoolExpression; use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; @@ -30,13 +29,6 @@ class Match implements QueryInterface */ private $fieldMapper; - /** - * @deprecated - * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer - * @var PreprocessorInterface[] - */ - protected $preprocessorContainer; - /** * @var AttributeProvider */ @@ -58,29 +50,23 @@ class Match implements QueryInterface /** * @param FieldMapperInterface $fieldMapper - * @param PreprocessorInterface[] $preprocessorContainer - * @param AttributeProvider|null $attributeProvider - * @param TypeResolver|null $fieldTypeResolver - * @param ValueTransformerPool|null $valueTransformerPool - * @param Config|null $config + * @param AttributeProvider $attributeProvider + * @param TypeResolver $fieldTypeResolver + * @param ValueTransformerPool $valueTransformerPool + * @param Config $config */ public function __construct( FieldMapperInterface $fieldMapper, - array $preprocessorContainer, - AttributeProvider $attributeProvider = null, - TypeResolver $fieldTypeResolver = null, - ValueTransformerPool $valueTransformerPool = null, - Config $config = null + AttributeProvider $attributeProvider, + TypeResolver $fieldTypeResolver, + ValueTransformerPool $valueTransformerPool, + Config $config ) { $this->fieldMapper = $fieldMapper; - $this->preprocessorContainer = $preprocessorContainer; - $this->attributeProvider = $attributeProvider ?? ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldTypeResolver = $fieldTypeResolver ?? ObjectManager::getInstance() - ->get(TypeResolver::class); - $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() - ->get(ValueTransformerPool::class); - $this->config = $config ?? ObjectManager::getInstance()->get(Config::class); + $this->attributeProvider = $attributeProvider; + $this->fieldTypeResolver = $fieldTypeResolver; + $this->valueTransformerPool = $valueTransformerPool; + $this->config = $config; } /** @@ -183,20 +169,4 @@ protected function buildQueries(array $matches, array $queryValue) return $conditions; } - - /** - * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. - * - * @deprecated - * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer - * @param string $value - * @return string - */ - protected function escape($value) - { - $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; - $replace = '\\\$1'; - - return preg_replace($pattern, $replace, $value); - } } diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml new file mode 100644 index 0000000000000..5a2aba8df9f91 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml @@ -0,0 +1,26 @@ +<?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="AssertSearchResultActionGroup"> + <annotations> + <description>Check search result on Storefront</description> + </annotations> + <arguments> + <argument name="keyword" defaultValue="Simple Product A" type="string"/> + <argument name="product" type="string" defaultValue="Simple Product A"/> + </arguments> + <fillField userInput="{{keyword}}" selector="{{StorefrontQuickSearchSection.searchPhrase}}" stepKey="fillSearchBar"/> + <waitForPageLoad stepKey="waitForSearchButton"/> + <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(product)}}" stepKey="foundProductAOnPage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml new file mode 100644 index 0000000000000..6389ca31ba534 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ModifyCustomAttributeValueActionGroup"> + <annotations> + <description>Update value for custom attribute</description> + </annotations> + <arguments> + <argument name="attributeValue" defaultValue="3.5" type="string"/> + <argument name="product" type="string" defaultValue="SKU0001"/> + </arguments> + <click selector="{{AdminProductGridSection.productRowBySku(product)}}" stepKey="clickOnSimpleProductRow"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{AdminProductMultiselectAttributeSection.option(attributeValue)}}" stepKey="selectFirstOption"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="save"/> + <waitForPageLoad stepKey="waitForProductSaved"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml similarity index 75% rename from app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml rename to app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml index 03a1c63f71515..5a1118d079158 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml @@ -10,7 +10,7 @@ <entity name="SearchEngineElasticsearchConfigData"> <data key="path">catalog/search/engine</data> <data key="scope_id">1</data> - <data key="label">Elasticsearch 6.x</data> - <data key="value">elasticsearch6</data> + <data key="label">Elasticsearch {{_ENV.ELASTICSEARCH_VERSION}}.0+</data> + <data key="value">elasticsearch{{_ENV.ELASTICSEARCH_VERSION}}</data> </entity> </entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml similarity index 100% rename from app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml rename to app/code/Magento/Elasticsearch/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml index 9fcc1909ab42c..a59e260bc4d94 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-94995"/> <group value="Catalog"/> + <group value="SearchEngineElasticsearch" /> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> @@ -28,16 +29,16 @@ <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="categoryFirst" stepKey="deleteCategory"/> - <actionGroup ref="ResetSearchEngineConfigurationActionGroup" stepKey="resetCatalogSearchConfiguration"/> - <actionGroup ref="UpdateIndexerOnSaveActionGroup" stepKey="resetIndexerBackToOriginalState"> + <comment userInput="The test was moved to elasticsearch suite" stepKey="resetCatalogSearchConfiguration"/> + <actionGroup ref="updateIndexerOnSave" stepKey="resetIndexerBackToOriginalState"> <argument name="indexerName" value="catalogsearch_fulltext"/> </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <comment userInput="Change Catalog search engine option to Elastic Search 5.x" stepKey="chooseElasticSearch5"/> - <actionGroup ref="ChooseElasticSearchAsSearchEngineActionGroup" stepKey="chooseES5"/> + <comment userInput="Change Catalog search engine option to Elastic Search 5.0+" stepKey="chooseElasticSearch5"/> + <comment userInput="The test was moved to elasticsearch suite" stepKey="chooseES5"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> <actionGroup ref="UpdateIndexerByScheduleActionGroup" stepKey="updateAnIndexerBySchedule"> <argument name="indexerName" value="catalogsearch_fulltext"/> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml new file mode 100644 index 0000000000000..c48f0d63b06ca --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml @@ -0,0 +1,99 @@ +<?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="StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest"> + <annotations> + <features value="Search"/> + <stories value="Elasticsearch 7.x.x Upgrade"/> + <title value="Search decimal attribute with ElasticSearch"/> + <description value="User should be able to search decimal attribute using ElasticSearch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-31239"/> + <group value="SearchEngineElasticsearch" /> + </annotations> + <before> + <!-- Create product attribute with 3 options --> + <createData entity="multipleSelectProductAttribute" stepKey="customAttribute"/> + <createData entity="ProductAttributeOption10" stepKey="option1"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ProductAttributeOption11" stepKey="option2"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ProductAttributeOption12" stepKey="option3"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + + <!-- Add custom attribute to Default Set --> + <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + + <!-- Create subcategory --> + <createData entity="SimpleSubCategory" stepKey="newCategory"/> + <createData entity="SimpleProduct" stepKey="product1"> + <requiredEntity createDataKey="newCategory"/> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product2"> + <requiredEntity createDataKey="newCategory"/> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="product1" stepKey="deleteProduct1"/> + <deleteData createDataKey="product2" stepKey="deleteProduct2"/> + <deleteData createDataKey="newCategory" stepKey="deleteCategory"/> + <!--Delete attribute--> + <deleteData createDataKey="customAttribute" stepKey="deleteCustomAttribute"/> + <!--Reindex and clear cache--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:clean" stepKey="cleanCache"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <!--Navigate to backend and update value for custom attribute --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$product1$$"/> + </actionGroup> + <actionGroup ref="ModifyCustomAttributeValueActionGroup" stepKey="ModifyCustomAttributeValueProduct1"> + <argument name="attributeValue" value="3.5"/> + <argument name="product" value="$$product1.sku$$"/> + </actionGroup> + + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForApiSimpleProduct"> + <argument name="product" value="$$product2$$"/> + </actionGroup> + <actionGroup ref="ModifyCustomAttributeValueActionGroup" stepKey="ModifyCustomAttributeValueProduct2"> + <argument name="attributeValue" value="10.12"/> + <argument name="product" value="$$product2.sku$$"/> + </actionGroup> + + <!--Reindex and clear cache--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:clean" stepKey="cleanCache"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForHomePageToLoad"/> + + <!-- Navigate to Storefront and search --> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult0"> + <argument name="keyword" value="$$product1.name$$"/> + <argument name="product" value="$$product1.name$$"/> + </actionGroup> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult1"> + <argument name="keyword" value="3.5"/> + <argument name="product" value="$$product1.name$$"/> + </actionGroup> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult2"> + <argument name="keyword" value="10.12"/> + <argument name="product" value="$$product2.name$$"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php deleted file mode 100644 index ced529660af06..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php +++ /dev/null @@ -1,400 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\DataMapper; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Stdlib\DateTime; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Elasticsearch\Model\Adapter\Container\Attribute as AttributeContainer; -use Magento\Elasticsearch\Model\Adapter\Document\Builder; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper; -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\AdvancedSearch\Model\ResourceModel\Index as AdvancedSearchIndex; -use Magento\Store\Api\Data\StoreInterface; - -/** - * Class ProductDataMapperTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ProductDataMapperTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ProductDataMapper - */ - protected $model; - - /** - * @var Builder|\PHPUnit_Framework_MockObject_MockObject - */ - private $builderMock; - - /** - * @var AttributeContainer|\PHPUnit_Framework_MockObject_MockObject - */ - private $attributeContainerMock; - - /** - * @var Attribute|\PHPUnit_Framework_MockObject_MockObject - */ - private $attribute; - - /** - * @var Index|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceIndex; - - /** - * @var AdvancedSearchIndex|\PHPUnit_Framework_MockObject_MockObject - */ - private $advancedSearchIndex; - - /** - * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $fieldMapperMock; - - /** - * @var DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - private $dateTimeMock; - - /** - * @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $localeDateMock; - - /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $scopeConfigMock; - - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeInterface; - - /** - * Set up test environment. - */ - protected function setUp() - { - $this->builderMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\Document\Builder::class) - ->setMethods(['addField', 'addFields', 'build']) - ->disableOriginalConstructor() - ->getMock(); - - $this->attributeContainerMock = $this->getMockBuilder( - \Magento\Elasticsearch\Model\Adapter\Container\Attribute::class - )->setMethods(['getAttribute', 'setStoreId', 'getBackendType', 'getFrontendInput']) - ->disableOriginalConstructor() - ->getMock(); - - $this->resourceIndex = $this->getMockBuilder(\Magento\Elasticsearch\Model\ResourceModel\Index::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getPriceIndexData', - 'getFullCategoryProductIndexData', - 'getFullProductIndexData', - ]) - ->getMock(); - - $this->fieldMapperMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) - ->setMethods(['getFieldName', 'getAllAttributesTypes']) - ->disableOriginalConstructor() - ->getMock(); - - $this->dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime::class) - ->setMethods(['isEmptyDate', 'setTimezone', 'format']) - ->disableOriginalConstructor() - ->getMock(); - - $this->localeDateMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->advancedSearchIndex = $this->getMockBuilder(\Magento\AdvancedSearch\Model\ResourceModel\Index::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeInterface = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->model = $objectManager->getObject( - \Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper::class, - [ - 'builder' => $this->builderMock, - 'attributeContainer' => $this->attributeContainerMock, - 'resourceIndex' => $this->resourceIndex, - 'fieldMapper' => $this->fieldMapperMock, - 'dateTime' => $this->dateTimeMock, - 'localeDate' => $this->localeDateMock, - 'scopeConfig' => $this->scopeConfigMock, - 'storeManager' => $this->storeManagerMock - ] - ); - } - - /** - * Tests modules data returns array - * - * @dataProvider mapProvider - * @param int $productId - * @param array $productData - * @param int $storeId - * @param bool $emptyDate - * @param string $type - * @param string $frontendInput - * - * @return void - */ - public function testGetMap($productId, $productData, $storeId, $emptyDate, $type, $frontendInput) - { - $this->attributeContainerMock->expects($this->any())->method('getAttribute')->will( - $this->returnValue($this->attribute) - ); - $this->resourceIndex->expects($this->any()) - ->method('getPriceIndexData') - ->with([1, ], 1) - ->willReturn([ - 1 => [1] - ]); - $this->resourceIndex->expects($this->any()) - ->method('getFullCategoryProductIndexData') - ->willReturn([ - 1 => [ - 0 => [ - 'id' => 2, - 'name' => 'Default Category', - 'position' => '1', - ], - 1 => [ - 'id' => 3, - 'name' => 'Gear', - 'position' => '1', - ], - 2 => [ - 'id' => 4, - 'name' => 'Bags', - 'position' => '1', - ], - ], - ]); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($this->storeInterface); - $this->storeInterface->expects($this->any()) - ->method('getWebsiteId') - ->willReturn(1); - $this->attributeContainerMock->expects($this->any())->method('setStoreId')->will( - $this->returnValue($this->attributeContainerMock) - ); - $this->attribute->expects($this->any())->method('getBackendType')->will( - $this->returnValue($type) - ); - $this->attribute->expects($this->any())->method('getFrontendInput')->will( - $this->returnValue($frontendInput) - ); - $this->dateTimeMock->expects($this->any())->method('isEmptyDate')->will( - $this->returnValue($emptyDate) - ); - $this->scopeConfigMock->expects($this->any())->method('getValue')->will( - $this->returnValue('Europe/London') - ); - $this->builderMock->expects($this->any())->method('addField')->will( - $this->returnValue([]) - ); - $this->builderMock->expects($this->any())->method('addFields')->will( - $this->returnValue([]) - ); - $this->builderMock->expects($this->any())->method('build')->will( - $this->returnValue([]) - ); - - $this->resourceIndex->expects($this->once()) - ->method('getFullProductIndexData') - ->willReturn($productData); - - $this->assertInternalType( - 'array', - $this->model->map($productId, $productData, $storeId) - ); - } - - /** - * @return array - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public static function mapProvider() - { - return [ - [ - '1', - ['price'=>'11','created_at'=>'00-00-00 00:00:00', 'color_value'=>'11'], - '1', - false, - 'datetime', - 'select', - ], - [ - '1', - ['price'=>'11','created_at'=>'00-00-00 00:00:00', 'color_value'=>'11'], - '1', - false, - 'time', - 'multiselect', - ], - [ - '1', - ['price'=>'11','created_at'=>null,'color_value'=>'11', ], - '1', - true, - 'datetime', - 'select', - ], - [ - '1', - [ - 'tier_price'=> - [[ - 'price_id'=>'1', - 'website_id'=>'1', - 'all_groups'=>'1', - 'cust_group'=>'1', - 'price_qty'=>'1', - 'website_price'=>'1', - 'price'=>'1' - ]], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['image'=>'11','created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - [ - 'image' => '1', - 'small_image' => '1', - 'thumbnail' => '1', - 'swatch_image' => '1', - 'media_gallery'=> - [ - 'images' => - [[ - 'file'=>'1', - 'media_type'=>'image', - 'position'=>'1', - 'disabled'=>'1', - 'label'=>'1', - 'title'=>'1', - 'base_image'=>'1', - 'small_image'=>'1', - 'thumbnail'=>'1', - 'swatch_image'=>'1' - ]] - ], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - [ - 'image' => '1', - 'small_image' => '1', - 'thumbnail' => '1', - 'swatch_image' => '1', - 'media_gallery'=> - [ - 'images' => - [[ - 'file'=>'1', - 'media_type'=>'video', - 'position'=>'1', - 'disabled'=>'1', - 'label'=>'1', - 'title'=>'1', - 'base_image'=>'1', - 'small_image'=>'1', - 'thumbnail'=>'1', - 'swatch_image'=>'1', - 'video_title'=>'1', - 'video_url'=>'1', - 'video_description'=>'1', - 'video_metadata'=>'1', - 'video_provider'=>'1' - ]] - ], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['quantity_and_stock_status'=>'11','created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['quantity_and_stock_status'=>['is_in_stock' => '1', 'qty' => '12'],'created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['price'=>'11','created_at'=>'1995-12-31 23:59:59','options'=>['value1','value2']], - '1', - false, - 'string', - 'select', - ], - ]; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php index 326c04aad6165..153deb8bff845 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php @@ -12,12 +12,12 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface; use Psr\Log\LoggerInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver; /** - * Class ElasticsearchTest + * Test for Elasticsearch client * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -73,6 +73,11 @@ class ElasticsearchTest extends \PHPUnit\Framework\TestCase */ protected $indexNameResolver; + /** + * @var \Magento\Elasticsearch\Model\Adapter\DataMapperInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $documentDataMapper; + /** * Setup * @@ -135,7 +140,7 @@ protected function setUp() $elasticsearchClientMock->expects($this->any()) ->method('indices') ->willReturn($indicesMock); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class) ->setConstructorArgs( [ 'options' => $this->getClientOptions(), diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php index 3a294ba20e206..0491517fbe979 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php @@ -9,7 +9,7 @@ use Psr\Log\LoggerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** @@ -112,7 +112,7 @@ protected function setUp() $elasticsearchClientMock->expects($this->any()) ->method('indices') ->willReturn($indicesMock); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class) ->setConstructorArgs([ 'options' => $this->getClientOptions(), 'elasticsearchClient' => $elasticsearchClientMock diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php similarity index 97% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php index b3c60b70ffa8e..f948cd0ed194d 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch6\Test\Unit\Model\DataProvider; +namespace Magento\Elasticsearch\Test\Unit\Model\DataProvider\Base; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\DataProvider\Suggestions; @@ -103,7 +103,7 @@ protected function setUp() $objectManager = new ObjectManagerHelper($this); $this->model = $objectManager->getObject( - \Magento\Elasticsearch6\Model\DataProvider\Suggestions::class, + \Magento\Elasticsearch\Model\DataProvider\Base\Suggestions::class, [ 'queryResultFactory' => $this->queryResultFactory, 'connectionManager' => $this->connectionManager, diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php index 584b89545499d..1be84a3ec21d2 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -148,9 +148,10 @@ public function testGetItems() ->method('getQueryText') ->willReturn('query'); - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) ->disableOriginalConstructor() - ->getMock(); + ->setMethods(['suggest']) + ->getMockForAbstractClass(); $this->connectionManager->expects($this->any()) ->method('getConnection') diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php index 7b86a13563b1e..426e23c11844d 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php @@ -8,6 +8,10 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\Indexer\IndexerHandler; +/** + * Test for \Magento\Elasticsearch\Model\Indexer\IndexerHandler + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class IndexerHandlerTest extends \PHPUnit\Framework\TestCase { /** @@ -41,7 +45,7 @@ class IndexerHandlerTest extends \PHPUnit\Framework\TestCase private $indexNameResolver; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ private $client; @@ -89,8 +93,8 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['ping']) + $this->client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['ping', 'testConnection','prepareDocsPerStore','addDocs', 'cleanIndex']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php deleted file mode 100644 index 424edd4768a81..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\SearchAdapter; - -use Magento\Elasticsearch\SearchAdapter\Adapter; -use Magento\Elasticsearch\SearchAdapter\QueryContainer; -use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -/** - * Class AdapterTest - */ -class AdapterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var QueryContainerFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $queryContainerFactory; - - /** - * @var Adapter - */ - protected $model; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\ConnectionManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connectionManager; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\Mapper|\PHPUnit_Framework_MockObject_MockObject - */ - protected $mapper; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\ResponseFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseFactory; - - /** - * @var \Magento\Framework\Search\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $request; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\Aggregation\Builder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $aggregationBuilder; - - /** - * Setup method - * @return void - */ - protected function setUp() - { - $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mapper = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Mapper::class) - ->setMethods([ - 'buildQuery', - ]) - ->disableOriginalConstructor() - ->getMock(); - - $this->responseFactory = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ResponseFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->aggregationBuilder = $this->getMockBuilder( - \Magento\Elasticsearch\SearchAdapter\Aggregation\Builder::class - ) - ->setMethods([ - 'build', - 'setQuery' - ]) - ->disableOriginalConstructor() - ->getMock(); - - $this->queryContainerFactory = $this->getMockBuilder(QueryContainerFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->model = $objectManager->getObject( - \Magento\Elasticsearch\SearchAdapter\Adapter::class, - [ - 'connectionManager' => $this->connectionManager, - 'mapper' => $this->mapper, - 'responseFactory' => $this->responseFactory, - 'aggregationBuilder' => $this->aggregationBuilder, - 'queryContainerFactory' => $this->queryContainerFactory, - ] - ); - } - - /** - * Test query() method - * - * @return void - */ - public function testQuery() - { - $searchQuery = [ - 'index' => 'indexName', - 'type' => 'product', - 'body' => [ - 'from' => 0, - 'size' => 1000, - 'fields' => ['_id', '_score'], - 'query' => [], - ], - ]; - - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) - ->disableOriginalConstructor() - ->getMock(); - - $this->connectionManager->expects($this->once()) - ->method('getConnection') - ->willReturn($client); - - $queryContainer = $this->getMockBuilder(QueryContainer::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->queryContainerFactory->expects($this->once()) - ->method('create') - ->with(['query' => $searchQuery]) - ->willReturn($queryContainer); - - $this->aggregationBuilder->expects($this->once()) - ->method('setQuery') - ->with($queryContainer); - - $this->mapper->expects($this->once()) - ->method('buildQuery') - ->with($this->request) - ->willReturn($searchQuery); - - $client->expects($this->once()) - ->method('query') - ->willReturn([ - 'hits' => [ - 'total' => 1, - 'hits' => [ - [ - '_index' => 'indexName', - '_type' => 'product', - '_id' => 1, - '_score' => 1.0, - ], - ], - ], - ]); - $this->aggregationBuilder->expects($this->once()) - ->method('build') - ->willReturn($client); - - $this->model->query($this->request); - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php deleted file mode 100644 index 169c6d71062ba..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php +++ /dev/null @@ -1,358 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\SearchAdapter\Aggregation; - -use Magento\Elasticsearch\SearchAdapter\Aggregation\Interval; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Model\Session as CustomerSession; -use Magento\Elasticsearch\Model\Config; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class IntervalTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Interval - */ - protected $model; - - /** - * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connectionManager; - - /** - * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $fieldMapper; - - /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject - */ - protected $clientConfig; - - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManager; - - /** - * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerSession; - - /** - * @var ElasticsearchClient|\PHPUnit_Framework_MockObject_MockObject - */ - protected $clientMock; - - /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeMock; - - /** - * @var SearchIndexNameResolver|\PHPUnit_Framework_MockObject_MockObject - */ - protected $searchIndexNameResolver; - - /** - * Set up test environment. - * - * @return void - */ - protected function setUp() - { - $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) - ->setMethods(['getConnection']) - ->disableOriginalConstructor() - ->getMock(); - $this->fieldMapper = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->clientConfig = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class) - ->setMethods([ - 'getIndexName', - 'getEntityType', - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) - ->setMethods(['getCustomerGroupId']) - ->disableOriginalConstructor() - ->getMock(); - $this->customerSession->expects($this->any()) - ->method('getCustomerGroupId') - ->willReturn(1); - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver = $this - ->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeMock->expects($this->any()) - ->method('getWebsiteId') - ->willReturn(1); - $this->storeMock->expects($this->any()) - ->method('getId') - ->willReturn(1); - $this->clientConfig->expects($this->any()) - ->method('getIndexName') - ->willReturn('indexName'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('product'); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) - ->disableOriginalConstructor() - ->getMock(); - $this->connectionManager->expects($this->any()) - ->method('getConnection') - ->willReturn($this->clientMock); - - $objectManagerHelper = new ObjectManagerHelper($this); - $this->model = $objectManagerHelper->getObject( - \Magento\Elasticsearch\SearchAdapter\Aggregation\Interval::class, - [ - 'connectionManager' => $this->connectionManager, - 'fieldMapper' => $this->fieldMapper, - 'clientConfig' => $this->clientConfig, - 'searchIndexNameResolver' => $this->searchIndexNameResolver, - 'fieldName' => 'price_0_1', - 'storeId' => 1, - 'entityIds' => [265, 313, 281] - ] - ); - } - - /** - * @dataProvider loadParamsProvider - * @param string $limit - * @param string $offset - * @param string $lower - * @param string $upper - * Test load() method - */ - public function testLoad($limit, $offset, $lower, $upper) - { - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = [25]; - - $this->clientMock->expects($this->once()) - ->method('query') - ->willReturn([ - 'hits' => [ - 'hits' => [ - [ - 'fields' => [ - - 'price_0_1' => [25], - - ], - ], - ], - ], - ]); - $this->assertEquals( - $expectedResult, - $this->model->load($limit, $offset, $lower, $upper) - ); - } - - /** - * @dataProvider loadPrevParamsProvider - * @param string $data - * @param string $index - * @param string $lower - * Test loadPrevious() method with offset - */ - public function testLoadPrevArray($data, $index, $lower) - { - $queryResult = [ - 'hits' => [ - 'total'=> '1', - 'hits' => [ - [ - 'fields' => [ - 'price_0_1' => ['25'] - ] - ], - ], - ], - ]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = ['25.0']; - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertEquals( - $expectedResult, - $this->model->loadPrevious($data, $index, $lower) - ); - } - - /** - * @dataProvider loadPrevParamsProvider - * @param string $data - * @param string $index - * @param string $lower - * Test loadPrevious() method without offset - */ - public function testLoadPrevFalse($data, $index, $lower) - { - $queryResult = ['hits' => ['total'=> '0']]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertFalse( - $this->model->loadPrevious($data, $index, $lower) - ); - } - - /** - * @dataProvider loadNextParamsProvider - * @param string $data - * @param string $rightIndex - * @param string $upper - * Test loadNext() method with offset - */ - public function testLoadNextArray($data, $rightIndex, $upper) - { - $queryResult = [ - 'hits' => [ - 'total'=> '1', - 'hits' => [ - [ - 'fields' => [ - 'price_0_1' => ['25'] - ] - ], - ], - ] - ]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = ['25.0']; - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertEquals( - $expectedResult, - $this->model->loadNext($data, $rightIndex, $upper) - ); - } - - /** - * @dataProvider loadNextParamsProvider - * @param string $data - * @param string $rightIndex - * @param string $upper - * Test loadNext() method without offset - */ - public function testLoadNextFalse($data, $rightIndex, $upper) - { - $queryResult = ['hits' => ['total'=> '0']]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertFalse( - $this->model->loadNext($data, $rightIndex, $upper) - ); - } - - /** - * @return array - */ - public static function loadParamsProvider() - { - return [ - ['6', '2', '24', '42'], - ]; - } - - /** - * @return array - */ - public static function loadPrevParamsProvider() - { - return [ - ['24', '1', '24'], - ]; - } - - /** - * @return array - */ - public static function loadNextParamsProvider() - { - return [ - ['24', '2', '42'], - ]; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php index 5d80ef62a5d20..7d5865650e54a 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php @@ -12,7 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class ConnectionManagerTest + * Test for Magento\Elasticsearch\SearchAdapter\ConnectionManager */ class ConnectionManagerTest extends \PHPUnit\Framework\TestCase { @@ -81,7 +81,7 @@ protected function setUp() */ public function testGetConnectionSuccessfull() { - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) ->disableOriginalConstructor() ->getMock(); $this->clientFactory->expects($this->once()) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php index 6258a4a20d694..9e2d3c3b35a98 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php @@ -66,7 +66,7 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase protected $storeMock; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $clientMock; @@ -145,8 +145,8 @@ private function setUpMockObjects() $this->clientConfig->expects($this->any()) ->method('getEntityType') ->willReturn('product'); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) + $this->clientMock = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['query', 'testConnection', ]) ->disableOriginalConstructor() ->getMock(); $this->connectionManager->expects($this->any()) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php deleted file mode 100644 index 845e1acb2d646..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php +++ /dev/null @@ -1,202 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\SearchAdapter; - -use Magento\Elasticsearch\SearchAdapter\Mapper; -use Magento\Elasticsearch\SearchAdapter\Query\Builder as QueryBuilder; -use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; -use Magento\Elasticsearch\SearchAdapter\Filter\Builder as FilterBuilder; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class MapperTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Mapper - */ - protected $model; - - /** - * @var QueryBuilder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $queryBuilder; - - /** - * @var MatchQueryBuilder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $matchQueryBuilder; - - /** - * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $filterBuilder; - - /** - * Setup method - * @return void - */ - protected function setUp() - { - $this->queryBuilder = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Query\Builder::class) - ->setMethods([ - 'initQuery', - 'initAggregations', - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->matchQueryBuilder = $this->getMockBuilder( - \Magento\Elasticsearch\SearchAdapter\Query\Builder\Match::class - ) - ->setMethods(['build']) - ->disableOriginalConstructor() - ->getMock(); - $this->filterBuilder = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Filter\Builder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->queryBuilder->expects($this->any()) - ->method('initQuery') - ->willReturn([ - 'body' => [ - 'query' => [], - ], - ]); - $this->queryBuilder->expects($this->any()) - ->method('initAggregations') - ->willReturn([ - 'body' => [ - 'query' => [], - ], - ]); - $this->matchQueryBuilder->expects($this->any()) - ->method('build') - ->willReturn([]); - - $objectManagerHelper = new ObjectManagerHelper($this); - $this->model = $objectManagerHelper->getObject( - \Magento\Elasticsearch\SearchAdapter\Mapper::class, - [ - 'queryBuilder' => $this->queryBuilder, - 'matchQueryBuilder' => $this->matchQueryBuilder, - 'filterBuilder' => $this->filterBuilder - ] - ); - } - - /** - * Test buildQuery() method with exception - * @expectedException \InvalidArgumentException - */ - public function testBuildQueryFailure() - { - $request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $query = $this->getMockBuilder(\Magento\Framework\Search\Request\QueryInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $request->expects($this->once()) - ->method('getQuery') - ->willReturn($query); - $query->expects($this->atLeastOnce()) - ->method('getType') - ->willReturn('unknown'); - - $this->model->buildQuery($request); - } - - /** - * Test buildQuery() method - * - * @param string $queryType - * @param string $queryMock - * @param string $referenceType - * @param string $filterMock - * @dataProvider buildQueryDataProvider - */ - public function testBuildQuery($queryType, $queryMock, $referenceType, $filterMock) - { - $request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $query = $this->getMockBuilder($queryMock) - ->setMethods(['getMust', 'getMustNot', 'getType', 'getShould', 'getReferenceType', 'getReference']) - ->disableOriginalConstructor() - ->getMock(); - $matchQuery = $this->getMockBuilder(\Magento\Framework\Search\Request\Query\Match::class) - ->disableOriginalConstructor() - ->getMock(); - $filterQuery = $this->getMockBuilder($filterMock) - ->disableOriginalConstructor() - ->getMock(); - $request->expects($this->once()) - ->method('getQuery') - ->willReturn($query); - - $query->expects($this->atLeastOnce()) - ->method('getType') - ->willReturn($queryType); - $query->expects($this->any()) - ->method('getMust') - ->willReturn([$matchQuery]); - $query->expects($this->any()) - ->method('getShould') - ->willReturn([]); - $query->expects($this->any()) - ->method('getMustNot') - ->willReturn([]); - $query->expects($this->any()) - ->method('getReferenceType') - ->willReturn($referenceType); - $query->expects($this->any()) - ->method('getReference') - ->willReturn($filterQuery); - $matchQuery->expects($this->any()) - ->method('getType') - ->willReturn('matchQuery'); - $filterQuery->expects($this->any()) - ->method('getType') - ->willReturn('matchQuery'); - $filterQuery->expects($this->any()) - ->method('getType') - ->willReturn('matchQuery'); - $this->filterBuilder->expects(($this->any())) - ->method('build') - ->willReturn([ - 'bool' => [ - 'must' => [], - ], - ]); - - $this->model->buildQuery($request); - } - - /** - * @return array - */ - public function buildQueryDataProvider() - { - return [ - [ - 'matchQuery', \Magento\Framework\Search\Request\Query\Match::class, - 'query', \Magento\Framework\Search\Request\QueryInterface::class, - ], - [ - 'boolQuery', \Magento\Framework\Search\Request\Query\BoolExpression::class, - 'query', \Magento\Framework\Search\Request\QueryInterface::class, - ], - [ - 'filteredQuery', \Magento\Framework\Search\Request\Query\Filter::class, - 'query', \Magento\Framework\Search\Request\QueryInterface::class, - ], - [ - 'filteredQuery', \Magento\Framework\Search\Request\Query\Filter::class, - 'filter', \Magento\Framework\Search\Request\FilterInterface::class, - ], - ]; - } -} diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index 3cf5444d86223..9a4898ee330e2 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "*", "magento/module-catalog-inventory": "*", "magento/framework": "*", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" + "elasticsearch/elasticsearch": "~7.6" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml index 6d87c4948a9d9..1727e51371383 100644 --- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml @@ -9,68 +9,6 @@ <system> <section id="catalog"> <group id="search"> - <!-- Elasticsearch 2.0+ --> - <field id="elasticsearch_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1"> - <label>Elasticsearch Server Hostname</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_server_port" translate="label" type="text" sortOrder="62" showInDefault="1"> - <label>Elasticsearch Server Port</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1"> - <label>Elasticsearch Index Prefix</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1"> - <label>Enable Elasticsearch HTTP Auth</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_username" translate="label" type="text" sortOrder="65" showInDefault="1"> - <label>Elasticsearch HTTP Username</label> - <depends> - <field id="engine">elasticsearch</field> - <field id="elasticsearch_enable_auth">1</field> - </depends> - </field> - <field id="elasticsearch_password" translate="label" type="text" sortOrder="66" showInDefault="1"> - <label>Elasticsearch HTTP Password</label> - <depends> - <field id="engine">elasticsearch</field> - <field id="elasticsearch_enable_auth">1</field> - </depends> - </field> - <field id="elasticsearch_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1"> - <label>Elasticsearch Server Timeout</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1"> - <label/> - <button_label>Test Connection</button_label> - <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection</frontend_model> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> - <label>Minimum Terms to Match</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> - <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> - </field> <!-- Elasticsearch 5.x --> <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1"> <label>Elasticsearch Server Hostname</label> diff --git a/app/code/Magento/Elasticsearch/etc/config.xml b/app/code/Magento/Elasticsearch/etc/config.xml index 9df21978b5414..1d55730fce378 100644 --- a/app/code/Magento/Elasticsearch/etc/config.xml +++ b/app/code/Magento/Elasticsearch/etc/config.xml @@ -9,12 +9,12 @@ <default> <catalog> <search> - <elasticsearch_server_hostname>localhost</elasticsearch_server_hostname> - <elasticsearch_server_port>9200</elasticsearch_server_port> - <elasticsearch_index_prefix>magento2</elasticsearch_index_prefix> - <elasticsearch_enable_auth>0</elasticsearch_enable_auth> - <elasticsearch_server_timeout>15</elasticsearch_server_timeout> - <elasticsearch_minimum_should_match></elasticsearch_minimum_should_match> +<!-- <elasticsearch_server_hostname>localhost</elasticsearch_server_hostname>--> +<!-- <elasticsearch_server_port>9200</elasticsearch_server_port>--> +<!-- <elasticsearch_index_prefix>magento2</elasticsearch_index_prefix>--> +<!-- <elasticsearch_enable_auth>0</elasticsearch_enable_auth>--> +<!-- <elasticsearch_server_timeout>15</elasticsearch_server_timeout>--> +<!-- <elasticsearch_minimum_should_match></elasticsearch_minimum_should_match>--> <elasticsearch5_server_hostname>localhost</elasticsearch5_server_hostname> <elasticsearch5_server_port>9200</elasticsearch5_server_port> diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index bb16bba127b56..fa8686b6262df 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -16,7 +16,6 @@ <type name="Magento\Elasticsearch\Model\Config"> <arguments> <argument name="engineList" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">elasticsearch</item> <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> </argument> </arguments> @@ -61,7 +60,6 @@ <arguments> <argument name="factories" xsi:type="array"> <item name="mysql" xsi:type="object">Magento\CatalogSearch\Model\ResourceModel\Fulltext\SearchCollectionFactory</item> - <item name="elasticsearch" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> </argument> </arguments> @@ -84,7 +82,6 @@ <arguments> <argument name="factories" xsi:type="array"> <item name="mysql" xsi:type="object">Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory</item> - <item name="elasticsearch" xsi:type="object">elasticsearchCategoryCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchCategoryCollectionFactory</item> </argument> </arguments> @@ -106,7 +103,6 @@ <type name="Magento\CatalogSearch\Model\Search\ItemCollectionProvider"> <arguments> <argument name="factories" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> </argument> </arguments> @@ -114,7 +110,6 @@ <type name="Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider"> <arguments> <argument name="strategies" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> </argument> </arguments> @@ -168,15 +163,13 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Elasticsearch</item> - <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.x</item> + <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+ (Deprecated)</item> </argument> </arguments> </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> <arguments> <argument name="categoryFieldsProviders" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> </argument> </arguments> @@ -184,7 +177,6 @@ <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> <arguments> <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> </argument> </arguments> @@ -192,7 +184,6 @@ <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> <arguments> <argument name="productFieldMappers" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper</item> </argument> </arguments> @@ -200,19 +191,16 @@ <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> <arguments> <argument name="clientFactories" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">\Magento\Elasticsearch\Model\Client\ElasticsearchFactory</item> <item name="elasticsearch5" xsi:type="string">\Magento\Elasticsearch\Elasticsearch5\Model\Client\ElasticsearchFactory</item> </argument> <argument name="clientOptions" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> - <item name="elasticsearch5" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> + <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Config</item> </argument> </arguments> </type> <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> <arguments> <argument name="handlers" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> </argument> </arguments> @@ -220,7 +208,6 @@ <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory"> <arguments> <argument name="structures" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> </argument> </arguments> @@ -228,7 +215,6 @@ <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> </argument> </arguments> @@ -236,7 +222,6 @@ <type name="Magento\Search\Model\AdapterFactory"> <arguments> <argument name="adapters" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Adapter</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter</item> </argument> </arguments> @@ -244,7 +229,6 @@ <type name="Magento\Search\Model\EngineResolver"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">elasticsearch</item> <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> </argument> </arguments> @@ -279,7 +263,6 @@ <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> <arguments> <argument name="clientFactories" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Client\ElasticsearchFactory</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Client\ElasticsearchFactory</item> </argument> </arguments> @@ -292,7 +275,6 @@ <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> <arguments> <argument name="intervals" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Aggregation\Interval</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval</item> </argument> </arguments> @@ -300,7 +282,6 @@ <type name="Magento\Framework\Search\Dynamic\DataProviderFactory"> <arguments> <argument name="dataProviders" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> </argument> </arguments> @@ -347,15 +328,9 @@ <argument name="cacheId" xsi:type="string">elasticsearch_index_config</argument> </arguments> </type> - <virtualType name="Magento\Elasticsearch\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> - <arguments> - <argument name="clientClass" xsi:type="string">Magento\Elasticsearch\Model\Client\Elasticsearch</argument> - </arguments> - </virtualType> <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> <arguments> <argument name="data" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\DataProvider\Suggestions</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\DataProvider\Suggestions</item> </argument> </arguments> @@ -368,22 +343,11 @@ <type name="Magento\Config\Model\Config\TypePool"> <arguments> <argument name="sensitive" xsi:type="array"> - <item name="catalog/search/elasticsearch_password" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_hostname" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_username" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch5_password" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_server_hostname" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_username" xsi:type="string">1</item> </argument> <argument name="environment" xsi:type="array"> - <item name="catalog/search/elasticsearch_enable_auth" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_index_prefix" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_password" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_hostname" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_port" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_username" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_timeout" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_enable_auth" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_index_prefix" xsi:type="string">1</item> @@ -405,6 +369,7 @@ <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> </arguments> </type> +<!-- todo check if we need this configuration--> <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> @@ -518,13 +483,6 @@ <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> </arguments> </type> - <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper"> - <arguments> - <argument name="attributeAdapterProvider" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider</argument> - <argument name="fieldProvider" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface</argument> - <argument name="fieldNameResolver" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface</argument> - </arguments> - </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\IndexResolver"> <arguments> <argument name="converter" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter</argument> @@ -535,7 +493,6 @@ <type name="Magento\Search\Model\Search\PageSizeProvider"> <arguments> <argument name="pageSizeBySearchEngine" xsi:type="array"> - <item name="elasticsearch" xsi:type="number">10000</item> <item name="elasticsearch5" xsi:type="number">10000</item> </argument> </arguments> diff --git a/app/code/Magento/Elasticsearch/etc/search_engine.xml b/app/code/Magento/Elasticsearch/etc/search_engine.xml index 51af3038b9c8d..72dd49504fe81 100644 --- a/app/code/Magento/Elasticsearch/etc/search_engine.xml +++ b/app/code/Magento/Elasticsearch/etc/search_engine.xml @@ -6,9 +6,6 @@ */ --> <engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> - <engine name="elasticsearch"> - <feature name="synonyms" support="true" /> - </engine> <engine name="elasticsearch5"> <feature name="synonyms" support="true" /> </engine> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml index 71e0401a1c30a..0929c691257e5 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -18,13 +18,14 @@ <testCaseId value="MC-6310"/> <useCaseId value="MAGETWO-91625"/> <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> </annotations> <before> <!-- Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront --> <actionGroup ref="LoginAsAdmin" stepKey="login"/> <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForChina.scope}} --scope-code={{GeneralLocalCodeConfigsForChina.scope_code}} {{GeneralLocalCodeConfigsForChina.path}} {{GeneralLocalCodeConfigsForChina.value}}" stepKey="setLocaleToChina"/> - <magentoCLI command="config:set {{EnableElasticSearch6Config.path}} {{EnableElasticSearch6Config.value}}" stepKey="enableElasticsearch6"/> - <actionGroup ref="AdminElasticConnectionTestActionGroup" stepKey="checkConnection"/> + <comment userInput="Moved to appropriate test suite" stepKey="enableElasticsearch6"/> + <comment userInput="Moved to appropriate test suite" stepKey="checkConnection"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProduct"> @@ -35,7 +36,7 @@ <after> <!-- Delete created data and reset initial configuration --> <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForUS.scope}} --scope-code={{GeneralLocalCodeConfigsForUS.scope_code}} {{GeneralLocalCodeConfigsForUS.path}} {{GeneralLocalCodeConfigsForUS.value}}" stepKey="setLocaleToUS"/> - <magentoCLI command="config:set {{SetDefaultSearchEngineConfig.path}} {{SetDefaultSearchEngineConfig.value}}" stepKey="resetSearchEnginePreviousState"/> + <comment userInput="Moved to appropriate test suite" stepKey="resetSearchEnginePreviousState"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index 3d840d5a808af..e3bcc3d219538 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -6,7 +6,7 @@ namespace Magento\Elasticsearch6\Test\Unit\Model\Client; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch6\Model\Client\Elasticsearch; @@ -593,7 +593,7 @@ public function testDeleteMappingFailure() */ public function testQuery() { - $query = 'test phrase query'; + $query = ['test phrase query']; $this->elasticsearchClientMock->expects($this->once()) ->method('search') ->with($query) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index b2411f6740fae..26cd5b1ad8305 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -7,9 +7,8 @@ "magento/module-advanced-search": "*", "magento/module-catalog-search": "*", "magento/module-search": "*", - "magento/module-store": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" + "elasticsearch/elasticsearch": "~7.6" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 69efe1a4a4f59..f7b22c05027b1 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -145,11 +145,11 @@ </arguments> </type> - <type name="Magento\Elasticsearch6\Model\DataProvider\Suggestions"> + <virtualType name="Magento\Elasticsearch6\Model\DataProvider\Suggestions" type="Magento\Elasticsearch\Model\DataProvider\Base\Suggestions"> <arguments> <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> </arguments> - </type> + </virtualType> <virtualType name="elasticsearch6FieldNameResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> diff --git a/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php new file mode 100644 index 0000000000000..e35f292778ab1 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Block\Adminhtml\System\Config; + +/** + * Elasticsearch 7.x test connection block + */ +class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection +{ + /** + * @inheritdoc + */ + protected function _getFieldMapping(): array + { + $fields = [ + 'engine' => 'catalog_search_engine', + 'hostname' => 'catalog_search_elasticsearch7_server_hostname', + 'port' => 'catalog_search_elasticsearch7_server_port', + 'index' => 'catalog_search_elasticsearch7_index_prefix', + 'enableAuth' => 'catalog_search_elasticsearch7_enable_auth', + 'username' => 'catalog_search_elasticsearch7_username', + 'password' => 'catalog_search_elasticsearch7_password', + 'timeout' => 'catalog_search_elasticsearch7_server_timeout', + ]; + + return array_merge(parent::_getFieldMapping(), $fields); + } +} diff --git a/app/code/Magento/Elasticsearch7/LICENSE.txt b/app/code/Magento/Elasticsearch7/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Elasticsearch7/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch7/LICENSE_AFL.txt b/app/code/Magento/Elasticsearch7/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php new file mode 100644 index 0000000000000..8387bcf0296ff --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver + as DefaultFiledNameResolver; + +/** + * Default name resolver for Elasticsearch 7 + */ +class DefaultResolver implements ResolverInterface +{ + /** + * @var DefaultFiledNameResolver + */ + private $baseResolver; + + /** + * DefaultResolver constructor. + * @param DefaultFiledNameResolver $baseResolver + */ + public function __construct(DefaultFiledNameResolver $baseResolver) + { + $this->baseResolver = $baseResolver; + } + + /** + * Get field name. + * + * @param AttributeAdapter $attribute + * @param array $context + * @return string|null + */ + public function getFieldName(AttributeAdapter $attribute, $context = []): ?string + { + $fieldName = $this->baseResolver->getFieldName($attribute, $context); + if ($fieldName === '_all') { + $fieldName = '_search'; + } + + return $fieldName; + } +} diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php similarity index 60% rename from app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php rename to app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php index d2b677a95c7c0..c6c85b0edc338 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php @@ -3,8 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Elasticsearch\Model\Client; +namespace Magento\Elasticsearch7\Model\Client; use Magento\Framework\Exception\LocalizedException; use Magento\AdvancedSearch\Model\Client\ClientInterface; @@ -14,6 +15,11 @@ */ class Elasticsearch implements ClientInterface { + /** + * @var array + */ + private $clientOptions; + /** * Elasticsearch Client instances * @@ -21,18 +27,13 @@ class Elasticsearch implements ClientInterface */ private $client; - /** - * @var array - */ - private $clientOptions; - /** * @var bool */ private $pingResult; /** - * Initialize Elasticsearch Client + * Initialize Elasticsearch 7 Client * * @param array $options * @param \Elasticsearch\Client|null $elasticsearchClient @@ -43,65 +44,76 @@ public function __construct( $elasticsearchClient = null ) { if (empty($options['hostname']) || ((!empty($options['enableAuth']) && - ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { + ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { throw new LocalizedException( __('The search failed because of a search engine misconfiguration.') ); } - - if (!($elasticsearchClient instanceof \Elasticsearch\Client)) { - $config = $this->buildConfig($options); - $elasticsearchClient = \Elasticsearch\ClientBuilder::fromConfig($config, true); + // phpstan:ignore + if ($elasticsearchClient instanceof \Elasticsearch\Client) { + $this->client[getmypid()] = $elasticsearchClient; } - $this->client[getmypid()] = $elasticsearchClient; $this->clientOptions = $options; } /** - * Get Elasticsearch Client + * Execute suggest query for Elasticsearch 7 + * + * @param array $query + * @return array + */ + public function suggest(array $query) : array + { + return $this->getElasticsearchClient()->suggest($query); + } + + /** + * Get Elasticsearch 7 Client * * @return \Elasticsearch\Client */ - private function getClient() + private function getElasticsearchClient(): \Elasticsearch\Client { $pid = getmypid(); if (!isset($this->client[$pid])) { - $config = $this->buildConfig($this->clientOptions); + $config = $this->buildESConfig($this->clientOptions); $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true); } return $this->client[$pid]; } /** - * Ping the Elasticsearch client + * Ping the Elasticsearch 7 client * * @return bool */ - public function ping() + public function ping() : bool { if ($this->pingResult === null) { - $this->pingResult = $this->getClient()->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); + $this->pingResult = $this->getElasticsearchClient() + ->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); } + return $this->pingResult; } /** - * Validate connection params + * Validate connection params for Elasticsearch 7 * * @return bool */ - public function testConnection() + public function testConnection() : bool { return $this->ping(); } /** - * Build config. + * Build config for Elasticsearch 7 * * @param array $options * @return array */ - private function buildConfig($options = []) + private function buildESConfig(array $options = []) : array { $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart @@ -129,26 +141,26 @@ private function buildConfig($options = []) } /** - * Performs bulk query over Elasticsearch index + * Performs bulk query over Elasticsearch 7 index * * @param array $query * @return void */ - public function bulkQuery($query) + public function bulkQuery(array $query) { - $this->getClient()->bulk($query); + $this->getElasticsearchClient()->bulk($query); } /** - * Creates an Elasticsearch index. + * Creates an Elasticsearch 7 index. * * @param string $index * @param array $settings * @return void */ - public function createIndex($index, $settings) + public function createIndex(string $index, array $settings) { - $this->getClient()->indices()->create( + $this->getElasticsearchClient()->indices()->create( [ 'index' => $index, 'body' => $settings, @@ -157,14 +169,14 @@ public function createIndex($index, $settings) } /** - * Delete an Elasticsearch index. + * Delete an Elasticsearch 7 index. * * @param string $index * @return void */ - public function deleteIndex($index) + public function deleteIndex(string $index) { - $this->getClient()->indices()->delete(['index' => $index]); + $this->getElasticsearchClient()->indices()->delete(['index' => $index]); } /** @@ -173,10 +185,10 @@ public function deleteIndex($index) * @param string $index * @return bool */ - public function isEmptyIndex($index) + public function isEmptyIndex(string $index) : bool { - $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); - if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) { + $stats = $this->getElasticsearchClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); + if ($stats['indices'][$index]['primaries']['docs']['count'] === 0) { return true; } return false; @@ -190,9 +202,13 @@ public function isEmptyIndex($index) * @param string $oldIndex * @return void */ - public function updateAlias($alias, $newIndex, $oldIndex = '') + public function updateAlias(string $alias, string $newIndex, string $oldIndex = '') { - $params['body'] = ['actions' => []]; + $params = [ + 'body' => [ + 'actions' => [] + ] + ]; if ($oldIndex) { $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]]; } @@ -200,34 +216,34 @@ public function updateAlias($alias, $newIndex, $oldIndex = '') $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; } - $this->getClient()->indices()->updateAliases($params); + $this->getElasticsearchClient()->indices()->updateAliases($params); } /** - * Checks whether Elasticsearch index exists + * Checks whether Elasticsearch 7 index exists * * @param string $index * @return bool */ - public function indexExists($index) + public function indexExists(string $index) : bool { - return $this->getClient()->indices()->exists(['index' => $index]); + return $this->getElasticsearchClient()->indices()->exists(['index' => $index]); } /** - * Check if alias exists. + * Exists alias. * * @param string $alias * @param string $index * @return bool */ - public function existsAlias($alias, $index = '') + public function existsAlias(string $alias, string $index = '') : bool { $params = ['name' => $alias]; if ($index) { $params['index'] = $index; } - return $this->getClient()->indices()->existsAlias($params); + return $this->getElasticsearchClient()->indices()->existsAlias($params); } /** @@ -236,87 +252,74 @@ public function existsAlias($alias, $index = '') * @param string $alias * @return array */ - public function getAlias($alias) + public function getAlias(string $alias) : array { - return $this->getClient()->indices()->getAlias(['name' => $alias]); + return $this->getElasticsearchClient()->indices()->getAlias(['name' => $alias]); } /** - * Add mapping to Elasticsearch index + * Add mapping to Elasticsearch 7 index * * @param array $fields * @param string $index * @param string $entityType * @return void */ - public function addFieldsMapping(array $fields, $index, $entityType) + public function addFieldsMapping(array $fields, string $index, string $entityType) { $params = [ 'index' => $index, 'type' => $entityType, + 'include_type_name' => true, 'body' => [ $entityType => [ - '_all' => [ - 'enabled' => true, - 'type' => 'string' + 'properties' => [ + '_search' => [ + 'type' => 'text' + ], ], - 'properties' => [], 'dynamic_templates' => [ [ 'price_mapping' => [ 'match' => 'price_*', - 'match_mapping' => 'string', + 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'float', - 'store' => true + 'store' => true, ], ], ], [ 'position_mapping' => [ 'match' => 'position_*', - 'match_mapping' => 'string', + 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => 'not_analyzed', + 'index' => true, ], ], ], [ 'string_mapping' => [ 'match' => '*', - 'match_mapping' => 'string', + 'match_mapping_type' => 'string', 'mapping' => [ - 'type' => 'string', - 'index' => 'not_analyzed', + 'type' => 'text', + 'index' => true, + 'copy_to' => '_search' ], ], - ] + ], ], ], ], ]; + foreach ($fields as $field => $fieldInfo) { $params['body'][$entityType]['properties'][$field] = $fieldInfo; } - $this->getClient()->indices()->putMapping($params); - } - /** - * Delete mapping in Elasticsearch index - * - * @param string $index - * @param string $entityType - * @return void - */ - public function deleteMapping($index, $entityType) - { - $this->getClient()->indices()->deleteMapping( - [ - 'index' => $index, - 'type' => $entityType, - ] - ); + $this->getElasticsearchClient()->indices()->putMapping($params); } /** @@ -325,19 +328,25 @@ public function deleteMapping($index, $entityType) * @param array $query * @return array */ - public function query($query) + public function query(array $query) : array { - return $this->getClient()->search($query); + return $this->getElasticsearchClient()->search($query); } /** - * Execute suggest query + * Delete mapping in Elasticsearch 7 index * - * @param array $query - * @return array + * @param string $index + * @param string $entityType + * @return void */ - public function suggest($query) + public function deleteMapping(string $index, string $entityType) { - return $this->getClient()->suggest($query); + $this->getElasticsearchClient()->indices()->deleteMapping( + [ + 'index' => $index, + 'type' => $entityType, + ] + ); } } diff --git a/app/code/Magento/Elasticsearch7/README.md b/app/code/Magento/Elasticsearch7/README.md new file mode 100644 index 0000000000000..f8331665360c5 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/README.md @@ -0,0 +1,2 @@ +Magento\Elasticsearch7 module allows to use Elastic search engine (v7) for product searching capabilities. +The module implements Magento\Search library interfaces. diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php b/app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php similarity index 53% rename from app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php rename to app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php index 6f9ef552351fd..bbc7985f4519d 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php +++ b/app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php @@ -3,13 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch\SearchAdapter; +declare(strict_types=1); + +namespace Magento\Elasticsearch7\SearchAdapter; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Search\AdapterInterface; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Search\Response\QueryResponse; use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch\SearchAdapter\ResponseFactory; +use Psr\Log\LoggerInterface; +use Magento\Framework\Search\AdapterInterface; +use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; /** * Elasticsearch Search Adapter @@ -21,71 +26,107 @@ class Adapter implements AdapterInterface * * @var Mapper */ - protected $mapper; + private $mapper; /** * Response Factory * * @var ResponseFactory */ - protected $responseFactory; + private $responseFactory; /** * @var ConnectionManager */ - protected $connectionManager; + private $connectionManager; /** * @var AggregationBuilder */ - protected $aggregationBuilder; + private $aggregationBuilder; /** * @var QueryContainerFactory */ private $queryContainerFactory; + /** + * Empty response from Elasticsearch + * + * @var array + */ + private static $emptyRawResponse = [ + "hits" => + [ + "hits" => [] + ], + "aggregations" => + [ + "price_bucket" => [], + "category_bucket" => + [ + "buckets" => [] + + ] + ] + ]; + + /** + * @var LoggerInterface + */ + private $logger; + /** * @param ConnectionManager $connectionManager * @param Mapper $mapper * @param ResponseFactory $responseFactory * @param AggregationBuilder $aggregationBuilder * @param QueryContainerFactory $queryContainerFactory + * @param LoggerInterface $logger */ public function __construct( ConnectionManager $connectionManager, Mapper $mapper, ResponseFactory $responseFactory, AggregationBuilder $aggregationBuilder, - QueryContainerFactory $queryContainerFactory = null + QueryContainerFactory $queryContainerFactory, + LoggerInterface $logger ) { $this->connectionManager = $connectionManager; $this->mapper = $mapper; $this->responseFactory = $responseFactory; $this->aggregationBuilder = $aggregationBuilder; - $this->queryContainerFactory = $queryContainerFactory - ?: ObjectManager::getInstance()->get(QueryContainerFactory::class); + $this->queryContainerFactory = $queryContainerFactory; + $this->logger = $logger; } /** - * @inheritdoc + * Search query + * + * @param RequestInterface $request + * @return QueryResponse */ - public function query(RequestInterface $request) + public function query(RequestInterface $request) : QueryResponse { $client = $this->connectionManager->getConnection(); $aggregationBuilder = $this->aggregationBuilder; - $query = $this->mapper->buildQuery($request); $aggregationBuilder->setQuery($this->queryContainerFactory->create(['query' => $query])); - $rawResponse = $client->query($query); - $rawDocuments = isset($rawResponse['hits']['hits']) ? $rawResponse['hits']['hits'] : []; + try { + $rawResponse = $client->query($query); + } catch (\Exception $e) { + $this->logger->critical($e); + // return empty search result in case an exception is thrown from Elasticsearch + $rawResponse = self::$emptyRawResponse; + } + $rawDocuments = $rawResponse['hits']['hits'] ?? []; $queryResponse = $this->responseFactory->create( [ 'documents' => $rawDocuments, 'aggregations' => $aggregationBuilder->build($request, $rawResponse), - 'total' => isset($rawResponse['hits']['total']) ? $rawResponse['hits']['total'] : 0 + 'total' => $rawResponse['hits']['total']['value'] ?? 0 ] ); return $queryResponse; diff --git a/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php b/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php new file mode 100644 index 0000000000000..a47d9b6b19cca --- /dev/null +++ b/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Elasticsearch7\SearchAdapter; + +use Magento\Framework\Search\RequestInterface; + +/** + * Elasticsearch7 mapper class + */ +class Mapper +{ + /** + * @var \Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper + */ + private $mapper; + + /** + * Mapper constructor. + * @param \Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $mapper + */ + public function __construct(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $mapper) + { + $this->mapper = $mapper; + } + + /** + * Build adapter dependent query + * + * @param RequestInterface $request + * @return array + */ + public function buildQuery(RequestInterface $request) : array + { + $searchQuery = $this->mapper->buildQuery($request); + $searchQuery['track_total_hits'] = true; + return $searchQuery; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php similarity index 93% rename from app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php rename to app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php index 5a735da96b754..cb07cfb7bf83d 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php @@ -4,43 +4,45 @@ * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Client; +declare(strict_types=1); -use Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +namespace Magento\Elasticsearch7\Test\Unit\Model\Client; + +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch7\Model\Client\Elasticsearch; /** - * Test elasticsearch client methods. + * Class ElasticsearchTest to test Elasticsearch 7 */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { /** * @var ElasticsearchClient */ - protected $model; + private $model; /** * @var \Elasticsearch\Client|\PHPUnit_Framework_MockObject_MockObject */ - protected $elasticsearchClientMock; + private $elasticsearchClientMock; /** * @var \Elasticsearch\Namespaces\IndicesNamespace|\PHPUnit_Framework_MockObject_MockObject */ - protected $indicesMock; + private $indicesMock; /** * @var ObjectManagerHelper */ - protected $objectManager; + private $objectManager; /** * Setup * * @return void */ - protected function setUp(): void + protected function setUp() { $this->elasticsearchClientMock = $this->getMockBuilder(\Elasticsearch\Client::class) ->setMethods( @@ -81,11 +83,11 @@ protected function setUp(): void ->willReturn(true); $this->elasticsearchClientMock->expects($this->any()) ->method('info') - ->willReturn(['version' => ['number' => '5.0.0']]); + ->willReturn(['version' => ['number' => '7.0.0']]); $this->objectManager = new ObjectManagerHelper($this); $this->model = $this->objectManager->getObject( - \Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class, + Elasticsearch::class, [ 'options' => $this->getOptions(), 'elasticsearchClient' => $this->elasticsearchClientMock @@ -99,7 +101,7 @@ protected function setUp(): void public function testConstructorOptionsException() { $result = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, + Elasticsearch::class, [ 'options' => [] ] @@ -113,7 +115,7 @@ public function testConstructorOptionsException() public function testConstructorWithOptions() { $result = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, + \Magento\Elasticsearch7\Model\Client\Elasticsearch::class, [ 'options' => $this->getOptions() ] @@ -121,6 +123,69 @@ public function testConstructorWithOptions() $this->assertNotNull($result); } + /** + * Ensure that configuration returns correct url. + * + * @param array $options + * @param string $expectedResult + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + * @dataProvider getOptionsDataProvider + */ + public function testBuildConfig(array $options, $expectedResult): void + { + $buildConfig = new Elasticsearch($options); + $config = $this->getPrivateMethod(Elasticsearch::class, 'buildESConfig'); + $result = $config->invoke($buildConfig, $options); + $this->assertEquals($expectedResult, $result['hosts'][0]); + } + + /** + * Return private method for elastic search class. + * + * @param $className + * @param $methodName + * @return \ReflectionMethod + * @throws \ReflectionException + */ + private function getPrivateMethod($className, $methodName) + { + $reflector = new \ReflectionClass($className); + $method = $reflector->getMethod($methodName); + $method->setAccessible(true); + + return $method; + } + + /** + * Get options data provider. + */ + public function getOptionsDataProvider() + { + return [ + [ + 'without_protocol' => [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'http://localhost:9200' + ], + [ + 'with_protocol' => [ + 'hostname' => 'https://localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'https://localhost:9200' + ] + ]; + } + /** * Test ping functionality */ @@ -154,7 +219,7 @@ public function testTestConnectionFalse() public function testTestConnectionPing() { $this->model = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, + \Magento\Elasticsearch7\Model\Client\Elasticsearch::class, [ 'options' => $this->getEmptyIndexOption(), 'elasticsearchClient' => $this->elasticsearchClientMock @@ -351,13 +416,13 @@ public function testAddFieldsMapping() [ 'index' => 'indexName', 'type' => 'product', + 'include_type_name' => true, 'body' => [ 'product' => [ - '_all' => [ - 'enabled' => true, - 'type' => 'text', - ], 'properties' => [ + '_search' => [ + 'type' => 'text', + ], 'name' => [ 'type' => 'text', ], @@ -379,7 +444,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => true + 'index' => true, ], ], ], @@ -390,9 +455,10 @@ public function testAddFieldsMapping() 'mapping' => [ 'type' => 'text', 'index' => true, + 'copy_to' => '_search' ], ], - ], + ] ], ], ], @@ -421,13 +487,13 @@ public function testAddFieldsMappingFailure() [ 'index' => 'indexName', 'type' => 'product', + 'include_type_name' => true, 'body' => [ 'product' => [ - '_all' => [ - 'enabled' => true, - 'type' => 'text', - ], 'properties' => [ + '_search' => [ + 'type' => 'text', + ], 'name' => [ 'type' => 'text', ], @@ -460,6 +526,7 @@ public function testAddFieldsMappingFailure() 'mapping' => [ 'type' => 'text', 'index' => true, + 'copy_to' => '_search' ], ], ] @@ -499,40 +566,6 @@ public function testDeleteMapping() ); } - /** - * Ensure that configuration returns correct url. - * - * @param array $options - * @param string $expectedResult - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \ReflectionException - * @dataProvider getOptionsDataProvider - */ - public function testBuildConfig(array $options, $expectedResult): void - { - $buildConfig = new Elasticsearch($options); - $config = $this->getPrivateMethod(Elasticsearch::class, 'buildConfig'); - $result = $config->invoke($buildConfig, $options); - $this->assertEquals($expectedResult, $result['hosts'][0]); - } - - /** - * Return private method for elastic search class. - * - * @param $className - * @param $methodName - * @return \ReflectionMethod - * @throws \ReflectionException - */ - private function getPrivateMethod($className, $methodName) - { - $reflector = new \ReflectionClass($className); - $method = $reflector->getMethod($methodName); - $method->setAccessible(true); - - return $method; - } - /** * Test deleteMapping() method * @expectedException \Exception @@ -560,7 +593,7 @@ public function testDeleteMappingFailure() */ public function testQuery() { - $query = 'test phrase query'; + $query = ['test phrase query']; $this->elasticsearchClientMock->expects($this->once()) ->method('search') ->with($query) @@ -574,42 +607,13 @@ public function testQuery() */ public function testSuggest() { - $query = 'query'; + $query = ['query']; $this->elasticsearchClientMock->expects($this->once()) ->method('suggest') ->willReturn([]); $this->assertEquals([], $this->model->suggest($query)); } - /** - * Get options data provider. - */ - public function getOptionsDataProvider() - { - return [ - [ - 'without_protocol' => [ - 'hostname' => 'localhost', - 'port' => '9200', - 'timeout' => 15, - 'index' => 'magento2', - 'enableAuth' => 0, - ], - 'expected_result' => 'http://localhost:9200' - ], - [ - 'with_protocol' => [ - 'hostname' => 'https://localhost', - 'port' => '9200', - 'timeout' => 15, - 'index' => 'magento2', - 'enableAuth' => 0, - ], - 'expected_result' => 'https://localhost:9200' - ] - ]; - } - /** * Get elasticsearch client options * @@ -631,7 +635,7 @@ protected function getOptions() /** * @return array */ - protected function getEmptyIndexOption() + private function getEmptyIndexOption() { return [ 'hostname' => 'localhost', diff --git a/app/code/Magento/Elasticsearch7/composer.json b/app/code/Magento/Elasticsearch7/composer.json new file mode 100644 index 0000000000000..c6af833231be1 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/composer.json @@ -0,0 +1,29 @@ +{ + "name": "magento/module-elasticsearch-7", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-advanced-search": "*", + "magento/module-catalog-search": "*", + "magento/module-search": "*", + "magento/module-elasticsearch": "*", + "elasticsearch/elasticsearch": "~7.6" + }, + "suggest": { + "magento/module-config": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Elasticsearch7\\": "" + } + } +} diff --git a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..cb7cdc2a5b531 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="catalog"> + <group id="search"> + <!-- Elasticsearch 7.0+ --> + <field id="elasticsearch7_server_hostname" translate="label" type="text" sortOrder="81" + showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Hostname</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_server_port" translate="label" type="text" sortOrder="82" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Port</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_index_prefix" translate="label" type="text" sortOrder="83" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Index Prefix</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_enable_auth" translate="label" type="select" sortOrder="84" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Enable Elasticsearch HTTP Auth</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_username" translate="label" type="text" sortOrder="85" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Username</label> + <depends> + <field id="engine">elasticsearch7</field> + <field id="elasticsearch7_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch7_password" translate="label" type="text" sortOrder="86" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Password</label> + <depends> + <field id="engine">elasticsearch7</field> + <field id="elasticsearch7_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch7_server_timeout" translate="label" type="text" sortOrder="87" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Timeout</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_test_connect_wizard" translate="button_label" sortOrder="88" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch7\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/config.xml b/app/code/Magento/Elasticsearch7/etc/config.xml new file mode 100644 index 0000000000000..63d832c8445f5 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/config.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <catalog> + <search> + <elasticsearch7_server_hostname>localhost</elasticsearch7_server_hostname> + <elasticsearch7_server_port>9200</elasticsearch7_server_port> + <elasticsearch7_index_prefix>magento2</elasticsearch7_index_prefix> + <elasticsearch7_enable_auth>0</elasticsearch7_enable_auth> + <elasticsearch7_server_timeout>15</elasticsearch7_server_timeout> + </search> + </catalog> + </default> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/di.xml b/app/code/Magento/Elasticsearch7/etc/di.xml new file mode 100644 index 0000000000000..1e480894bc630 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/di.xml @@ -0,0 +1,222 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">elasticsearch7</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Elasticsearch 7.0+</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> + <arguments> + <argument name="categoryFieldsProviders" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> + <arguments> + <argument name="dataMappers" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> + <arguments> + <argument name="productFieldMappers" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch7\Model\Adapter\FieldMapper\ProductFieldMapper</item> + </argument> + </arguments> + </type> + + <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch7\Model\Client\ElasticsearchFactory</item> + </argument> + <argument name="clientOptions" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> + <arguments> + <argument name="handlers" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory"> + <arguments> + <argument name="structures" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\AdapterFactory"> + <arguments> + <argument name="adapters" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch7\SearchAdapter\Adapter</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\EngineResolver"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">elasticsearch7</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch7\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> + <arguments> + <argument name="clientClass" xsi:type="string">Magento\Elasticsearch7\Model\Client\Elasticsearch</argument> + </arguments> + </virtualType> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch7\Model\Client\ElasticsearchFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> + <arguments> + <argument name="intervals" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\DataProviderFactory"> + <arguments> + <argument name="dataProviders" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch7\Model\DataProvider\Suggestions" type="Magento\Elasticsearch\Model\DataProvider\Base\Suggestions"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + </arguments> + </virtualType> + <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> + <arguments> + <argument name="data" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch7\Model\DataProvider\Suggestions</item> + </argument> + </arguments> + </type> +<!--todo rename!!!--> + <virtualType name="\Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> + <arguments> + <argument name="items" xsi:type="array"> + <item name="notEav" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\NotEavAttribute</item> + <item name="special" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\SpecialAttribute</item> + <item name="price" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\Price</item> + <item name="categoryName" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CategoryName</item> + <item name="position" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\Position</item> + <item name="default" xsi:type="object">Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</item> + </argument> + </arguments> + </virtualType> + + <virtualType name="Magento\Elasticsearch7\Model\Adapter\FieldMapper\ProductFieldMapper" + type="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + <argument name="fieldNameResolver" xsi:type="object">\Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver</argument> + </arguments> + </virtualType> + + <type name="Magento\Search\Model\Search\PageSizeProvider"> + <arguments> + <argument name="pageSizeBySearchEngine" xsi:type="array"> + <item name="elasticsearch7" xsi:type="number">10000</item> + </argument> + </arguments> + </type> + + <virtualType name="elasticsearchLayerCategoryItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Category\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchCategoryCollectionFactory</item> + </argument> + </arguments> + </virtualType> + + <type name="Magento\CatalogSearch\Model\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider"> + <arguments> + <argument name="strategies" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> + </argument> + </arguments> + </type> + + <virtualType name="elasticsearchLayerSearchItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> + </argument> + </arguments> + </virtualType> + + <type name="Magento\Config\Model\Config\TypePool"> + <arguments> + <argument name="sensitive" xsi:type="array"> + <item name="catalog/search/elasticsearch7_password" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_hostname" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_username" xsi:type="string">1</item> + </argument> + <argument name="environment" xsi:type="array"> + <item name="catalog/search/elasticsearch7_enable_auth" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_index_prefix" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_password" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_hostname" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_port" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_username" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_timeout" xsi:type="string">1</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/module.xml b/app/code/Magento/Elasticsearch7/etc/module.xml new file mode 100644 index 0000000000000..c5ad0d70cd7d1 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/module.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_Elasticsearch7"> + <sequence> + <module name="Magento_CatalogSearch"/> + <module name="Magento_Search"/> + <module name="Magento_AdvancedSearch"/> + <module name="Magento_Elasticsearch"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/search_engine.xml b/app/code/Magento/Elasticsearch7/etc/search_engine.xml new file mode 100644 index 0000000000000..9633d18669141 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/search_engine.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="elasticsearch7"> + <feature name="synonyms" support="true" /> + </engine> +</engines> diff --git a/app/code/Magento/Elasticsearch7/registration.php b/app/code/Magento/Elasticsearch7/registration.php new file mode 100644 index 0000000000000..63e13cfbed8f0 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_Elasticsearch7', + __DIR__ +); diff --git a/composer.json b/composer.json index ac005f9da6a1e..c3e799e138675 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", + "elasticsearch/elasticsearch": "~7.6", "guzzlehttp/guzzle": "^6.3.3", "league/flysystem": "^1.0", "league/flysystem-aws-s3-v3": "^1.0", @@ -167,6 +167,7 @@ "magento/module-eav": "*", "magento/module-elasticsearch": "*", "magento/module-elasticsearch-6": "*", + "magento/module-elasticsearch-7": "*", "magento/module-email": "*", "magento/module-encryption-key": "*", "magento/module-fedex": "*", diff --git a/composer.lock b/composer.lock index 1236d2b0ae82f..0c5b414eba0b7 100644 --- a/composer.lock +++ b/composer.lock @@ -1,23 +1,23 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "522d676db5baf5864a824409c54948fc", + "content-hash": "637866c2490d86265bb99d3e159d3771", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.133.24", + "version": "3.133.29", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105" + "reference": "ec8f628041e0d1de4fad76ac041c6025bd38d2da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/726426e1514be5220d55ecf02eb1f938a3b4a105", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/ec8f628041e0d1de4fad76ac041c6025bd38d2da", + "reference": "ec8f628041e0d1de4fad76ac041c6025bd38d2da", "shasum": "" }, "require": { @@ -88,7 +88,7 @@ "s3", "sdk" ], - "time": "2020-02-27T19:13:45+00:00" + "time": "2020-03-04T19:10:59+00:00" }, { "name": "braintree/braintree_php", @@ -542,16 +542,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "cbe23383749496fe0f373345208b79568e4bc248" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", - "reference": "cbe23383749496fe0f373345208b79568e4bc248", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { @@ -582,7 +582,7 @@ "Xdebug", "performance" ], - "time": "2019-11-06T16:40:04+00:00" + "time": "2020-03-01T12:26:26+00:00" }, { "name": "container-interop/container-interop", @@ -618,33 +618,33 @@ }, { "name": "elasticsearch/elasticsearch", - "version": "v6.7.2", + "version": "v7.6.1", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "9ba89f905ebf699e72dacffa410331c7fecc8255" + "reference": "d4f24bc43c2af60aece3df20eb689d322f9c8acf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/9ba89f905ebf699e72dacffa410331c7fecc8255", - "reference": "9ba89f905ebf699e72dacffa410331c7fecc8255", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/d4f24bc43c2af60aece3df20eb689d322f9c8acf", + "reference": "d4f24bc43c2af60aece3df20eb689d322f9c8acf", "shasum": "" }, "require": { "ext-json": ">=1.3.7", - "guzzlehttp/ringphp": "~1.0", - "php": "^7.0", + "ezimuel/ringphp": "^1.1.2", + "php": "^7.1", "psr/log": "~1.0" }, "require-dev": { - "cpliakas/git-wrapper": "^1.7 || ^2.1", - "doctrine/inflector": "^1.1", + "cpliakas/git-wrapper": "~2.0", + "doctrine/inflector": "^1.3", "mockery/mockery": "^1.2", - "phpstan/phpstan-shim": "^0.9 || ^0.11", - "phpunit/phpunit": "^5.7 || ^6.5", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^7.5", "squizlabs/php_codesniffer": "^3.4", - "symfony/finder": "^2.8", - "symfony/yaml": "^2.8" + "symfony/finder": "~4.0", + "symfony/yaml": "~4.0" }, "suggest": { "ext-curl": "*", @@ -652,6 +652,9 @@ }, "type": "library", "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { "Elasticsearch\\": "src/Elasticsearch/" } @@ -674,7 +677,108 @@ "elasticsearch", "search" ], - "time": "2019-07-19T14:48:24+00:00" + "time": "2020-02-15T00:09:00+00:00" + }, + { + "name": "ezimuel/guzzlestreams", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/ezimuel/guzzlestreams.git", + "reference": "abe3791d231167f14eb80d413420d1eab91163a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezimuel/guzzlestreams/zipball/abe3791d231167f14eb80d413420d1eab91163a8", + "reference": "abe3791d231167f14eb80d413420d1eab91163a8", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Fork of guzzle/streams (abandoned) to be used with elasticsearch-php", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2020-02-14T23:11:50+00:00" + }, + { + "name": "ezimuel/ringphp", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/ezimuel/ringphp.git", + "reference": "0b78f89d8e0bb9e380046c31adfa40347e9f663b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezimuel/ringphp/zipball/0b78f89d8e0bb9e380046c31adfa40347e9f663b", + "reference": "0b78f89d8e0bb9e380046c31adfa40347e9f663b", + "shasum": "" + }, + "require": { + "ezimuel/guzzlestreams": "^3.0.1", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Fork of guzzle/RingPHP (abandoned) to be used with elasticsearch-php", + "time": "2020-02-14T23:51:21+00:00" }, { "name": "guzzlehttp/guzzle", @@ -865,109 +969,6 @@ ], "time": "2019-07-01T23:21:34+00:00" }, - { - "name": "guzzlehttp/ringphp", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b", - "shasum": "" - }, - "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "abandoned": true, - "time": "2018-07-31T13:22:33+00:00" - }, - { - "name": "guzzlehttp/streams", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "stream" - ], - "abandoned": true, - "time": "2014-10-12T19:18:40+00:00" - }, { "name": "justinrainbow/json-schema", "version": "5.2.9", @@ -2415,16 +2416,16 @@ }, { "name": "symfony/console", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656" + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f512001679f37e6a042b51897ed24a2f05eba656", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656", + "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", "shasum": "" }, "require": { @@ -2487,20 +2488,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2020-01-25T12:44:29+00:00" + "time": "2020-02-24T13:10:00+00:00" }, { "name": "symfony/css-selector", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465" + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a167b1860995b926d279f9bb538f873e3bfa3465", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", "shasum": "" }, "require": { @@ -2540,20 +2541,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-04T09:01:01+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b" + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9e3de195e5bc301704dd6915df55892f6dfc208b", - "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d", + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d", "shasum": "" }, "require": { @@ -2610,7 +2611,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2020-01-10T21:54:01+00:00" + "time": "2020-02-04T09:32:40+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2672,7 +2673,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2722,16 +2723,16 @@ }, { "name": "symfony/finder", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3a50be43515590faf812fbd7708200aabc327ec3" + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3a50be43515590faf812fbd7708200aabc327ec3", - "reference": "3a50be43515590faf812fbd7708200aabc327ec3", + "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357", + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357", "shasum": "" }, "require": { @@ -2767,7 +2768,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-14T07:42:58+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2946,16 +2947,16 @@ }, { "name": "symfony/process", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36" + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f5697ab4cb14a5deed7473819e63141bf5352c36", - "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36", + "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7", + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7", "shasum": "" }, "require": { @@ -2991,7 +2992,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2020-01-09T09:50:08+00:00" + "time": "2020-02-07T20:06:44+00:00" }, { "name": "symfony/service-contracts", @@ -7912,16 +7913,16 @@ }, { "name": "php-webdriver/webdriver", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "262ea0d209c292e0330be1041424887bbbffef04" + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/262ea0d209c292e0330be1041424887bbbffef04", - "reference": "262ea0d209c292e0330be1041424887bbbffef04", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", "shasum": "" }, "require": { @@ -7973,7 +7974,7 @@ "selenium", "webdriver" ], - "time": "2020-02-17T08:14:38+00:00" + "time": "2020-03-04T14:40:12+00:00" }, { "name": "phpcollection/phpcollection", @@ -8188,26 +8189,25 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", "shasum": "" }, "require": { - "php": "^7.1", + "php": "^7.2", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" }, "type": "library", "extra": { @@ -8231,7 +8231,7 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" + "time": "2020-02-18T18:59:58+00:00" }, { "name": "phpmd/phpmd", @@ -8358,16 +8358,16 @@ }, { "name": "phpspec/prophecy", - "version": "v1.10.2", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { @@ -8417,20 +8417,20 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.11", + "version": "0.12.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b" + "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ca5f2b7cf81c6d8fba74f9576970399c5817e03b", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37bdd26a80235d0f9045b49f4151102b7831cbe2", + "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2", "shasum": "" }, "require": { @@ -8456,7 +8456,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-02-16T14:00:29+00:00" + "time": "2020-03-02T22:29:43+00:00" }, { "name": "phpunit/php-code-coverage", @@ -9650,16 +9650,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "45cae6dd8683d2de56df7ec23638e9429c70135f" + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/45cae6dd8683d2de56df7ec23638e9429c70135f", - "reference": "45cae6dd8683d2de56df7ec23638e9429c70135f", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/090ce406505149d6852a7c03b0346dec3b8cf612", + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612", "shasum": "" }, "require": { @@ -9705,20 +9705,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-23T10:00:59+00:00" }, { "name": "symfony/config", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "4d3979f54472637169080f802dc82197e21fdcce" + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4d3979f54472637169080f802dc82197e21fdcce", - "reference": "4d3979f54472637169080f802dc82197e21fdcce", + "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", "shasum": "" }, "require": { @@ -9769,20 +9769,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-04T09:32:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a" + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ec60a7d12f5e8ab0f99456adce724717d9c1784a", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342", + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342", "shasum": "" }, "require": { @@ -9842,20 +9842,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:49:27+00:00" + "time": "2020-02-29T09:50:10+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "b66fe8ccc850ea11c4cd31677706c1219768bea1" + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b66fe8ccc850ea11c4cd31677706c1219768bea1", - "reference": "b66fe8ccc850ea11c4cd31677706c1219768bea1", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/11dcf08f12f29981bf770f097a5d64d65bce5929", + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929", "shasum": "" }, "require": { @@ -9903,20 +9903,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-29T10:05:28+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5" + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/491a20dfa87e0b3990170593bc2de0bb34d828a5", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648", + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648", "shasum": "" }, "require": { @@ -9958,20 +9958,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:11:17+00:00" + "time": "2020-02-13T19:40:01+00:00" }, { "name": "symfony/mime", - "version": "v5.0.4", + "version": "v5.0.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59" + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/2a3c7fee1f1a0961fa9cf360d5da553d05095e59", - "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59", + "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", "shasum": "" }, "require": { @@ -10020,11 +10020,11 @@ "mime", "mime-type" ], - "time": "2020-01-04T14:08:26+00:00" + "time": "2020-02-04T09:41:09+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -10254,7 +10254,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -10304,16 +10304,16 @@ }, { "name": "symfony/yaml", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "cd014e425b3668220adb865f53bff64b3ad21767" + "reference": "94d005c176db2080e98825d98e01e8b311a97a88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/cd014e425b3668220adb865f53bff64b3ad21767", - "reference": "cd014e425b3668220adb865f53bff64b3ad21767", + "url": "https://api.github.com/repos/symfony/yaml/zipball/94d005c176db2080e98825d98e01e8b311a97a88", + "reference": "94d005c176db2080e98825d98e01e8b311a97a88", "shasum": "" }, "require": { @@ -10359,7 +10359,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2020-01-21T11:12:16+00:00" + "time": "2020-02-03T10:46:43+00:00" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php new file mode 100644 index 0000000000000..5e006a0aa1197 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestModuleCatalogSearch\Model; + +use Magento\TestFramework\Helper\Curl; + +/** + * Retrieve elasticsearch version by curl request + */ +class ElasticsearchVersionChecker +{ + /** + * @var int + */ + private $version; + + /** + * @return int + */ + public function getVersion() : int + { + if (!$this->version) { + $curl = new Curl(); + $url = 'http://localhost:9200'; + $curl->get($url); + $curl->addHeader('content-type', 'application/json'); + $data = $curl->getBody(); + $versionData = explode('.', json_decode($data, true)['version']['number']); + $this->version = (int)array_shift($versionData); + } + + return $this->version; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php index d3c7972453a4b..7bebf9c49d14b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php @@ -85,6 +85,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProvider * @param string $sortBy * @param string $direction @@ -100,6 +101,7 @@ public function testProductListSortOrder(string $sortBy, string $direction, arra /** * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProvider * @param string $sortBy * @param string $direction @@ -172,6 +174,7 @@ public function productListSortOrderDataProvider(): array /** * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProviderOnStoreView * @param string $sortBy * @param string $direction @@ -195,6 +198,7 @@ public function testProductListSortOrderOnStoreView( /** * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProviderOnStoreView * @param string $sortBy * @param string $direction diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php index 32b7df03f922d..fc24658bb01fa 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php @@ -135,8 +135,8 @@ public function searchStringDataProvider(): array 'description' => '', 'short_description' => '', 'price' => [ - 'from' => '50', - 'to' => '150', + 'from' => 50, + 'to' => 150, ], 'test_searchable_attribute' => '', ], diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php index a52c5bb9e21b7..fd0ce8e684665 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class AdapterTest + * Class AdapterTest to test Elasticsearch search adapter */ class AdapterTest extends \PHPUnit\Framework\TestCase { @@ -20,7 +20,7 @@ class AdapterTest extends \PHPUnit\Framework\TestCase private $adapter; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit\Framework\MockObject\MockObject + * @var \Magento\Elasticsearch6\Model\Client\Elasticsearch|\PHPUnit\Framework\MockObject\MockObject */ private $clientMock; @@ -78,7 +78,6 @@ protected function setUp() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php index 3eea2497daa1f..545a20d4a6475 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php @@ -13,6 +13,8 @@ use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\Framework\Search\EngineResolverInterface; /** * @magentoDbIsolation enabled @@ -21,6 +23,11 @@ */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ConnectionManager */ @@ -65,6 +72,15 @@ protected function setUp() $indexer->reindexAll(); } + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + /** * @param string $text * @return array @@ -95,7 +111,6 @@ private function search($text) } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductName() @@ -104,7 +119,6 @@ public function testSearchConfigurableProductBySimpleProductName() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect() @@ -113,7 +127,6 @@ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeSelect() @@ -122,7 +135,6 @@ public function testSearchConfigurableProductBySimpleProductAttributeSelect() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeShortDescription() @@ -146,4 +158,19 @@ private function assertProductWithSkuFound($sku, array $result) } return false; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php index 77533e83b719c..cb94df5ffbcd1 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php @@ -17,6 +17,8 @@ use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; use Magento\Indexer\Model\Indexer; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; /** * Important: Please make sure that each integration test file works with unique elastic search index. In order to @@ -29,6 +31,11 @@ */ class IndexHandlerTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ProductRepositoryInterface */ @@ -87,7 +94,15 @@ protected function setUp() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + + /** * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -106,7 +121,6 @@ public function testReindexAll(): void /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -131,7 +145,6 @@ public function testReindexRowAfterEdit(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -170,7 +183,6 @@ public function testReindexRowAfterMassAction(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoAppArea adminhtml * @return void @@ -192,7 +204,6 @@ public function testReindexRowAfterDelete(): void /** * @magentoDbIsolation enabled * @magentoAppArea adminhtml - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php * @return void @@ -254,4 +265,19 @@ private function searchByName(string $text, int $storeId): array return $products; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php index 031e0d6ad6fd1..828e45953cca1 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php @@ -10,9 +10,11 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; /** * Important: Please make sure that each integration test file works with unique elastic search index. In order to @@ -22,9 +24,15 @@ * * @magentoDbIsolation disabled * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ReindexAllTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ConnectionManager */ @@ -65,10 +73,18 @@ protected function setUp() $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + /** * Test search of all products after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -83,7 +99,6 @@ public function testSearchAll() * Test sorting of all products after full reindex * * @magentoDbIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -118,7 +133,6 @@ public function testSort() /** * Test search of specific product after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -206,4 +220,19 @@ private function reindexAll() $indexer->load('catalogsearch_fulltext'); $indexer->reindexAll(); } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index a3da32e0d6c40..1a3618965ce02 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Elasticsearch\SearchAdapter; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; + /** * Class AdapterTest * @@ -17,6 +20,7 @@ * * In ElasticSearch, a reindex is required if the test includes a new data fixture with new items to search, see * testAdvancedSearchDateField(). + * phpstan:ignore * */ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest @@ -24,7 +28,7 @@ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest /** * @var string */ - protected $searchEngine = 'elasticsearch6'; + protected $searchEngine; /** * Get request config path @@ -41,13 +45,22 @@ protected function getRequestConfigPath() */ protected function createAdapter() { - return $this->objectManager->create(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter::class); + return $this->objectManager->create(\Magento\Search\Model\AdapterFactory::class)->create(); + } + + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = $this->objectManager->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); } /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testMatchQuery() { @@ -56,7 +69,6 @@ public function testMatchQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchOrderedQuery() @@ -68,7 +80,6 @@ public function testMatchOrderedQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAggregationsQuery() @@ -78,8 +89,8 @@ public function testAggregationsQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testMatchQueryFilters() { @@ -90,8 +101,8 @@ public function testMatchQueryFilters() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithAllFields() { @@ -102,8 +113,8 @@ public function testRangeFilterWithAllFields() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithoutFromField() { @@ -114,8 +125,8 @@ public function testRangeFilterWithoutFromField() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithoutToField() { @@ -126,8 +137,8 @@ public function testRangeFilterWithoutToField() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testTermFilter() { @@ -138,8 +149,8 @@ public function testTermFilter() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testTermFilterArray() { @@ -150,8 +161,8 @@ public function testTermFilterArray() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testWildcardFilter() { @@ -162,8 +173,8 @@ public function testWildcardFilter() * Request limits test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testSearchLimit() { @@ -174,8 +185,8 @@ public function testSearchLimit() * Bool filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilter() { @@ -186,8 +197,8 @@ public function testBoolFilter() * Test bool filter with nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilterWithNestedNegativeBoolFilter() { @@ -198,8 +209,8 @@ public function testBoolFilterWithNestedNegativeBoolFilter() * Test range inside nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() { @@ -211,12 +222,12 @@ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() * * @dataProvider elasticSearchAdvancedSearchDataProvider * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @param string $nameQuery * @param string $descriptionQuery * @param array $rangeFilter * @param int $expectedRecordsCount + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testSimpleAdvancedSearch( $nameQuery, @@ -257,7 +268,6 @@ public function elasticSearchAdvancedSearchDataProvider() /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testCustomFilterableAttribute() @@ -272,7 +282,6 @@ public function testCustomFilterableAttribute() * * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attributes.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @dataProvider filterByAttributeValuesDataProvider * @param string $requestName @@ -292,7 +301,6 @@ public function testFilterByAttributeValues($requestName, $additionalData) * @param $rangeFilter * @param $expectedRecordsCount * @magentoDataFixture Magento/Framework/Search/_files/date_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @magentoAppIsolation enabled * @dataProvider dateDataProvider @@ -307,8 +315,8 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount) /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testAdvancedSearchCompositeProductWithOutOfStockOption() { @@ -318,7 +326,6 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption() /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithDisabledChild() @@ -331,7 +338,6 @@ public function testAdvancedSearchCompositeProductWithDisabledChild() /** * @magentoDataFixture Magento/Framework/Search/_files/search_weight_products.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchQueryBoost() @@ -381,4 +387,19 @@ public function filterByAttributeValuesDataProvider() return $variations; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = $this->objectManager->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php deleted file mode 100644 index 5f20c1bf82062..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Controller\Advanced; - -use Magento\CatalogSearch\Controller\Advanced\ResultTest as CatalogSearchResultTest; - -/** - * Test cases for catalog advanced search using Elasticsearch 6.0+ search engine. - * - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - */ -class ResultTest extends CatalogSearchResultTest -{ - /** - * Advanced search test by difference product attributes. - * - * @magentoAppArea frontend - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @dataProvider searchStringDataProvider - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @param array $searchParams - * @return void - */ - public function testExecute(array $searchParams): void - { - parent::testExecute($searchParams); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php deleted file mode 100644 index 492983eb8726d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Controller\Result; - -use Magento\CatalogSearch\Controller\Result\IndexTest as CatalogSearchIndexTest; - -/** - * Test cases for catalog quick search using Elasticsearch 6.0+ search engine. - * - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - */ -class IndexTest extends CatalogSearchIndexTest -{ - /** - * Quick search test by difference product attributes. - * - * @magentoAppArea frontend - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @dataProvider searchStringDataProvider - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @param string $searchString - * @return void - */ - public function testExecute(string $searchString): void - { - parent::testExecute($searchString); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php deleted file mode 100644 index b50d9034c0f88..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Model\Indexer\fulltext\Action; - -use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProviderTest as CatalogSearchDataProviderTest; - -/** - * Search products by attribute value using Elasticsearch 6.0+ search engine. - */ -class DataProviderTest extends CatalogSearchDataProviderTest -{ - /** - * Search product by custom attribute value. - * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @magentoDbIsolation disabled - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @return void - */ - public function testSearchProductByAttribute(): void - { - parent::testSearchProductByAttribute(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php index 71dfebe5a4e84..84fec6dafd089 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -8,10 +8,11 @@ namespace Magento\Elasticsearch6\CatalogSearch\Model\Search; use Magento\CatalogSearch\Model\Search\AttributeSearchWeightTest as CatalogSearchAttributeSearchWeightTest; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\TestFramework\Helper\Bootstrap; /** * Test founded products order after quick search with changed attribute search weight - * using Elasticsearch 6.0+ search engine. * * @magentoAppIsolation enabled */ @@ -20,7 +21,6 @@ class AttributeSearchWeightTest extends CatalogSearchAttributeSearchWeightTest /** * Perform search by word and check founded product order in different cases. * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoDataFixture Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php * @dataProvider attributeSearchWeightDataProvider diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php deleted file mode 100644 index 50cb4974a9cf1..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\ConfigurableProduct\Model; - -use Magento\ConfigurableProduct\Model\QuickSearchTest as ConfigurableProductQuickSearchTest; - -/** - * Test cases related to find configurable product via quick search using Elasticsearch 6.0+ search engine. - * - * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - */ -class QuickSearchTest extends ConfigurableProductQuickSearchTest -{ - /** - * Assert that configurable child products has not found by query using Elasticsearch 6.0+ search engine. - * - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * - * @return void - */ - public function testChildProductsHasNotFoundedByQuery(): void - { - parent::testChildProductsHasNotFoundedByQuery(); - } - - /** - * Assert that child product of configurable will be available by search after - * set to product visibility by catalog and search using Elasticsearch 6.0+ search engine. - * - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * - * @dataProvider productAvailabilityInSearchByVisibilityDataProvider - * - * @param int $visibility - * @param bool $expectedResult - * @return void - */ - public function testOneOfChildIsAvailableBySearch(int $visibility, bool $expectedResult): void - { - parent::testOneOfChildIsAvailableBySearch($visibility, $expectedResult); - } - - /** - * Assert that configurable product was found by option value using Elasticsearch 6.0+ search engine. - * - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * - * @return void - */ - public function testSearchByOptionValue(): void - { - parent::testSearchByOptionValue(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php index 637d3d1a9d252..eb6cf6971573c 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php @@ -9,6 +9,7 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; /** * Tests quick search on Storefront. @@ -26,6 +27,12 @@ class QuickSearchTest extends AbstractController protected function setUp() { parent::setUp(); + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $checker = $this->_objectManager->get(ElasticsearchVersionChecker::class); + + if ($checker->getVersion() !== 6) { + $this->markTestSkipped('The installed elasticsearch version isn\'t supported by test'); + } $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); } @@ -42,7 +49,7 @@ protected function setUp() * @magentoConfigFixture fixturestore_store catalog/search/elasticsearch6_index_prefix storefront_quick_search * @magentoDataFixture Magento/Catalog/_files/products_for_search.php * @magentoDataFixture Magento/Store/_files/core_fixturestore.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @magentoDataFixture Magento/Elasticsearch6/_files/full_reindex.php */ public function testQuickSearchWithImprovedPriceRangeCalculation() { diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php new file mode 100644 index 0000000000000..66c556d25ae19 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$checker = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker::class +); +if ($checker->getVersion() === 6) { + include __DIR__ . '/../../../Magento/CatalogSearch/_files/full_reindex.php'; +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock index d9da3edf3d209..e5cb7bd3d57fe 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock @@ -4991,7 +4991,7 @@ "shasum": "ed1da1137848560dde1a85f0f54dc2fac262359e" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog": "103.0.*", @@ -5031,7 +5031,7 @@ "shasum": "a9da3243900390ad163efc7969b07116d2eb793f" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog-search": "101.0.*", @@ -9664,7 +9664,7 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "dotmailer/dotmailer-magento2-extension": "3.1.1", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "ext-bcmath": "*", "ext-ctype": "*", "ext-curl": "*", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock index d755bfa0479e6..e0a4d7f6929e8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock @@ -4991,7 +4991,7 @@ "shasum": "ed1da1137848560dde1a85f0f54dc2fac262359e" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog": "103.0.*", @@ -5031,7 +5031,7 @@ "shasum": "a9da3243900390ad163efc7969b07116d2eb793f" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog-search": "101.0.*", @@ -9664,7 +9664,7 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "dotmailer/dotmailer-magento2-extension": "3.1.1", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "ext-bcmath": "*", "ext-ctype": "*", "ext-curl": "*", diff --git a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php index b3a4bd9ae0791..3da08f324d761 100644 --- a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php +++ b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php @@ -61,6 +61,7 @@ public function formatErrors(AnalysisResult $analysisResult, Output $output): in $clearedAnalysisResult = new AnalysisResult( $fileSpecificErrorsWithoutIgnoredErrors, $analysisResult->getNotFileSpecificErrors(), + $analysisResult->getWarnings(), $analysisResult->isDefaultLevelUsed(), $analysisResult->hasInferrablePropertyTypesFromConstructor(), $analysisResult->getProjectConfigFile() diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index af785c28db414..6954983d049a5 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4243,4 +4243,11 @@ ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], ['Magento\Wishlist\Setup\Patch\Schema\AddProductIdConstraint'], + ['Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection'], + ['Magento\Elasticsearch\Model\Adapter\BatchDataMapper\CategoryFieldsProvider'], + ['Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper'], + ['Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper'], + ['Magento\Elasticsearch\Model\Client\Elasticsearch'], + ['Magento\Elasticsearch\SearchAdapter\Aggregation\Interval'], + ]; From af251eb2311a7d477f5c6516195b7032704c755a Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Fri, 6 Mar 2020 21:10:28 +0100 Subject: [PATCH 294/734] MC-26683: Some sort of example how I see the solution of this. --- .../Magento/GraphQl/Controller/GraphQl.php | 2 + .../Model/Cart/AddProductsToCart.php | 18 +++- .../Exception/GraphQlCartInputException.php | 97 +++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php diff --git a/app/code/Magento/GraphQl/Controller/GraphQl.php b/app/code/Magento/GraphQl/Controller/GraphQl.php index 2d72fde91b031..a5b8a14ae0793 100644 --- a/app/code/Magento/GraphQl/Controller/GraphQl.php +++ b/app/code/Magento/GraphQl/Controller/GraphQl.php @@ -160,6 +160,8 @@ public function dispatch(RequestInterface $request) : ResponseInterface } catch (\Exception $error) { $result['errors'] = isset($result) && isset($result['errors']) ? $result['errors'] : []; $result['errors'][] = $this->graphQlError->create($error); + // here we should have data from GraphQlCartInputException + $result['data'] = $error->getData(); $statusCode = ExceptionFormatter::HTTP_GRAPH_QL_SCHEMA_ERROR_STATUS; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index cfe78389fffe4..b9722d276975a 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -8,11 +8,12 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Message\MessageInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; /** - * Adding products to cart using GraphQL + * Add products to cart */ class AddProductsToCart { @@ -43,16 +44,29 @@ public function __construct( * * @param Quote $cart * @param array $cartItems + * @return \Magento\Framework\GraphQl\Exception\GraphQlCartInputException * @throws GraphQlInputException * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException */ - public function execute(Quote $cart, array $cartItems): void + public function execute(Quote $cart, array $cartItems): \Magento\Framework\GraphQl\Exception\GraphQlCartInputException { foreach ($cartItems as $cartItemData) { $this->addProductToCart->execute($cart, $cartItemData); } + if ($cart->getData('has_error')) { + $e = new \Magento\Framework\GraphQl\Exception\GraphQlCartInputException(__('Shopping cart errors')); + $errors = $cart->getErrors(); + foreach ($errors as $error) { + /** @var MessageInterface $error */ + $e->addError(new GraphQlInputException(__($error->getText()))); + } + $e->addData($cartItems); + + throw $e; + } + $this->cartRepository->save($cart); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php new file mode 100644 index 0000000000000..2dbbc1c20476e --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\GraphQl\Exception; + +use Magento\Framework\Exception\AggregateExceptionInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; +use GraphQL\Error\ClientAware; + +class GraphQlCartInputException extends LocalizedException implements AggregateExceptionInterface, ClientAware +{ + const EXCEPTION_CATEGORY = 'graphql-input'; + + /** + * @var boolean + */ + private $isSafe; + + /** + * The array of errors that have been added via the addError() method + * + * @var \Magento\Framework\Exception\LocalizedException[] + */ + private $errors = []; + + /** + * @var array + */ + private $data = []; + + /** + * Initialize object + * + * @param Phrase $phrase + * @param \Exception $cause + * @param int $code + * @param boolean $isSafe + */ + public function __construct(Phrase $phrase, \Exception $cause = null, $code = 0, $isSafe = true) + { + $this->isSafe = $isSafe; + parent::__construct($phrase, $cause, $code); + } + + /** + * @inheritdoc + */ + public function isClientSafe() : bool + { + return $this->isSafe; + } + + /** + * @inheritdoc + */ + public function getCategory() : string + { + return self::EXCEPTION_CATEGORY; + } + + /** + * Add child error if used as aggregate exception + * + * @param LocalizedException $exception + * @return $this + */ + public function addError(LocalizedException $exception): self + { + $this->errors[] = $exception; + return $this; + } + + /** + * Get child errors if used as aggregate exception + * + * @return LocalizedException[] + */ + public function getErrors(): array + { + return $this->errors; + } + + /** + * @param array $data + * @return GraphQlInputException + */ + public function addData(array $data): self + { + $this->data = $data; + return $this; + } +} From 428605d04e815c782cfbe20922f244970d153006 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Fri, 6 Mar 2020 22:29:04 +0200 Subject: [PATCH 295/734] Static tests fixes --- .../Magento/Wishlist/Block/Share/Email/Items.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Share/Email/Items.php b/app/code/Magento/Wishlist/Block/Share/Email/Items.php index c7ff49943b222..130c7cb136afb 100644 --- a/app/code/Magento/Wishlist/Block/Share/Email/Items.php +++ b/app/code/Magento/Wishlist/Block/Share/Email/Items.php @@ -4,11 +4,6 @@ * See COPYING.txt for license details. */ -/** - * Wishlist block customer items - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Wishlist\Block\Share\Email; use Magento\Catalog\Model\Product; @@ -19,6 +14,8 @@ use Magento\Wishlist\Model\Item; /** + * Wishlist share items + * * @api * @since 100.0.2 */ @@ -36,6 +33,7 @@ class Items extends \Magento\Wishlist\Block\AbstractBlock /** * Items constructor. + * * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Framework\App\Http\Context $httpContext * @param array $data @@ -59,6 +57,7 @@ public function __construct( * Identify the product from which thumbnail should be taken. * * @param Item $item + * * @return Product */ public function getProductForThumbnail(Item $item): Product @@ -71,6 +70,7 @@ public function getProductForThumbnail(Item $item): Product * * @param \Magento\Catalog\Model\Product $product * @param array $additional + * * @return string */ public function getProductUrl($product, $additional = []) @@ -84,6 +84,7 @@ public function getProductUrl($product, $additional = []) * * @param \Magento\Catalog\Model\Product $product * @param array $additional + * * @return string */ public function getAddToCartUrl($product, $additional = []) @@ -97,6 +98,7 @@ public function getAddToCartUrl($product, $additional = []) * Check whether wishlist item has description * * @param \Magento\Wishlist\Model\Item $item + * * @return bool */ public function hasDescription($item) From b2d07939fa03becf51151ebc49597614a71d3397 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Fri, 6 Mar 2020 22:32:03 +0200 Subject: [PATCH 296/734] Static tests fixes --- .../view/frontend/templates/email/items.phtml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml index 8c71a8de033dc..782b7d4892e62 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml @@ -11,14 +11,15 @@ <table> <tr> <?php $i = 0; - foreach ($block->getWishlistItems() as $item) : $i++ ?> - <?php /* @var $item \Magento\Wishlist\Model\Item */ ?> - <?php /* @var $_product \Magento\Catalog\Model\Product */ ?> + foreach ($block->getWishlistItems() as $item): $i++ ?> + <?php /* @var \Magento\Wishlist\Model\Item $item */ ?> + <?php /* @var \Magento\Catalog\Model\Product $_product */ ?> <?php $_product = $item->getProduct(); ?> <td class="col product"> <p> <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> - <?= /* @noEscape */ $block->getImage($block->getProductForThumbnail($item), 'product_small_image')->toHtml() ?> + <?php $productThumbnail = $block->getProductForThumbnail($item) ?> + <?= /* @noEscape */ $block->getImage($productThumbnail, 'product_small_image')->toHtml() ?> </a> </p> @@ -27,7 +28,7 @@ <strong><?= $block->escapeHtml($_product->getName()) ?></strong> </a> </p> - <?php if ($block->hasDescription($item)) : ?> + <?php if ($block->hasDescription($item)): ?> <p> <strong><?= $block->escapeHtml(__('Comment')) ?>:</strong> <br/><?= /* @noEscape */ $block->getEscapedDescription($item) ?> @@ -39,14 +40,14 @@ </a> </p> </td> - <?php if ($i % 3 != 0) : ?> + <?php if ($i % 3 != 0): ?> <td></td> - <?php else : ?> + <?php else: ?> </tr> <tr> <td colspan="5"> </td> </tr> - <?php if ($i < $l) : ?> + <?php if ($i < $l): ?> <tr> <?php endif ?> <?php endif ?> From f363870fd5fc33917816531ee91df134978b3f7a Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Sat, 7 Mar 2020 17:38:41 +0000 Subject: [PATCH 297/734] Updated to use collectionFactory to generate collections instead of DI --- .../ProductProcessUrlRewriteSavingObserver.php | 15 ++++++++------- ...ProductProcessUrlRewriteSavingObserverTest.php | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index d97553f83f473..377e19a22a31a 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -8,7 +8,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; -use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; @@ -59,9 +59,9 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface private $deleteEntitiesFromStores; /** - * @var Collection + * @var CollectionFactory */ - private $productCollection; + private $collectionFactory; /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator @@ -70,7 +70,7 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface * @param StoreManagerInterface $storeManager * @param ProductScopeRewriteGenerator $productScopeRewriteGenerator * @param DeleteEntitiesFromStores $deleteEntitiesFromStores - * @param Collection $productCollection + * @param CollectionFactory $collectionFactory */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, @@ -79,7 +79,7 @@ public function __construct( StoreManagerInterface $storeManager, ProductScopeRewriteGenerator $productScopeRewriteGenerator, DeleteEntitiesFromStores $deleteEntitiesFromStores, - Collection $productCollection + CollectionFactory $collectionFactory ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; @@ -87,7 +87,7 @@ public function __construct( $this->storeManager = $storeManager; $this->productScopeRewriteGenerator = $productScopeRewriteGenerator; $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; - $this->productCollection = $productCollection; + $this->collectionFactory = $collectionFactory; } /** @@ -117,7 +117,8 @@ public function execute(Observer $observer) $storeIdsToRemove = []; $productWebsiteMap = array_flip($product->getWebsiteIds()); - $storeVisibilities = $this->productCollection->getAllAttributeValues(ProductInterface::VISIBILITY); + $storeVisibilities = $this->collectionFactory->create() + ->getAllAttributeValues(ProductInterface::VISIBILITY); if ($this->productScopeRewriteGenerator->isGlobalScope($product->getStoreId())) { //Remove any rewrite URLs for websites the product is not in, or is not visible in. Global Scope. foreach ($this->storeManager->getStores() as $store) { diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 2912fcb1b7fad..602bfabcd45f2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -7,6 +7,7 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -106,6 +107,11 @@ class ProductProcessUrlRewriteSavingObserverTest extends TestCase */ private $productCollection; + /** + * @var CollectionFactory|MockObject + */ + private $collectionFactory; + /** * Set up * Website_ID = 0 -> Store_ID = 0 @@ -129,10 +135,17 @@ protected function setUp() $this->product = $this->initialiseProduct(); + $this->collectionFactory = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); $this->productCollection = $this->createPartialMock( Collection::class, ['getAllAttributeValues'] ); + $this->collectionFactory->expects($this->any()) + ->method('create') + ->willReturn($this->productCollection); $this->deleteEntitiesFromStores = $this->createPartialMock( DeleteEntitiesFromStores::class, @@ -192,7 +205,7 @@ protected function setUp() 'storeWebsiteRelation' => $this->storeWebsiteRelation, 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores, 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator, - 'productCollection' => $this->productCollection + 'collectionFactory' => $this->collectionFactory ] ); } From b91a20406aa353dd969dae294550ec0ff0f63371 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sat, 7 Mar 2020 20:06:57 +0200 Subject: [PATCH 298/734] Added MFTF tests to check presence/absence of the category filter item in the layered navigation --- .../Catalog/Test/Mftf/Data/ConfigData.xml | 19 ++++++ ...rontCategoryPageWithCategoryFilterTest.xml | 64 +++++++++++++++++++ ...tCategoryPageWithoutCategoryFilterTest.xml | 64 +++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..35c5c8ac3c866 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,19 @@ +<?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="EnableCategoryFilterOnCategoryPageConfigData"> + <data key="path">catalog/layered_navigation/display_category</data> + <data key="value">1</data> + </entity> + <entity name="DisableCategoryFilterOnCategoryPageConfigData"> + <data key="path">catalog/layered_navigation/display_category</data> + <data key="value">0</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml new file mode 100644 index 0000000000000..35a532b27ea10 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml @@ -0,0 +1,64 @@ +<?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="StorefrontCategoryPageWithCategoryFilterTest"> + <annotations> + <title value="Category with Layered Navigation - verify presence of category filter"/> + <stories value="Category page: Layered Navigation with category filter"/> + <description value="Verify that the category filter is present in layered navigation on category page"/> + <features value="Catalog"/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + + <before> + <!-- Create one category --> + <createData entity="_defaultCategory" stepKey="defaultCategory"> + <field key="name">TopCategory</field> + </createData> + <!-- Create second category, having as parent the 1st one --> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <field key="name">SubCategory</field> + <field key="parent_id">$$defaultCategory.id$$</field> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create a product assigned to the 1st category --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create 2nd product assigned to the 2nd category --> + <!-- The "Category filter" item is not shown in layered navigation --> + <!-- if there are not subcategories with products to show --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="subCategory"/> + </createData> + + <!-- Set the category filter to be present on the category page layered navigation --> + <magentoCLI command="config:set {{EnableCategoryFilterOnCategoryPageConfigData.path}} {{EnableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="setCategoryFilterVisibleOnStorefront"/> + + <!-- Flush cache --> + <magentoCLI command="cache:flush" stepKey="clearCache1"/> + </before> + + <after> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + + <!-- Verify category filter item is present --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml new file mode 100644 index 0000000000000..7604415a2d7ff --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml @@ -0,0 +1,64 @@ +<?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="StorefrontCategoryPageWithoutCategoryFilterTest"> + <annotations> + <title value="Category with Layered Navigation - verify absence of category filter"/> + <stories value="Category page: Layered Navigation without category filter"/> + <description value="Verify that the category filter is NOT present in layered navigation on category page"/> + <features value="Catalog"/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + + <before> + <!-- Create one category --> + <createData entity="_defaultCategory" stepKey="defaultCategory"> + <field key="name">TopCategory</field> + </createData> + <!-- Create second category, having as parent the 1st one --> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <field key="name">SubCategory</field> + <field key="parent_id">$$defaultCategory.id$$</field> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create a product assigned to the 1st category --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create 2nd product assigned to the 2nd category --> + <!-- The "Category filter" item is not shown in layered navigation --> + <!-- if there are not subcategories with products to show --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="subCategory"/> + </createData> + + <!-- Set the category filter to NOT be present on the category page layered navigation --> + <magentoCLI command="config:set {{DisableCategoryFilterOnCategoryPageConfigData.path}} {{DisableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="hideCategoryFilterOnStorefront"/> + + <!-- Flush cache --> + <magentoCLI command="cache:flush" stepKey="clearCache"/> + </before> + + <after> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + + <!-- Verify category filter item is NOT present --> + <dontSee selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </test> +</tests> From 05e2f32ef611437912360da44c1292da6d742614 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sat, 7 Mar 2020 21:49:19 +0200 Subject: [PATCH 299/734] Updated the MFTF tests previously created --- ...sibilityInLayeredNavigationActionGroup.xml | 20 +++++++++++++++++++ ...rontCategoryPageWithCategoryFilterTest.xml | 9 ++++----- ...tCategoryPageWithoutCategoryFilterTest.xml | 9 ++++----- 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml new file mode 100644 index 0000000000000..124e49a01c290 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- On a category page with layered navigation, verify if the category filter item is present --> + <actionGroup name="StorefrontCheckCategoryFilterIsVisibleInLayeredNavigationActionGroup"> + <!-- Verify category filter item is present --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </actionGroup> + + <!-- On a category page with layered navigation, verify if the category filter item is NOT present --> + <actionGroup name="StorefrontCheckCategoryFilterIsNotVisibleInLayeredNavigationActionGroup"> + <!-- Verify category filter item is NOT present --> + <dontSee selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml index 35a532b27ea10..4431588d07ded 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml @@ -44,7 +44,6 @@ <!-- Set the category filter to be present on the category page layered navigation --> <magentoCLI command="config:set {{EnableCategoryFilterOnCategoryPageConfigData.path}} {{EnableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="setCategoryFilterVisibleOnStorefront"/> - <!-- Flush cache --> <magentoCLI command="cache:flush" stepKey="clearCache1"/> </before> @@ -55,10 +54,10 @@ <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> </after> - <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$defaultCategory$$"/> + </actionGroup> - <!-- Verify category filter item is present --> - <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + <actionGroup ref="StorefrontCheckCategoryFilterIsVisibleInLayeredNavigationActionGroup" stepKey="checkCategoryFilterIsPresent" /> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml index 7604415a2d7ff..db76552dd4eb8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml @@ -44,7 +44,6 @@ <!-- Set the category filter to NOT be present on the category page layered navigation --> <magentoCLI command="config:set {{DisableCategoryFilterOnCategoryPageConfigData.path}} {{DisableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="hideCategoryFilterOnStorefront"/> - <!-- Flush cache --> <magentoCLI command="cache:flush" stepKey="clearCache"/> </before> @@ -55,10 +54,10 @@ <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> </after> - <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$defaultCategory$$"/> + </actionGroup> - <!-- Verify category filter item is NOT present --> - <dontSee selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + <actionGroup ref="StorefrontCheckCategoryFilterIsNotVisibleInLayeredNavigationActionGroup" stepKey="checkCategoryFilterIsPresent" /> </test> </tests> From 5b0bd95e28479bc5ff5f257b5056e1c6007f3afc Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 9 Mar 2020 08:37:45 +0200 Subject: [PATCH 300/734] Adjusted MFTF added tests action groups names --- ...igationCategoryFilterNotVisibleActionGroup.xml} | 8 +------- ...dNavigationCategoryFilterVisibleActionGroup.xml | 14 ++++++++++++++ ...torefrontCategoryPageWithCategoryFilterTest.xml | 2 +- ...efrontCategoryPageWithoutCategoryFilterTest.xml | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/ActionGroup/{StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml => AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml} (55%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml similarity index 55% rename from app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml rename to app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml index 124e49a01c290..c892594201f17 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontChangeCategoryFilterVisibilityInLayeredNavigationActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml @@ -6,14 +6,8 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- On a category page with layered navigation, verify if the category filter item is present --> - <actionGroup name="StorefrontCheckCategoryFilterIsVisibleInLayeredNavigationActionGroup"> - <!-- Verify category filter item is present --> - <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> - </actionGroup> - <!-- On a category page with layered navigation, verify if the category filter item is NOT present --> - <actionGroup name="StorefrontCheckCategoryFilterIsNotVisibleInLayeredNavigationActionGroup"> + <actionGroup name="AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup"> <!-- Verify category filter item is NOT present --> <dontSee selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml new file mode 100644 index 0000000000000..8eebafd3cd8ab --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml @@ -0,0 +1,14 @@ +<?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"> + <!-- On a category page with layered navigation, verify if the category filter item is present --> + <actionGroup name="AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup"> + <!-- Verify category filter item is present --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml index 4431588d07ded..8955f43e1b335 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml @@ -58,6 +58,6 @@ <argument name="category" value="$$defaultCategory$$"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategoryFilterIsVisibleInLayeredNavigationActionGroup" stepKey="checkCategoryFilterIsPresent" /> + <actionGroup ref="AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup" stepKey="checkCategoryFilterIsPresent" /> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml index db76552dd4eb8..7900a712e0664 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml @@ -58,6 +58,6 @@ <argument name="category" value="$$defaultCategory$$"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategoryFilterIsNotVisibleInLayeredNavigationActionGroup" stepKey="checkCategoryFilterIsPresent" /> + <actionGroup ref="AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup" stepKey="checkCategoryFilterIsNotPresent" /> </test> </tests> From e2f2b7930373dfda19e36dcd70031ae7e5e5fb4f Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 9 Mar 2020 08:45:19 +0200 Subject: [PATCH 301/734] Fixed failed unit test after review adjustments --- .../Catalog/Test/Unit/Model/Layer/FilterListTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php index 2b579ecea6b83..84c433379b156 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php @@ -71,8 +71,8 @@ protected function setUp() $this->model = new FilterList( $this->objectManagerMock, $this->attributeListMock, - $filters, - $this->layerCategoryConfigMock + $this->layerCategoryConfigMock, + $filters ); } @@ -112,7 +112,7 @@ public function testGetFilters($method, $value, $expectedClass) ->will($this->returnValue([$this->attributeMock])); $this->layerCategoryConfigMock->expects($this->once()) - ->method('isCategoryVisibleInLayer') + ->method('isCategoryFilterVisibleInLayerNavigation') ->willReturn(true); $this->assertEquals(['filter', 'filter'], $this->model->getFilters($this->layerMock)); @@ -156,7 +156,7 @@ public function testGetFiltersWithoutCategoryFilter( ->will($this->returnValue([$this->attributeMock])); $this->layerCategoryConfigMock->expects($this->once()) - ->method('isCategoryVisibleInLayer') + ->method('isCategoryFilterVisibleInLayerNavigation') ->willReturn(false); $this->assertEquals($expectedResult, $this->model->getFilters($this->layerMock)); From ba83e58a8381ab9397d853f54a1a5b77efce5d1d Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Mon, 9 Mar 2020 09:23:04 -0500 Subject: [PATCH 302/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 986d9c5e9eb24..9c38e300e3f69 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "32e76001ac157f2953fedfed1a3ed32020ecf6ec" + "reference": "b43323e51d27b75292ee62f357c7a6b98a7ccb03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/32e76001ac157f2953fedfed1a3ed32020ecf6ec", - "reference": "32e76001ac157f2953fedfed1a3ed32020ecf6ec", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/b43323e51d27b75292ee62f357c7a6b98a7ccb03", + "reference": "b43323e51d27b75292ee62f357c7a6b98a7ccb03", "shasum": "" }, "require": { @@ -7575,7 +7575,7 @@ "magento", "testing" ], - "time": "2020-03-05T17:05:08+00:00" + "time": "2020-03-06T19:37:11+00:00" }, { "name": "mikey179/vfsstream", From 007a89114b9d67b1ecfe1646f85447c1ccb28506 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 9 Mar 2020 17:34:38 +0100 Subject: [PATCH 303/734] DEPRECATED: AbstractAccount for Magento_Customer controllers --- .../Customer/Controller/AbstractAccount.php | 4 ++-- .../Customer/Controller/Plugin/Account.php | 23 ++++++++++++------- app/code/Magento/Customer/etc/frontend/di.xml | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Customer/Controller/AbstractAccount.php b/app/code/Magento/Customer/Controller/AbstractAccount.php index 21611329ed9bc..aa0eca1423c17 100644 --- a/app/code/Magento/Customer/Controller/AbstractAccount.php +++ b/app/code/Magento/Customer/Controller/AbstractAccount.php @@ -9,9 +9,9 @@ use Magento\Framework\App\Action\Action; /** - * Class AbstractAccount - * @package Magento\Customer\Controller * @SuppressWarnings(PHPMD.NumberOfChildren) + * @deprecated 2.4.0 + * @see \Magento\Customer\Controller\AccountInterface */ abstract class AbstractAccount extends Action implements AccountInterface { diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index 179da148e7f78..5fc65eb845563 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -3,13 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Controller\Plugin; +use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Model\Session; use Magento\Framework\App\ActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\App\Action\AbstractAction; use Magento\Framework\Controller\ResultInterface; class Account @@ -23,29 +25,35 @@ class Account * @var array */ private $allowedActions = []; + /** + * @var RequestInterface + */ + private $request; /** + * @param RequestInterface $request * @param Session $customerSession * @param array $allowedActions List of actions that are allowed for not authorized users */ public function __construct( + RequestInterface $request, Session $customerSession, array $allowedActions = [] ) { $this->session = $customerSession; $this->allowedActions = $allowedActions; + $this->request = $request; } /** * Dispatch actions allowed for not authorized users * - * @param AbstractAction $subject - * @param RequestInterface $request + * @param AccountInterface $subject * @return void */ - public function beforeDispatch(AbstractAction $subject, RequestInterface $request) + public function beforeExecute(AccountInterface $subject) { - $action = strtolower($request->getActionName()); + $action = strtolower($this->request->getActionName()); $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i'; if (!preg_match($pattern, $action)) { @@ -60,13 +68,12 @@ public function beforeDispatch(AbstractAction $subject, RequestInterface $reques /** * Remove No-referer flag from customer session * - * @param AbstractAction $subject + * @param AccountInterface $subject * @param ResponseInterface|ResultInterface $result - * @param RequestInterface $request * @return ResponseInterface|ResultInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterDispatch(AbstractAction $subject, $result, RequestInterface $request) + public function afterExecute(AccountInterface $subject, $result) { $this->session->unsNoReferer(false); return $result; diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index a479d0d2af328..8867042cc6dc9 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -51,7 +51,7 @@ </argument> </arguments> </type> - <type name="Magento\Customer\Controller\AbstractAccount"> + <type name="Magento\Customer\Controller\AccountInterface"> <plugin name="customer_account" type="Magento\Customer\Controller\Plugin\Account" /> </type> <type name="Magento\Checkout\Block\Cart\Sidebar"> From 26aa6bdeba46c180e05941f2592a457537ef83be Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 9 Mar 2020 18:10:20 +0100 Subject: [PATCH 304/734] Missing PHPDoc to modified files --- .../Magento/Customer/Controller/AbstractAccount.php | 2 ++ .../Magento/Customer/Controller/Plugin/Account.php | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/AbstractAccount.php b/app/code/Magento/Customer/Controller/AbstractAccount.php index aa0eca1423c17..36a96521cc6d1 100644 --- a/app/code/Magento/Customer/Controller/AbstractAccount.php +++ b/app/code/Magento/Customer/Controller/AbstractAccount.php @@ -9,6 +9,8 @@ use Magento\Framework\App\Action\Action; /** + * AbstractAccount class is deprecated, in favour of Composition approach to build Controllers + * * @SuppressWarnings(PHPMD.NumberOfChildren) * @deprecated 2.4.0 * @see \Magento\Customer\Controller\AccountInterface diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index 5fc65eb845563..b7352873269e9 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -14,6 +14,9 @@ use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; +/** + * Plugin verifies permissions using Action Name against injected (`fontend/di.xml`) rules + */ class Account { /** @@ -21,15 +24,16 @@ class Account */ protected $session; - /** - * @var array - */ - private $allowedActions = []; /** * @var RequestInterface */ private $request; + /** + * @var array + */ + private $allowedActions = []; + /** * @param RequestInterface $request * @param Session $customerSession From 54ae63a63dd0fd7101299cf4b4390c98a33a3f29 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 9 Mar 2020 18:19:49 +0100 Subject: [PATCH 305/734] Adjust Unit Tests for the change --- .../Unit/Controller/Plugin/AccountTest.php | 90 +++++++++---------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php index 2c70a8bda28fe..7dd376d57bdb0 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php @@ -3,18 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Controller\Plugin; +use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Controller\Plugin\Account; use Magento\Customer\Model\Session; use Magento\Framework\App\ActionFlag; use Magento\Framework\App\ActionInterface; -use Magento\Framework\App\Action\AbstractAction; use Magento\Framework\App\Request\Http; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class AccountTest extends \PHPUnit\Framework\TestCase +class AccountTest extends TestCase { /** * @var string @@ -27,59 +31,51 @@ class AccountTest extends \PHPUnit\Framework\TestCase protected $plugin; /** - * @var Session | \PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $session; + protected $sessionMock; /** - * @var AbstractAction | \PHPUnit_Framework_MockObject_MockObject + * @var AccountInterface|MockObject */ - protected $subject; + protected $actionMock; /** - * @var Http | \PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ - protected $request; + protected $requestMock; /** - * @var ActionFlag | \PHPUnit_Framework_MockObject_MockObject + * @var ActionFlag|MockObject */ - protected $actionFlag; + protected $actionFlagMock; /** - * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResultInterface|MockObject */ - private $resultInterface; + private $resultMock; protected function setUp() { - $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'setNoReferer', - 'unsNoReferer', - 'authenticate', - ]) + ->setMethods(['setNoReferer', 'unsNoReferer', 'authenticate']) ->getMock(); - $this->subject = $this->getMockBuilder(AbstractAction::class) - ->setMethods([ - 'getActionFlag', - ]) + $this->actionMock = $this->getMockBuilder(AccountInterface::class) + ->setMethods(['getActionFlag']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->requestMock = $this->getMockBuilder(HttpRequest::class) ->disableOriginalConstructor() - ->setMethods([ - 'getActionName', - ]) + ->setMethods(['getActionName']) ->getMock(); - $this->resultInterface = $this->getMockBuilder(ResultInterface::class) + $this->resultMock = $this->getMockBuilder(ResultInterface::class) ->getMockForAbstractClass(); - $this->actionFlag = $this->getMockBuilder(\Magento\Framework\App\ActionFlag::class) + $this->actionFlagMock = $this->getMockBuilder(ActionFlag::class) ->disableOriginalConstructor() ->getMock(); } @@ -90,47 +86,43 @@ protected function setUp() * @param boolean $isActionAllowed * @param boolean $isAuthenticated * - * @dataProvider beforeDispatchDataProvider + * @dataProvider beforeExecuteDataProvider */ - public function testBeforeDispatch( - $action, - $allowedActions, - $isActionAllowed, - $isAuthenticated - ) { - $this->request->expects($this->once()) + public function testBeforeExecute($action, $allowedActions, $isActionAllowed, $isAuthenticated) + { + $this->requestMock->expects($this->once()) ->method('getActionName') ->willReturn($action); if ($isActionAllowed) { - $this->session->expects($this->once()) + $this->sessionMock->expects($this->once()) ->method('setNoReferer') ->with(true) ->willReturnSelf(); } else { - $this->session->expects($this->once()) + $this->sessionMock->expects($this->once()) ->method('authenticate') ->willReturn($isAuthenticated); if (!$isAuthenticated) { - $this->subject->expects($this->once()) + $this->actionMock->expects($this->once()) ->method('getActionFlag') - ->willReturn($this->actionFlag); + ->willReturn($this->actionFlagMock); - $this->actionFlag->expects($this->once()) + $this->actionFlagMock->expects($this->once()) ->method('set') ->with('', ActionInterface::FLAG_NO_DISPATCH, true) ->willReturnSelf(); } } - $plugin = new Account($this->session, $allowedActions); - $plugin->beforeDispatch($this->subject, $this->request); + $plugin = new Account($this->requestMock, $this->sessionMock, $allowedActions); + $plugin->beforeExecute($this->actionMock); } /** * @return array */ - public function beforeDispatchDataProvider() + public function beforeExecuteDataProvider() { return [ [ @@ -166,9 +158,9 @@ public function beforeDispatchDataProvider() ]; } - public function testAfterDispatch() + public function testAfterExecute() { - $this->session->expects($this->once()) + $this->sessionMock->expects($this->once()) ->method('unsNoReferer') ->with(false) ->willReturnSelf(); @@ -176,13 +168,13 @@ public function testAfterDispatch() $plugin = (new ObjectManager($this))->getObject( Account::class, [ - 'session' => $this->session, + 'session' => $this->sessionMock, 'allowedActions' => ['testaction'] ] ); $this->assertSame( - $this->resultInterface, - $plugin->afterDispatch($this->subject, $this->resultInterface, $this->request) + $this->resultMock, + $plugin->afterExecute($this->actionMock, $this->resultMock, $this->requestMock) ); } } From c47a6a26aae710f2fe64e92d25e03e456c90ae79 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Mon, 9 Mar 2020 22:17:08 +0100 Subject: [PATCH 306/734] MC-26683: Added errors to the return model --- .../Magento/GraphQl/Controller/GraphQl.php | 2 - .../Model/Cart/AddProductsToCart.php | 17 +--- .../QuoteGraphQl/Model/Resolver/CartItems.php | 7 ++ .../Exception/GraphQlCartInputException.php | 97 ------------------- 4 files changed, 9 insertions(+), 114 deletions(-) delete mode 100644 lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php diff --git a/app/code/Magento/GraphQl/Controller/GraphQl.php b/app/code/Magento/GraphQl/Controller/GraphQl.php index a5b8a14ae0793..2d72fde91b031 100644 --- a/app/code/Magento/GraphQl/Controller/GraphQl.php +++ b/app/code/Magento/GraphQl/Controller/GraphQl.php @@ -160,8 +160,6 @@ public function dispatch(RequestInterface $request) : ResponseInterface } catch (\Exception $error) { $result['errors'] = isset($result) && isset($result['errors']) ? $result['errors'] : []; $result['errors'][] = $this->graphQlError->create($error); - // here we should have data from GraphQlCartInputException - $result['data'] = $error->getData(); $statusCode = ExceptionFormatter::HTTP_GRAPH_QL_SCHEMA_ERROR_STATUS; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index b9722d276975a..0360d9ccf5476 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -13,7 +13,7 @@ use Magento\Quote\Model\Quote; /** - * Add products to cart + * Adding products to cart using GraphQL */ class AddProductsToCart { @@ -44,29 +44,16 @@ public function __construct( * * @param Quote $cart * @param array $cartItems - * @return \Magento\Framework\GraphQl\Exception\GraphQlCartInputException * @throws GraphQlInputException * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException */ - public function execute(Quote $cart, array $cartItems): \Magento\Framework\GraphQl\Exception\GraphQlCartInputException + public function execute(Quote $cart, array $cartItems): void { foreach ($cartItems as $cartItemData) { $this->addProductToCart->execute($cart, $cartItemData); } - if ($cart->getData('has_error')) { - $e = new \Magento\Framework\GraphQl\Exception\GraphQlCartInputException(__('Shopping cart errors')); - $errors = $cart->getErrors(); - foreach ($errors as $error) { - /** @var MessageInterface $error */ - $e->addError(new GraphQlInputException(__($error->getText()))); - } - $e->addData($cartItems); - - throw $e; - } - $this->cartRepository->save($cart); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php index 2674b3728619a..8017a91b5cfd2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Model\Quote\Item as QuoteItem; @@ -29,6 +30,12 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $value['model']; $itemsData = []; + if ($cart->getData('has_error')) { + $errors = $cart->getErrors(); + foreach ($errors as $error) { + $itemsData[] = new GraphQlInputException(__($error->getText())); + } + } foreach ($cart->getAllVisibleItems() as $cartItem) { /** * @var QuoteItem $cartItem diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php deleted file mode 100644 index 2dbbc1c20476e..0000000000000 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlCartInputException.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Framework\GraphQl\Exception; - -use Magento\Framework\Exception\AggregateExceptionInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Phrase; -use GraphQL\Error\ClientAware; - -class GraphQlCartInputException extends LocalizedException implements AggregateExceptionInterface, ClientAware -{ - const EXCEPTION_CATEGORY = 'graphql-input'; - - /** - * @var boolean - */ - private $isSafe; - - /** - * The array of errors that have been added via the addError() method - * - * @var \Magento\Framework\Exception\LocalizedException[] - */ - private $errors = []; - - /** - * @var array - */ - private $data = []; - - /** - * Initialize object - * - * @param Phrase $phrase - * @param \Exception $cause - * @param int $code - * @param boolean $isSafe - */ - public function __construct(Phrase $phrase, \Exception $cause = null, $code = 0, $isSafe = true) - { - $this->isSafe = $isSafe; - parent::__construct($phrase, $cause, $code); - } - - /** - * @inheritdoc - */ - public function isClientSafe() : bool - { - return $this->isSafe; - } - - /** - * @inheritdoc - */ - public function getCategory() : string - { - return self::EXCEPTION_CATEGORY; - } - - /** - * Add child error if used as aggregate exception - * - * @param LocalizedException $exception - * @return $this - */ - public function addError(LocalizedException $exception): self - { - $this->errors[] = $exception; - return $this; - } - - /** - * Get child errors if used as aggregate exception - * - * @return LocalizedException[] - */ - public function getErrors(): array - { - return $this->errors; - } - - /** - * @param array $data - * @return GraphQlInputException - */ - public function addData(array $data): self - { - $this->data = $data; - return $this; - } -} From 3f0bde0b712427079f92ed6d96531775b5c2f1fd Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 9 Mar 2020 17:01:37 -0500 Subject: [PATCH 307/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas -- fix merge conflict --- composer.json | 61 +- composer.lock | 5461 +++++++++-------- .../Customer/Controller/AccountTest.php | 4 +- .../HeaderProvider/AbstractHeaderTestCase.php | 5 +- lib/internal/Magento/Framework/composer.json | 26 +- 5 files changed, 2884 insertions(+), 2673 deletions(-) diff --git a/composer.json b/composer.json index ac005f9da6a1e..882fb5f93b0ef 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,35 @@ "composer/composer": "^1.6", "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", "guzzlehttp/guzzle": "^6.3.3", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-dependency-plugin": "^1.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^3.0.0", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.11.2", "league/flysystem": "^1.0", "league/flysystem-aws-s3-v3": "^1.0", "league/flysystem-azure-blob-storage": "^0.1.6", @@ -55,35 +84,7 @@ "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", - "wikimedia/less.php": "~1.8.0", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^3.0.0", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.11.2" + "wikimedia/less.php": "~1.8.0" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", @@ -322,7 +323,7 @@ "Magento\\Framework\\": "lib/internal/Magento/Framework/", "Magento\\Setup\\": "setup/src/Magento/Setup/", "Magento\\": "app/code/Magento/", - "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" + "Laminas\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" }, "psr-0": { "": [ diff --git a/composer.lock b/composer.lock index 1236d2b0ae82f..39e6fe4845513 100644 --- a/composer.lock +++ b/composer.lock @@ -1,23 +1,23 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "522d676db5baf5864a824409c54948fc", + "content-hash": "92b62c12f24800168ad73c8c41bb31e1", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.133.24", + "version": "3.133.32", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105" + "reference": "c4bd227436446f02d2b9963f3ea4ae6dc380420b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/726426e1514be5220d55ecf02eb1f938a3b4a105", - "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c4bd227436446f02d2b9963f3ea4ae6dc380420b", + "reference": "c4bd227436446f02d2b9963f3ea4ae6dc380420b", "shasum": "" }, "require": { @@ -88,7 +88,7 @@ "s3", "sdk" ], - "time": "2020-02-27T19:13:45+00:00" + "time": "2020-03-09T18:10:46+00:00" }, { "name": "braintree/braintree_php", @@ -542,16 +542,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "cbe23383749496fe0f373345208b79568e4bc248" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", - "reference": "cbe23383749496fe0f373345208b79568e4bc248", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { @@ -582,7 +582,7 @@ "Xdebug", "performance" ], - "time": "2019-11-06T16:40:04+00:00" + "time": "2020-03-01T12:26:26+00:00" }, { "name": "container-interop/container-interop", @@ -1035,4414 +1035,4626 @@ "time": "2019-09-25T14:49:45+00:00" }, { - "name": "league/flysystem", - "version": "1.0.64", + "name": "laminas/laminas-captcha", + "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "d13c43dbd4b791f815215959105a008515d1a2e0" + "url": "https://github.com/laminas/laminas-captcha.git", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d13c43dbd4b791f815215959105a008515d1a2e0", - "reference": "d13c43dbd4b791f815215959105a008515d1a2e0", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/b88f650f3adf2d902ef56f6377cceb5cd87b9876", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "php": ">=5.5.9" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" + "replace": { + "zendframework/zend-captcha": "self.version" }, "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.26" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-recaptcha": "^3.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" } }, "autoload": { "psr-4": { - "League\\Flysystem\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } + "BSD-3-Clause" ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "homepage": "https://laminas.dev", "keywords": [ - "Cloud Files", - "WebDAV", - "abstraction", - "aws", - "cloud", - "copy.com", - "dropbox", - "file systems", - "files", - "filesystem", - "filesystems", - "ftp", - "rackspace", - "remote", - "s3", - "sftp", - "storage" + "captcha", + "laminas" ], - "time": "2020-02-05T18:14:17+00:00" + "time": "2019-12-31T16:24:14+00:00" }, { - "name": "league/flysystem-aws-s3-v3", - "version": "1.0.24", + "name": "laminas/laminas-code", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/128784abc7a0d9e1fcc30c446533aa6f1db1f999", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999", "shasum": "" }, "require": { - "aws/aws-sdk-php": "^3.0.0", - "league/flysystem": "^1.0.40", - "php": ">=5.5.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.1" + }, + "replace": { + "zendframework/zend-code": "self.version" }, "require-dev": { - "henrikbjorn/phpspec-code-coverage": "~1.0.1", - "phpspec/phpspec": "^2.0.0" + "doctrine/annotations": "^1.0", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^1.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "phpunit/phpunit": "^7.5.15" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { "psr-4": { - "League\\Flysystem\\AwsS3v3\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", + "keywords": [ + "code", + "laminas" ], - "description": "Flysystem adapter for the AWS S3 SDK v3.x", - "time": "2020-02-23T13:31:58+00:00" + "time": "2019-12-31T16:28:14+00:00" }, { - "name": "league/flysystem-azure-blob-storage", - "version": "0.1.6", + "name": "laminas/laminas-config", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem-azure-blob-storage.git", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d" + "url": "https://github.com/laminas/laminas-config.git", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-azure-blob-storage/zipball/97215345f3c42679299ba556a4d16d4847ee7f6d", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/71ba6d5dd703196ce66b25abc4d772edb094dae1", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.5", - "league/flysystem": "^1.0", - "microsoft/azure-storage-blob": "^1.1", - "php": ">=5.6" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-config": "self.version" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { "psr-4": { - "League\\Flysystem\\AzureBlobStorage\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "homepage": "https://laminas.dev", + "keywords": [ + "config", + "laminas" ], - "time": "2019-06-07T20:42:16+00:00" + "time": "2019-12-31T16:30:04+00:00" }, { - "name": "magento/composer", - "version": "1.6.x-dev", + "name": "laminas/laminas-console", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/magento/composer.git", - "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa" + "url": "https://github.com/laminas/laminas-console.git", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/composer/zipball/fe738ac9155f550b669b260b3cfa6422eacb53fa", - "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/478a6ceac3e31fb38d6314088abda8b239ee23a5", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5", "shasum": "" }, "require": { - "composer/composer": "^1.6", - "php": "~7.1.3||~7.2.0||~7.3.0", - "symfony/console": "~4.4.0" + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-console": "self.version" }, "require-dev": { - "phpunit/phpunit": "~7.0.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.23 || ^6.4.3" + }, + "suggest": { + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" + } + }, "autoload": { "psr-4": { - "Magento\\Composer\\": "src" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0", - "AFL-3.0" + "BSD-3-Clause" ], - "description": "Magento composer library helps to instantiate Composer application and run composer commands.", - "time": "2020-01-17T16:43:51+00:00" + "description": "Build console applications using getopt syntax or routing, complete with prompts", + "homepage": "https://laminas.dev", + "keywords": [ + "console", + "laminas" + ], + "time": "2019-12-31T16:31:45+00:00" }, { - "name": "magento/magento-composer-installer", - "version": "0.1.13", + "name": "laminas/laminas-crypt", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/magento/magento-composer-installer.git", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" + "url": "https://github.com/laminas/laminas-crypt.git", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0" + "container-interop/container-interop": "~1.0", + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" }, "replace": { - "magento-hackathon/magento-composer-installer": "*" + "zendframework/zend-crypt": "self.version" }, "require-dev": { - "composer/composer": "*@dev", - "firegento/phpcs": "dev-patch-1", - "mikey179/vfsstream": "*", - "phpunit/phpunit": "*", - "phpunit/phpunit-mock-objects": "dev-master", - "squizlabs/php_codesniffer": "1.4.7", - "symfony/process": "*" + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" }, - "type": "composer-plugin", + "suggest": { + "ext-mcrypt": "Required for most features of Laminas\\Crypt" + }, + "type": "library", "extra": { - "composer-command-registry": [ - "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" - ], - "class": "MagentoHackathon\\Composer\\Magento\\Plugin" + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } }, "autoload": { - "psr-0": { - "MagentoHackathon\\Composer\\Magento": "src/" + "psr-4": { + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Vinai Kopp", - "email": "vinai@netzarbeiter.com" - }, - { - "name": "Daniel Fahlke aka Flyingmana", - "email": "flyingmana@googlemail.com" - }, - { - "name": "Jörg Weller", - "email": "weller@flagbit.de" - }, - { - "name": "Karl Spies", - "email": "karl.spies@gmx.net" - }, - { - "name": "Tobias Vogt", - "email": "tobi@webguys.de" - }, - { - "name": "David Fuhr", - "email": "fuhr@flagbit.de" - } + "BSD-3-Clause" ], - "description": "Composer installer for Magento modules", - "homepage": "https://github.com/magento/magento-composer-installer", + "homepage": "https://laminas.dev", "keywords": [ - "composer-installer", - "magento" + "crypt", + "laminas" ], - "time": "2017-12-29T16:45:24+00:00" + "time": "2019-12-31T16:33:11+00:00" }, { - "name": "magento/zendframework1", - "version": "1.14.3", + "name": "laminas/laminas-db", + "version": "2.11.2", "source": { "type": "git", - "url": "https://github.com/magento/zf1.git", - "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5" + "url": "https://github.com/laminas/laminas-db.git", + "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/726855dfb080089dc7bc7b016624129f8e7bc4e5", - "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/76f9527da996c2fef32ef1f3a939e18ca5e9d962", + "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962", "shasum": "" }, "require": { - "php": ">=5.2.11" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-db": "self.version" }, "require-dev": { - "phpunit/dbunit": "1.3.*", - "phpunit/phpunit": "3.7.*" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14" + }, + "suggest": { + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" + }, + "laminas": { + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { - "psr-0": { - "Zend_": "library/" + "psr-4": { + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "library/" - ], "license": [ "BSD-3-Clause" ], - "description": "Magento Zend Framework 1", - "homepage": "http://framework.zend.com/", + "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "homepage": "https://laminas.dev", "keywords": [ - "ZF1", - "framework" + "db", + "laminas" ], - "time": "2019-11-26T15:09:40+00:00" + "time": "2020-01-14T13:07:26+00:00" }, { - "name": "microsoft/azure-storage-blob", - "version": "1.5.0", + "name": "laminas/laminas-dependency-plugin", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/Azure/azure-storage-blob-php.git", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919" + "url": "https://github.com/laminas/laminas-dependency-plugin.git", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-blob-php/zipball/6a333cd28a3742c3e99e79042dc6510f9f917919", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919", + "url": "https://api.github.com/repos/laminas/laminas-dependency-plugin/zipball/f269716dc584cd7b69e7f6e8ac1092d645ab56d5", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5", "shasum": "" }, "require": { - "microsoft/azure-storage-common": "~1.4", - "php": ">=5.6.0" + "composer-plugin-api": "^1.1", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "composer/composer": "^1.9", + "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^8.4", + "roave/security-advisories": "dev-master", + "webimpress/coding-standard": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" + }, + "class": "Laminas\\DependencyPlugin\\DependencyRewriterPlugin" }, - "type": "library", "autoload": { "psr-4": { - "MicrosoftAzure\\Storage\\Blob\\": "src/Blob" + "Laminas\\DependencyPlugin\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } - ], - "description": "This project provides a set of PHP client libraries that make it easy to access Microsoft Azure Storage Blob APIs.", - "keywords": [ - "azure", - "blob", - "php", - "sdk", - "storage" + "BSD-3-Clause" ], - "time": "2020-01-02T07:18:59+00:00" + "description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.", + "time": "2020-01-14T19:36:52+00:00" }, { - "name": "microsoft/azure-storage-common", - "version": "1.4.1", + "name": "laminas/laminas-di", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/Azure/azure-storage-common-php.git", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0" + "url": "https://github.com/laminas/laminas-di.git", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-common-php/zipball/be4df800761d0d0fa91a9460c7f42517197d57a0", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/239b22408a1f8eacda6fc2b838b5065c4cf1d88e", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "~6.0", - "php": ">=5.6.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^0.4.5 || ^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-di": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { "psr-4": { - "MicrosoftAzure\\Storage\\Common\\": "src/Common" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } + "BSD-3-Clause" ], - "description": "This project provides a set of common code shared by Azure Storage Blob, Table, Queue and File PHP client libraries.", + "homepage": "https://laminas.dev", "keywords": [ - "azure", - "common", - "php", - "sdk", - "storage" + "di", + "laminas" ], - "time": "2020-01-02T07:15:54+00:00" + "time": "2019-12-31T15:17:33+00:00" }, { - "name": "monolog/monolog", - "version": "1.25.3", + "name": "laminas/laminas-diactoros", + "version": "1.8.7p1", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1" + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fa82921994db851a8becaf3787a9e73c5976b6f1", - "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/56a9aca1f89231763d24d2ae13531b97fa5f4029", + "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" }, "provide": { - "psr/log-implementation": "1.0.0" + "psr/http-message-implementation": "1.0" }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "replace": { + "zendframework/zend-diactoros": "self.version" }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-coding-standard": "~1.0", + "php-http/psr7-integration-tests": "dev-master", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-release-1.8": "1.8.x-dev" } }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php", + "src/functions/create_uploaded_file.legacy.php", + "src/functions/marshal_headers_from_sapi.legacy.php", + "src/functions/marshal_method_from_sapi.legacy.php", + "src/functions/marshal_protocol_version_from_sapi.legacy.php", + "src/functions/marshal_uri_from_sapi.legacy.php", + "src/functions/normalize_server.legacy.php", + "src/functions/normalize_uploaded_files.legacy.php", + "src/functions/parse_cookie_header.legacy.php" + ], "psr-4": { - "Monolog\\": "src/Monolog" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "logging", - "psr-3" + "http", + "laminas", + "psr", + "psr-7" ], - "time": "2019-12-20T14:15:16+00:00" + "time": "2020-01-07T19:25:17+00:00" }, { - "name": "mtdowling/jmespath.php", - "version": "2.5.0", + "name": "laminas/laminas-escaper", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "52168cb9472de06979613d365c7f1ab8798be895" + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", - "reference": "52168cb9472de06979613d365c7f1ab8798be895", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/25f2a053eadfa92ddacb609dcbbc39362610da70", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70", "shasum": "" }, "require": { - "php": ">=5.4.0", - "symfony/polyfill-mbstring": "^1.4" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-escaper": "self.version" }, "require-dev": { - "composer/xdebug-handler": "^1.2", - "phpunit/phpunit": "^4.8.36|^7.5.15" + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, - "bin": [ - "bin/jp.php" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { "psr-4": { - "JmesPath\\": "src/" - }, - "files": [ - "src/JmesPath.php" - ] + "Laminas\\Escaper\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } + "BSD-3-Clause" ], - "description": "Declaratively specify how to extract elements from a JSON document", + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", "keywords": [ - "json", - "jsonpath" + "escaper", + "laminas" ], - "time": "2019-12-30T18:03:34+00:00" + "time": "2019-12-31T16:43:30+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.99", + "name": "laminas/laminas-eventmanager", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "url": "https://github.com/laminas/laminas-eventmanager.git", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", "shasum": "" }, "require": { - "php": "^7" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-eventmanager": "self.version" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev", + "dev-develop": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\EventManager\\": "src/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } + "BSD-3-Clause" ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://laminas.dev", "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" + "event", + "eventmanager", + "events", + "laminas" ], - "time": "2018-07-02T15:55:56+00:00" + "time": "2019-12-31T16:44:52+00:00" }, { - "name": "paragonie/sodium_compat", - "version": "v1.12.2", + "name": "laminas/laminas-feed", + "version": "2.12.0", "source": { "type": "git", - "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "3b953109fdfc821c1979bc829c8b7421721fef82" + "url": "https://github.com/laminas/laminas-feed.git", + "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3b953109fdfc821c1979bc829c8b7421721fef82", - "reference": "3b953109fdfc821c1979bc829c8b7421721fef82", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/64d25e18a6ea3db90c27fe2d6b95630daa1bf602", + "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602", "shasum": "" }, "require": { - "paragonie/random_compat": ">=1", - "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "phpunit/phpunit": "^3|^4|^5|^6|^7" + "replace": { + "zendframework/zend-feed": "self.version" + }, + "require-dev": { + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-message": "^1.0.1" }, "suggest": { - "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", - "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + } + }, "autoload": { - "files": [ - "autoload.php" - ] + "psr-4": { + "Laminas\\Feed\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com" - }, - { - "name": "Frank Denis", - "email": "jedisct1@pureftpd.org" - } + "BSD-3-Clause" ], - "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "description": "provides functionality for consuming RSS and Atom feeds", + "homepage": "https://laminas.dev", "keywords": [ - "Authentication", - "BLAKE2b", - "ChaCha20", - "ChaCha20-Poly1305", - "Chapoly", - "Curve25519", - "Ed25519", - "EdDSA", - "Edwards-curve Digital Signature Algorithm", - "Elliptic Curve Diffie-Hellman", - "Poly1305", - "Pure-PHP cryptography", - "RFC 7748", - "RFC 8032", - "Salpoly", - "Salsa20", - "X25519", - "XChaCha20-Poly1305", - "XSalsa20-Poly1305", - "Xchacha20", - "Xsalsa20", - "aead", - "cryptography", - "ecdh", - "elliptic curve", - "elliptic curve cryptography", - "encryption", - "libsodium", - "php", - "public-key cryptography", - "secret-key cryptography", - "side-channel resistant" + "feed", + "laminas" ], - "time": "2019-12-30T03:11:08+00:00" + "time": "2019-12-31T16:46:54+00:00" }, { - "name": "pelago/emogrifier", - "version": "v2.2.0", + "name": "laminas/laminas-filter", + "version": "2.9.3", "source": { "type": "git", - "url": "https://github.com/MyIntervals/emogrifier.git", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" + "url": "https://github.com/laminas/laminas-filter.git", + "reference": "52b5cdbef8902280996e687e7352a648a8e22f31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/52b5cdbef8902280996e687e7352a648a8e22f31", + "reference": "52b5cdbef8902280996e687e7352a648a8e22f31", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", - "symfony/css-selector": "^3.4.0 || ^4.0.0" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "laminas/laminas-validator": "<2.10.1" + }, + "replace": { + "zendframework/zend-filter": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.2.0", - "phpmd/phpmd": "^2.6.0", - "phpunit/phpunit": "^4.8.0", - "squizlabs/php_codesniffer": "^3.3.2" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6", + "pear/archive_tar": "^1.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-factory": "^1.0" + }, + "suggest": { + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter", + "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Pelago\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Oliver Klee", - "email": "github@oliverklee.de" - }, - { - "name": "Zoli Szabó", - "email": "zoli.szabo+github@gmail.com" - }, - { - "name": "John Reeve", - "email": "jreeve@pelagodesign.com" - }, - { - "name": "Jake Hotson", - "email": "jake@qzdesign.co.uk" - }, - { - "name": "Cameron Brooks" - }, - { - "name": "Jaime Prado" - } + "BSD-3-Clause" ], - "description": "Converts CSS styles into inline style attributes in your HTML code", - "homepage": "https://www.myintervals.com/emogrifier.php", + "description": "Programmatically filter and normalize data and files", + "homepage": "https://laminas.dev", "keywords": [ - "css", - "email", - "pre-processing" + "filter", + "laminas" ], - "time": "2019-09-04T16:07:59+00:00" + "time": "2020-01-07T20:43:53+00:00" }, { - "name": "php-amqplib/php-amqplib", - "version": "v2.10.1", + "name": "laminas/laminas-form", + "version": "2.14.3", "source": { "type": "git", - "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" + "url": "https://github.com/laminas/laminas-form.git", + "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/012aae01366cb8c8fb64e39a887363ef82f388dd", + "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd", "shasum": "" }, "require": { - "ext-bcmath": "*", - "ext-sockets": "*", - "php": ">=5.6" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, "replace": { - "videlalvaro/php-amqplib": "self.version" + "zendframework/zend-form": "self.version" }, "require-dev": { - "ext-curl": "*", - "nategood/httpful": "^0.2.20", - "phpunit/phpunit": "^5.7|^6.5|^7.0", - "squizlabs/php_codesniffer": "^2.5" + "doctrine/annotations": "~1.0", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-recaptcha": "^3.0.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "phpunit/phpunit": "^5.7.23 || ^6.5.3" + }, + "suggest": { + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use laminas-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for laminas-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using laminas-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the laminas-form view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev" + "dev-master": "2.14.x-dev", + "dev-develop": "2.15.x-dev" + }, + "laminas": { + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "PhpAmqpLib\\": "PhpAmqpLib/" - } + "Laminas\\Form\\": "src/" + }, + "files": [ + "autoload/formElementManagerPolyfill.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Alvaro Videla", - "role": "Original Maintainer" - }, - { - "name": "John Kelly", - "email": "johnmkelly86@gmail.com", - "role": "Maintainer" - }, - { - "name": "Raúl Araya", - "email": "nubeiro@gmail.com", - "role": "Maintainer" - }, - { - "name": "Luke Bakken", - "email": "luke@bakken.io", - "role": "Maintainer" - } + "BSD-3-Clause" ], - "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", - "homepage": "https://github.com/php-amqplib/php-amqplib/", + "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "homepage": "https://laminas.dev", "keywords": [ - "message", - "queue", - "rabbitmq" + "form", + "laminas" ], - "time": "2019-10-10T13:23:40+00:00" + "time": "2019-12-31T16:56:34+00:00" }, { - "name": "phpseclib/mcrypt_compat", - "version": "1.0.8", + "name": "laminas/laminas-http", + "version": "2.11.2", "source": { "type": "git", - "url": "https://github.com/phpseclib/mcrypt_compat.git", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" + "url": "https://github.com/laminas/laminas-http.git", + "reference": "8c66963b933c80da59433da56a44dfa979f3ec88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/8c66963b933c80da59433da56a44dfa979f3ec88", + "reference": "8c66963b933c80da59433da56a44dfa979f3ec88", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpseclib/phpseclib": ">=2.0.11 <3.0.0" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-http": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.7|^6.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3" }, "suggest": { - "ext-openssl": "Will enable faster cryptographic operations" + "paragonie/certainty": "For automated management of cacert.pem" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" + } + }, "autoload": { - "files": [ - "lib/mcrypt.php" - ] + "psr-4": { + "Laminas\\Http\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "homepage": "http://phpseclib.sourceforge.net" - } + "BSD-3-Clause" ], - "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", + "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://laminas.dev", "keywords": [ - "cryptograpy", - "encryption", - "mcrypt" + "http", + "http client", + "laminas" ], - "time": "2018-08-22T03:11:43+00:00" + "time": "2019-12-31T17:02:36+00:00" }, { - "name": "phpseclib/phpseclib", - "version": "2.0.25", + "name": "laminas/laminas-hydrator", + "version": "2.4.2", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0" + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c18159618ed7cd7ff721ac1a8fec7860a475d2f0", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/4a0e81cf05f32edcace817f1f48cb4055f689d85", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-stdlib": "^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-hydrator": "self.version" }, "require-dev": { - "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "sami/sami": "~2.0", - "squizlabs/php_codesniffer": "~2.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "suggest": { - "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", - "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", - "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", + "extra": { + "branch-alias": { + "dev-release-2.4": "2.4.x-dev" + }, + "laminas": { + "component": "Laminas\\Hydrator", + "config-provider": "Laminas\\Hydrator\\ConfigProvider" + } + }, "autoload": { - "files": [ - "phpseclib/bootstrap.php" - ], "psr-4": { - "phpseclib\\": "phpseclib/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" - }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" - }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" - }, - { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" - } + "BSD-3-Clause" ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", + "description": "Serialize objects to arrays, and vice versa", + "homepage": "https://laminas.dev", "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" + "hydrator", + "laminas" ], - "time": "2020-02-25T04:16:50+00:00" + "time": "2019-12-31T17:06:38+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "laminas/laminas-i18n", + "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/laminas/laminas-i18n.git", + "reference": "815be447f1c77f70a86bf24d00087fcb975b39ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/815be447f1c77f70a86bf24d00087fcb975b39ff", + "reference": "815be447f1c77f70a86bf24d00087fcb975b39ff", "shasum": "" }, "require": { - "php": ">=5.3.0" + "ext-intl": "*", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "phpspec/prophecy": "<1.9.0" + }, + "replace": { + "zendframework/zend-i18n": "self.version" + }, + "require-dev": { + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Provide translations for your application, and filter and validate internationalized values", + "homepage": "https://laminas.dev", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "i18n", + "laminas" ], - "time": "2017-02-14T16:28:37+00:00" + "time": "2019-12-31T17:07:17+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "laminas/laminas-inputfilter", + "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "git@github.com:laminas/laminas-inputfilter.git", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/b29ce8f512c966468eee37ea4873ae5fb545d00a", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-inputfilter": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", + "psr/http-message": "^1.0" + }, + "suggest": { + "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "homepage": "https://laminas.dev", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "inputfilter", + "laminas" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2019-12-31T17:11:54+00:00" }, { - "name": "psr/log", - "version": "1.1.2", + "name": "laminas/laminas-json", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "url": "https://github.com/laminas/laminas-json.git", + "reference": "db58425b7f0eba44a7539450cc926af80915951a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/db58425b7f0eba44a7539450cc926af80915951a", + "reference": "db58425b7f0eba44a7539450cc926af80915951a", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-json": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-xml": "^1.0.2", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "laminas/laminas-xml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "psr", - "psr-3" + "json", + "laminas" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2019-12-31T17:15:00+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "laminas/laminas-loader", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/laminas/laminas-loader.git", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/5d01c2c237ae9e68bec262f339947e2ea18979bc", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc", "shasum": "" }, "require": { - "php": ">=5.6" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-loader": "self.version" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" + } + }, "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "Laminas\\Loader\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } + "description": "Autoloading and plugin loading strategies", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "loader" ], - "description": "A polyfill for getallheaders.", - "time": "2019-03-08T08:55:37+00:00" + "time": "2019-12-31T17:18:27+00:00" }, { - "name": "ramsey/uuid", - "version": "3.8.0", + "name": "laminas/laminas-log", + "version": "2.12.0", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "url": "https://github.com/laminas/laminas-log.git", + "reference": "4e92d841b48868714a070b10866e94be80fc92ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/4e92d841b48868714a070b10866e94be80fc92ff", + "reference": "4e92d841b48868714a070b10866e94be80fc92ff", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/log": "^1.1.2" + }, + "provide": { + "psr/log-implementation": "1.0.0" }, "replace": { - "rhumsaa/uuid": "self.version" + "zendframework/zend-log": "self.version" }, "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1", + "mikey179/vfsstream": "^1.6.7", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15" }, "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "ext-mongo": "mongo extension to use Mongo writer", + "ext-mongodb": "mongodb extension to use MongoDB writer", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + }, + "laminas": { + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - }, - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - } - ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", + "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "homepage": "https://laminas.dev", "keywords": [ - "guid", - "identifier", - "uuid" + "laminas", + "log", + "logging" ], - "time": "2018-07-19T23:38:55+00:00" + "time": "2019-12-31T17:18:59+00:00" }, { - "name": "react/promise", - "version": "v2.7.1", + "name": "laminas/laminas-mail", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + "url": "https://github.com/laminas/laminas-mail.git", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/019fb670c1dff6be7fc91d3b88942bd0a5f68792", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792", "shasum": "" }, "require": { - "php": ">=5.4.0" + "ext-iconv": "*", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "true/punycode": "^2.1" + }, + "replace": { + "zendframework/zend-mail": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4" + }, + "suggest": { + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" + } + }, "autoload": { "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "Laminas\\Mail\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } + "BSD-3-Clause" ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", + "homepage": "https://laminas.dev", "keywords": [ - "promise", - "promises" + "laminas", + "mail" ], - "time": "2019-01-07T21:25:54+00:00" + "time": "2019-12-31T17:21:22+00:00" }, { - "name": "seld/jsonlint", - "version": "1.7.2", + "name": "laminas/laminas-math", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "url": "https://github.com/laminas/laminas-math.git", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/8027b37e00accc43f28605c7d8fd081baed1f475", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-math": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "fabpot/php-cs-fixer": "1.7.*", + "ircmaxell/random-lib": "~1.1", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-bcmath": "If using the bcmath functionality", + "ext-gmp": "If using the gmp functionality", + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + } + }, "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "JSON Linter", + "homepage": "https://laminas.dev", "keywords": [ - "json", - "linter", - "parser", - "validator" + "laminas", + "math" ], - "time": "2019-10-24T14:27:39+00:00" + "time": "2019-12-31T17:24:15+00:00" }, { - "name": "seld/phar-utils", - "version": "1.1.0", + "name": "laminas/laminas-mime", + "version": "2.7.3", "source": { "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" + "url": "https://github.com/laminas/laminas-mime.git", + "reference": "e844abb02e868fae154207929190292ad25057cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/e844abb02e868fae154207929190292ad25057cc", + "reference": "e844abb02e868fae154207929190292ad25057cc", "shasum": "" }, "require": { - "php": ">=5.3" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-mime": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" + }, + "suggest": { + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Seld\\PharUtils\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } + "BSD-3-Clause" ], - "description": "PHAR file format utilities, for when PHP phars you up", + "description": "Create and parse MIME messages and parts", + "homepage": "https://laminas.dev", "keywords": [ - "phar" + "laminas", + "mime" ], - "time": "2020-02-14T15:25:33+00:00" + "time": "2020-03-06T08:38:03+00:00" }, { - "name": "symfony/console", - "version": "v4.4.4", + "name": "laminas/laminas-modulemanager", + "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656" + "url": "https://github.com/laminas/laminas-modulemanager.git", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f512001679f37e6a042b51897ed24a2f05eba656", - "reference": "f512001679f37e6a042b51897ed24a2f05eba656", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/92b1cde1aab5aef687b863face6dd5d9c6751c78", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", - "symfony/lock": "<4.4", - "symfony/process": "<3.3" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "provide": { - "psr/log-implementation": "1.0" + "replace": { + "zendframework/zend-modulemanager": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\ModuleManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Modular application system for laminas-mvc applications", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "modulemanager" ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2020-01-25T12:44:29+00:00" + "time": "2019-12-31T17:26:56+00:00" }, { - "name": "symfony/css-selector", - "version": "v4.4.4", + "name": "laminas/laminas-mvc", + "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465" + "url": "https://github.com/laminas/laminas-mvc.git", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a167b1860995b926d279f9bb538f873e3bfa3465", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/7e7198b03556a57fb5fd3ed919d9e1cf71500642", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642", "shasum": "" }, "require": { - "php": "^7.1.3" + "container-interop/container-interop": "^1.1", + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-mvc": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "1.7.*", + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9", + "phpunit/phpunit": "^4.8.36", + "sebastian/comparator": "^1.2.4", + "sebastian/version": "^1.0.4" + }, + "suggest": { + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Laminas\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using laminas-servicemanager v3 and requiring the laminas-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Mvc\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mvc" ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2019-12-31T17:32:15+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v4.4.4", + "name": "laminas/laminas-psr7bridge", + "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b" + "url": "https://github.com/laminas/laminas-psr7bridge.git", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9e3de195e5bc301704dd6915df55892f6dfc208b", - "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/14780ef1d40effd59d77ab29c6d439b2af42cdfa", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/event-dispatcher-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5", + "psr/http-message": "^1.0" }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "1.1" + "replace": { + "zendframework/zend-psr7bridge": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/http-foundation": "^3.4|^4.0|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "1.0-dev", + "dev-develop": "1.1-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Psr7Bridge\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-7" ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2020-01-10T21:54:01+00:00" + "time": "2019-12-31T17:38:47+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.7", + "name": "laminas/laminas-serializer", + "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" + "url": "https://github.com/laminas/laminas-serializer.git", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/c1c9361f114271b0736db74e0083a919081af5e0", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-serializer": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "psr/event-dispatcher": "", - "symfony/event-dispatcher-implementation": "" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", + "description": "Serialize and deserialize PHP structures to a variety of representations", + "homepage": "https://laminas.dev", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "laminas", + "serializer" ], - "time": "2019-09-17T09:54:03+00:00" + "time": "2019-12-31T17:42:11+00:00" }, { - "name": "symfony/filesystem", - "version": "v4.4.4", + "name": "laminas/laminas-server", + "version": "2.8.1", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd" + "url": "https://github.com/laminas/laminas-server.git", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/4aaca9174c40a2fab2e2aa77999da99f71bdd88e", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-server": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Server\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Create Reflection-based RPC servers", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "server" ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2020-01-21T08:20:44+00:00" + "time": "2019-12-31T17:43:03+00:00" }, { - "name": "symfony/finder", - "version": "v4.4.4", + "name": "laminas/laminas-servicemanager", + "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "3a50be43515590faf812fbd7708200aabc327ec3" + "url": "https://github.com/laminas/laminas-servicemanager.git", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3a50be43515590faf812fbd7708200aabc327ec3", - "reference": "3a50be43515590faf812fbd7708200aabc327ec3", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/841abb656c6018afebeec1f355be438426d6a3dd", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd", "shasum": "" }, "require": { - "php": "^7.1.3" + "container-interop/container-interop": "~1.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-servicemanager": "self.version" + }, + "require-dev": { + "athletic/athletic": "dev-master", + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-di": "Laminas\\Di component", + "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\ServiceManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "servicemanager" ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2019-12-31T17:44:16+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.14.0", + "name": "laminas/laminas-session", + "version": "2.9.2", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" + "url": "https://github.com/laminas/laminas-session.git", + "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/fdba34c1b257235dba2fff6ed4df1844390f85f6", + "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-session": "self.version" + }, + "require-dev": { + "container-interop/container-interop": "^1.1", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "mongodb/mongodb": "^1.0.1", + "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" }, "suggest": { - "ext-ctype": "For best performance" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "mongodb/mongodb": "If you want to use the MongoDB session save handler" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Session\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", + "description": "Object-oriented interface to PHP sessions and storage", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "laminas", + "session" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-03-06T09:44:45+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.14.0", + "name": "laminas/laminas-soap", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" + "url": "https://github.com/laminas/laminas-soap.git", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-soap": "*", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-soap": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4", + "phpunit/phpunit": "^5.7.21 || ^6.3" }, "suggest": { - "ext-mbstring": "For best performance" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Soap\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "laminas", + "soap" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-12-31T17:48:49+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.14.0", + "name": "laminas/laminas-stdlib", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] + "Laminas\\Stdlib\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "laminas", + "stdlib" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2019-12-31T17:51:15+00:00" }, { - "name": "symfony/process", - "version": "v4.4.4", + "name": "laminas/laminas-text", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36" + "url": "https://github.com/laminas/laminas-text.git", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f5697ab4cb14a5deed7473819e63141bf5352c36", - "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/3601b5eacb06ed0a12f658df860cc0f9613cf4db", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-text": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Text\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Create FIGlets and text-based tables", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "text" ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2020-01-09T09:50:08+00:00" + "time": "2019-12-31T17:54:52+00:00" }, { - "name": "symfony/service-contracts", - "version": "v2.0.1", + "name": "laminas/laminas-uri", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "144c5e51266b281231e947b51223ba14acf1a749" + "url": "https://github.com/laminas/laminas-uri.git", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", - "reference": "144c5e51266b281231e947b51223ba14acf1a749", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/6be8ce19622f359b048ce4faebf1aa1bca73a7ff", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff", "shasum": "" }, "require": { - "php": "^7.2.5", - "psr/container": "^1.0" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "suggest": { - "symfony/service-implementation": "" + "replace": { + "zendframework/zend-uri": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", + "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://laminas.dev", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "laminas", + "uri" ], - "time": "2019-11-18T17:27:11+00:00" + "time": "2019-12-31T17:56:00+00:00" }, { - "name": "tedivm/jshrink", - "version": "v1.3.3", + "name": "laminas/laminas-validator", + "version": "2.13.1", "source": { "type": "git", - "url": "https://github.com/tedious/JShrink.git", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" + "url": "https://github.com/laminas/laminas-validator.git", + "reference": "36702f033486bf1953e254f5299aad205302e79d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/36702f033486bf1953e254f5299aad205302e79d", + "reference": "36702f033486bf1953e254f5299aad205302e79d", "shasum": "" }, "require": { - "php": "^5.6|^7.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.1" }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.8", - "php-coveralls/php-coveralls": "^1.1.0", - "phpunit/phpunit": "^6" + "replace": { + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^7.5.20 || ^8.5.2", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0" + }, + "suggest": { + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.13.x-dev", + "dev-develop": "2.14.x-dev" + }, + "laminas": { + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" + } + }, "autoload": { - "psr-0": { - "JShrink": "src/" + "psr-4": { + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Robert Hafner", - "email": "tedivm@tedivm.com" - } - ], - "description": "Javascript Minifier built in PHP", - "homepage": "http://github.com/tedious/JShrink", + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "homepage": "https://laminas.dev", "keywords": [ - "javascript", - "minifier" + "laminas", + "validator" ], - "time": "2019-06-28T18:11:46+00:00" + "time": "2020-01-15T09:59:30+00:00" }, { - "name": "true/punycode", - "version": "v2.1.1", + "name": "laminas/laminas-view", + "version": "2.11.4", "source": { "type": "git", - "url": "https://github.com/true/php-punycode.git", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" + "url": "https://github.com/laminas/laminas-view.git", + "reference": "3bbb2e94287383604c898284a18d2d06cf17301e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/3bbb2e94287383604c898284a18d2d06cf17301e", + "reference": "3bbb2e94287383604c898284a18d2d06cf17301e", "shasum": "" }, "require": { - "php": ">=5.3.0", - "symfony/polyfill-mbstring": "^1.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1 || ^3.0", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "phpunit/phpunit": "~4.7", - "squizlabs/php_codesniffer": "~2.0" + "replace": { + "zendframework/zend-view": "self.version" + }, + "require-dev": { + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^5.7.15 || ^6.0.8" + }, + "suggest": { + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "laminas-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with laminas-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, + "bin": [ + "bin/templatemap_generator.php" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" + } + }, "autoload": { "psr-4": { - "TrueBV\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Renan Gonçalves", - "email": "renan.saddam@gmail.com" - } + "BSD-3-Clause" ], - "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", - "homepage": "https://github.com/true/php-punycode", + "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "idna", - "punycode" + "laminas", + "view" ], - "time": "2016-11-16T10:37:54+00:00" + "time": "2019-12-31T18:03:30+00:00" }, { - "name": "tubalmartin/cssmin", - "version": "v4.1.1", + "name": "laminas/laminas-zendframework-bridge", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/0fb9675b84a1666ab45182b6c5b29956921e818d", + "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d", "shasum": "" }, "require": { - "ext-pcre": "*", - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { - "cogpowered/finediff": "0.3.*", - "phpunit/phpunit": "4.8.*" + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1", + "squizlabs/php_codesniffer": "^3.5" }, - "bin": [ - "cssmin" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" + }, + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" + } + }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "tubalmartin\\CssMin\\": "src" + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Túbal Martín", - "homepage": "http://tubalmartin.me/" - } - ], - "description": "A PHP port of the YUI CSS compressor", - "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", + "description": "Alias legacy ZF class names to Laminas Project equivalents.", "keywords": [ - "compress", - "compressor", - "css", - "cssmin", - "minify", - "yui" + "ZendFramework", + "autoloading", + "laminas", + "zf" ], - "time": "2018-01-15T15:26:51+00:00" + "time": "2020-01-07T22:58:31+00:00" }, { - "name": "webonyx/graphql-php", - "version": "v0.13.8", + "name": "league/flysystem", + "version": "1.0.65", "source": { "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "8f17b3ba67097aafb8318cd5c553b1acf7c891c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8f17b3ba67097aafb8318cd5c553b1acf7c891c8", + "reference": "8f17b3ba67097aafb8318cd5c553b1acf7c891c8", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1||^8.0" + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpbench/phpbench": "^0.14.0", - "phpstan/phpstan": "^0.11.4", - "phpstan/phpstan-phpunit": "^0.11.0", - "phpstan/phpstan-strict-rules": "^0.11.0", - "phpunit/phpcov": "^5.0", - "phpunit/phpunit": "^7.2", - "psr/http-message": "^1.0", - "react/promise": "2.*" + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.26" }, "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, "autoload": { "psr-4": { - "GraphQL\\": "src/" + "League\\Flysystem\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", "keywords": [ - "api", - "graphql" + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" ], - "time": "2019-08-25T10:32:47+00:00" + "time": "2020-03-08T18:53:20+00:00" }, { - "name": "wikimedia/less.php", - "version": "1.8.2", + "name": "league/flysystem-aws-s3-v3", + "version": "1.0.24", "source": { "type": "git", - "url": "https://github.com/wikimedia/less.php.git", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266" + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570", + "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570", "shasum": "" }, "require": { - "php": ">=7.2.9" + "aws/aws-sdk-php": "^3.0.0", + "league/flysystem": "^1.0.40", + "php": ">=5.5.0" }, "require-dev": { - "phpunit/phpunit": "7.5.14" + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "^2.0.0" }, - "bin": [ - "bin/lessc" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { - "psr-0": { - "Less": "lib/" - }, - "classmap": [ - "lessc.inc.php" - ] + "psr-4": { + "League\\Flysystem\\AwsS3v3\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" - }, - { - "name": "Matt Agar", - "homepage": "https://github.com/agar" - }, - { - "name": "Martin Jantošovič", - "homepage": "https://github.com/Mordred" + "name": "Frank de Jonge", + "email": "info@frenky.net" } ], - "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", - "keywords": [ - "css", - "less", - "less.js", - "lesscss", - "php", - "stylesheet" - ], - "time": "2019-11-06T18:30:11+00:00" + "description": "Flysystem adapter for the AWS S3 SDK v3.x", + "time": "2020-02-23T13:31:58+00:00" }, { - "name": "zendframework/zend-captcha", - "version": "2.9.0", + "name": "league/flysystem-azure-blob-storage", + "version": "0.1.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3" + "url": "https://github.com/thephpleague/flysystem-azure-blob-storage.git", + "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", + "url": "https://api.github.com/repos/thephpleague/flysystem-azure-blob-storage/zipball/97215345f3c42679299ba556a4d16d4847ee7f6d", + "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" + "guzzlehttp/psr7": "^1.5", + "league/flysystem": "^1.0", + "microsoft/azure-storage-blob": "^1.1", + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" - }, - "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "phpunit/phpunit": "^5.7" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "League\\Flysystem\\AzureBlobStorage\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", - "keywords": [ - "ZendFramework", - "captcha", - "zf" + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } ], - "abandoned": "laminas/laminas-captcha", - "time": "2019-06-18T09:32:52+00:00" + "time": "2019-06-07T20:42:16+00:00" }, { - "name": "zendframework/zend-code", - "version": "3.3.2", + "name": "magento/composer", + "version": "1.6.x-dev", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b" + "url": "https://github.com/magento/composer.git", + "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/936fa7ad4d53897ea3e3eb41b5b760828246a20b", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b", + "url": "https://api.github.com/repos/magento/composer/zipball/fe738ac9155f550b669b260b3cfa6422eacb53fa", + "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa", "shasum": "" }, "require": { - "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "composer/composer": "^1.6", + "php": "~7.1.3||~7.2.0||~7.3.0", + "symfony/console": "~4.4.0" }, "require-dev": { - "doctrine/annotations": "^1.0", - "ext-phar": "*", - "phpunit/phpunit": "^7.5.15", - "zendframework/zend-coding-standard": "^1.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "phpunit/phpunit": "~7.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3.x-dev", - "dev-develop": "3.4.x-dev" + "autoload": { + "psr-4": { + "Magento\\Composer\\": "src" } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Magento composer library helps to instantiate Composer application and run composer commands.", + "time": "2020-01-17T16:43:51+00:00" + }, + { + "name": "magento/magento-composer-installer", + "version": "0.1.13", + "source": { + "type": "git", + "url": "https://github.com/magento/magento-composer-installer.git", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "replace": { + "magento-hackathon/magento-composer-installer": "*" + }, + "require-dev": { + "composer/composer": "*@dev", + "firegento/phpcs": "dev-patch-1", + "mikey179/vfsstream": "*", + "phpunit/phpunit": "*", + "phpunit/phpunit-mock-objects": "dev-master", + "squizlabs/php_codesniffer": "1.4.7", + "symfony/process": "*" + }, + "type": "composer-plugin", + "extra": { + "composer-command-registry": [ + "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" + ], + "class": "MagentoHackathon\\Composer\\Magento\\Plugin" + }, "autoload": { - "psr-4": { - "Zend\\Code\\": "src/" + "psr-0": { + "MagentoHackathon\\Composer\\Magento": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "OSL-3.0" ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "authors": [ + { + "name": "Vinai Kopp", + "email": "vinai@netzarbeiter.com" + }, + { + "name": "Daniel Fahlke aka Flyingmana", + "email": "flyingmana@googlemail.com" + }, + { + "name": "Jörg Weller", + "email": "weller@flagbit.de" + }, + { + "name": "Karl Spies", + "email": "karl.spies@gmx.net" + }, + { + "name": "Tobias Vogt", + "email": "tobi@webguys.de" + }, + { + "name": "David Fuhr", + "email": "fuhr@flagbit.de" + } + ], + "description": "Composer installer for Magento modules", + "homepage": "https://github.com/magento/magento-composer-installer", "keywords": [ - "ZendFramework", - "code", - "zf" + "composer-installer", + "magento" ], - "abandoned": "laminas/laminas-code", - "time": "2019-08-31T14:14:34+00:00" + "time": "2017-12-29T16:45:24+00:00" }, { - "name": "zendframework/zend-config", - "version": "2.6.0", + "name": "magento/zendframework1", + "version": "1.14.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "url": "https://github.com/magento/zf1.git", + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/magento/zf1/zipball/726855dfb080089dc7bc7b016624129f8e7bc4e5", + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.2.11" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "phpunit/dbunit": "1.3.*", + "phpunit/phpunit": "3.7.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "1.12.x-dev" } }, "autoload": { - "psr-4": { - "Zend\\Config\\": "src/" + "psr-0": { + "Zend_": "library/" } }, "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "library/" + ], "license": [ "BSD-3-Clause" ], - "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "description": "Magento Zend Framework 1", + "homepage": "http://framework.zend.com/", "keywords": [ - "config", - "zf2" + "ZF1", + "framework" ], - "abandoned": "laminas/laminas-config", - "time": "2016-02-04T23:01:10+00:00" + "time": "2019-11-26T15:09:40+00:00" }, { - "name": "zendframework/zend-console", - "version": "2.8.0", + "name": "microsoft/azure-storage-blob", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae" + "url": "https://github.com/Azure/azure-storage-blob-php.git", + "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/95817ae78f73c48026972e350a2ecc31c6d9f9ae", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae", + "url": "https://api.github.com/repos/Azure/azure-storage-blob-php/zipball/6a333cd28a3742c3e99e79042dc6510f9f917919", + "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "microsoft/azure-storage-common": "~1.4", + "php": ">=5.6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "autoload": { + "psr-4": { + "MicrosoftAzure\\Storage\\Blob\\": "src/Blob" } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Azure Storage PHP Client Library", + "email": "dmsh@microsoft.com" + } + ], + "description": "This project provides a set of PHP client libraries that make it easy to access Microsoft Azure Storage Blob APIs.", + "keywords": [ + "azure", + "blob", + "php", + "sdk", + "storage" + ], + "time": "2020-01-02T07:18:59+00:00" + }, + { + "name": "microsoft/azure-storage-common", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/Azure/azure-storage-common-php.git", + "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Azure/azure-storage-common-php/zipball/be4df800761d0d0fa91a9460c7f42517197d57a0", + "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~6.0", + "php": ">=5.6.0" + }, + "type": "library", "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "MicrosoftAzure\\Storage\\Common\\": "src/Common" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Build console applications using getopt syntax or routing, complete with prompts", + "authors": [ + { + "name": "Azure Storage PHP Client Library", + "email": "dmsh@microsoft.com" + } + ], + "description": "This project provides a set of common code shared by Azure Storage Blob, Table, Queue and File PHP client libraries.", "keywords": [ - "ZendFramework", - "console", - "zf" + "azure", + "common", + "php", + "sdk", + "storage" ], - "abandoned": "laminas/laminas-console", - "time": "2019-02-04T19:48:22+00:00" + "time": "2020-01-02T07:15:54+00:00" }, { - "name": "zendframework/zend-crypt", - "version": "2.6.0", + "name": "monolog/monolog", + "version": "1.25.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fa82921994db851a8becaf3787a9e73c5976b6f1", + "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } ], - "homepage": "https://github.com/zendframework/zend-crypt", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", "keywords": [ - "crypt", - "zf2" + "log", + "logging", + "psr-3" ], - "abandoned": "laminas/laminas-crypt", - "time": "2016-02-03T23:46:30+00:00" + "time": "2019-12-20T14:15:16+00:00" }, { - "name": "zendframework/zend-db", - "version": "2.11.0", + "name": "mtdowling/jmespath.php", + "version": "2.5.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", - "reference": "71626f95f6f9ee326e4be3c34228c1c466300a2c" + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/71626f95f6f9ee326e4be3c34228c1c466300a2c", - "reference": "71626f95f6f9ee326e4be3c34228c1c466300a2c", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" }, + "bin": [ + "bin/jp.php" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - }, - "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "dev-master": "2.5-dev" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" - } + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", "keywords": [ - "ZendFramework", - "db", - "zf" + "json", + "jsonpath" ], - "abandoned": "laminas/laminas-db", - "time": "2019-12-31T19:43:46+00:00" + "time": "2019-12-30T18:03:34+00:00" }, { - "name": "zendframework/zend-di", - "version": "2.6.1", + "name": "paragonie/random_compat", + "version": "v9.99.99", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^7" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, - "autoload": { - "psr-4": { - "Zend\\Di\\": "src/" - } + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, + "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } ], - "homepage": "https://github.com/zendframework/zend-di", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ - "di", - "zf2" + "csprng", + "polyfill", + "pseudorandom", + "random" ], - "abandoned": "laminas/laminas-di", - "time": "2016-04-25T20:58:11+00:00" + "time": "2018-07-02T15:55:56+00:00" }, { - "name": "zendframework/zend-diactoros", - "version": "1.8.7", + "name": "paragonie/sodium_compat", + "version": "v1.12.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b" + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "3b953109fdfc821c1979bc829c8b7421721fef82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/a85e67b86e9b8520d07e6415fcbcb8391b44a75b", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3b953109fdfc821c1979bc829c8b7421721fef82", + "reference": "3b953109fdfc821c1979bc829c8b7421721fef82", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { - "ext-dom": "*", - "ext-libxml": "*", - "php-http/psr7-integration-tests": "dev-master", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "phpunit/phpunit": "^3|^4|^5|^6|^7" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-release-1.8": "1.8.x-dev" - } + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." }, + "type": "library", "autoload": { "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" - ], - "psr-4": { - "Zend\\Diactoros\\": "src/" - } + "autoload.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "ISC" ], - "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", "keywords": [ - "http", - "psr", - "psr-7" + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" ], - "abandoned": "laminas/laminas-diactoros", - "time": "2019-08-06T17:53:53+00:00" + "time": "2019-12-30T03:11:08+00:00" }, { - "name": "zendframework/zend-escaper", - "version": "2.6.1", + "name": "pelago/emogrifier", + "version": "v2.2.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f" + "url": "https://github.com/MyIntervals/emogrifier.git", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", + "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", + "symfony/css-selector": "^3.4.0 || ^4.0.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "friendsofphp/php-cs-fixer": "^2.2.0", + "phpmd/phpmd": "^2.6.0", + "phpunit/phpunit": "^4.8.0", + "squizlabs/php_codesniffer": "^3.3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Pelago\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "authors": [ + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Zoli Szabó", + "email": "zoli.szabo+github@gmail.com" + }, + { + "name": "John Reeve", + "email": "jreeve@pelagodesign.com" + }, + { + "name": "Jake Hotson", + "email": "jake@qzdesign.co.uk" + }, + { + "name": "Cameron Brooks" + }, + { + "name": "Jaime Prado" + } + ], + "description": "Converts CSS styles into inline style attributes in your HTML code", + "homepage": "https://www.myintervals.com/emogrifier.php", "keywords": [ - "ZendFramework", - "escaper", - "zf" + "css", + "email", + "pre-processing" ], - "abandoned": "laminas/laminas-escaper", - "time": "2019-09-05T20:03:20+00:00" + "time": "2019-09-04T16:07:59+00:00" }, { - "name": "zendframework/zend-eventmanager", - "version": "3.2.1", + "name": "php-amqplib/php-amqplib", + "version": "v2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd" + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "ext-bcmath": "*", + "ext-sockets": "*", + "php": ">=5.6" }, - "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + "replace": { + "videlalvaro/php-amqplib": "self.version" }, - "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + "require-dev": { + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" + "dev-master": "2.10-dev" } }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "PhpAmqpLib\\": "PhpAmqpLib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "LGPL-2.1-or-later" ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://github.com/zendframework/zend-eventmanager", + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "John Kelly", + "email": "johnmkelly86@gmail.com", + "role": "Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" + } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", "keywords": [ - "event", - "eventmanager", - "events", - "zf2" + "message", + "queue", + "rabbitmq" ], - "abandoned": "laminas/laminas-eventmanager", - "time": "2018-04-25T15:33:34+00:00" + "time": "2019-10-10T13:23:40+00:00" }, { - "name": "zendframework/zend-feed", - "version": "2.12.0", + "name": "phpseclib/mcrypt_compat", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733" + "url": "https://github.com/phpseclib/mcrypt_compat.git", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/d926c5af34b93a0121d5e2641af34ddb1533d733", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733", + "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^3.2.1" + "php": ">=5.3.3", + "phpseclib/phpseclib": ">=2.0.11 <3.0.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "phpunit/phpunit": "^4.8.35|^5.7|^6.0" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "ext-openssl": "Will enable faster cryptographic operations" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\Feed\\": "src/" - } + "files": [ + "lib/mcrypt.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides functionality for consuming RSS and Atom feeds", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "homepage": "http://phpseclib.sourceforge.net" + } + ], + "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", "keywords": [ - "ZendFramework", - "feed", - "zf" + "cryptograpy", + "encryption", + "mcrypt" ], - "abandoned": "laminas/laminas-feed", - "time": "2019-03-05T20:08:49+00:00" + "time": "2018-08-22T03:11:43+00:00" }, { - "name": "zendframework/zend-filter", - "version": "2.9.2", + "name": "phpseclib/phpseclib", + "version": "2.0.25", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef" + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/d78f2cdde1c31975e18b2a0753381ed7b61118ef", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c18159618ed7cd7ff721ac1a8fec7860a475d2f0", + "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" - }, - "conflict": { - "zendframework/zend-validator": "<2.10.1" + "php": ">=5.3.3" }, "require-dev": { - "pear/archive_tar": "^1.4.3", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "sami/sami": "~2.0", + "squizlabs/php_codesniffer": "~2.0" }, "suggest": { - "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" - } - }, "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { - "Zend\\Filter\\": "src/" + "phpseclib\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Programmatically filter and normalize data and files", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", "keywords": [ - "ZendFramework", - "filter", - "zf" + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" ], - "abandoned": "laminas/laminas-filter", - "time": "2019-08-19T07:08:04+00:00" + "time": "2020-02-25T04:16:50+00:00" }, { - "name": "zendframework/zend-form", - "version": "2.14.3", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/0b1616c59b1f3df194284e26f98c81ad0c377871", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" - }, - "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "php": ">=5.3.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.14.x-dev", - "dev-develop": "2.15.x-dev" - }, - "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "branch-alias": { + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" - }, - "files": [ - "autoload/formElementManagerPolyfill.php" - ] + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "ZendFramework", - "form", - "zf" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "abandoned": "laminas/laminas-form", - "time": "2019-10-04T10:46:36+00:00" + "time": "2017-02-14T16:28:37+00:00" }, { - "name": "zendframework/zend-http", - "version": "2.11.2", + "name": "psr/http-message", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", - "reference": "e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a" + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a", - "reference": "e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" - }, - "suggest": { - "paragonie/certainty": "For automated management of cacert.pem" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "ZendFramework", "http", - "http client", - "zend", - "zf" + "http-message", + "psr", + "psr-7", + "request", + "response" ], - "abandoned": "laminas/laminas-http", - "time": "2019-12-30T20:47:33+00:00" + "time": "2016-08-06T14:39:51+00:00" }, { - "name": "zendframework/zend-hydrator", - "version": "2.4.2", + "name": "psr/log", + "version": "1.1.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285" + "url": "https://github.com/php-fig/log.git", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/2bfc6845019e7b6d38b0ab5e55190244dc510285", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285", + "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-release-2.4": "2.4.x-dev" - }, - "zf": { - "component": "Zend\\Hydrator", - "config-provider": "Zend\\Hydrator\\ConfigProvider" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize objects to arrays, and vice versa", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "ZendFramework", - "hydrator", - "zf" + "log", + "psr", + "psr-3" ], - "abandoned": "laminas/laminas-hydrator", - "time": "2019-10-04T11:17:36+00:00" + "time": "2019-11-01T11:05:21+00:00" }, { - "name": "zendframework/zend-i18n", - "version": "2.10.1", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/84038e6a1838b611dcc491b1c40321fa4c3a123c", - "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "ext-intl": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "conflict": { - "phpspec/prophecy": "<1.9.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" - }, - "suggest": { - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" - } - }, "autoload": { - "psr-4": { - "Zend\\I18n\\": "src/" - } + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provide translations for your application, and filter and validate internationalized values", - "keywords": [ - "ZendFramework", - "i18n", - "zf" + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } ], - "abandoned": "laminas/laminas-i18n", - "time": "2019-12-12T14:08:22+00:00" + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" }, { - "name": "zendframework/zend-inputfilter", - "version": "2.10.1", + "name": "ramsey/uuid", + "version": "3.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e" + "url": "https://github.com/ramsey/uuid.git", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/1f44a2e9bc394a71638b43bc7024b572fa65410e", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "paragonie/random_compat": "^1.0|^2.0|9.99.99", + "php": "^5.4 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "replace": { + "rhumsaa/uuid": "self.version" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "codeception/aspect-mock": "^1.0 | ~2.0.0", + "doctrine/annotations": "~1.2.0", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "mockery/mockery": "^0.9.9", + "moontoast/math": "^1.1", + "php-mock/php-mock-phpunit": "^0.3|^1.1", + "phpunit/phpunit": "^4.7|^5.0|^6.5", + "squizlabs/php_codesniffer": "^2.3" }, "suggest": { - "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" + "ext-ctype": "Provides support for PHP Ctype functions", + "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", + "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Ramsey\\Uuid\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + }, + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", "keywords": [ - "ZendFramework", - "inputfilter", - "zf" + "guid", + "identifier", + "uuid" ], - "abandoned": "laminas/laminas-inputfilter", - "time": "2019-08-28T19:45:32+00:00" + "time": "2018-07-19T23:38:55+00:00" }, { - "name": "zendframework/zend-json", - "version": "2.6.1", + "name": "react/promise", + "version": "v2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" + "php": ">=5.4.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", - "zendframework/zendxml": "^1.0.2" - }, - "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "phpunit/phpunit": "~4.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" - } + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "json", - "zf2" + "promise", + "promises" ], - "abandoned": "laminas/laminas-json", - "time": "2016-02-04T21:20:26+00:00" + "time": "2019-01-07T21:25:54+00:00" }, { - "name": "zendframework/zend-loader", - "version": "2.6.1", + "name": "seld/jsonlint", + "version": "1.7.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", - "reference": "91da574d29b58547385b2298c020b257310898c6" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/91da574d29b58547385b2298c020b257310898c6", - "reference": "91da574d29b58547385b2298c020b257310898c6", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, + "bin": [ + "bin/jsonlint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Autoloading and plugin loading strategies", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", "keywords": [ - "ZendFramework", - "loader", - "zf" + "json", + "linter", + "parser", + "validator" ], - "abandoned": "laminas/laminas-loader", - "time": "2019-09-04T19:38:14+00:00" + "time": "2019-10-24T14:27:39+00:00" }, { - "name": "zendframework/zend-log", - "version": "2.12.0", + "name": "seld/phar-utils", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", - "reference": "e5ec088dc8a7b4d96a3a6627761f720a738a36b8" + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/e5ec088dc8a7b4d96a3a6627761f720a738a36b8", - "reference": "e5ec088dc8a7b4d96a3a6627761f720a738a36b8", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/log": "^1.1.2", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "mikey179/vfsstream": "^1.6.7", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "ext-mongo": "mongo extension to use Mongo writer", - "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" - }, - "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Seld\\PharUtils\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "ZendFramework", - "log", - "logging", - "zf" + "phar" ], - "abandoned": "laminas/laminas-log", - "time": "2019-12-27T16:18:31+00:00" + "time": "2020-02-14T15:25:33+00:00" }, { - "name": "zendframework/zend-mail", - "version": "2.10.0", + "name": "symfony/console", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" + "url": "https://github.com/symfony/console.git", + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": "^5.6 || ^7.0", - "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" - } + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", - "keywords": [ - "ZendFramework", - "mail", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-mail", - "time": "2018-06-07T13:37:07+00:00" + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2020-02-24T13:10:00+00:00" }, { - "name": "zendframework/zend-math", - "version": "2.7.1", + "name": "symfony/css-selector", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" + "url": "https://github.com/symfony/css-selector.git", + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "ircmaxell/random-lib": "~1.1", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-bcmath": "If using the bcmath functionality", - "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" - } + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-math", - "keywords": [ - "math", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-math", - "time": "2018-12-04T15:34:17+00:00" + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2020-02-04T09:01:01+00:00" }, { - "name": "zendframework/zend-mime", - "version": "2.7.2", + "name": "symfony/event-dispatcher", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/c91e0350be53cc9d29be15563445eec3b269d7c1", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d", + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^7.1.3", + "symfony/event-dispatcher-contracts": "^1.1" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "1.1" }, "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/stopwatch": "^3.4|^4.0|^5.0" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" - } + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create and parse MIME messages and parts", - "keywords": [ - "ZendFramework", - "mime", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-mime", - "time": "2019-10-16T19:30:37+00:00" + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2020-02-04T09:32:40+00:00" }, { - "name": "zendframework/zend-modulemanager", - "version": "2.8.4", + "name": "symfony/event-dispatcher-contracts", + "version": "v1.1.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "php": "^7.1.3" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "psr/event-dispatcher": "", + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "description": "Modular application system for zend-mvc applications", + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "modulemanager", - "zf" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "abandoned": "laminas/laminas-modulemanager", - "time": "2019-10-28T13:29:38+00:00" + "time": "2019-09-17T09:54:03+00:00" }, { - "name": "zendframework/zend-mvc", - "version": "2.7.15", + "name": "symfony/filesystem", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" + "url": "https://github.com/symfony/filesystem.git", + "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd", + "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" - }, - "replace": { - "zendframework/zend-router": "^2.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", - "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" - }, - "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "4.4-dev" } }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { - "Zend\\Mvc\\": "src/" - } + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-mvc", - "keywords": [ - "mvc", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-mvc", - "time": "2018-05-03T13:13:41+00:00" + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2020-01-21T08:20:44+00:00" }, { - "name": "zendframework/zend-psr7bridge", - "version": "0.2.2", + "name": "symfony/finder", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" + "url": "https://github.com/symfony/finder.git", + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357", + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357", "shasum": "" }, "require": { - "php": ">=5.5", - "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.7", - "squizlabs/php_codesniffer": "^2.3" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" - } + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", - "keywords": [ - "http", - "psr", - "psr-7" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-psr7bridge", - "time": "2016-05-10T21:44:39+00:00" + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2020-02-14T07:42:58+00:00" }, { - "name": "zendframework/zend-serializer", - "version": "2.9.1", + "name": "symfony/polyfill-ctype", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "php": ">=5.3.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "dev-master": "1.14-dev" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" - } + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize and deserialize PHP structures to a variety of representations", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "serializer", - "zf" + "compatibility", + "ctype", + "polyfill", + "portable" ], - "abandoned": "laminas/laminas-serializer", - "time": "2019-10-19T08:06:30+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { - "name": "zendframework/zend-server", - "version": "2.8.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/d80c44700ebb92191dd9a3005316a6ab6637c0d1", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "1.14-dev" } }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" - } + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create Reflection-based RPC servers", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "server", - "zf" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], - "abandoned": "laminas/laminas-server", - "time": "2019-10-16T18:27:05+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { - "name": "zendframework/zend-servicemanager", - "version": "2.7.11", + "name": "symfony/polyfill-php73", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", + "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "athletic/athletic": "dev-master", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" - }, - "suggest": { - "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "1.14-dev" } }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" - } + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "servicemanager", - "zf2" + "compatibility", + "polyfill", + "portable", + "shim" ], - "abandoned": "laminas/laminas-servicemanager", - "time": "2018-06-22T14:49:54+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { - "name": "zendframework/zend-session", - "version": "2.9.1", + "name": "symfony/process", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" + "url": "https://github.com/symfony/process.git", + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", + "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7", + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "container-interop/container-interop": "^1.1", - "mongodb/mongodb": "^1.0.1", - "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" - }, - "suggest": { - "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" - } + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Object-oriented interface to PHP sessions and storage", - "keywords": [ - "ZendFramework", - "session", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "abandoned": "laminas/laminas-session", - "time": "2019-10-28T19:40:43+00:00" + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2020-02-07T20:06:44+00:00" }, { - "name": "zendframework/zend-soap", - "version": "2.8.0", + "name": "symfony/service-contracts", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", - "reference": "8762d79efa220d82529c43ce08d70554146be645" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "144c5e51266b281231e947b51223ba14acf1a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/8762d79efa220d82529c43ce08d70554146be645", - "reference": "8762d79efa220d82529c43ce08d70554146be645", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", "shasum": "" }, "require": { - "ext-soap": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "php": "^7.2.5", + "psr/container": "^1.0" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Symfony\\Contracts\\Service\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "homepage": "https://github.com/zendframework/zend-soap", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", "keywords": [ - "soap", - "zf2" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "abandoned": "laminas/laminas-soap", - "time": "2019-04-30T16:45:35+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { - "name": "zendframework/zend-stdlib", - "version": "3.2.1", + "name": "tedivm/jshrink", + "version": "v1.3.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "66536006722aff9e62d1b331025089b7ec71c065" + "url": "https://github.com/tedious/JShrink.git", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", - "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6|^7.0" }, "require-dev": { - "phpbench/phpbench": "^0.13", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "friendsofphp/php-cs-fixer": "^2.8", + "php-coveralls/php-coveralls": "^1.1.0", + "phpunit/phpunit": "^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\Stdlib\\": "src/" + "psr-0": { + "JShrink": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "SPL extensions, array utilities, error handlers, and more", + "authors": [ + { + "name": "Robert Hafner", + "email": "tedivm@tedivm.com" + } + ], + "description": "Javascript Minifier built in PHP", + "homepage": "http://github.com/tedious/JShrink", "keywords": [ - "ZendFramework", - "stdlib", - "zf" + "javascript", + "minifier" ], - "abandoned": "laminas/laminas-stdlib", - "time": "2018-08-28T21:34:05+00:00" + "time": "2019-06-28T18:11:46+00:00" }, { - "name": "zendframework/zend-text", - "version": "2.7.1", + "name": "true/punycode", + "version": "v2.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d" + "url": "https://github.com/true/php-punycode.git", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/41e32dafa4015e160e2f95a7039554385c71624d", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d", + "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.3.0", + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "phpunit/phpunit": "~4.7", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "TrueBV\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create FIGlets and text-based tables", + "authors": [ + { + "name": "Renan Gonçalves", + "email": "renan.saddam@gmail.com" + } + ], + "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", + "homepage": "https://github.com/true/php-punycode", "keywords": [ - "ZendFramework", - "text", - "zf" + "idna", + "punycode" ], - "abandoned": "laminas/laminas-text", - "time": "2019-10-16T20:36:27+00:00" + "time": "2016-11-16T10:37:54+00:00" }, { - "name": "zendframework/zend-uri", - "version": "2.7.1", + "name": "tubalmartin/cssmin", + "version": "v4.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083" + "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bfc4a5b9a309711e968d7c72afae4ac50c650083", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083", + "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "ext-pcre": "*", + "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "cogpowered/finediff": "0.3.*", + "phpunit/phpunit": "4.8.*" }, + "bin": [ + "cssmin" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "tubalmartin\\CssMin\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "authors": [ + { + "name": "Túbal Martín", + "homepage": "http://tubalmartin.me/" + } + ], + "description": "A PHP port of the YUI CSS compressor", + "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", "keywords": [ - "ZendFramework", - "uri", - "zf" + "compress", + "compressor", + "css", + "cssmin", + "minify", + "yui" ], - "abandoned": "laminas/laminas-uri", - "time": "2019-10-07T13:35:33+00:00" + "time": "2018-01-15T15:26:51+00:00" }, { - "name": "zendframework/zend-validator", - "version": "2.13.0", + "name": "webonyx/graphql-php", + "version": "v0.13.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", - "reference": "b54acef1f407741c5347f2a97f899ab21f2229ef" + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/b54acef1f407741c5347f2a97f899ab21f2229ef", - "reference": "b54acef1f407741c5347f2a97f899ab21f2229ef", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^7.1", - "zendframework/zend-stdlib": "^3.2.1" + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1||^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", + "doctrine/coding-standard": "^6.0", + "phpbench/phpbench": "^0.14.0", + "phpstan/phpstan": "^0.11.4", + "phpstan/phpstan-phpunit": "^0.11.0", + "phpstan/phpstan-strict-rules": "^0.11.0", + "phpunit/phpcov": "^5.0", + "phpunit/phpunit": "^7.2", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "react/promise": "2.*" }, "suggest": { - "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.13.x-dev", - "dev-develop": "2.14.x-dev" - }, - "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" - } - }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "GraphQL\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", "keywords": [ - "ZendFramework", - "validator", - "zf" + "api", + "graphql" ], - "abandoned": "laminas/laminas-validator", - "time": "2019-12-28T04:07:18+00:00" + "time": "2019-08-25T10:32:47+00:00" }, { - "name": "zendframework/zend-view", - "version": "2.11.4", + "name": "wikimedia/less.php", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", - "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d" + "url": "https://github.com/wikimedia/less.php.git", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", - "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1 || ^3.0", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "php": ">=7.2.9" }, - "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "require-dev": { + "phpunit/phpunit": "7.5.14" }, "bin": [ - "bin/templatemap_generator.php" + "bin/lessc" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\View\\": "src/" - } + "psr-0": { + "Less": "lib/" + }, + "classmap": [ + "lessc.inc.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "Apache-2.0" ], - "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, + { + "name": "Matt Agar", + "homepage": "https://github.com/agar" + }, + { + "name": "Martin Jantošovič", + "homepage": "https://github.com/Mordred" + } + ], + "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", "keywords": [ - "ZendFramework", - "view", - "zf" + "css", + "less", + "less.js", + "lesscss", + "php", + "stylesheet" ], - "abandoned": "laminas/laminas-view", - "time": "2019-12-04T08:40:50+00:00" + "time": "2019-11-06T18:30:11+00:00" } ], "packages-dev": [ @@ -7912,16 +8124,16 @@ }, { "name": "php-webdriver/webdriver", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "262ea0d209c292e0330be1041424887bbbffef04" + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/262ea0d209c292e0330be1041424887bbbffef04", - "reference": "262ea0d209c292e0330be1041424887bbbffef04", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", "shasum": "" }, "require": { @@ -7973,7 +8185,7 @@ "selenium", "webdriver" ], - "time": "2020-02-17T08:14:38+00:00" + "time": "2020-03-04T14:40:12+00:00" }, { "name": "phpcollection/phpcollection", @@ -8188,26 +8400,25 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", "shasum": "" }, "require": { - "php": "^7.1", + "php": "^7.2", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" }, "type": "library", "extra": { @@ -8231,7 +8442,7 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" + "time": "2020-02-18T18:59:58+00:00" }, { "name": "phpmd/phpmd", @@ -8358,16 +8569,16 @@ }, { "name": "phpspec/prophecy", - "version": "v1.10.2", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { @@ -8417,20 +8628,20 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.11", + "version": "0.12.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b" + "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ca5f2b7cf81c6d8fba74f9576970399c5817e03b", - "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37bdd26a80235d0f9045b49f4151102b7831cbe2", + "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2", "shasum": "" }, "require": { @@ -8456,7 +8667,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-02-16T14:00:29+00:00" + "time": "2020-03-02T22:29:43+00:00" }, { "name": "phpunit/php-code-coverage", @@ -9650,16 +9861,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "45cae6dd8683d2de56df7ec23638e9429c70135f" + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/45cae6dd8683d2de56df7ec23638e9429c70135f", - "reference": "45cae6dd8683d2de56df7ec23638e9429c70135f", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/090ce406505149d6852a7c03b0346dec3b8cf612", + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612", "shasum": "" }, "require": { @@ -9705,20 +9916,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-23T10:00:59+00:00" }, { "name": "symfony/config", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "4d3979f54472637169080f802dc82197e21fdcce" + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4d3979f54472637169080f802dc82197e21fdcce", - "reference": "4d3979f54472637169080f802dc82197e21fdcce", + "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", "shasum": "" }, "require": { @@ -9769,20 +9980,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-04T09:32:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a" + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ec60a7d12f5e8ab0f99456adce724717d9c1784a", - "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342", + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342", "shasum": "" }, "require": { @@ -9842,20 +10053,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:49:27+00:00" + "time": "2020-02-29T09:50:10+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "b66fe8ccc850ea11c4cd31677706c1219768bea1" + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b66fe8ccc850ea11c4cd31677706c1219768bea1", - "reference": "b66fe8ccc850ea11c4cd31677706c1219768bea1", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/11dcf08f12f29981bf770f097a5d64d65bce5929", + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929", "shasum": "" }, "require": { @@ -9903,20 +10114,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-29T10:05:28+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5" + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/491a20dfa87e0b3990170593bc2de0bb34d828a5", - "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648", + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648", "shasum": "" }, "require": { @@ -9958,20 +10169,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-01-31T09:11:17+00:00" + "time": "2020-02-13T19:40:01+00:00" }, { "name": "symfony/mime", - "version": "v5.0.4", + "version": "v5.0.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59" + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/2a3c7fee1f1a0961fa9cf360d5da553d05095e59", - "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59", + "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", "shasum": "" }, "require": { @@ -10020,11 +10231,11 @@ "mime", "mime-type" ], - "time": "2020-01-04T14:08:26+00:00" + "time": "2020-02-04T09:41:09+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -10254,7 +10465,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -10304,16 +10515,16 @@ }, { "name": "symfony/yaml", - "version": "v4.4.4", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "cd014e425b3668220adb865f53bff64b3ad21767" + "reference": "94d005c176db2080e98825d98e01e8b311a97a88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/cd014e425b3668220adb865f53bff64b3ad21767", - "reference": "cd014e425b3668220adb865f53bff64b3ad21767", + "url": "https://api.github.com/repos/symfony/yaml/zipball/94d005c176db2080e98825d98e01e8b311a97a88", + "reference": "94d005c176db2080e98825d98e01e8b311a97a88", "shasum": "" }, "require": { @@ -10359,7 +10570,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2020-01-21T11:12:16+00:00" + "time": "2020-02-03T10:46:43+00:00" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 9b0b53e11615f..c5fdd050bb46b 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -691,14 +691,14 @@ public function testConfirmationEmailWithSpecialCharacters(): void $message = $this->transportBuilderMock->getSentMessage(); $rawMessage = $message->getRawMessage(); - /** @var \Zend\Mime\Part $messageBodyPart */ + /** @var \Laminas\Mime\Part $messageBodyPart */ $messageBodyParts = $message->getBody()->getParts(); $messageBodyPart = reset($messageBodyParts); $messageEncoding = $messageBodyPart->getCharset(); $name = 'John Smith'; if (strtoupper($messageEncoding) !== 'ASCII') { - $name = \Zend\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); + $name = \Laminas\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); } $nameEmail = sprintf('%s <%s>', $name, $email); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php index cb338d00cab16..1fc58ff136c92 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php @@ -5,8 +5,8 @@ */ namespace Magento\Framework\App\Response\HeaderProvider; +use Laminas\Http\Header\HeaderInterface; use Magento\Framework\App\Response\Http as HttpResponse; -use Zend\Http\Header\HeaderInterface; /** * Class AbstractHeaderTestCase @@ -26,8 +26,7 @@ public function setUp() parent::setUp(); $this->_objectManager->configure( [ - 'preferences' => - [ + 'preferences' => [ HttpResponse::class => 'Magento\Framework\App\Response\Http\Interceptor' ] ] diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index a5a04228b4d2a..35fc402d3805f 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -11,6 +11,7 @@ }, "require": { "php": "~7.1.3||~7.2.0||~7.3.0", + "ext-bcmath": "*", "ext-curl": "*", "ext-dom": "*", "ext-gd": "*", @@ -20,27 +21,26 @@ "ext-openssl": "*", "ext-simplexml": "*", "ext-xsl": "*", - "ext-bcmath": "*", "lib-libxml": "*", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", + "guzzlehttp/guzzle": "^6.3.3", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", - "wikimedia/less.php": "~1.8.0", + "ramsey/uuid": "~3.8.0", "symfony/console": "~4.4.0", "symfony/process": "~4.4.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", - "guzzlehttp/guzzle": "^6.3.3", - "ramsey/uuid": "~3.8.0" + "wikimedia/less.php": "~1.8.0" }, "archive": { "exclude": [ From 5a836abef542dffa23bd131f74d9f9afc9860c17 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 9 Mar 2020 17:02:32 -0500 Subject: [PATCH 308/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas --- .../CacheInvalidate/Model/PurgeCache.php | 2 +- .../CacheInvalidate/Model/SocketFactory.php | 7 + .../Captcha/Model/ResourceModel/Log.php | 2 +- .../Product/Frontend/Action/Synchronize.php | 8 +- .../Model/Cart/RequestQuantityProcessor.php | 3 + .../Patch/Data/AddDownloadableHostsConfig.php | 13 +- .../Test/Unit/Model/Oauth/ConsumerTest.php | 4 +- .../App/FrontController/BuiltinPlugin.php | 2 + .../Test/Unit/Model/Cache/ServerTest.php | 3 + .../Quote/Model/Quote/Item/Updater.php | 3 +- .../Controller/Adminhtml/Feed/IndexTest.php | 3 - .../Test/Unit/Controller/Feed/IndexTest.php | 4 +- .../Ui/Controller/Adminhtml/Index/Render.php | 8 +- .../Magento/Ui/Controller/Index/Render.php | 7 +- .../Webapi/Model/Config/ClassReflector.php | 30 ++-- app/code/Magento/Webapi/Model/Soap/Wsdl.php | 2 +- .../Model/Soap/Wsdl/ComplexTypeStrategy.php | 39 +++-- app/etc/di.xml | 1 + composer.json | 2 +- .../Authentication/OauthHelper.php | 18 ++- .../Magento/TestFramework/Request.php | 2 + .../Controller/Cards/DeleteActionTest.php | 7 +- .../SourceClassWithNamespace.php | 2 + .../GraphQl/Config/GraphQlReaderTest.php | 2 + .../Controller/Subscriber/NewActionTest.php | 2 +- .../Magento/Phpserver/PhpserverTest.php | 1 + .../Formatters/FilteredErrorFormatter.php | 1 + .../Integrity/Library/Injectable.php | 3 + .../Test/Integrity/Library/InjectableTest.php | 10 +- .../Api/ExtensibleInterfacesTest.php | 3 +- .../Magento/Test/Integrity/PublicCodeTest.php | 1 + lib/internal/Magento/Framework/App/Feed.php | 2 +- .../Magento/Framework/App/Request/Http.php | 3 +- .../Code/Generator/ClassGenerator.php | 14 +- .../Code/Generator/EntityAbstract.php | 22 ++- .../Generator/InterfaceMethodGenerator.php | 4 +- .../Framework/Code/Reader/ArgumentsReader.php | 3 + .../Framework/DB/Adapter/Pdo/Mysql.php | 2 + .../Data/Test/Unit/Form/FormKeyTest.php | 7 +- .../Magento/Framework/Filesystem/Glob.php | 7 +- .../Magento/Framework/Mail/EmailMessage.php | 4 +- .../Magento/Framework/Mail/Message.php | 2 +- .../Magento/Framework/Mail/MimeMessage.php | 2 +- .../Magento/Framework/Mail/Transport.php | 3 + .../Code/Generator/RemoteServiceGenerator.php | 10 +- .../Framework/Oauth/Helper/Request.php | 5 +- .../Code/Generator/Repository.php | 5 +- .../ExtensionAttributesProcessor.php | 6 + .../Framework/Reflection/MethodsMap.php | 7 + .../Framework/Reflection/NameFinder.php | 3 + .../Validator/CookieDomainValidator.php | 5 +- .../Magento/Framework/Stdlib/Parameters.php | 2 +- .../Magento/Framework/Url/Validator.php | 17 +-- .../Framework/Validator/AllowedProtocols.php | 10 +- setup/config/module.config.php | 2 +- setup/src/Magento/Setup/Application.php | 4 +- .../Magento/Setup/Controller/AddDatabase.php | 5 + .../Setup/Controller/BackupActionItems.php | 58 +++++--- .../Setup/Controller/CompleteBackup.php | 9 +- .../Setup/Controller/CreateAdminAccount.php | 5 + .../Magento/Setup/Controller/CreateBackup.php | 5 + .../Setup/Controller/CustomizeYourStore.php | 8 +- .../Setup/Controller/DatabaseCheck.php | 2 +- .../Setup/Controller/DependencyCheck.php | 15 +- .../Magento/Setup/Controller/Environment.php | 5 + setup/src/Magento/Setup/Controller/Home.php | 2 + setup/src/Magento/Setup/Controller/Index.php | 2 + .../src/Magento/Setup/Controller/Install.php | 20 +-- .../src/Magento/Setup/Controller/License.php | 7 +- .../Magento/Setup/Controller/Maintenance.php | 8 +- .../Magento/Setup/Controller/Marketplace.php | 13 +- .../Controller/MarketplaceCredentials.php | 7 +- .../src/Magento/Setup/Controller/Modules.php | 3 + .../Magento/Setup/Controller/Navigation.php | 17 ++- .../Controller/ReadinessCheckInstaller.php | 11 +- .../Controller/ReadinessCheckUpdater.php | 11 +- .../Setup/Controller/SelectVersion.php | 7 +- .../src/Magento/Setup/Controller/Session.php | 3 + .../src/Magento/Setup/Controller/Success.php | 6 + .../Magento/Setup/Controller/SystemConfig.php | 8 +- .../Setup/Controller/UpdaterSuccess.php | 10 +- .../src/Magento/Setup/Controller/UrlCheck.php | 4 + .../Setup/Controller/WebConfiguration.php | 5 + .../Setup/Model/AdminAccountFactory.php | 7 +- .../Model/ConfigOptionsListCollector.php | 4 +- .../Magento/Setup/Model/Cron/JobFactory.php | 14 +- .../Magento/Setup/Model/InstallerFactory.php | 9 +- setup/src/Magento/Setup/Model/Navigation.php | 9 ++ .../src/Magento/Setup/Model/PackagesAuth.php | 15 +- setup/src/Magento/Setup/Module.php | 21 ++- .../Setup/Module/ConnectionFactory.php | 3 +- .../Module/Di/Code/Reader/FileScanner.php | 6 +- .../Magento/Setup/Module/ResourceFactory.php | 20 ++- .../Mvc/View/Http/InjectTemplateListener.php | 5 + .../Unit/Controller/ExtensionGridTest.php | 3 +- .../Test/Unit/Controller/ModuleGridTest.php | 2 +- .../Test/Unit/Controller/StartUpdaterTest.php | 3 +- .../Controller/UpdateExtensionGridTest.php | 2 +- .../Unit/Model/ObjectManagerProviderTest.php | 12 +- .../Mvc/Bootstrap/InitParamListenerTest.php | 134 +++++++++++------- .../LazyControllerAbstractFactory.php | 18 ++- 101 files changed, 599 insertions(+), 290 deletions(-) diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index a111414fd3074..aeef8d00f720a 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -8,7 +8,7 @@ use Magento\Framework\Cache\InvalidateLogger; /** - * Class PurgeCache + * PurgeCache model */ class PurgeCache { diff --git a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php index 25b4228d9de5e..5a2d602308e92 100644 --- a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php +++ b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php @@ -3,11 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CacheInvalidate\Model; +/** + * Factory for the \Laminas\Http\Client\Adapter\Socket + */ class SocketFactory { /** + * Create object + * * @return \Laminas\Http\Client\Adapter\Socket */ public function create() diff --git a/app/code/Magento/Captcha/Model/ResourceModel/Log.php b/app/code/Magento/Captcha/Model/ResourceModel/Log.php index 30c20fdeb3956..83d055da7c26d 100644 --- a/app/code/Magento/Captcha/Model/ResourceModel/Log.php +++ b/app/code/Magento/Captcha/Model/ResourceModel/Log.php @@ -13,7 +13,7 @@ class Log extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** - * Type Remote Address + * Remote Address log type */ const TYPE_REMOTE_ADDRESS = 1; diff --git a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php index f6896fe6a7a99..6e76d3510103a 100644 --- a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php +++ b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php @@ -7,12 +7,13 @@ use Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\JsonFactory; /** * Synchronizes Product Frontend Actions with database */ -class Synchronize extends \Magento\Framework\App\Action\Action +class Synchronize extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface { /** * @var Context @@ -46,6 +47,8 @@ public function __construct( } /** + * @inheritDoc + * * This is handle for synchronizing between frontend and backend product actions: * - visit product page (recently_viewed) * - compare products (recently_compared) @@ -57,9 +60,6 @@ public function __construct( * 'added_at' => "JS_TIMESTAMP" * ] * ] - * - * - * @inheritdoc */ public function execute() { diff --git a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php index d8597eb3640b3..27566ba6805af 100644 --- a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php +++ b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php @@ -9,6 +9,9 @@ use Magento\Framework\Locale\ResolverInterface; +/** + * Cart request quantity processor + */ class RequestQuantityProcessor { /** diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 642b1734310ea..b962e3af6e0aa 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -7,21 +7,22 @@ namespace Magento\Downloadable\Setup\Patch\Data; +use Laminas\Uri\Uri as UriHandler; +use Magento\Backend\App\Area\FrontNameResolver; use Magento\Config\Model\Config\Backend\Admin\Custom; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Url\ScopeResolverInterface; use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; -use Laminas\Uri\Uri as UriHandler; -use Magento\Framework\Url\ScopeResolverInterface; -use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Backend\App\Area\FrontNameResolver; /** * Adding base url as allowed downloadable domain. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AddDownloadableHostsConfig implements DataPatchInterface { @@ -79,7 +80,7 @@ public function __construct( } /** - * @inheritdoc + * @inheritDoc */ public function apply() { diff --git a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php index 13df65f05fcca..29b96f229b4b1 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php @@ -111,8 +111,8 @@ protected function setUp() ); $this->validDataArray = [ - 'key' => md5(uniqid()), - 'secret' => md5(uniqid()), + 'key' => md5(uniqid()), // phpcs:ignore Magento2.Security.InsecureFunction + 'secret' => md5(uniqid()), // phpcs:ignore Magento2.Security.InsecureFunction 'callback_url' => 'http://example.com/callback', 'rejected_callback_url' => 'http://example.com/rejectedCallback' ]; diff --git a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php index 8fcecea2e23d2..7018efa6238b6 100644 --- a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php @@ -53,6 +53,8 @@ public function __construct( } /** + * Add PageCache functionality to Dispatch method + * * @param \Magento\Framework\App\FrontControllerInterface $subject * @param callable $proceed * @param \Magento\Framework\App\RequestInterface $request diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php index 57c4bb7107b13..553d86abd6546 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php @@ -23,6 +23,9 @@ class ServerTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\UrlInterface */ protected $urlBuilderMock; + /** @var \PHPUnit_Framework_MockObject_MockObject| \Magento\Framework\Cache\InvalidateLogger */ + private $loggerMock; + protected function setUp() { $this->configMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); diff --git a/app/code/Magento/Quote/Model/Quote/Item/Updater.php b/app/code/Magento/Quote/Model/Quote/Item/Updater.php index 410eb69e96ff5..270d9160161a8 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Updater.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Updater.php @@ -8,12 +8,11 @@ use Magento\Catalog\Model\ProductFactory; use Magento\Framework\Locale\FormatInterface; use Magento\Framework\DataObject\Factory as ObjectFactory; -use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item; use Laminas\Code\Exception\InvalidArgumentException; /** - * Class Updater + * Quote item updater */ class Updater { diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php index 3964cec18da8d..92348d2b9cb58 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php @@ -6,11 +6,8 @@ namespace Magento\Rss\Test\Unit\Controller\Adminhtml\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Laminas\Feed\Writer\Exception\InvalidArgumentException; /** - * Class IndexTest - * @package Magento\Rss\Controller\Feed * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class IndexTest extends \PHPUnit\Framework\TestCase diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php index 3afd9f7833bba..51d6b01b48bb5 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php @@ -6,11 +6,9 @@ namespace Magento\Rss\Test\Unit\Controller\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Laminas\Feed\Writer\Exception\InvalidArgumentException; /** - * Class IndexTest - * @package Magento\Rss\Controller\Feed + * Test for \Magento\Rss\Controller\Feed\Index */ class IndexTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index bbdbcc637a5a0..a1502b0650e2b 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -13,11 +13,15 @@ use Psr\Log\LoggerInterface; use Magento\Framework\Escaper; use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultInterface; /** * Render a component. * * @SuppressWarnings(PHPMD.AllPurposeAction) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Render extends AbstractAction { @@ -68,7 +72,9 @@ public function __construct( } /** - * @inheritdoc + * Render a component + * + * @return ResponseInterface|Json|ResultInterface|void */ public function execute() { diff --git a/app/code/Magento/Ui/Controller/Index/Render.php b/app/code/Magento/Ui/Controller/Index/Render.php index f74123955ce23..42818686840aa 100644 --- a/app/code/Magento/Ui/Controller/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Index/Render.php @@ -14,6 +14,9 @@ use Magento\Framework\Controller\Result\JsonFactory; use Psr\Log\LoggerInterface; use Magento\Framework\AuthorizationInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultInterface; /** * Is responsible for providing ui components information on store front. @@ -87,7 +90,9 @@ public function __construct( } /** - * @inheritdoc + * Provides ui component + * + * @return ResponseInterface|Json|ResultInterface|void */ public function execute() { diff --git a/app/code/Magento/Webapi/Model/Config/ClassReflector.php b/app/code/Magento/Webapi/Model/Config/ClassReflector.php index af34a3852caab..be880a0767bba 100644 --- a/app/code/Magento/Webapi/Model/Config/ClassReflector.php +++ b/app/code/Magento/Webapi/Model/Config/ClassReflector.php @@ -5,24 +5,24 @@ */ namespace Magento\Webapi\Model\Config; +use Laminas\Code\Reflection\ClassReflection; use Laminas\Code\Reflection\MethodReflection; +use Magento\Framework\Reflection\TypeProcessor; /** - * Class reflector. + * Config class reflector */ class ClassReflector { /** - * @var \Magento\Framework\Reflection\TypeProcessor + * @var TypeProcessor */ protected $_typeProcessor; /** - * Construct reflector. - * - * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor + * @param TypeProcessor $typeProcessor */ - public function __construct(\Magento\Framework\Reflection\TypeProcessor $typeProcessor) + public function __construct(TypeProcessor $typeProcessor) { $this->_typeProcessor = $typeProcessor; } @@ -60,12 +60,13 @@ public function __construct(\Magento\Framework\Reflection\TypeProcessor $typePro * ), * ... * )</pre> + * @throws \ReflectionException */ public function reflectClassMethods($className, $methods) { $data = []; - $classReflection = new \Laminas\Code\Reflection\ClassReflection($className); - /** @var \Laminas\Code\Reflection\MethodReflection $methodReflection */ + $classReflection = new ClassReflection($className); + /** @var MethodReflection $methodReflection */ foreach ($classReflection->getMethods() as $methodReflection) { $methodName = $methodReflection->getName(); if (in_array($methodName, $methods) || array_key_exists($methodName, $methods)) { @@ -78,11 +79,12 @@ public function reflectClassMethods($className, $methods) /** * Retrieve method interface and documentation description. * - * @param \Laminas\Code\Reflection\MethodReflection $method + * @param MethodReflection $method * @return array * @throws \InvalidArgumentException + * @throws \ReflectionException */ - public function extractMethodData(\Laminas\Code\Reflection\MethodReflection $method) + public function extractMethodData(MethodReflection $method) { $methodData = ['documentation' => $this->extractMethodDescription($method), 'interface' => []]; /** @var \Laminas\Code\Reflection\ParameterReflection $parameter */ @@ -116,10 +118,11 @@ public function extractMethodData(\Laminas\Code\Reflection\MethodReflection $met /** * Retrieve method full documentation description. * - * @param \Laminas\Code\Reflection\MethodReflection $method + * @param MethodReflection $method * @return string + * @throws \ReflectionException */ - protected function extractMethodDescription(\Laminas\Code\Reflection\MethodReflection $method) + protected function extractMethodDescription(MethodReflection $method) { $methodReflection = new MethodReflection( $method->getDeclaringClass()->getName(), @@ -141,10 +144,11 @@ protected function extractMethodDescription(\Laminas\Code\Reflection\MethodRefle * * @param string $className * @return string + * @throws \ReflectionException */ public function extractClassDescription($className) { - $classReflection = new \Laminas\Code\Reflection\ClassReflection($className); + $classReflection = new ClassReflection($className); $docBlock = $classReflection->getDocBlock(); if (!$docBlock) { return ''; diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl.php b/app/code/Magento/Webapi/Model/Soap/Wsdl.php index 870dfec1fc072..14e9990d25c3e 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl.php @@ -14,12 +14,12 @@ class Wsdl extends \Laminas\Soap\Wsdl { /** - * Constructor. * Save URI for targetNamespace generation. * * @param string $name * @param string|\Laminas\Uri\Uri $uri * @param ComplexTypeStrategy $strategy + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function __construct($name, $uri, ComplexTypeStrategy $strategy) { diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php index 2087f9e5e3d6e..93a0cf9d835c7 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php @@ -184,7 +184,7 @@ protected function _processArrayParameter($type, $callInfo = []) * Revert required call info data if needed. * * @param bool $isRequired - * @param array &$callInfo + * @param array $callInfo * @return void */ protected function _revertRequiredCallInfo($isRequired, &$callInfo) @@ -236,19 +236,7 @@ public function addAnnotation(\DOMElement $element, $documentation, $default = n $tagValue = $matches[2][$i]; switch ($tagName) { case 'callInfo': - $callInfoRegExp = '/([a-z].+):(returned|requiredInput):(yes|no|always|conditionally)/i'; - if (preg_match($callInfoRegExp, $tagValue)) { - list($callName, $direction, $condition) = explode(':', $tagValue); - $condition = strtolower($condition); - if (preg_match('/allCallsExcept\(([a-zA-Z].+)\)/', $callName, $calls)) { - $callInfo[$direction][$condition] = [ - 'allCallsExcept' => $calls[1], - ]; - } elseif (!isset($callInfo[$direction][$condition]['allCallsExcept'])) { - $this->_overrideCallInfoName($callInfo, $callName); - $callInfo[$direction][$condition]['calls'][] = $callName; - } - } + $this->processCallInfo($callInfo, $tagValue); break; case 'seeLink': $this->_processSeeLink($appInfoNode, $tagValue); @@ -451,4 +439,27 @@ protected function _overrideCallInfoName(&$callInfo, $callName) } } } + + /** + * Process CallInfo data + * + * @param array $callInfo + * @param string $tagValue + */ + private function processCallInfo(array &$callInfo, string $tagValue): void + { + $callInfoRegExp = '/([a-z].+):(returned|requiredInput):(yes|no|always|conditionally)/i'; + if (preg_match($callInfoRegExp, $tagValue)) { + list($callName, $direction, $condition) = explode(':', $tagValue); + $condition = strtolower($condition); + if (preg_match('/allCallsExcept\(([a-zA-Z].+)\)/', $callName, $calls)) { + $callInfo[$direction][$condition] = [ + 'allCallsExcept' => $calls[1], + ]; + } elseif (!isset($callInfo[$direction][$condition]['allCallsExcept'])) { + $this->_overrideCallInfoName($callInfo, $callName); + $callInfo[$direction][$condition]['calls'][] = $callName; + } + } + } } diff --git a/app/etc/di.xml b/app/etc/di.xml index 85d05b6be38fa..b5255e73b6d27 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -285,6 +285,7 @@ <type name="Magento\Framework\App\Request\Http"> <arguments> <argument name="pathInfoProcessor" xsi:type="object">Magento\Backend\App\Request\PathInfoProcessor\Proxy</argument> + <argument name="routeConfig" xsi:type="object">Magento\Framework\App\Route\ConfigInterface\Proxy</argument> </arguments> </type> <type name="Magento\Framework\App\Response\Http"> diff --git a/composer.json b/composer.json index 882fb5f93b0ef..421e29123151b 100644 --- a/composer.json +++ b/composer.json @@ -323,7 +323,7 @@ "Magento\\Framework\\": "lib/internal/Magento/Framework/", "Magento\\Setup\\": "setup/src/Magento/Setup/", "Magento\\": "app/code/Magento/", - "Laminas\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" + "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" }, "psr-0": { "": [ diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php index 2d7fbae640ef1..eb5cedf9e477c 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php @@ -7,12 +7,18 @@ */ namespace Magento\TestFramework\Authentication; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Oauth\Exception; use Magento\TestFramework\Authentication\Rest\OauthClient; use Magento\TestFramework\Helper\Bootstrap; use OAuth\Common\Consumer\Credentials; use Laminas\Stdlib\Exception\LogicException; use Magento\Integration\Model\Integration; +/** + * Authentication Oauth helper + */ class OauthHelper { /** @var array */ @@ -20,6 +26,7 @@ class OauthHelper /** * Generate authentication credentials + * * @param string $date consumer creation date * @return array * <pre> @@ -31,6 +38,8 @@ class OauthHelper * 'token' => $token // retrieved token Model * ); * </pre> + * @throws LocalizedException + * @throws Exception */ public static function getConsumerCredentials($date = null) { @@ -69,6 +78,9 @@ public static function getConsumerCredentials($date = null) * 'oauth_client' => $oauthClient // OauthClient instance used to fetch the access token * ); * </pre> + * @throws LocalizedException + * @throws Exception + * @throws \OAuth\Common\Http\Exception\TokenResponseException */ public static function getAccessToken() { @@ -104,7 +116,8 @@ public static function getAccessToken() * 'integration' => $integration // Integration instance associated with access token * ); * </pre> - * @throws LogicException + * @throws LocalizedException + * @throws Exception */ public static function getApiAccessCredentials($resources = null, Integration $integrationModel = null) { @@ -170,7 +183,8 @@ protected static function _rmRecursive($dir, $doSaveRoot = false) * * @param array $resources * @return \Magento\Integration\Model\Integration - * @throws \Laminas\Stdlib\Exception\LogicException + * @throws LogicException + * @throws IntegrationException */ protected static function _createIntegration($resources) { diff --git a/dev/tests/integration/framework/Magento/TestFramework/Request.php b/dev/tests/integration/framework/Magento/TestFramework/Request.php index ede2f5a54bf05..b6afc6e4c4ae3 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Request.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Request.php @@ -9,6 +9,7 @@ /** * HTTP request implementation that is used instead core one for testing + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Request extends \Magento\Framework\App\Request\Http { @@ -21,6 +22,7 @@ class Request extends \Magento\Framework\App\Request\Http /** * Retrieve HTTP HOST. + * * This method is a stub - all parameters are ignored, just static value returned. * * @param bool $trimPort diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php index 9257130cea121..325f02bd621c1 100644 --- a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Controller\Cards; use Magento\Customer\Model\Session; @@ -12,7 +13,7 @@ use Laminas\Http\Request; /** - * Class DeleteActionTest + * Test for \Magento\Vault\Controller\Cards\DeleteAction */ class DeleteActionTest extends AbstractController { @@ -26,7 +27,7 @@ public function testExecute() /** @var Session $session */ $session = $this->_objectManager->get(Session::class); $session->setCustomerId($customerId); - + /** @var CustomerTokenManagement $tokenManagement */ $tokenManagement = $this->_objectManager->get(CustomerTokenManagement::class); $tokens = $tokenManagement->getCustomerSessionTokens(); @@ -44,7 +45,7 @@ public function testExecute() ]) ->setMethod(Request::METHOD_POST); $this->dispatch('vault/cards/deleteaction'); - + static::assertTrue($this->getResponse()->isRedirect()); static::assertRedirect(static::stringContains('vault/cards/listaction')); static::assertSessionMessages(static::equalTo(['Stored Payment Method was successfully removed'])); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php index 0bc86f36a6357..d76167a9af6ed 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php @@ -152,6 +152,7 @@ public function public71( */ public function public71Another(?\DateTime $arg1, $arg2 = false): ?string { + // phpstan:ignore } /** @@ -164,5 +165,6 @@ public function public71Another(?\DateTime $arg1, $arg2 = false): ?string */ public function publicWithSelf($arg = false): self { + // phpstan:ignore } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 1e72485a9859f..d5eb962b7cb91 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -57,10 +57,12 @@ protected function setUp() ['fileResolver' => $fileResolverMock] ); $reader = $this->objectManager->create( + // phpstan:ignore \Magento\Framework\GraphQlSchemaStitching\Reader::class, ['readers' => ['graphql_reader' => $graphQlReader]] ); $data = $this->objectManager->create( + // phpstan:ignore \Magento\Framework\GraphQl\Config\Data ::class, ['reader' => $reader] ); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php index 0f07d8b31d13b..d64702f80fe61 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php @@ -14,7 +14,7 @@ use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResource; use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Class checks subscription behaviour from frontend diff --git a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php index 237bb0aa118bc..2c0c3e5233d51 100644 --- a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php @@ -42,6 +42,7 @@ public static function setUpBeforeClass() $baseDir, static::BASE_URL ); + // phpcs:ignore exec($command, $return); static::$serverPid = (int) $return[0]; } diff --git a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php index b3a4bd9ae0791..3da08f324d761 100644 --- a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php +++ b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php @@ -61,6 +61,7 @@ public function formatErrors(AnalysisResult $analysisResult, Output $output): in $clearedAnalysisResult = new AnalysisResult( $fileSpecificErrorsWithoutIgnoredErrors, $analysisResult->getNotFileSpecificErrors(), + $analysisResult->getWarnings(), $analysisResult->isDefaultLevelUsed(), $analysisResult->hasInferrablePropertyTypesFromConstructor(), $analysisResult->getProjectConfigFile() diff --git a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php index c4b69cc5150ba..524a4f7be3616 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php +++ b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php @@ -10,6 +10,7 @@ use Laminas\Code\Reflection\ParameterReflection; /** + * Provide dependencies for the file */ class Injectable { @@ -19,6 +20,8 @@ class Injectable protected $dependencies = []; /** + * Get dependencies + * * @param FileReflection $fileReflection * @return \ReflectionException[] * @throws \ReflectionException diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php index 856ba1f80ac68..2ead129e34043 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Integrity\Library\Injectable; /** + * Test for Magento\TestFramework\Integrity\Library\Injectable */ class InjectableTest extends \PHPUnit\Framework\TestCase { @@ -106,7 +107,7 @@ public function testGetDependencies() )->method( 'getName' )->will( - $this->returnValue(\Magento\Core\Model\Object::class) + $this->returnValue(\Magento\Framework\DataObject::class) ); $this->parameterReflection->expects( @@ -118,7 +119,7 @@ public function testGetDependencies() ); $this->assertEquals( - [\Magento\Core\Model\Object::class], + [\Magento\Framework\DataObject::class], $this->injectable->getDependencies($this->fileReflection) ); } @@ -133,13 +134,14 @@ public function testGetDependenciesWithException() $this->parameterReflection->expects($this->once())->method('getClass')->will( $this->returnCallback( function () { - throw new \ReflectionException('Class Magento\Core\Model\Object does not exist'); + throw new \ReflectionException('Class Magento\Framework\DataObject does not exist'); } ) ); $this->assertEquals( - [\Magento\Core\Model\Object::class], + + [\Magento\Framework\DataObject::class], $this->injectable->getDependencies($this->fileReflection) ); } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php index 23c72593c9fc8..9076c16981a49 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php @@ -155,7 +155,7 @@ private function checkSetExtensionAttributes( /** * Ensure that all classes extended from extensible classes implement getter and setter for extension attributes. */ - public function testExtensibleClassesWithMissingInterface() + public function testExtensibleClassesWithMissingInterface() //phpcs:ignore Generic.Metrics.NestingLevel { $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); $invoker( @@ -241,6 +241,7 @@ protected function getFiles($dir, $pattern) { $files = glob($dir . '/' . $pattern, GLOB_NOSORT); foreach (glob($dir . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $newDir) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($files, $this->getFiles($newDir, $pattern)); } return $files; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php index 849a57911a77e..a3dc60641795c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php @@ -42,6 +42,7 @@ private function getWhitelist(): array ); $whiteListItems = []; foreach (glob($whiteListFiles) as $fileName) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $whiteListItems = array_merge( $whiteListItems, file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) diff --git a/lib/internal/Magento/Framework/App/Feed.php b/lib/internal/Magento/Framework/App/Feed.php index c504f463f68dc..d6a38a90bdf17 100644 --- a/lib/internal/Magento/Framework/App/Feed.php +++ b/lib/internal/Magento/Framework/App/Feed.php @@ -29,7 +29,7 @@ public function __construct(array $data) } /** - * {@inheritdoc} + * @inheritDoc */ public function getFormattedContent() : string { diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index fbcf20b9f4f99..5fc4716f4bbf8 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -8,7 +8,7 @@ use Magento\Framework\App\HttpRequestInterface; use Magento\Framework\App\RequestContentInterface; use Magento\Framework\App\RequestSafetyInterface; -use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface; +use Magento\Framework\App\Route\ConfigInterface; use Magento\Framework\HTTP\PhpEnvironment\Request; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; @@ -16,6 +16,7 @@ /** * Http request + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Http extends Request implements RequestContentInterface, RequestSafetyInterface, HttpRequestInterface { diff --git a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php index 397d2c09fbf47..b3521698bf396 100644 --- a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Code\Generator; use Laminas\Code\Generator\MethodGenerator; use Laminas\Code\Generator\PropertyGenerator; +/** + * Class code generator + */ class ClassGenerator extends \Laminas\Code\Generator\ClassGenerator implements - \Magento\Framework\Code\Generator\CodeGeneratorInterface + CodeGeneratorInterface { /** * Possible doc block options @@ -64,6 +68,8 @@ class ClassGenerator extends \Laminas\Code\Generator\ClassGenerator implements ]; /** + * Set data to object + * * @param object $object * @param array $data * @param array $map @@ -94,7 +100,7 @@ public function setClassDocBlock(array $docBlock) } /** - * addMethods() + * Add methods * * @param array $methods * @return $this @@ -157,7 +163,7 @@ public function addMethodFromGenerator(MethodGenerator $method) } /** - * addProperties() + * Add properties * * @param array $properties * @return $this @@ -212,6 +218,8 @@ protected function createMethodGenerator() } /** + * Get namespace name + * * @return string|null */ public function getNamespaceName() diff --git a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php index 78360b9b7b1ad..f29474f476b45 100644 --- a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php +++ b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php @@ -7,10 +7,13 @@ use Laminas\Code\Generator\ValueGenerator; +/** + * Abstract entity + */ abstract class EntityAbstract { /** - * Entity type + * Entity type abstract */ const ENTITY_TYPE = 'abstract'; @@ -183,7 +186,6 @@ protected function _getDefaultResultClassName($modelClassName) */ protected function _getClassProperties() { - // protected $_objectManager = null; $objectManager = [ 'name' => '_objectManager', 'visibility' => 'protected', @@ -238,6 +240,8 @@ protected function _addError($message) } /** + * Validate data + * * @return bool */ protected function _validateData() @@ -263,6 +267,8 @@ protected function _validateData() } /** + * Get class DocBlock + * * @return array */ protected function _getClassDocBlock() @@ -272,6 +278,8 @@ protected function _getClassDocBlock() } /** + * Get generated code + * * @return string */ protected function _getGeneratedCode() @@ -281,6 +289,8 @@ protected function _getGeneratedCode() } /** + * Fix code style + * * @param string $sourceCode * @return string */ @@ -305,8 +315,9 @@ protected function _getNullDefaultValue() } /** - * @param \ReflectionParameter $parameter + * Extract parameter type * + * @param \ReflectionParameter $parameter * @return null|string */ private function extractParameterType( @@ -336,9 +347,11 @@ private function extractParameterType( } /** - * @param \ReflectionParameter $parameter + * Extract parameter default value * + * @param \ReflectionParameter $parameter * @return null|ValueGenerator + * @throws \ReflectionException */ private function extractParameterDefaultValue( \ReflectionParameter $parameter @@ -362,6 +375,7 @@ private function extractParameterDefaultValue( * * @param \ReflectionParameter $parameter * @return array + * @throws \ReflectionException */ protected function _getMethodParameterInfo(\ReflectionParameter $parameter) { diff --git a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php index ddcabdcf85724..173906f0fffcd 100644 --- a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php @@ -6,12 +6,12 @@ namespace Magento\Framework\Code\Generator; /** - * Interface method generator. + * Interface method code generator */ class InterfaceMethodGenerator extends \Laminas\Code\Generator\MethodGenerator { /** - * {@inheritdoc} + * @inheritDoc */ public function generate() { diff --git a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php index 08f93ec514b55..c9a8cce706af4 100644 --- a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php +++ b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Code\Reader; +/** + * The class arguments reader + */ class ArgumentsReader { const NO_DEFAULT_VALUE = 'NO-DEFAULT'; diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index d42715fee9923..ec2731c667ee6 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -1827,6 +1827,7 @@ public function modifyColumnByDdl($tableName, $columnName, $definition, $flushDa */ protected function _getColumnTypeByDdl($column) { + // phpstan:ignore switch ($column['DATA_TYPE']) { case 'bool': return Table::TYPE_BOOLEAN; @@ -2732,6 +2733,7 @@ public function addIndex( } catch (\Exception $e) { if (in_array(strtolower($indexType), ['primary', 'unique'])) { $match = []; + // phpstan:ignore if (preg_match('#SQLSTATE\[23000\]: [^:]+: 1062[^\']+\'([\d-\.]+)\'#', $e->getMessage(), $match)) { $ids = explode('-', $match[1]); $this->_removeDuplicateEntry($tableName, $fields, $ids); diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php index ab1b1e9cc65f5..7c9682ea444d8 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php @@ -7,11 +7,12 @@ namespace Magento\Framework\Data\Test\Unit\Form; use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Escaper; use Magento\Framework\Math\Random; use Magento\Framework\Session\SessionManager; /** - * Class FormKeyTest + * Test for Magento\Framework\Data\Form\FormKey */ class FormKeyTest extends \PHPUnit\Framework\TestCase { @@ -26,7 +27,7 @@ class FormKeyTest extends \PHPUnit\Framework\TestCase protected $sessionMock; /** - * @var \Laminas\Escaper\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ protected $escaperMock; @@ -40,7 +41,7 @@ protected function setUp() $this->mathRandomMock = $this->createMock(\Magento\Framework\Math\Random::class); $methods = ['setData', 'getData']; $this->sessionMock = $this->createPartialMock(\Magento\Framework\Session\SessionManager::class, $methods); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any())->method('escapeJs')->willReturnArgument(0); $this->formKey = new FormKey( $this->mathRandomMock, diff --git a/lib/internal/Magento/Framework/Filesystem/Glob.php b/lib/internal/Magento/Framework/Filesystem/Glob.php index e7f049dfdf80d..9d8de333e35fa 100644 --- a/lib/internal/Magento/Framework/Filesystem/Glob.php +++ b/lib/internal/Magento/Framework/Filesystem/Glob.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem; use Laminas\Stdlib\Glob as LaminasGlob; @@ -16,9 +17,9 @@ class Glob extends LaminasGlob /** * Find pathnames matching a pattern. * - * @param string $pattern - * @param int $flags - * @param bool $forceFallback + * @param string $pattern + * @param int $flags + * @param bool $forceFallback * @return array */ public static function glob($pattern, $flags = 0, $forceFallback = false) diff --git a/lib/internal/Magento/Framework/Mail/EmailMessage.php b/lib/internal/Magento/Framework/Mail/EmailMessage.php index ccb8a77227a27..5083d5475465e 100644 --- a/lib/internal/Magento/Framework/Mail/EmailMessage.php +++ b/lib/internal/Magento/Framework/Mail/EmailMessage.php @@ -13,7 +13,7 @@ use Laminas\Mime\Message as LaminasMimeMessage; /** - * Email message + * Magento Framework Email message */ class EmailMessage extends Message implements EmailMessageInterface { @@ -28,8 +28,6 @@ class EmailMessage extends Message implements EmailMessageInterface private $addressFactory; /** - * EmailMessage constructor - * * @param MimeMessageInterface $body * @param array $to * @param MimeMessageInterfaceFactory $mimeMessageFactory diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 1d572e29fecc1..b140676466e5f 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -11,7 +11,7 @@ /** * Class Message for email transportation * - * @deprecated + * @deprecated a new message implementation was added * @see \Magento\Framework\Mail\EmailMessage */ class Message implements MailMessageInterface diff --git a/lib/internal/Magento/Framework/Mail/MimeMessage.php b/lib/internal/Magento/Framework/Mail/MimeMessage.php index 78d2a42637ff2..3482fb33bd848 100644 --- a/lib/internal/Magento/Framework/Mail/MimeMessage.php +++ b/lib/internal/Magento/Framework/Mail/MimeMessage.php @@ -10,7 +10,7 @@ use Laminas\Mime\Message as LaminasMimeMessage; /** - * Class MimeMessage + * Magento Framework Mime message */ class MimeMessage implements MimeMessageInterface { diff --git a/lib/internal/Magento/Framework/Mail/Transport.php b/lib/internal/Magento/Framework/Mail/Transport.php index 0be387f22ac08..c1772075baaf3 100644 --- a/lib/internal/Magento/Framework/Mail/Transport.php +++ b/lib/internal/Magento/Framework/Mail/Transport.php @@ -10,6 +10,9 @@ use Laminas\Mail\Message as LaminasMessage; use Laminas\Mail\Transport\Sendmail; +/** + * Mail transport + */ class Transport implements \Magento\Framework\Mail\TransportInterface { /** diff --git a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php index 22d93b2cc7dcf..160d2b4fa8d5a 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php +++ b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php @@ -74,7 +74,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getDefaultConstructorDefinition() { @@ -97,7 +97,7 @@ protected function _getDefaultConstructorDefinition() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getClassProperties() { @@ -119,7 +119,7 @@ protected function _getClassProperties() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getClassMethods() { @@ -166,7 +166,7 @@ protected function _getClassMethods() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _validateData() { @@ -175,7 +175,7 @@ protected function _validateData() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _generateCode() { diff --git a/lib/internal/Magento/Framework/Oauth/Helper/Request.php b/lib/internal/Magento/Framework/Oauth/Helper/Request.php index d5f2fa45e6843..1134275270bc0 100644 --- a/lib/internal/Magento/Framework/Oauth/Helper/Request.php +++ b/lib/internal/Magento/Framework/Oauth/Helper/Request.php @@ -8,6 +8,9 @@ use Magento\Framework\App\RequestInterface; use Laminas\Uri\UriFactory; +/** + * Request helper + */ class Request { /**#@+ @@ -110,7 +113,7 @@ protected function _processRequest($authHeaderValue, $contentTypeHeader, $reques /** * Retrieve protocol parameters from query string * - * @param array &$protocolParams + * @param array $protocolParams * @param array $queryString * @return void */ diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php index aeca85a6e9ae0..b0687d619fac6 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php @@ -23,7 +23,7 @@ class Repository extends \Magento\Framework\Code\Generator\EntityAbstract { /** - * Entity type + * Entity type repository */ const ENTITY_TYPE = 'repository'; @@ -130,6 +130,7 @@ protected function _getSourcePersistorPropertyName() /** * Returns source collection factory property name + * * @return string */ protected function _getSourceCollectionFactoryPropertyName() @@ -620,7 +621,7 @@ protected function _getClassMethods() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _validateData() { diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php index fb22cf6872b9e..8f3e31811ccec 100644 --- a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php @@ -140,6 +140,8 @@ public function buildOutputDataArray(ExtensionAttributesInterface $dataObject, $ } /** + * Is attribute permissions valid + * * @param string $dataObjectType * @param string $attributeCode * @return bool @@ -158,6 +160,8 @@ private function isAttributePermissionValid($dataObjectType, $attributeCode) } /** + * Get regular type for extension attribute type + * * @param string $name * @return string */ @@ -167,6 +171,8 @@ private function getRegularTypeForExtensionAttributesType($name) } /** + * Get permissions for attribute type + * * @param string $typeName * @param string $attributeCode * @return string[] A list of permissions diff --git a/lib/internal/Magento/Framework/Reflection/MethodsMap.php b/lib/internal/Magento/Framework/Reflection/MethodsMap.php index 57347c62e4244..c4a738ac28caa 100644 --- a/lib/internal/Magento/Framework/Reflection/MethodsMap.php +++ b/lib/internal/Magento/Framework/Reflection/MethodsMap.php @@ -14,6 +14,7 @@ /** * Gathers method metadata information. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MethodsMap { @@ -51,6 +52,11 @@ class MethodsMap */ private $serializer; + /** + * @var \Magento\Framework\Api\AttributeTypeResolverInterface + */ + private $attributeTypeResolver; + /** * @param \Magento\Framework\Cache\FrontendInterface $cache * @param TypeProcessor $typeProcessor @@ -99,6 +105,7 @@ public function getMethodReturnType($typeName, $methodName) */ public function getMethodsMap($interfaceName) { + //phpcs:ignore Magento2.Security.InsecureFunction $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName); if (!isset($this->serviceInterfaceMethodsMap[$key])) { $methodMap = $this->cache->load($key); diff --git a/lib/internal/Magento/Framework/Reflection/NameFinder.php b/lib/internal/Magento/Framework/Reflection/NameFinder.php index 81eb4782c4c98..476aa23e6e841 100644 --- a/lib/internal/Magento/Framework/Reflection/NameFinder.php +++ b/lib/internal/Magento/Framework/Reflection/NameFinder.php @@ -8,6 +8,9 @@ use Laminas\Code\Reflection\ClassReflection; +/** + * Reflection NameFinder + */ class NameFinder { /** diff --git a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php index f4092622e0b01..e145484d22a43 100644 --- a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php +++ b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php @@ -6,10 +6,13 @@ namespace Magento\Framework\Session\Config\Validator; +/** + * Session cookie domain validator + */ class CookieDomainValidator extends \Magento\Framework\Validator\AbstractValidator { /** - * {@inheritdoc} + * @inheritDoc */ public function isValid($value) { diff --git a/lib/internal/Magento/Framework/Stdlib/Parameters.php b/lib/internal/Magento/Framework/Stdlib/Parameters.php index 98bb7aa945226..580d94ac983fc 100644 --- a/lib/internal/Magento/Framework/Stdlib/Parameters.php +++ b/lib/internal/Magento/Framework/Stdlib/Parameters.php @@ -10,7 +10,7 @@ use Laminas\Stdlib\Parameters as LaminasParameters; /** - * Class Parameters + * Stdlib parameters */ class Parameters { diff --git a/lib/internal/Magento/Framework/Url/Validator.php b/lib/internal/Magento/Framework/Url/Validator.php index c85853bf48fd2..6ce201e511e3e 100644 --- a/lib/internal/Magento/Framework/Url/Validator.php +++ b/lib/internal/Magento/Framework/Url/Validator.php @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Validate URL - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Framework\Url; +use Laminas\Validator\Uri; + +/** + * URL validator + */ class Validator extends \Zend_Validate_Abstract { /**#@+ @@ -20,14 +20,15 @@ class Validator extends \Zend_Validate_Abstract /**#@-*/ /** - * @var \Laminas\Validator\Uri + * @var Uri */ private $validator; /** - * Object constructor + * @param Uri $validator + * @throws \Zend_Validate_Exception */ - public function __construct(\Laminas\Validator\Uri $validator) + public function __construct(Uri $validator) { // set translated message template $this->setMessage((string)new \Magento\Framework\Phrase("Invalid URL '%value%'."), self::INVALID_URL); diff --git a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php index c1473097d7262..7e6df4655d990 100644 --- a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php +++ b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php @@ -1,19 +1,15 @@ <?php /** - * Protocol validator - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Validator; use \Laminas\Uri\Uri; /** - * Check is URI starts from allowed protocol - * - * Class AllowedProtocols - * @package Magento\Framework\Validator + * Protocol validator */ class AllowedProtocols extends AbstractValidator { @@ -29,6 +25,7 @@ class AllowedProtocols extends AbstractValidator /** * Constructor. + * * @param array $listOfProtocols */ public function __construct($listOfProtocols = []) @@ -54,6 +51,7 @@ public function isValid($value) if (!$isValid) { $this->_addMessages(["Protocol isn't allowed"]); } + return $isValid; } } diff --git a/setup/config/module.config.php b/setup/config/module.config.php index 4e6b89ee70d91..7816c8e3599f3 100644 --- a/setup/config/module.config.php +++ b/setup/config/module.config.php @@ -37,7 +37,7 @@ ], 'controllers' => [ 'abstract_factories' => [ - \Laminas\Mvc\Controller\LazyControllerAbstractFactory::class, + \Zend\Mvc\Controller\LazyControllerAbstractFactory::class, ], ], ]; diff --git a/setup/src/Magento/Setup/Application.php b/setup/src/Magento/Setup/Application.php index 5a729dc03e97e..5881bfad3b209 100644 --- a/setup/src/Magento/Setup/Application.php +++ b/setup/src/Magento/Setup/Application.php @@ -10,7 +10,9 @@ use Laminas\ServiceManager\ServiceManager; /** - * This class is wrapper on \Laminas\Mvc\Application and allows to do more customization like services loading, which + * This class is wrapper on \Laminas\Mvc\Application + * + * It allows to do more customization like services loading, which * cannot be loaded via configuration. */ class Application diff --git a/setup/src/Magento/Setup/Controller/AddDatabase.php b/setup/src/Magento/Setup/Controller/AddDatabase.php index 7002f8e7f64a4..4d12ea6a945d6 100644 --- a/setup/src/Magento/Setup/Controller/AddDatabase.php +++ b/setup/src/Magento/Setup/Controller/AddDatabase.php @@ -8,9 +8,14 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * AddDatabase controller + */ class AddDatabase extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/BackupActionItems.php b/setup/src/Magento/Setup/Controller/BackupActionItems.php index a79d9a566dab0..a9255d3324374 100644 --- a/setup/src/Magento/Setup/Controller/BackupActionItems.php +++ b/setup/src/Magento/Setup/Controller/BackupActionItems.php @@ -3,57 +3,65 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; +use Laminas\Http\Response; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Backup\Factory; use Magento\Framework\Backup\Filesystem; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Setup\BackupRollback; -use Laminas\Json\Json; -use Laminas\Mvc\Controller\AbstractActionController; -use Laminas\View\Model\JsonModel; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Model\WebLogger; +/** + * BackupActionItems controller + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class BackupActionItems extends AbstractActionController { - /** * Handler for BackupRollback * - * @var \Magento\Framework\Setup\BackupRollback + * @var BackupRollback */ private $backupHandler; /** * Filesystem * - * @var \Magento\Framework\Backup\Filesystem + * @var Filesystem */ private $fileSystem; /** * Filesystem Directory List * - * @var \Magento\Framework\App\Filesystem\DirectoryList + * @var DirectoryList */ private $directoryList; /** - * Constructor - * - * @param \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider - * @param \Magento\Setup\Model\WebLogger $logger - * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList - * @param \Magento\Framework\Backup\Filesystem $fileSystem + * @param ObjectManagerProvider $objectManagerProvider + * @param WebLogger $logger + * @param DirectoryList $directoryList + * @param Filesystem $fileSystem + * @throws \Magento\Setup\Exception */ public function __construct( - \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider, - \Magento\Setup\Model\WebLogger $logger, - \Magento\Framework\App\Filesystem\DirectoryList $directoryList, - \Magento\Framework\Backup\Filesystem $fileSystem + ObjectManagerProvider $objectManagerProvider, + WebLogger $logger, + DirectoryList $directoryList, + Filesystem $fileSystem ) { $objectManager = $objectManagerProvider->get(); $this->backupHandler = $objectManager->create( - \Magento\Framework\Setup\BackupRollback::class, + BackupRollback::class, ['log' => $logger] ); $this->directoryList = $directoryList; @@ -63,20 +71,22 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Laminas\View\Model\ViewModel + * @return ViewModel */ public function indexAction() { - $view = new \Laminas\View\Model\ViewModel; + $view = new ViewModel(); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(Response::STATUS_CODE_404); + return $view; } /** * Checks disk space availability * - * @return \Laminas\View\Model\JsonModel + * @return JsonModel + * @throws FileSystemException */ public function checkAction() { @@ -95,6 +105,7 @@ public function checkAction() $totalSize += $this->backupHandler->getDBDiskSpace(); } $this->fileSystem->validateAvailableDiscSpace($backupDir, $totalSize); + return new JsonModel( [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, @@ -114,7 +125,7 @@ public function checkAction() /** * Takes backup for code, media or DB * - * @return \Laminas\View\Model\JsonModel + * @return JsonModel */ public function createAction() { @@ -131,6 +142,7 @@ public function createAction() if (isset($params['options']['db']) && $params['options']['db']) { $backupFiles[] = $this->backupHandler->dbBackup($time); } + return new JsonModel( [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, diff --git a/setup/src/Magento/Setup/Controller/CompleteBackup.php b/setup/src/Magento/Setup/Controller/CompleteBackup.php index 158e0b724fd8f..9f925d405cc41 100644 --- a/setup/src/Magento/Setup/Controller/CompleteBackup.php +++ b/setup/src/Magento/Setup/Controller/CompleteBackup.php @@ -5,14 +5,17 @@ */ namespace Magento\Setup\Controller; -use Magento\Framework\App\MaintenanceMode; use Laminas\Mvc\Controller\AbstractActionController; -use Laminas\View\Model\JsonModel; use Laminas\View\Model\ViewModel; +/** + * CompleteBackup controller + */ class CompleteBackup extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() @@ -24,6 +27,8 @@ public function indexAction() } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() diff --git a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php index d06407796ff9b..79c32c5b932e0 100644 --- a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php +++ b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php @@ -8,9 +8,14 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * CreateAdminAccount controller + */ class CreateAdminAccount extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/CreateBackup.php b/setup/src/Magento/Setup/Controller/CreateBackup.php index 97c6f0deef188..42c86c42a5a15 100644 --- a/setup/src/Magento/Setup/Controller/CreateBackup.php +++ b/setup/src/Magento/Setup/Controller/CreateBackup.php @@ -8,9 +8,14 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * CreateBackup controller + */ class CreateBackup extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php index cc987e8339008..128e4b42e6d5e 100644 --- a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php +++ b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php @@ -5,7 +5,6 @@ */ namespace Magento\Setup\Controller; -use Magento\Framework\Filesystem; use Magento\Framework\Module\FullModuleList; use Magento\Framework\Setup\Lists; use Magento\Setup\Model\ObjectManagerProvider; @@ -13,6 +12,9 @@ use Laminas\View\Model\ViewModel; use Laminas\View\Model\JsonModel; +/** + * CustomizeYourStore controller + */ class CustomizeYourStore extends AbstractActionController { /** @@ -43,7 +45,10 @@ public function __construct(FullModuleList $moduleList, Lists $list, ObjectManag } /** + * Index action + * * @return ViewModel + * @throws \Magento\Setup\Exception */ public function indexAction() { @@ -76,6 +81,7 @@ public function indexAction() */ public function defaultTimeZoneAction() { + // phpcs:ignore Generic.PHP.NoSilencedErrors $defaultTimeZone = trim(@date_default_timezone_get()); if (empty($defaultTimeZone)) { return new JsonModel(['defaultTimeZone' => 'UTC']); diff --git a/setup/src/Magento/Setup/Controller/DatabaseCheck.php b/setup/src/Magento/Setup/Controller/DatabaseCheck.php index cf6c6ae1b4409..f84b6e680ab25 100644 --- a/setup/src/Magento/Setup/Controller/DatabaseCheck.php +++ b/setup/src/Magento/Setup/Controller/DatabaseCheck.php @@ -12,7 +12,7 @@ use Laminas\View\Model\JsonModel; /** - * Class DatabaseCheck + * DatabaseCheck controller */ class DatabaseCheck extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/DependencyCheck.php b/setup/src/Magento/Setup/Controller/DependencyCheck.php index 44f205ace3925..49c2b661a8681 100644 --- a/setup/src/Magento/Setup/Controller/DependencyCheck.php +++ b/setup/src/Magento/Setup/Controller/DependencyCheck.php @@ -6,7 +6,9 @@ namespace Magento\Setup\Controller; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Module\Status; +use Magento\Framework\Phrase; use Magento\Setup\Model\DependencyReadinessCheck; use Magento\Setup\Model\ModuleStatusFactory; use Magento\Setup\Model\UninstallDependencyCheck; @@ -15,9 +17,7 @@ use Laminas\View\Model\JsonModel; /** - * Class DependencyCheck - * - * Checks dependencies. + * DependencyCheck controller */ class DependencyCheck extends AbstractActionController { @@ -43,8 +43,6 @@ class DependencyCheck extends AbstractActionController protected $moduleStatus; /** - * Constructor - * * @param DependencyReadinessCheck $dependencyReadinessCheck * @param UninstallDependencyCheck $uninstallDependencyCheck * @param ModuleStatusFactory $moduleStatusFactory @@ -63,6 +61,7 @@ public function __construct( * Verifies component dependency * * @return JsonModel + * @throws \Exception */ public function componentDependencyAction() { @@ -119,11 +118,11 @@ public function enableDisableDependencyCheckAction() try { if (empty($data['packages'])) { - throw new \Exception('No packages have been found.'); + throw new LocalizedException(new Phrase('No packages have been found.')); } if (empty($data['type'])) { - throw new \Exception('Can not determine the flow.'); + throw new LocalizedException(new Phrase('Can not determine the flow.')); } $modules = $data['packages']; @@ -133,7 +132,7 @@ public function enableDisableDependencyCheckAction() $modulesToChange = []; foreach ($modules as $module) { if (!isset($module['name'])) { - throw new \Exception('Can not find module name.'); + throw new LocalizedException(new Phrase('Can not find module name.')); } $modulesToChange[] = $module['name']; } diff --git a/setup/src/Magento/Setup/Controller/Environment.php b/setup/src/Magento/Setup/Controller/Environment.php index 2a330bd1453a8..063d23f5a48d6 100644 --- a/setup/src/Magento/Setup/Controller/Environment.php +++ b/setup/src/Magento/Setup/Controller/Environment.php @@ -43,6 +43,11 @@ class Environment extends AbstractActionController */ protected $phpReadinessCheck; + /** + * @var \Magento\Framework\Setup\FilePermissions + */ + private $permissions; + /** * Constructor * diff --git a/setup/src/Magento/Setup/Controller/Home.php b/setup/src/Magento/Setup/Controller/Home.php index a9b45af731b81..f06cfb89cb1d3 100644 --- a/setup/src/Magento/Setup/Controller/Home.php +++ b/setup/src/Magento/Setup/Controller/Home.php @@ -15,6 +15,8 @@ class Home extends AbstractActionController { /** + * Index action + * * @return ViewModel|\Laminas\Http\Response */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/Index.php b/setup/src/Magento/Setup/Controller/Index.php index 347ef5738add3..36dd60dbbcf0f 100644 --- a/setup/src/Magento/Setup/Controller/Index.php +++ b/setup/src/Magento/Setup/Controller/Index.php @@ -15,6 +15,8 @@ class Index extends AbstractActionController { /** + * Index action + * * @return ViewModel|\Laminas\Http\Response */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/Install.php b/setup/src/Magento/Setup/Controller/Install.php index a47c0e375500f..f110595a8b872 100644 --- a/setup/src/Magento/Setup/Controller/Install.php +++ b/setup/src/Magento/Setup/Controller/Install.php @@ -6,18 +6,17 @@ namespace Magento\Setup\Controller; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants as SetupConfigOptionsList; -use Magento\SampleData; use Magento\Setup\Model\Installer; use Magento\Setup\Model\Installer\ProgressFactory; use Magento\Setup\Model\InstallerFactory; use Magento\Setup\Model\RequestDataConverter; use Magento\Setup\Model\WebLogger; -use Laminas\Json\Json; -use Laminas\Mvc\Controller\AbstractActionController; -use Laminas\View\Model\JsonModel; -use Laminas\View\Model\ViewModel; /** * Install controller @@ -57,8 +56,6 @@ class Install extends AbstractActionController private $requestDataConverter; /** - * Default Constructor - * * @param WebLogger $logger * @param InstallerFactory $installerFactory * @param ProgressFactory $progressFactory @@ -83,12 +80,15 @@ public function __construct( } /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); + return $view; } @@ -100,7 +100,7 @@ public function indexAction() public function startAction() { $this->log->clear(); - $json = new JsonModel; + $json = new JsonModel(); try { $this->checkForPriorInstall(); $content = $this->getRequest()->getContent(); @@ -121,6 +121,7 @@ public function startAction() $json->setVariable('messages', $e->getMessage()); $json->setVariable('success', false); } + return $json; } @@ -155,6 +156,7 @@ public function progressAction() } catch (\Exception $e) { $contents = [(string)$e]; } + return $json->setVariables(['progress' => $percent, 'success' => $success, 'console' => $contents]); } diff --git a/setup/src/Magento/Setup/Controller/License.php b/setup/src/Magento/Setup/Controller/License.php index 69778a4bca908..9cf32e7b31baf 100644 --- a/setup/src/Magento/Setup/Controller/License.php +++ b/setup/src/Magento/Setup/Controller/License.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Magento\Setup\Model\License as LicenseModel; @@ -10,9 +11,7 @@ use Laminas\View\Model\ViewModel; /** - * Class LicenseController - * - * @package Magento\Setup\Controller + * License controller */ class License extends AbstractActionController { @@ -41,7 +40,7 @@ public function __construct(LicenseModel $license) public function indexAction() { $contents = $this->license->getContents(); - $view = new ViewModel; + $view = new ViewModel(); if ($contents === false) { $view->setTemplate('error/404'); $view->setVariable('message', 'Cannot find license file.'); diff --git a/setup/src/Magento/Setup/Controller/Maintenance.php b/setup/src/Magento/Setup/Controller/Maintenance.php index d95b23453e2c9..769f961f7fc0e 100644 --- a/setup/src/Magento/Setup/Controller/Maintenance.php +++ b/setup/src/Magento/Setup/Controller/Maintenance.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; -use Magento\Framework\App\MaintenanceMode; +use Laminas\Json\Json; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\JsonModel; -use Laminas\Json\Json; +use Magento\Framework\App\MaintenanceMode; +/** + * Maintenance controller + */ class Maintenance extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/Marketplace.php b/setup/src/Magento/Setup/Controller/Marketplace.php index 99e935baa9169..7746fa08aac5c 100644 --- a/setup/src/Magento/Setup/Controller/Marketplace.php +++ b/setup/src/Magento/Setup/Controller/Marketplace.php @@ -5,13 +5,16 @@ */ namespace Magento\Setup\Controller; -use Laminas\Mvc\Controller\AbstractActionController; -use Laminas\View\Model\ViewModel; use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\JsonModel; -use Magento\Setup\Model\PackagesData; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\PackagesAuth; +use Magento\Setup\Model\PackagesData; +/** + * Marketplace controller + */ class Marketplace extends AbstractActionController { /** @@ -41,7 +44,7 @@ public function __construct(PackagesAuth $packagesAuth, PackagesData $packagesDa */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/error/404.phtml'); $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; @@ -118,6 +121,8 @@ public function removeCredentialsAction() } /** + * Popup Auth action + * * @return array|ViewModel */ public function popupAuthAction() diff --git a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php index 189acebc2a5f8..9541b8ef7250f 100644 --- a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php +++ b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php @@ -8,14 +8,19 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * MarketplaceCredentials controller + */ class MarketplaceCredentials extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/Modules.php b/setup/src/Magento/Setup/Controller/Modules.php index 4264e8f986641..35b225d1e6bba 100644 --- a/setup/src/Magento/Setup/Controller/Modules.php +++ b/setup/src/Magento/Setup/Controller/Modules.php @@ -11,6 +11,9 @@ use Laminas\View\Model\JsonModel; use Laminas\Json\Json; +/** + * Modules controller + */ class Modules extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 8d7145ccb9751..c1d42d905b3eb 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -5,15 +5,14 @@ */ namespace Magento\Setup\Controller; -use Magento\Setup\Model\Navigation as NavModel; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\JsonModel; use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Cron\Status; +use Magento\Setup\Model\Navigation as NavModel; /** - * Class Navigation - * + * Navigation controller */ class Navigation extends AbstractActionController { @@ -40,17 +39,19 @@ public function __construct(NavModel $navigation, Status $status) { $this->navigation = $navigation; $this->status = $status; - $this->view = new ViewModel; + $this->view = new ViewModel(); $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); } /** + * Index action + * * @return JsonModel */ public function indexAction() { - $json = new JsonModel; + $json = new JsonModel(); $json->setVariable('nav', $this->navigation->getData()); $json->setVariable('menu', $this->navigation->getMenuItems()); $json->setVariable('main', $this->navigation->getMainItems()); @@ -59,6 +60,8 @@ public function indexAction() } /** + * Menu action + * * @return array|ViewModel */ public function menuAction() @@ -71,6 +74,8 @@ public function menuAction() } /** + * Side menu action + * * @return array|ViewModel */ public function sideMenuAction() @@ -82,6 +87,8 @@ public function sideMenuAction() } /** + * Head bar action + * * @return array|ViewModel */ public function headerBarAction() diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php index 26bcb8dd2f34d..e507c645c2d02 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php @@ -8,16 +8,21 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * ReadinessCheckInstaller controller + */ class ReadinessCheckInstaller extends AbstractActionController { const INSTALLER = 'installer'; /** + * Index action + * * @return array|ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/readiness-check.phtml'); $view->setVariable('actionFrom', self::INSTALLER); @@ -25,11 +30,13 @@ public function indexAction() } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/magento/setup/readiness-check/progress.phtml'); $view->setTerminal(true); return $view; diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php index c272e64a4ef62..59220004d2ed8 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php @@ -8,16 +8,21 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * ReadinessCheckUpdater controller + */ class ReadinessCheckUpdater extends AbstractActionController { const UPDATER = 'updater'; /** + * Index action + * * @return array|ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/readiness-check.phtml'); $view->setVariable('actionFrom', self::UPDATER); @@ -25,11 +30,13 @@ public function indexAction() } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/magento/setup/readiness-check/progress.phtml'); $view->setTerminal(true); return $view; diff --git a/setup/src/Magento/Setup/Controller/SelectVersion.php b/setup/src/Magento/Setup/Controller/SelectVersion.php index 613a1504d77e6..f22b41a8614b1 100644 --- a/setup/src/Magento/Setup/Controller/SelectVersion.php +++ b/setup/src/Magento/Setup/Controller/SelectVersion.php @@ -6,11 +6,10 @@ namespace Magento\Setup\Controller; -use Magento\Composer\InfoCommand; -use Magento\Setup\Model\SystemPackage; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\JsonModel; use Laminas\View\Model\ViewModel; +use Magento\Setup\Model\SystemPackage; /** * Controller for selecting version @@ -32,11 +31,13 @@ public function __construct( } /** + * Index action + * * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/select-version.phtml'); return $view; diff --git a/setup/src/Magento/Setup/Controller/Session.php b/setup/src/Magento/Setup/Controller/Session.php index 76f6f2e859abc..fa25924d01a15 100644 --- a/setup/src/Magento/Setup/Controller/Session.php +++ b/setup/src/Magento/Setup/Controller/Session.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; /** @@ -65,6 +66,7 @@ public function prolongAction() $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); /** @var \Magento\Backend\Model\Url $backendUrl */ $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); $cookiePath = $urlPath . 'setup'; $sessionConfig->setCookiePath($cookiePath); @@ -80,6 +82,7 @@ public function prolongAction() $session->prolong(); return new \Laminas\View\Model\JsonModel(['success' => true]); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (\Exception $e) { } return new \Laminas\View\Model\JsonModel(['success' => false]); diff --git a/setup/src/Magento/Setup/Controller/Success.php b/setup/src/Magento/Setup/Controller/Success.php index 4df88c5891071..c597dd8b1bc0a 100644 --- a/setup/src/Magento/Setup/Controller/Success.php +++ b/setup/src/Magento/Setup/Controller/Success.php @@ -10,6 +10,9 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * Success controller + */ class Success extends AbstractActionController { /** @@ -33,7 +36,10 @@ public function __construct(ModuleList $moduleList, ObjectManagerProvider $objec } /** + * Index action + * * @return ViewModel + * @throws \Magento\Setup\Exception */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/SystemConfig.php b/setup/src/Magento/Setup/Controller/SystemConfig.php index f189de9fee95a..0067752997e17 100644 --- a/setup/src/Magento/Setup/Controller/SystemConfig.php +++ b/setup/src/Magento/Setup/Controller/SystemConfig.php @@ -3,19 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * SystemConfig controller + */ class SystemConfig extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php index 4a4a5ce7f665f..c12d9a164ac4c 100644 --- a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php +++ b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php @@ -3,12 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; -use Magento\Framework\App\MaintenanceMode; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +use Magento\Framework\App\MaintenanceMode; +/** + * UpdaterSuccess controller + */ class UpdaterSuccess extends AbstractActionController { /** @@ -27,12 +31,14 @@ public function __construct(MaintenanceMode $maintenanceMode) } /** + * Index action + * * @return ViewModel */ public function indexAction() { $this->maintenanceMode->set(false); - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/UrlCheck.php b/setup/src/Magento/Setup/Controller/UrlCheck.php index af7d3738be1e2..1dfe838e6ca2c 100644 --- a/setup/src/Magento/Setup/Controller/UrlCheck.php +++ b/setup/src/Magento/Setup/Controller/UrlCheck.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Laminas\Mvc\Controller\AbstractActionController; @@ -10,6 +11,9 @@ use Laminas\Json\Json; use Magento\Framework\Validator\Url as UrlValidator; +/** + * UrlCheck controller + */ class UrlCheck extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/WebConfiguration.php b/setup/src/Magento/Setup/Controller/WebConfiguration.php index 68800f0f7404c..eebe01a008afb 100644 --- a/setup/src/Magento/Setup/Controller/WebConfiguration.php +++ b/setup/src/Magento/Setup/Controller/WebConfiguration.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Magento\Framework\App\SetupInfo; @@ -10,6 +11,9 @@ use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +/** + * WebConfiguration controller + */ class WebConfiguration extends AbstractActionController { /** @@ -19,6 +23,7 @@ class WebConfiguration extends AbstractActionController */ public function indexAction() { + // phpcs:ignore Magento2.Security.Superglobal $setupInfo = new SetupInfo($_SERVER); $view = new ViewModel( [ diff --git a/setup/src/Magento/Setup/Model/AdminAccountFactory.php b/setup/src/Magento/Setup/Model/AdminAccountFactory.php index 86687fd28c23e..0811be79f63f6 100644 --- a/setup/src/Magento/Setup/Model/AdminAccountFactory.php +++ b/setup/src/Magento/Setup/Model/AdminAccountFactory.php @@ -6,11 +6,12 @@ namespace Magento\Setup\Model; -use Magento\Setup\Module\Setup; use Laminas\ServiceManager\ServiceLocatorInterface; -use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\DB\Adapter\AdapterInterface; +/** + * Factory for \Magento\Setup\Model\AdminAccount + */ class AdminAccountFactory { /** @@ -27,6 +28,8 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** + * Create object + * * @param AdapterInterface $connection * @param array $data * @return AdminAccount diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php index 191d37f00a132..a97685920f13a 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php @@ -76,9 +76,11 @@ public function __construct( /** * Auto discover ConfigOptionsList class and collect them. + * * These classes should reside in <module>/Setup directories. * - * @return \Magento\Framework\Setup\ConfigOptionsListInterface[] + * @return ConfigOptionsListInterface[] + * @throws \Magento\Setup\Exception */ public function collectOptionsLists() { diff --git a/setup/src/Magento/Setup/Model/Cron/JobFactory.php b/setup/src/Magento/Setup/Model/Cron/JobFactory.php index 26cdc7aa3de80..cae149ed38e8f 100644 --- a/setup/src/Magento/Setup/Model/Cron/JobFactory.php +++ b/setup/src/Magento/Setup/Model/Cron/JobFactory.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Model\Cron; use Magento\Backend\Console\Command\CacheDisableCommand; @@ -64,7 +65,9 @@ public function __construct(ServiceLocatorInterface $serviceLocator) public function create($name, array $params = []) { $cronStatus = $this->serviceLocator->get(\Magento\Setup\Model\Cron\Status::class); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $statusStream = fopen($cronStatus->getStatusFilePath(), 'a+'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $logStream = fopen($cronStatus->getLogFilePath(), 'a+'); $streamOutput = new MultipleStreamOutput([$statusStream, $logStream]); $objectManagerProvider = $this->serviceLocator->get(\Magento\Setup\Model\ObjectManagerProvider::class); @@ -81,7 +84,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_DB_ROLLBACK: return new JobDbRollback( $objectManager->get(\Magento\Framework\Setup\BackupRollbackFactory::class), @@ -91,7 +93,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_STATIC_REGENERATE: return new JobStaticRegenerate( $objectManagerProvider, @@ -100,7 +101,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_COMPONENT_UNINSTALL: $moduleUninstall = new Helper\ModuleUninstall( $this->serviceLocator->get(\Magento\Setup\Model\ModuleUninstaller::class), @@ -123,7 +123,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MODULE_ENABLE: return new JobModule( $this->serviceLocator->get(ModuleEnableCommand::class), @@ -133,7 +132,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MODULE_DISABLE: return new JobModule( $this->serviceLocator->get(ModuleDisableCommand::class), @@ -143,7 +141,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_ENABLE_CACHE: return new JobSetCache( $objectManager->get(CacheEnableCommand::class), @@ -153,7 +150,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_DISABLE_CACHE: return new JobSetCache( $objectManager->get(CacheDisableCommand::class), @@ -162,7 +158,6 @@ public function create($name, array $params = []) $cronStatus, $name ); - break; case self::JOB_MAINTENANCE_MODE_ENABLE: return new JobSetMaintenanceMode( $this->serviceLocator->get(MaintenanceEnableCommand::class), @@ -172,7 +167,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MAINTENANCE_MODE_DISABLE: return new JobSetMaintenanceMode( $this->serviceLocator->get(MaintenanceDisableCommand::class), @@ -182,10 +176,8 @@ public function create($name, array $params = []) $name, $params ); - break; default: throw new \RuntimeException(sprintf('"%s" job is not supported.', $name)); - break; } } } diff --git a/setup/src/Magento/Setup/Model/InstallerFactory.php b/setup/src/Magento/Setup/Model/InstallerFactory.php index 24634be6beba8..aeb5be93614fb 100644 --- a/setup/src/Magento/Setup/Model/InstallerFactory.php +++ b/setup/src/Magento/Setup/Model/InstallerFactory.php @@ -7,11 +7,13 @@ namespace Magento\Setup\Model; use Laminas\ServiceManager\ServiceLocatorInterface; -use Magento\Setup\Module\ResourceFactory; use Magento\Framework\App\ErrorHandler; use Magento\Framework\Setup\LoggerInterface; +use Magento\Setup\Module\ResourceFactory; /** + * Factory for \Magento\Setup\Model\Installer + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InstallerFactory @@ -29,8 +31,6 @@ class InstallerFactory private $resourceFactory; /** - * Constructor - * * @param ServiceLocatorInterface $serviceLocator * @param ResourceFactory $resourceFactory */ @@ -50,6 +50,7 @@ public function __construct( * * @param LoggerInterface $log * @return Installer + * @throws \Magento\Setup\Exception */ public function create(LoggerInterface $log) { @@ -83,7 +84,7 @@ public function create(LoggerInterface $log) } /** - * creates Resource Factory + * Create Resource Factory * * @return Resource */ diff --git a/setup/src/Magento/Setup/Model/Navigation.php b/setup/src/Magento/Setup/Model/Navigation.php index 1c5cee2303c2a..3eaa1fad4016e 100644 --- a/setup/src/Magento/Setup/Model/Navigation.php +++ b/setup/src/Magento/Setup/Model/Navigation.php @@ -9,6 +9,9 @@ use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\App\DeploymentConfig; +/** + * Navigation model + */ class Navigation { /**#@+ @@ -34,6 +37,8 @@ class Navigation /** * @param ServiceLocatorInterface $serviceLocator * @param DeploymentConfig $deploymentConfig + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException */ public function __construct(ServiceLocatorInterface $serviceLocator, DeploymentConfig $deploymentConfig) { @@ -49,6 +54,8 @@ public function __construct(ServiceLocatorInterface $serviceLocator, DeploymentC } /** + * Get type + * * @return string */ public function getType() @@ -57,6 +64,8 @@ public function getType() } /** + * Get data + * * @return array */ public function getData() diff --git a/setup/src/Magento/Setup/Model/PackagesAuth.php b/setup/src/Magento/Setup/Model/PackagesAuth.php index 502952db1aa16..b0363a5363d41 100644 --- a/setup/src/Magento/Setup/Model/PackagesAuth.php +++ b/setup/src/Magento/Setup/Model/PackagesAuth.php @@ -7,7 +7,8 @@ namespace Magento\Setup\Model; use Magento\Framework\App\Filesystem\DirectoryList; -use Laminas\View\Model\JsonModel; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; /** * Class PackagesAuth, checks, saves and removes auth details related to packages. @@ -70,19 +71,23 @@ public function __construct( $this->serviceLocator = $serviceLocator; $this->curlClient = $curl; $this->filesystem = $filesystem; - $this->serializer = $serializer?: \Magento\Framework\App\ObjectManager::getInstance() + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** + * Get packages json URL + * * @return string */ private function getPackagesJsonUrl() { - return $this->urlPrefix . $this->getCredentialBaseUrl() . '/packages.json'; + return $this->urlPrefix . $this->getCredentialBaseUrl() . '/packages.json'; } /** + * Get credentials base URL + * * @return string */ public function getCredentialBaseUrl() @@ -92,6 +97,8 @@ public function getCredentialBaseUrl() } /** + * Check credentials + * * @param string $token * @param string $secretKey * @return string @@ -148,7 +155,7 @@ private function getAuthJson() $data = $directory->readFile(self::PATH_TO_AUTH_FILE); return json_decode($data, true); } catch (\Exception $e) { - throw new \Exception('Error in reading Auth file'); + throw new LocalizedException(new Phrase('Error in reading Auth file')); } } return false; diff --git a/setup/src/Magento/Setup/Module.php b/setup/src/Magento/Setup/Module.php index 635d954dcda84..7808ead3808e3 100644 --- a/setup/src/Magento/Setup/Module.php +++ b/setup/src/Magento/Setup/Module.php @@ -6,27 +6,32 @@ namespace Magento\Setup; -use Magento\Framework\App\Response\HeaderProvider\XssProtection; -use Magento\Setup\Mvc\View\Http\InjectTemplateListener; use Laminas\EventManager\EventInterface; +use Laminas\EventManager\EventManager; use Laminas\ModuleManager\Feature\BootstrapListenerInterface; use Laminas\ModuleManager\Feature\ConfigProviderInterface; use Laminas\Mvc\ModuleRouteListener; use Laminas\Mvc\MvcEvent; +use Laminas\Stdlib\DispatchableInterface; +use Magento\Framework\App\Response\HeaderProvider\XssProtection; +use Magento\Setup\Mvc\View\Http\InjectTemplateListener; +/** + * Laminas module declaration + */ class Module implements BootstrapListenerInterface, ConfigProviderInterface { /** - * {@inheritdoc} + * @inheritDoc */ public function onBootstrap(EventInterface $e) { - /** @var \Laminas\Mvc\MvcEvent $e */ + /** @var MvcEvent $e */ /** @var \Laminas\Mvc\Application $application */ $application = $e->getApplication(); - /** @var \Laminas\EventManager\EventManager $events */ + /** @var EventManager $events */ $events = $application->getEventManager(); /** @var \Laminas\EventManager\SharedEventManager $sharedEvents */ $sharedEvents = $events->getSharedManager(); @@ -38,7 +43,7 @@ public function onBootstrap(EventInterface $e) // to process templates by Vendor/Module $injectTemplateListener = new InjectTemplateListener(); $sharedEvents->attach( - \Laminas\Stdlib\DispatchableInterface::class, + DispatchableInterface::class, MvcEvent::EVENT_DISPATCH, [$injectTemplateListener, 'injectTemplate'], -89 @@ -63,10 +68,11 @@ public function onBootstrap(EventInterface $e) } /** - * {@inheritdoc} + * @inheritDoc */ public function getConfig() { + // phpcs:disable $result = array_merge_recursive( include __DIR__ . '/../../../config/module.config.php', include __DIR__ . '/../../../config/router.config.php', @@ -82,6 +88,7 @@ public function getConfig() include __DIR__ . '/../../../config/languages.config.php', include __DIR__ . '/../../../config/marketplace.config.php' ); + // phpcs:enable return $result; } } diff --git a/setup/src/Magento/Setup/Module/ConnectionFactory.php b/setup/src/Magento/Setup/Module/ConnectionFactory.php index 5f50d5efdf381..dafab58c49130 100644 --- a/setup/src/Magento/Setup/Module/ConnectionFactory.php +++ b/setup/src/Magento/Setup/Module/ConnectionFactory.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Module; use Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql; @@ -31,7 +32,7 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** - * {@inheritdoc} + * @inheritDoc */ public function create(array $connectionConfig) { diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php index 8c80f339a3a70..27ac2b2794bb9 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php @@ -7,6 +7,8 @@ namespace Magento\Setup\Module\Di\Code\Reader; /** + * FileScanner code reader + * * @SuppressWarnings(PHPMD) */ class FileScanner extends \Laminas\Code\Scanner\FileScanner @@ -17,7 +19,7 @@ class FileScanner extends \Laminas\Code\Scanner\FileScanner private $tokenType; /** - * {@inheritdoc} + * @inheritDoc */ protected function scan() { @@ -106,6 +108,7 @@ protected function scan() return $infoIndex; }; + // phpcs:disable /** * START FINITE STATE MACHINE FOR SCANNING TOKENS */ @@ -357,5 +360,6 @@ protected function scan() * END FINITE STATE MACHINE FOR SCANNING TOKENS */ $this->isScanned = true; + // phpcs:enable } } diff --git a/setup/src/Magento/Setup/Module/ResourceFactory.php b/setup/src/Magento/Setup/Module/ResourceFactory.php index 947afda59dc34..0948542f29e0e 100644 --- a/setup/src/Magento/Setup/Module/ResourceFactory.php +++ b/setup/src/Magento/Setup/Module/ResourceFactory.php @@ -3,12 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Module; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ResourceConnection; use Magento\Setup\Module\Setup\ResourceConfig; -use Laminas\ServiceManager\ServiceLocatorInterface; +/** + * Factory for Magento\Framework\App\ResourceConnection + */ class ResourceFactory { /** @@ -19,8 +24,6 @@ class ResourceFactory protected $serviceLocator; /** - * Constructor - * * @param ServiceLocatorInterface $serviceLocator */ public function __construct(ServiceLocatorInterface $serviceLocator) @@ -29,17 +32,20 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** - * @param \Magento\Framework\App\DeploymentConfig $deploymentConfig - * @return Resource + * Create object + * + * @param DeploymentConfig $deploymentConfig + * @return ResourceConnection */ - public function create(\Magento\Framework\App\DeploymentConfig $deploymentConfig) + public function create(DeploymentConfig $deploymentConfig) { - $connectionFactory = $this->serviceLocator->get(\Magento\Setup\Module\ConnectionFactory::class); + $connectionFactory = $this->serviceLocator->get(ConnectionFactory::class); $resource = new ResourceConnection( new ResourceConfig(), $connectionFactory, $deploymentConfig ); + return $resource; } } diff --git a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php index 1f230ddbaefa5..537acc97ae385 100644 --- a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php +++ b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php @@ -9,6 +9,9 @@ use Laminas\Mvc\MvcEvent; use Laminas\Mvc\View\Http\InjectTemplateListener as LaminasInjectTemplateListener; +/** + * InjectTemplateListener for HTTP request + */ class InjectTemplateListener extends LaminasInjectTemplateListener { /** @@ -30,6 +33,8 @@ protected function deriveModuleNamespace($controller) } /** + * Get controller sub-namespace + * * @param string $namespace * @return string */ diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php index a97a8d96b34d2..452fa9f404683 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Test\Unit\Controller; use Magento\Setup\Controller\ExtensionGrid; @@ -12,7 +13,7 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class ExtensionGridTest + * Test for \Magento\Setup\Controller\ExtensionGrid */ class ExtensionGridTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php index 25bf754676c23..4d4d7e1d1a6ef 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php @@ -10,7 +10,7 @@ use Magento\Setup\Model\Grid\Module; /** - * Class ModuleGridTest + * Test for \Magento\Setup\Controller\ModuleGrid */ class ModuleGridTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php index 7daa5fc052d5b..412aaa2cfab71 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php @@ -6,11 +6,10 @@ namespace Magento\Setup\Test\Unit\Controller; -use Magento\Setup\Model\Navigation; use Magento\Setup\Controller\StartUpdater; /** - * Class StartUpdaterTest + * Test for \Magento\Setup\Controller\StartUpdater * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StartUpdaterTest extends \PHPUnit\Framework\TestCase diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php index 8a5286af19a06..e91740c8f558c 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php @@ -12,7 +12,7 @@ use Laminas\View\Model\ViewModel; /** - * Class UpdateExtensionGridTest + * CTest for \Magento\Setup\Controller\UpdateExtensionGrid */ class UpdateExtensionGridTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php index 1081ff3888eed..1c1bc6b36db4d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php @@ -6,18 +6,18 @@ namespace Magento\Setup\Test\Unit\Model; -use Magento\Setup\Model\ObjectManagerProvider; -use Magento\Setup\Model\Bootstrap; use Laminas\ServiceManager\ServiceLocatorInterface; -use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\App\ObjectManagerFactory; -use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Console\CommandListInterface; -use Symfony\Component\Console\Command\Command; +use Magento\Framework\ObjectManagerInterface; +use Magento\Setup\Model\Bootstrap; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; /** - * Class ObjectManagerProviderTest + * Test for \Magento\Setup\Model\ObjectManagerProvider */ class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index b063186c26fca..0c387f923c631 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -3,15 +3,37 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; -use \Magento\Setup\Mvc\Bootstrap\InitParamListener; +namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; +use Laminas\Console\Request; +use Laminas\EventManager\EventManagerInterface; +use Laminas\EventManager\SharedEventManager; +use Laminas\Http\Headers; +use Laminas\Http\Response; +use Laminas\Mvc\Application; +use Laminas\Mvc\Router\Http\RouteMatch; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceManager; +use Magento\Backend\App\BackendApp; +use Magento\Backend\App\BackendAppList; +use Magento\Backend\Model\Auth; +use Magento\Backend\Model\Auth\Session; +use Magento\Backend\Model\Session\AdminConfig; +use Magento\Backend\Model\Url; +use Magento\Framework\App\Area; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\State; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\App\Bootstrap as AppBootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Laminas\Mvc\MvcEvent; /** + * Test for \Magento\Setup\Mvc\Bootstrap\InitParamListener * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InitParamListenerTest extends \PHPUnit\Framework\TestCase @@ -46,28 +68,30 @@ public function testDetach() public function testOnBootstrap() { - /** @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ - $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); - $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + /** @var MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ + $mvcEvent = $this->createMock(MvcEvent::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); $mvcEvent->expects($this->once())->method('getApplication')->willReturn($mvcApplication); - $serviceManager = $this->createMock(\Laminas\ServiceManager\ServiceManager::class); + $serviceManager = $this->createMock(ServiceManager::class); $initParams[AppBootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS][DirectoryList::ROOT] = ['path' => '/test']; $serviceManager->expects($this->once())->method('get') ->willReturn($initParams); $serviceManager->expects($this->exactly(2))->method('setService') ->withConsecutive( [ - \Magento\Framework\App\Filesystem\DirectoryList::class, - $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class), + DirectoryList::class, + $this->isInstanceOf(DirectoryList::class), ], [ - \Magento\Framework\Filesystem::class, - $this->isInstanceOf(\Magento\Framework\Filesystem::class), + Filesystem::class, + $this->isInstanceOf(Filesystem::class), ] ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); - $eventManager = $this->getMockForAbstractClass(\Laminas\EventManager\EventManagerInterface::class); + $eventManager = $this->getMockForAbstractClass(EventManagerInterface::class); $mvcApplication->expects($this->any())->method('getEventManager')->willReturn($eventManager); $eventManager->expects($this->any())->method('attach'); @@ -95,10 +119,12 @@ public function testCreateDirectoryListException() public function testCreateServiceNotConsole() { /** - * @var \Laminas\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + $serviceLocator = $this->createMock(ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); $request = $this->createMock(\Laminas\Stdlib\RequestInterface::class); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); $serviceLocator->expects($this->once())->method('get')->with('Application') @@ -121,11 +147,13 @@ public function testCreateService($zfAppConfig, $env, $cliParam, $expectedArray) } $listener = new InitParamListener(); /** - * @var \Laminas\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Laminas\Mvc\Application::class)->disableOriginalConstructor()->getMock(); - $request = $this->getMockBuilder(\Laminas\Console\Request::class)->disableOriginalConstructor()->getMock(); + $serviceLocator = $this->createMock(ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); + $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock(); $request->expects($this->any()) ->method('getContent') ->willReturn( @@ -209,10 +237,10 @@ public function testCreateFilesystem() $testPath = 'test/path/'; /** - * @var \Magento\Framework\App\Filesystem\DirectoryList| + * @var DirectoryList| * \PHPUnit_Framework_MockObject_MockObject $directoryList */ - $directoryList = $this->getMockBuilder(\Magento\Framework\App\Filesystem\DirectoryList::class) + $directoryList = $this->getMockBuilder(DirectoryList::class) ->disableOriginalConstructor()->getMock(); $directoryList->expects($this->any())->method('getPath')->willReturn($testPath); $filesystem = $this->listener->createFilesystem($directoryList); @@ -228,14 +256,14 @@ public function testCreateFilesystem() */ private function prepareEventManager() { - $this->callbacks[] = [$this->listener, 'onBootstrap']; + $this->callbacks[] = [$this->listener, 'onBootstrap']; - /** @var \Laminas\EventManager\EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ - $eventManager = $this->createMock(\Laminas\EventManager\EventManagerInterface::class); + /** @var EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ + $eventManager = $this->createMock(EventManagerInterface::class); - $sharedManager = $this->createMock(\Laminas\EventManager\SharedEventManager::class); + $sharedManager = $this->createMock(SharedEventManager::class); $sharedManager->expects($this->once())->method('attach')->with( - \Laminas\Mvc\Application::class, + Application::class, MvcEvent::EVENT_BOOTSTRAP, [$this->listener, 'onBootstrap'] ); @@ -252,53 +280,53 @@ private function prepareEventManager() public function testAuthPreDispatch() { $cookiePath = 'test'; - $eventMock = $this->getMockBuilder(\Laminas\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Laminas\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(RouteMatch::class) ->disableOriginalConstructor() ->getMock(); - $applicationMock = $this->getMockBuilder(\Laminas\Mvc\Application::class) + $applicationMock = $this->getMockBuilder(Application::class) ->disableOriginalConstructor() ->getMock(); - $serviceManagerMock = $this->getMockBuilder(\Laminas\ServiceManager\ServiceManager::class) + $serviceManagerMock = $this->getMockBuilder(ServiceManager::class) ->disableOriginalConstructor() ->getMock(); - $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); $deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $omProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + $omProvider = $this->getMockBuilder(ObjectManagerProvider::class) ->disableOriginalConstructor() ->getMock(); - $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $adminAppStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); + $adminAppStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->getMock(); - $sessionConfigMock = $this->getMockBuilder(\Magento\Backend\Model\Session\AdminConfig::class) + $sessionConfigMock = $this->getMockBuilder(AdminConfig::class) ->disableOriginalConstructor() ->getMock(); - $backendAppListMock = $this->getMockBuilder(\Magento\Backend\App\BackendAppList::class) + $backendAppListMock = $this->getMockBuilder(BackendAppList::class) ->disableOriginalConstructor() ->getMock(); - $backendAppMock = $this->getMockBuilder(\Magento\Backend\App\BackendApp::class) + $backendAppMock = $this->getMockBuilder(BackendApp::class) ->disableOriginalConstructor() ->getMock(); - $urlMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + $urlMock = $this->getMockBuilder(Url::class) ->disableOriginalConstructor() ->getMock(); - $authenticationMock = $this->getMockBuilder(\Magento\Backend\Model\Auth::class) + $authenticationMock = $this->getMockBuilder(Auth::class) ->disableOriginalConstructor() ->getMock(); - $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + $adminSessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); - $responseMock = $this->getMockBuilder(\Laminas\Http\Response::class) + $responseMock = $this->getMockBuilder(Response::class) ->disableOriginalConstructor() ->getMock(); - $headersMock = $this->getMockBuilder(\Laminas\Http\Headers::class) + $headersMock = $this->getMockBuilder(Headers::class) ->disableOriginalConstructor() ->getMock(); @@ -329,12 +357,12 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Framework\App\DeploymentConfig::class, + DeploymentConfig::class, true, $deploymentConfigMock, ], [ - \Magento\Setup\Model\ObjectManagerProvider::class, + ObjectManagerProvider::class, true, $omProvider, ], @@ -345,19 +373,19 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Framework\App\State::class, + State::class, $adminAppStateMock, ], [ - \Magento\Backend\Model\Session\AdminConfig::class, + AdminConfig::class, $sessionConfigMock, ], [ - \Magento\Backend\App\BackendAppList::class, + BackendAppList::class, $backendAppListMock, ], [ - \Magento\Backend\Model\Auth::class, + Auth::class, $authenticationMock, ], ] @@ -367,7 +395,7 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Backend\Model\Auth\Session::class, + Session::class, [ 'sessionConfig' => $sessionConfigMock, 'appState' => $adminAppStateMock @@ -375,7 +403,7 @@ public function testAuthPreDispatch() $adminSessionMock, ], [ - \Magento\Backend\Model\Url::class, + Url::class, [], $urlMock, ], @@ -386,7 +414,7 @@ public function testAuthPreDispatch() ->willReturn($objectManagerMock); $adminAppStateMock->expects($this->once()) ->method('setAreaCode') - ->with(\Magento\Framework\App\Area::AREA_ADMINHTML); + ->with(Area::AREA_ADMINHTML); $applicationMock->expects($this->once()) ->method('getServiceManager') ->willReturn($serviceManagerMock); @@ -433,13 +461,13 @@ public function testAuthPreDispatch() public function testAuthPreDispatchSkip() { - $eventMock = $this->getMockBuilder(\Laminas\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Laminas\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(RouteMatch::class) ->disableOriginalConstructor() ->getMock(); - $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); diff --git a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php index b32f4970db1d8..57697cd9cd9bf 100644 --- a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php +++ b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php @@ -8,11 +8,9 @@ declare(strict_types=1); -namespace Laminas\Mvc\Controller; +namespace Zend\Mvc\Controller; use Interop\Container\ContainerInterface; -use ReflectionClass; -use ReflectionParameter; use Laminas\Console\Adapter\AdapterInterface as ConsoleAdapterInterface; use Laminas\Filter\FilterPluginManager; use Laminas\Hydrator\HydratorPluginManager; @@ -27,6 +25,8 @@ use Laminas\ServiceManager\ServiceLocatorInterface; use Laminas\Stdlib\DispatchableInterface; use Laminas\Validator\ValidatorPluginManager; +use ReflectionClass; +use ReflectionParameter; /** * Reflection-based factory for controllers. @@ -100,9 +100,10 @@ class LazyControllerAbstractFactory implements AbstractFactoryInterface ]; /** - * {@inheritDoc} + * @inheritDoc * * @return DispatchableInterface + * @throws \ReflectionException */ public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { @@ -127,7 +128,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o } /** - * {@inheritDoc} + * @inheritDoc */ public function canCreate(ContainerInterface $container, $requestedName) { @@ -168,7 +169,7 @@ private function resolveParameter(ContainerInterface $container, $requestedName) } if (! $parameter->getClass()) { - return; + return null; } $type = $parameter->getClass()->getName(); @@ -191,8 +192,10 @@ private function resolveParameter(ContainerInterface $container, $requestedName) * Determine if we can create a service with name * * @param ServiceLocatorInterface $serviceLocator + * phpcs:disable * @param $name * @param $requestedName + * phpcs:enable * @return bool * @SuppressWarnings("unused") */ @@ -205,10 +208,13 @@ public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator * Create service with name * * @param ServiceLocatorInterface $serviceLocator + * phpcs:disable * @param $name * @param $requestedName + * phpcs:enable * @return mixed * @SuppressWarnings("unused") + * @throws \ReflectionException */ public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName) { From 9ad08cb7e2d38226036345f1381c7fddb03c777c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 9 Mar 2020 20:50:46 +0100 Subject: [PATCH 309/734] Cleanup ObjectManager usage - Magento_Translation --- .../Magento/Translation/Model/FileManager.php | 51 +++++---- .../Translation/Model/Inline/Parser.php | 99 +++++++++-------- .../Translation/Model/Json/PreProcessor.php | 10 +- .../Model/ResourceModel/StringUtils.php | 77 +++++++------ .../Model/ResourceModel/Translate.php | 73 ++++++------ .../Test/Unit/Model/Inline/ParserTest.php | 105 ++++++++---------- 6 files changed, 210 insertions(+), 205 deletions(-) diff --git a/app/code/Magento/Translation/Model/FileManager.php b/app/code/Magento/Translation/Model/FileManager.php index 387173f6de0ba..95fb3f2a5d4e9 100644 --- a/app/code/Magento/Translation/Model/FileManager.php +++ b/app/code/Magento/Translation/Model/FileManager.php @@ -3,11 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; -use Magento\Translation\Model\Inline\File as TranslationFile; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\View\Asset\Repository; /** * A service for handling Translation config files @@ -20,41 +22,33 @@ class FileManager const TRANSLATION_CONFIG_FILE_NAME = 'Magento_Translation/js/i18n-config.js'; /** - * @var \Magento\Framework\View\Asset\Repository + * @var Repository */ private $assetRepo; /** - * @var \Magento\Framework\App\Filesystem\DirectoryList + * @var DirectoryList */ private $directoryList; /** - * @var \Magento\Framework\Filesystem\Driver\File + * @var File */ private $driverFile; /** - * @var TranslationFile - */ - private $translationFile; - - /** - * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList - * @param \Magento\Framework\Filesystem\Driver\File $driverFile - * @param TranslationFile $translationFile + * @param Repository $assetRepo + * @param DirectoryList $directoryList + * @param File $driverFile */ public function __construct( - \Magento\Framework\View\Asset\Repository $assetRepo, - \Magento\Framework\App\Filesystem\DirectoryList $directoryList, - \Magento\Framework\Filesystem\Driver\File $driverFile, - \Magento\Translation\Model\Inline\File $translationFile = null + Repository $assetRepo, + DirectoryList $directoryList, + File $driverFile ) { $this->assetRepo = $assetRepo; $this->directoryList = $directoryList; $this->driverFile = $driverFile; - $this->translationFile = $translationFile ?: ObjectManager::getInstance()->get(TranslationFile::class); } /** @@ -71,7 +65,7 @@ public function createTranslateConfigAsset() } /** - * gets current js-translation.json timestamp + * Gets current js-translation.json timestamp * * @return string|void */ @@ -87,18 +81,22 @@ public function getTranslationFileTimestamp() } /** + * Retrieve full path for translation file + * * @return string */ protected function getTranslationFileFullPath() { return $this->directoryList->getPath(DirectoryList::STATIC_VIEW) . - \DIRECTORY_SEPARATOR . - $this->assetRepo->getStaticViewFileContext()->getPath() . - \DIRECTORY_SEPARATOR . - Js\Config::DICTIONARY_FILE_NAME; + \DIRECTORY_SEPARATOR . + $this->assetRepo->getStaticViewFileContext()->getPath() . + \DIRECTORY_SEPARATOR . + Js\Config::DICTIONARY_FILE_NAME; } /** + * Retrieve path for translation file + * * @return string */ public function getTranslationFilePath() @@ -107,7 +105,10 @@ public function getTranslationFilePath() } /** + * Update translation file with content + * * @param string $content + * * @return void */ public function updateTranslationFileContent($content) @@ -115,9 +116,11 @@ public function updateTranslationFileContent($content) $translationDir = $this->directoryList->getPath(DirectoryList::STATIC_VIEW) . \DIRECTORY_SEPARATOR . $this->assetRepo->getStaticViewFileContext()->getPath(); + if (!$this->driverFile->isExists($this->getTranslationFileFullPath())) { $this->driverFile->createDirectory($translationDir); } + $this->driverFile->filePutContents($this->getTranslationFileFullPath(), $content); } diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php index ca66d288d6f60..9c0c8dc696c1b 100644 --- a/app/code/Magento/Translation/Model/Inline/Parser.php +++ b/app/code/Magento/Translation/Model/Inline/Parser.php @@ -3,24 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Translation\Model\Inline; +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Framework\Translate\Inline\ParserInterface; +use Magento\Translation\Model\ResourceModel\StringFactory; +use Magento\Translation\Model\ResourceModel\StringUtils; use Magento\Translation\Model\ResourceModel\StringUtilsFactory; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\State; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Translate\InlineInterface; use Magento\Framework\Escaper; -use Magento\Framework\App\ObjectManager; -use Magento\Translation\Model\Inline\CacheManager; /** * Parses content and applies necessary html element wrapping and client scripts for inline translation. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Parser implements \Magento\Framework\Translate\Inline\ParserInterface +class Parser implements ParserInterface { /** * data-translate html element attribute name @@ -105,7 +108,7 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface ]; /** - * @var \Magento\Translation\Model\ResourceModel\StringFactory + * @var StringFactory */ protected $_resourceFactory; @@ -144,23 +147,6 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface */ private $relatedCacheTypes; - /** - * Return cache manager - * - * @return CacheManager - * - * @deprecated 100.1.0 - */ - private function getCacheManger() - { - if (!$this->cacheManager instanceof CacheManager) { - $this->cacheManager = ObjectManager::getInstance()->get( - CacheManager::class - ); - } - return $this->cacheManager; - } - /** * Initialize base inline translation model * @@ -170,8 +156,9 @@ private function getCacheManger() * @param State $appState * @param TypeListInterface $appCache * @param InlineInterface $translateInline + * @param Escaper $escaper + * @param CacheManager $cacheManager * @param array $relatedCacheTypes - * @param Escaper|null $escaper */ public function __construct( StringUtilsFactory $resource, @@ -180,8 +167,9 @@ public function __construct( State $appState, TypeListInterface $appCache, InlineInterface $translateInline, - array $relatedCacheTypes = [], - Escaper $escaper = null + Escaper $escaper, + CacheManager $cacheManager, + array $relatedCacheTypes = [] ) { $this->_resourceFactory = $resource; $this->_storeManager = $storeManager; @@ -189,16 +177,16 @@ public function __construct( $this->_appState = $appState; $this->_appCache = $appCache; $this->_translateInline = $translateInline; + $this->escaper = $escaper; + $this->cacheManager = $cacheManager; $this->relatedCacheTypes = $relatedCacheTypes; - $this->escaper = $escaper ?? ObjectManager::getInstance()->get( - Escaper::class - ); } /** * Parse and save edited translation * * @param array $translateParams + * * @return array */ public function processAjaxPost(array $translateParams) @@ -206,6 +194,7 @@ public function processAjaxPost(array $translateParams) if (!$this->_translateInline->isAllowed()) { return ['inline' => 'not allowed']; } + if (!empty($this->relatedCacheTypes)) { $this->_appCache->invalidate($this->relatedCacheTypes); } @@ -216,18 +205,16 @@ public function processAjaxPost(array $translateParams) /** @var $validStoreId int */ $validStoreId = $this->_storeManager->getStore()->getId(); - /** @var $resource \Magento\Translation\Model\ResourceModel\StringUtils */ + /** @var $resource StringUtils */ $resource = $this->_resourceFactory->create(); foreach ($translateParams as $param) { - if ($this->_appState->getAreaCode() == \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) { + if ($this->_appState->getAreaCode() == FrontNameResolver::AREA_CODE) { + $storeId = 0; + } elseif (empty($param['perstore'])) { + $resource->deleteTranslate($param['original'], null, false); $storeId = 0; } else { - if (empty($param['perstore'])) { - $resource->deleteTranslate($param['original'], null, false); - $storeId = 0; - } else { - $storeId = $validStoreId; - } + $storeId = $validStoreId; } $resource->saveTranslate( $param['original'], @@ -237,13 +224,14 @@ public function processAjaxPost(array $translateParams) ); } - return $this->getCacheManger()->updateAndGetTranslations(); + return $this->cacheManager->updateAndGetTranslations(); } /** * Validate the structure of translation parameters * * @param array $translateParams + * * @return void * @throws \InvalidArgumentException */ @@ -263,6 +251,7 @@ protected function _validateTranslationParams(array $translateParams) * * @param array $translateParams * @param array $fieldNames Names of fields values of which are to be filtered + * * @return void */ protected function _filterTranslationParams(array &$translateParams, array $fieldNames) @@ -278,6 +267,7 @@ protected function _filterTranslationParams(array &$translateParams, array $fiel * Replace html body with translation wrapping. * * @param string $body + * * @return string */ public function processResponseBodyString($body) @@ -305,6 +295,7 @@ public function getContent() * Sets the body content that is being parsed passed upon the passed in string. * * @param string $content + * * @return void */ public function setContent($content) @@ -316,6 +307,7 @@ public function setContent($content) * Set flag about parsed content is Json * * @param bool $flag + * * @return $this */ public function setIsJson($flag) @@ -329,7 +321,9 @@ public function setIsJson($flag) * * @param array $matches * @param array $options + * * @return string + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _getAttributeLocation($matches, $options) @@ -343,26 +337,25 @@ protected function _getAttributeLocation($matches, $options) * * @param array $matches * @param array $options + * * @return string + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _getTagLocation($matches, $options) { $tagName = strtolower($options['tagName']); - if (isset($options['tagList'][$tagName])) { - return $options['tagList'][$tagName]; - } - - return ucfirst($tagName) . ' Text'; + return $options['tagList'][$tagName] ?? (ucfirst($tagName) . ' Text'); } /** - * Format translation for special tags. Adding translate mode attribute for vde requests. + * Format translation for special tags. Adding translate mode attribute for vde requests. * * @param string $tagHtml * @param string $tagName * @param array $trArr + * * @return string */ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) @@ -378,6 +371,7 @@ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) $specialTags .= '>' . strtoupper($tagName); } $specialTags .= '</span>'; + return $specialTags; } @@ -387,6 +381,7 @@ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) * @param string $tagHtml * @param string $tagName * @param array $trArr + * * @return string */ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) @@ -404,6 +399,7 @@ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) $simpleTags .= ' ' . $additionalAttr; } $simpleTags .= substr($tagHtml, strlen($tagName) + 1); + return $simpleTags; } @@ -414,6 +410,7 @@ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) * @param string $text * @param callable $locationCallback * @param array $options + * * @return array */ private function _getTranslateData(string $regexp, string &$text, callable $locationCallback, array $options = []) @@ -449,6 +446,7 @@ private function _tagAttributes() * Prepare tags inline translates for the content * * @param string &$content + * * @return void */ private function _prepareTagAttributesForContent(&$content) @@ -494,6 +492,7 @@ private function _prepareTagAttributesForContent(&$content) * * @param string $name * @param string $value + * * @return string */ private function _getHtmlAttribute($name, $value) @@ -505,6 +504,7 @@ private function _getHtmlAttribute($name, $value) * Add data-translate-mode attribute * * @param string $trAttr + * * @return string */ private function _addTranslateAttribute($trAttr) @@ -514,6 +514,7 @@ private function _addTranslateAttribute($trAttr) if ($additionalAttr !== null) { $translateAttr .= ' ' . $additionalAttr . ' '; } + return $translateAttr; } @@ -526,9 +527,9 @@ private function _getHtmlQuote() { if ($this->_isJson) { return '\"'; - } else { - return '"'; } + + return '"'; } /** @@ -560,6 +561,7 @@ function ($tagHtml, $tagName, $trArr) { * @param string $content * @param array $tagsList * @param callable $formatCallback + * * @return void */ private function _translateTags(string &$content, array $tagsList, callable $formatCallback) @@ -642,6 +644,7 @@ private function _translateTags(string &$content, array $tagsList, callable $for * @param string $body * @param string $tagName * @param int $from + * * @return bool|int return false if end of tag is not found */ private function _findEndOfTag($body, $tagName, $from) @@ -658,11 +661,11 @@ private function _findEndOfTag($body, $tagName, $from) } $length = $end - $from + $tagLength + 3; } - if (preg_match('#<\\\\?\/' . $tagName . '\s*?>#i', $body, $tagMatch, null, $end)) { + if (preg_match('#<\\\\?\/' . $tagName . '\s*?>#i', $body, $tagMatch, 0, $end)) { return $end + strlen($tagMatch[0]); - } else { - return false; } + + return false; } /** @@ -700,6 +703,7 @@ private function _otherText() * * @param string $data * @param string $text + * * @return string */ protected function _getDataTranslateSpan($data, $text) @@ -717,6 +721,7 @@ protected function _getDataTranslateSpan($data, $text) * Add an additional html attribute if needed. * * @param mixed $tagName + * * @return string */ protected function _getAdditionalHtmlAttribute($tagName = null) diff --git a/app/code/Magento/Translation/Model/Json/PreProcessor.php b/app/code/Magento/Translation/Model/Json/PreProcessor.php index f19d6a8fc80c4..a5ea52f8df581 100644 --- a/app/code/Magento/Translation/Model/Json/PreProcessor.php +++ b/app/code/Magento/Translation/Model/Json/PreProcessor.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Translation\Model\Json; use Magento\Framework\App\Area; use Magento\Framework\App\AreaList; -use Magento\Framework\App\ObjectManager; use Magento\Framework\TranslateInterface; use Magento\Framework\View\Asset\File\FallbackContext; use Magento\Framework\View\Asset\PreProcessor\Chain; @@ -57,26 +57,27 @@ class PreProcessor implements PreProcessorInterface * @param DataProviderInterface $dataProvider * @param AreaList $areaList * @param TranslateInterface $translate - * @param DesignInterface|null $viewDesign + * @param DesignInterface $viewDesign */ public function __construct( Config $config, DataProviderInterface $dataProvider, AreaList $areaList, TranslateInterface $translate, - DesignInterface $viewDesign = null + DesignInterface $viewDesign ) { $this->config = $config; $this->dataProvider = $dataProvider; $this->areaList = $areaList; $this->translate = $translate; - $this->viewDesign = $viewDesign ?? ObjectManager::getInstance()->get(DesignInterface::class); + $this->viewDesign = $viewDesign; } /** * Transform content and/or content type for the specified preprocessing chain object * * @param Chain $chain + * * @return void */ public function process(Chain $chain) @@ -110,6 +111,7 @@ public function process(Chain $chain) * Is provided path the path to translation dictionary * * @param string $path + * * @return bool */ protected function isDictionaryPath($path) diff --git a/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php b/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php index be7656fbf61a7..4cefdd58609a6 100644 --- a/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php +++ b/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php @@ -3,15 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model\ResourceModel; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\DB\Select; use Magento\Framework\Escaper; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Store\Model\Store; /** * String translation utilities */ -class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class StringUtils extends AbstractDb { /** * @var Escaper @@ -19,12 +27,12 @@ class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb private $escaper; /** - * @var \Magento\Framework\Locale\ResolverInterface + * @var ResolverInterface */ protected $_localeResolver; /** - * @var \Magento\Framework\App\ScopeResolverInterface + * @var ScopeResolverInterface */ protected $scopeResolver; @@ -34,27 +42,25 @@ class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $scope; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver + * @param Context $context + * @param ResolverInterface $localeResolver + * @param ScopeResolverInterface $scopeResolver + * @param Escaper $escaper * @param string $connectionName * @param string|null $scope - * @param Escaper|null $escaper */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Locale\ResolverInterface $localeResolver, - \Magento\Framework\App\ScopeResolverInterface $scopeResolver, + Context $context, + ResolverInterface $localeResolver, + ScopeResolverInterface $scopeResolver, + Escaper $escaper, $connectionName = null, - $scope = null, - Escaper $escaper = null + $scope = null ) { $this->_localeResolver = $localeResolver; $this->scopeResolver = $scopeResolver; + $this->escaper = $escaper; $this->scope = $scope; - $this->escaper = $escaper ?? ObjectManager::getInstance()->get( - Escaper::class - ); parent::__construct($context, $connectionName); } @@ -69,14 +75,15 @@ protected function _construct() } /** - * Load + * Load an object * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @param String $value * @param String $field + * * @return array|$this */ - public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) + public function load(AbstractModel $object, $value, $field = null) { if (is_string($value)) { $select = $this->getConnection()->select()->from( @@ -88,9 +95,9 @@ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $fi $object->setData($result); $this->_afterLoad($object); return $result; - } else { - return parent::load($object, $value, $field); } + + return parent::load($object, $value, $field); } /** @@ -98,23 +105,25 @@ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $fi * * @param String $field * @param String $value - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Framework\DB\Select + * @param AbstractModel $object + * + * @return Select */ protected function _getLoadSelect($field, $value, $object) { $select = parent::_getLoadSelect($field, $value, $object); - $select->where('store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + $select->where('store_id = ?', Store::DEFAULT_STORE_ID); return $select; } /** * After translation loading * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - public function _afterLoad(\Magento\Framework\Model\AbstractModel $object) + public function _afterLoad(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select()->from( @@ -131,10 +140,11 @@ public function _afterLoad(\Magento\Framework\Model\AbstractModel $object) /** * Before save * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + protected function _beforeSave(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select() @@ -142,7 +152,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) ->where('string = :string') ->where('store_id = :store_id'); - $bind = ['string' => $object->getString(), 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID]; + $bind = ['string' => $object->getString(), 'store_id' => Store::DEFAULT_STORE_ID]; $object->setId($connection->fetchOne($select, $bind)); return parent::_beforeSave($object); @@ -151,10 +161,11 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) /** * After save * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) + protected function _afterSave(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select()->from( @@ -192,6 +203,7 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) * @param string $string * @param string $locale * @param int|null $storeId + * * @return $this */ public function deleteTranslate($string, $locale = null, $storeId = null) @@ -203,7 +215,7 @@ public function deleteTranslate($string, $locale = null, $storeId = null) $where = ['locale = ?' => $locale, 'string = ?' => $string]; if ($storeId === false) { - $where['store_id > ?'] = \Magento\Store\Model\Store::DEFAULT_STORE_ID; + $where['store_id > ?'] = Store::DEFAULT_STORE_ID; } elseif ($storeId !== null) { $where['store_id = ?'] = $storeId; } @@ -220,6 +232,7 @@ public function deleteTranslate($string, $locale = null, $storeId = null) * @param String $translate * @param String $locale * @param int|null $storeId + * * @return $this */ public function saveTranslate($string, $translate, $locale = null, $storeId = null) diff --git a/app/code/Magento/Translation/Model/ResourceModel/Translate.php b/app/code/Magento/Translation/Model/ResourceModel/Translate.php index cf379254d4320..ef14758f9f917 100644 --- a/app/code/Magento/Translation/Model/ResourceModel/Translate.php +++ b/app/code/Magento/Translation/Model/ResourceModel/Translate.php @@ -3,26 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model\ResourceModel; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\Config; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Translate\ResourceInterface; use Magento\Translation\App\Config\Type\Translation; -class Translate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb implements - \Magento\Framework\Translate\ResourceInterface +/** + * Translate data resource model + */ +class Translate extends AbstractDb implements ResourceInterface { /** - * @var \Magento\Framework\App\ScopeResolverInterface + * @var ScopeResolverInterface */ protected $scopeResolver; - /** - * @var null|string - */ - protected $scope; - /** * @var Config */ @@ -34,19 +37,30 @@ class Translate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb imp private $deployedConfig; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver + * @var null|string + */ + protected $scope; + + /** + * @param Context $context + * @param ScopeResolverInterface $scopeResolver * @param string $connectionName * @param null|string $scope + * @param Config|null $appConfig + * @param DeploymentConfig|null $deployedConfig */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\App\ScopeResolverInterface $scopeResolver, + Context $context, + ScopeResolverInterface $scopeResolver, $connectionName = null, - $scope = null + $scope = null, + ?Config $appConfig = null, + ?DeploymentConfig $deployedConfig = null ) { $this->scopeResolver = $scopeResolver; $this->scope = $scope; + $this->appConfig = $appConfig ?? ObjectManager::getInstance()->get(Config::class); + $this->deployedConfig = $deployedConfig ?? ObjectManager::getInstance()->get(DeploymentConfig::class); parent::__construct($context, $connectionName); } @@ -65,6 +79,7 @@ protected function _construct() * * @param int $storeId * @param string $locale + * * @return array */ public function getTranslationArray($storeId = null, $locale = null) @@ -74,7 +89,7 @@ public function getTranslationArray($storeId = null, $locale = null) } $locale = (string) $locale; - $data = $this->getAppConfig()->get( + $data = $this->appConfig->get( Translation::CONFIG_TYPE, $locale . '/' . $this->getStoreCode($storeId), [] @@ -98,6 +113,7 @@ public function getTranslationArray($storeId = null, $locale = null) * * @param array $strings * @param int|null $storeId + * * @return array */ public function getTranslationArrayByStrings(array $strings, $storeId = null) @@ -141,7 +157,7 @@ public function getMainChecksum() */ public function getConnection() { - if (!$this->getDeployedConfig()->isDbAvailable()) { + if (!$this->deployedConfig->isDbAvailable()) { return false; } return parent::getConnection(); @@ -161,34 +177,11 @@ protected function getStoreId() * Retrieve store code by store id * * @param int $storeId + * * @return string */ private function getStoreCode($storeId) { return $this->scopeResolver->getScope($storeId)->getCode(); } - - /** - * @deprecated 100.1.2 - * @return DeploymentConfig - */ - private function getDeployedConfig() - { - if ($this->deployedConfig === null) { - $this->deployedConfig = ObjectManager::getInstance()->get(DeploymentConfig::class); - } - return $this->deployedConfig; - } - - /** - * @deprecated 100.1.2 - * @return Config - */ - private function getAppConfig() - { - if ($this->appConfig === null) { - $this->appConfig = ObjectManager::getInstance()->get(Config::class); - } - return $this->appConfig; - } } diff --git a/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php b/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php index e11618831552f..552f3118748f8 100644 --- a/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php +++ b/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php @@ -3,22 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Test\Unit\Model\Inline; -use Magento\Translation\Model\Inline\Parser; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\State; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Translate\InlineInterface; -use Magento\Framework\App\Cache\TypeListInterface; -use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\Data\StoreInterface; -use Magento\Translation\Model\ResourceModel\StringUtilsFactory; -use Magento\Translation\Model\ResourceModel\StringUtils; +use Magento\Store\Model\StoreManagerInterface; use Magento\Translation\Model\Inline\CacheManager; +use Magento\Translation\Model\Inline\Parser; +use Magento\Translation\Model\ResourceModel\StringUtils; +use Magento\Translation\Model\ResourceModel\StringUtilsFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class ParserTest to test \Magento\Translation\Model\Inline\Parser */ -class ParserTest extends \PHPUnit\Framework\TestCase +class ParserTest extends TestCase { /** * @var Parser @@ -31,47 +36,47 @@ class ParserTest extends \PHPUnit\Framework\TestCase private $objectManager; /** - * @var InlineInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InlineInterface|MockObject */ private $translateInlineMock; /** - * @var TypeListInterface|\PHPUnit_Framework_MockObject_MockObject + * @var TypeListInterface|MockObject */ private $appCacheMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ private $storeMock; /** - * @var \Zend_Filter_Interface|\PHPUnit_Framework_MockObject_MockObject + * @var \Zend_Filter_Interface|MockObject */ private $inputFilterMock; /** - * @var StringUtilsFactory|\PHPUnit_Framework_MockObject_MockObject + * @var StringUtilsFactory|MockObject */ private $resourceFactoryMock; /** - * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ private $appStateMock; /** - * @var StringUtils|\PHPUnit_Framework_MockObject_MockObject + * @var StringUtils|MockObject */ private $resourceMock; /** - * @var CacheManager|\PHPUnit_Framework_MockObject_MockObject + * @var CacheManager|MockObject */ private $cacheManagerMock; @@ -79,37 +84,49 @@ protected function setUp() { $this->objectManager = new ObjectManager($this); $this->translateInlineMock = - $this->getMockForAbstractClass(\Magento\Framework\Translate\InlineInterface::class); - $this->appCacheMock = $this->getMockForAbstractClass(\Magento\Framework\App\Cache\TypeListInterface::class); - $this->storeManagerMock = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); - $this->storeMock = $this->getMockForAbstractClass(\Magento\Store\Api\Data\StoreInterface::class); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') + $this->getMockForAbstractClass(InlineInterface::class); + $this->appCacheMock = $this->getMockForAbstractClass(TypeListInterface::class); + $this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class); + $this->storeMock = $this->getMockForAbstractClass(StoreInterface::class); + $this->storeManagerMock->method('getStore') ->willReturn($this->storeMock); $this->resourceFactoryMock = $this->getMockBuilder( - \Magento\Translation\Model\ResourceModel\StringUtilsFactory::class + StringUtilsFactory::class ) ->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); - $this->resourceMock = $this->getMockBuilder(\Magento\Translation\Model\ResourceModel\StringUtils::class) + $this->resourceMock = $this->getMockBuilder(StringUtils::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->inputFilterMock = $this->getMockBuilder('Zend_Filter_Interface'); + $this->inputFilterMock = $this->getMockForAbstractClass('Zend_Filter_Interface'); - $this->resourceFactoryMock->expects($this->any()) - ->method('create') + $this->resourceFactoryMock->method('create') ->willReturn($this->resourceMock); - $this->cacheManagerMock = $this->getMockBuilder(\Magento\Translation\Model\Inline\CacheManager::class) + $this->cacheManagerMock = $this->getMockBuilder(CacheManager::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->appStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $this->appStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); + + $this->model = $this->objectManager->getObject( + Parser::class, + [ + 'resource' => $this->resourceFactoryMock, + 'storeManager' => $this->storeManagerMock, + 'inputFilter' => $this->inputFilterMock, + 'appState' => $this->appStateMock, + 'appCache' => $this->appCacheMock, + 'translateInline' => $this->translateInlineMock, + 'cacheManager' => $this->cacheManagerMock, + ] + ); } public function testProcessAjaxPostNotAllowed() @@ -118,10 +135,6 @@ public function testProcessAjaxPostNotAllowed() $this->translateInlineMock->expects($this->once()) ->method('isAllowed') ->willReturn(false); - $this->model = $this->objectManager->getObject( - Parser::class, - ['translateInline' => $this->translateInlineMock] - ); $this->assertEquals($expected, $this->model->processAjaxPost([])); } @@ -130,15 +143,6 @@ public function testProcessAjaxPost() $this->translateInlineMock->expects($this->once()) ->method('isAllowed') ->willReturn(true); - $this->model = $this->objectManager->getObject( - Parser::class, - [ - 'cacheManager' => $this->cacheManagerMock, - 'resource' => $this->resourceFactoryMock, - 'storeManager' => $this->storeManagerMock, - 'translateInline' => $this->translateInlineMock - ] - ); $this->model->processAjaxPost([]); } @@ -153,26 +157,11 @@ public function testProcessResponseBodyStringProcessingAttributesCorrectly() "data-translate=\"[{'shown':'Password','translated':'Password','original':'Password'," . "'location':'Tag attribute (ALT, TITLE, etc.)'}]\"" ]; - $this->translateInlineMock->expects($this->any())->method('getAdditionalHtmlAttribute')->willReturn(null); - - $this->model = $this->objectManager->getObject( - Parser::class, - [ - 'cacheManager' => $this->cacheManagerMock, - 'resource' => $this->resourceFactoryMock, - 'storeManager' => $this->storeManagerMock, - 'translateInline' => $this->translateInlineMock, - '_resourceFactory' => $this->resourceMock, - '_inputFilter' => $this->inputFilterMock, - '_appState' => $this->appStateMock, - '_appCache' => $this->appCacheMock, - '_translateInline' => $this->translateInlineMock - ] - ); + $this->translateInlineMock->method('getAdditionalHtmlAttribute')->willReturn(null); $processedContent = $this->model->processResponseBodyString($testContent); foreach ($processedAttributes as $attribute) { - $this->assertContains($attribute, $processedContent, "data-translate attribute not processed correctly"); + $this->assertContains($attribute, $processedContent, 'data-translate attribute not processed correctly'); } } } From acfc589b812278750e6545edc9f0e7b71a80cea3 Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Fri, 6 Mar 2020 16:04:03 +0200 Subject: [PATCH 310/734] Added improvements to product attribute repository (save method) to convert frontend input and add swatch input type for rest area --- ...ttributeByCodeOnProductFormActionGroup.xml | 21 +++++ .../Product/Attribute/Plugin/Save.php | 41 ++++----- .../ConvertSwatchAttributeFrontendInput.php | 46 ++++++++++ .../Product/Attribute/RepositoryPlugin.php | 51 +++++++++++ .../Test/Mftf/Data/SwatchAttributeData.xml | 3 + ...heckTextSwatchAttributeAddedViaApiTest.xml | 44 ++++++++++ .../Product/Attribute/Plugin/SaveTest.php | 70 --------------- ...onvertSwatchAttributeFrontendInputTest.php | 86 +++++++++++++++++++ .../Magento/Swatches/etc/webapi_rest/di.xml | 12 +++ 9 files changed, 282 insertions(+), 92 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml create mode 100644 app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php create mode 100644 app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml delete mode 100644 app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php create mode 100644 app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php create mode 100644 app/code/Magento/Swatches/etc/webapi_rest/di.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml new file mode 100644 index 0000000000000..2432c974b79f4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductAttributeByCodeOnProductFormActionGroup"> + <annotations> + <description>Requires the navigation to the Product page. Provided dropdown attribute presents on the page.</description> + </annotations> + <arguments> + <argument name="productAttributeCode" type="string" defaultValue="{{textSwatchProductAttribute.attribute_code}}"/> + </arguments> + + <seeElement selector="{{AdminProductAttributesSection.attributeDropdownByCode(productAttributeCode)}}" stepKey="assertAttributeIsPresentOnForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php index 72d27152d639a..c264614b1a0cf 100644 --- a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php +++ b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php @@ -3,18 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Plugin; use Magento\Catalog\Controller\Adminhtml\Product\Attribute; use Magento\Framework\App\RequestInterface; -use Magento\Swatches\Model\Swatch; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; /** * Plugin for product attribute save controller. */ class Save { + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @param ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + */ + public function __construct( + ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + ) { + $this->convertSwatchAttributeFrontendInput = $convertSwatchAttributeFrontendInput; + } + /** * Performs the conversion of the frontend input value. * @@ -23,30 +38,12 @@ class Save * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(Attribute\Save $subject, RequestInterface $request) + public function beforeDispatch(Attribute\Save $subject, RequestInterface $request): array { $data = $request->getPostValue(); + $data = $this->convertSwatchAttributeFrontendInput->execute($data); + $request->setPostValue($data); - if (isset($data['frontend_input'])) { - switch ($data['frontend_input']) { - case 'swatch_visual': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_VISUAL; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - case 'swatch_text': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_TEXT; - $data['use_product_image_for_swatch'] = 0; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - case 'select': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_DROPDOWN; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - } - } return [$request]; } } diff --git a/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php b/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php new file mode 100644 index 0000000000000..698d0fcc4aea2 --- /dev/null +++ b/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Model; + +/** + * Performs the conversion of the frontend input value for attribute data + */ +class ConvertSwatchAttributeFrontendInput +{ + /** + * Performs the conversion of the frontend input value for attribute data + * + * @param array|null $data + * + * @return array|null + */ + public function execute(?array $data): ?array + { + if (!isset($data['frontend_input'])) { + return $data; + } + + switch ($data['frontend_input']) { + case 'swatch_visual': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + $data['frontend_input'] = 'select'; + break; + case 'swatch_text': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_TEXT; + $data['use_product_image_for_swatch'] = 0; + $data['frontend_input'] = 'select'; + break; + case 'select': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_DROPDOWN; + $data['frontend_input'] = 'select'; + break; + } + + return $data; + } +} diff --git a/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php b/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php new file mode 100644 index 0000000000000..cbc5ce7889110 --- /dev/null +++ b/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Plugin\Catalog\Model\Product\Attribute; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\Product\Attribute\Repository as ProductAttributeRepository; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; + +/** + * Plugin for product attribute repository + */ +class RepositoryPlugin +{ + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @param ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + */ + public function __construct( + ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + ) { + $this->convertSwatchAttributeFrontendInput = $convertSwatchAttributeFrontendInput; + } + + /** + * Performs the conversion of the frontend input value. + * + * @param ProductAttributeRepository $subject + * @param ProductAttributeInterface $attribute + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave( + ProductAttributeRepository $subject, + ProductAttributeInterface $attribute + ): array { + $data = $attribute->getData(); + $data = $this->convertSwatchAttributeFrontendInput->execute($data); + $attribute->setData($data); + + return [$attribute]; + } +} diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml index b05c9cc9e7a9a..6070ae25f570f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -18,4 +18,7 @@ <data key="default_label" unique="suffix">TextSwatchAttr</data> <data key="attribute_code" unique="suffix">text_swatch_attr</data> </entity> + <entity name="textSwatchProductAttribute" type="ProductAttribute" extends="productDropDownAttribute"> + <data key="frontend_input">swatch_text</data> + </entity> </entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml new file mode 100644 index 0000000000000..5e5515aa25a74 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml @@ -0,0 +1,44 @@ +<?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="AdminCheckTextSwatchAttributeAddedViaApiTest"> + <annotations> + <stories value="Add Swatch Text Product Attribute via API"/> + <title value="Add Swatch Text Product Attribute via API"/> + <description value="Login as admin, create swatch text product attribute.Go to New Product page, + check the created attribute is available on the page."/> + <group value="swatches"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="textSwatchProductAttribute" stepKey="createTextSwatchConfigProductAttribute"/> + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createTextSwatchConfigProductAttribute"/> + </createData> + </before> + <after> + <!-- Delete Created Data --> + <deleteData createDataKey="createTextSwatchConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Open the new simple product page --> + <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductPage"/> + <!-- Check created attribute presents on the page --> + <actionGroup ref="AssertAdminProductAttributeByCodeOnProductFormActionGroup" stepKey="checkTextSwatchConfigProductAttributeOnThePage"> + <argument name="productAttributeCode" value="$createTextSwatchConfigProductAttribute.attribute_code$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php b/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php deleted file mode 100644 index c9c826b3a7831..0000000000000 --- a/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Swatches\Test\Unit\Controller\Adminhtml\Product\Attribute\Plugin; - -class SaveTest extends \PHPUnit\Framework\TestCase -{ - /** - * @dataProvider dataRequest - */ - public function testBeforeDispatch($dataRequest, $runTimes) - { - $subject = $this->createMock(\Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save::class); - $request = $this->createPartialMock(\Magento\Framework\App\RequestInterface::class, [ - 'getPostValue', - 'setPostValue', - 'getModuleName', - 'setModuleName', - 'getActionName', - 'setActionName', - 'getParam', - 'setParams', - 'getParams', - 'getCookie', - 'isSecure' - ]); - - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $controller = $objectManager->getObject( - \Magento\Swatches\Controller\Adminhtml\Product\Attribute\Plugin\Save::class - ); - - $request->expects($this->once())->method('getPostValue')->willReturn($dataRequest); - $request->expects($this->exactly($runTimes))->method('setPostValue')->willReturn($this->returnSelf()); - - $controller->beforeDispatch($subject, $request); - } - - /** - * @return array - */ - public function dataRequest() - { - return [ - [ - ['frontend_input' => 'swatch_visual'], - 1 - ], - [ - ['frontend_input' => 'swatch_text'], - 1 - ], - [ - ['frontend_input' => 'select'], - 1 - ], - [ - [], - 0 - ], - [ - null, - 0 - ], - ]; - } -} diff --git a/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php b/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php new file mode 100644 index 0000000000000..1d27a33e5c244 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Swatches\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Swatches\Model\Swatch; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; + +/** + * Tests for \Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput. + */ +class ConvertSwatchAttributeFrontendInputTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->convertSwatchAttributeFrontendInput = + $objectManager->getObject(ConvertSwatchAttributeFrontendInput::class); + } + + /** + * @dataProvider attributeData + */ + public function testExecute($inputData, $outputData) + { + $result = $this->convertSwatchAttributeFrontendInput->execute($inputData); + $this->assertEquals($outputData, $result); + } + + /** + * @return array + */ + public function attributeData() + { + return [ + [ + [ + 'frontend_input' => 'swatch_visual' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_VISUAL, + ] + ], + [ + [ + 'frontend_input' => 'swatch_text' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_TEXT, + 'use_product_image_for_swatch' => 0 + ] + ], + [ + [ + 'frontend_input' => 'select' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_DROPDOWN, + ] + ], + [ + [], + [] + ], + [ + null, + null + ], + ]; + } +} diff --git a/app/code/Magento/Swatches/etc/webapi_rest/di.xml b/app/code/Magento/Swatches/etc/webapi_rest/di.xml new file mode 100644 index 0000000000000..f05b9727db28a --- /dev/null +++ b/app/code/Magento/Swatches/etc/webapi_rest/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Catalog\Model\Product\Attribute\Repository"> + <plugin name="swatches_product_attribute_repository_plugin" type="Magento\Swatches\Plugin\Catalog\Model\Product\Attribute\RepositoryPlugin"/> + </type> +</config> From aabff222676aa2c8c040c31f8f245c90910ab365 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <paliarus@adobe.com> Date: Tue, 10 Mar 2020 10:05:56 -0500 Subject: [PATCH 311/734] Reverted: ECP-261 Offload Catalog Image Resizing from Magento ECP-262 Offload Catalog Image Watermarking from Magento ECP-263 Deprecate Rotation Support in Magento --- .../Magento/Catalog/Block/Rss/Category.php | 4 +- .../Catalog/Block/Rss/Product/NewProducts.php | 28 +- .../Catalog/Block/Rss/Product/Special.php | 15 +- .../Adminhtml/Product/Gallery/Upload.php | 2 +- app/code/Magento/Catalog/Helper/Image.php | 43 +- .../Model/Config/CatalogMediaConfig.php | 50 -- .../Source/Web/CatalogMediaUrlFormat.php | 30 -- .../Magento/Catalog/Model/Product/Image.php | 23 +- .../Catalog/Model/View/Asset/Image.php | 90 +--- .../Observer/ImageResizeAfterProductSave.php | 21 +- .../Helper/Form/Gallery/ContentTest.php | 442 ++++++++++++++++++ .../Test/Unit/Model/ImageUploaderTest.php | 154 ++++++ .../Model/View/Asset/Image/ContextTest.php | 76 +++ .../Test/Unit/Model/View/Asset/ImageTest.php | 213 +++++++++ .../Product/Listing/Collector/ImageTest.php | 13 +- .../Component/Listing/Columns/Thumbnail.php | 12 +- .../Product/Form/Modifier/Related.php | 6 +- .../Product/Listing/Collector/Image.php | 16 +- .../Magento/Catalog/etc/adminhtml/system.xml | 7 - app/code/Magento/Catalog/etc/config.xml | 5 - .../Checkout/CustomerData/DefaultItem.php | 8 +- .../Checkout/Model/DefaultConfigProvider.php | 2 +- .../Console/Command/ImagesResizeCommand.php | 6 +- .../Wishlist/CustomerData/Wishlist.php | 27 +- .../Test/Unit/CustomerData/WishlistTest.php | 6 + .../Block/Product/View/GalleryTest.php | 102 ---- .../ResourceModel/Catalog/ProductTest.php | 2 - lib/internal/Magento/Framework/Image.php | 15 +- .../Image/Adapter/AbstractAdapter.php | 4 +- .../Image/Adapter/AdapterInterface.php | 4 - .../Magento/Framework/Image/Adapter/Gd2.php | 1 - .../Framework/Image/Adapter/ImageMagick.php | 21 +- nginx.conf.sample | 25 - 33 files changed, 981 insertions(+), 492 deletions(-) delete mode 100644 app/code/Magento/Catalog/Model/Config/CatalogMediaConfig.php delete mode 100644 app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php diff --git a/app/code/Magento/Catalog/Block/Rss/Category.php b/app/code/Magento/Catalog/Block/Rss/Category.php index f149114f2eab8..50967d2eb8dca 100644 --- a/app/code/Magento/Catalog/Block/Rss/Category.php +++ b/app/code/Magento/Catalog/Block/Rss/Category.php @@ -10,7 +10,9 @@ use Magento\Framework\Exception\NoSuchEntityException; /** - * Category feed block + * Class Category + * + * @package Magento\Catalog\Block\Rss * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ diff --git a/app/code/Magento/Catalog/Block/Rss/Product/NewProducts.php b/app/code/Magento/Catalog/Block/Rss/Product/NewProducts.php index 9ade8b198656c..20c4bef0845d6 100644 --- a/app/code/Magento/Catalog/Block/Rss/Product/NewProducts.php +++ b/app/code/Magento/Catalog/Block/Rss/Product/NewProducts.php @@ -8,7 +8,8 @@ use Magento\Framework\App\Rss\DataProviderInterface; /** - * New products feed block + * Class NewProducts + * @package Magento\Catalog\Block\Rss\Product */ class NewProducts extends \Magento\Framework\View\Element\AbstractBlock implements DataProviderInterface { @@ -54,8 +55,6 @@ public function __construct( } /** - * Init - * * @return void */ protected function _construct() @@ -65,7 +64,7 @@ protected function _construct() } /** - * @inheritdoc + * {@inheritdoc} */ public function isAllowed() { @@ -73,7 +72,7 @@ public function isAllowed() } /** - * @inheritdoc + * {@inheritdoc} */ public function getRssData() { @@ -98,13 +97,10 @@ public function getRssData() $item->setAllowedInRss(true); $item->setAllowedPriceInRss(true); - $this->_eventManager->dispatch( - 'rss_catalog_new_xml_callback', - [ - 'row' => $item->getData(), - 'product' => $item - ] - ); + $this->_eventManager->dispatch('rss_catalog_new_xml_callback', [ + 'row' => $item->getData(), + 'product' => $item + ]); if (!$item->getAllowedInRss()) { continue; @@ -136,8 +132,6 @@ public function getRssData() } /** - * Get store id - * * @return int */ protected function getStoreId() @@ -183,7 +177,7 @@ protected function renderPriceHtml(\Magento\Catalog\Model\Product $product) } /** - * @inheritdoc + * {@inheritdoc} */ public function getCacheLifetime() { @@ -191,8 +185,6 @@ public function getCacheLifetime() } /** - * Get feeds - * * @return array */ public function getFeeds() @@ -207,7 +199,7 @@ public function getFeeds() } /** - * @inheritdoc + * {@inheritdoc} */ public function isAuthRequired() { diff --git a/app/code/Magento/Catalog/Block/Rss/Product/Special.php b/app/code/Magento/Catalog/Block/Rss/Product/Special.php index 5e459413bb5a2..a9107f14cc5e4 100644 --- a/app/code/Magento/Catalog/Block/Rss/Product/Special.php +++ b/app/code/Magento/Catalog/Block/Rss/Product/Special.php @@ -9,7 +9,8 @@ use Magento\Framework\App\Rss\DataProviderInterface; /** - * Special products feed block + * Class Special + * @package Magento\Catalog\Block\Rss\Product * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Special extends \Magento\Framework\View\Element\AbstractBlock implements DataProviderInterface @@ -97,8 +98,6 @@ public function __construct( } /** - * Init - * * @return void */ protected function _construct() @@ -108,8 +107,6 @@ protected function _construct() } /** - * Get RSS data - * * @return array */ public function getRssData() @@ -159,8 +156,6 @@ public function getRssData() } /** - * Get entry data - * * @param \Magento\Catalog\Model\Product $item * @return array */ @@ -250,7 +245,7 @@ public function isAllowed() } /** - * @inheritdoc + * {@inheritdoc} */ public function getCacheLifetime() { @@ -258,8 +253,6 @@ public function getCacheLifetime() } /** - * Get feeds - * * @return array */ public function getFeeds() @@ -273,7 +266,7 @@ public function getFeeds() } /** - * @inheritdoc + * {@inheritdoc} */ public function isAuthRequired() { diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index 3e7cc3ee962b9..d43b313c43b3e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -11,7 +11,7 @@ use Magento\Framework\Exception\LocalizedException; /** - * Upload product image action controller + * Class Upload */ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterface { diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 5b0aa0c496ecd..110b798df9df9 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -5,19 +5,14 @@ */ namespace Magento\Catalog\Helper; -use Magento\Catalog\Model\Config\CatalogMediaConfig; -use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\App\Helper\AbstractHelper; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Element\Block\ArgumentInterface; /** - * Catalog image helper. + * Catalog image helper * * @api * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ class Image extends AbstractHelper implements ArgumentInterface @@ -45,7 +40,6 @@ class Image extends AbstractHelper implements ArgumentInterface * Scheduled for rotate image * * @var bool - * @deprecated unused */ protected $_scheduleRotate = false; @@ -53,7 +47,6 @@ class Image extends AbstractHelper implements ArgumentInterface * Angle * * @var int - * @deprecated unused */ protected $_angle; @@ -136,38 +129,31 @@ class Image extends AbstractHelper implements ArgumentInterface protected $attributes = []; /** - * @var PlaceholderFactory + * @var \Magento\Catalog\Model\View\Asset\PlaceholderFactory */ private $viewAssetPlaceholderFactory; - /** - * @var CatalogMediaConfig - */ - private $mediaConfig; - /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Catalog\Model\Product\ImageFactory $productImageFactory * @param \Magento\Framework\View\Asset\Repository $assetRepo * @param \Magento\Framework\View\ConfigInterface $viewConfig - * @param PlaceholderFactory $placeholderFactory - * @param CatalogMediaConfig $mediaConfig + * @param \Magento\Catalog\Model\View\Asset\PlaceholderFactory $placeholderFactory */ public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Catalog\Model\Product\ImageFactory $productImageFactory, \Magento\Framework\View\Asset\Repository $assetRepo, \Magento\Framework\View\ConfigInterface $viewConfig, - PlaceholderFactory $placeholderFactory = null, - CatalogMediaConfig $mediaConfig = null + \Magento\Catalog\Model\View\Asset\PlaceholderFactory $placeholderFactory = null ) { $this->_productImageFactory = $productImageFactory; parent::__construct($context); $this->_assetRepo = $assetRepo; $this->viewConfig = $viewConfig; $this->viewAssetPlaceholderFactory = $placeholderFactory - ?: ObjectManager::getInstance()->get(PlaceholderFactory::class); - $this->mediaConfig = $mediaConfig ?: ObjectManager::getInstance()->get(CatalogMediaConfig::class); + ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Catalog\Model\View\Asset\PlaceholderFactory::class); } /** @@ -396,10 +382,9 @@ public function constrainOnly($flag) */ public function backgroundColor($colorRGB) { - $args = func_get_args(); // assume that 3 params were given instead of array if (!is_array($colorRGB)) { - $colorRGB = $args; + $colorRGB = func_get_args(); } $this->_getModel()->setBackgroundColor($colorRGB); return $this; @@ -410,7 +395,6 @@ public function backgroundColor($colorRGB) * * @param int $angle * @return $this - * @deprecated unused */ public function rotate($angle) { @@ -542,16 +526,7 @@ protected function isScheduledActionsAllowed() public function getUrl() { try { - switch ($this->mediaConfig->getMediaUrlFormat()) { - case CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS: - $this->initBaseFile(); - break; - case CatalogMediaConfig::HASH: - $this->applyScheduledActions(); - break; - default: - throw new LocalizedException(__("The specified Catalog media URL format is not supported.")); - } + $this->applyScheduledActions(); return $this->_getModel()->getUrl(); } catch (\Exception $e) { return $this->getDefaultPlaceholderUrl(); @@ -620,7 +595,6 @@ protected function _getModel() * * @param int $angle * @return $this - * @deprecated unused */ protected function setAngle($angle) { @@ -632,7 +606,6 @@ protected function setAngle($angle) * Get Rotation Angle * * @return int - * @deprecated unused */ protected function getAngle() { diff --git a/app/code/Magento/Catalog/Model/Config/CatalogMediaConfig.php b/app/code/Magento/Catalog/Model/Config/CatalogMediaConfig.php deleted file mode 100644 index 9e5394f0d6585..0000000000000 --- a/app/code/Magento/Catalog/Model/Config/CatalogMediaConfig.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Catalog\Model\Config; - -use Magento\Framework\App\Config\ScopeConfigInterface; - -/** - * Config for catalog media - */ -class CatalogMediaConfig -{ - private const XML_PATH_CATALOG_MEDIA_URL_FORMAT = 'web/url/catalog_media_url_format'; - - const IMAGE_OPTIMIZATION_PARAMETERS = 'image_optimization_parameters'; - const HASH = 'hash'; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * Constructor - * - * @param ScopeConfigInterface $scopeConfig - */ - public function __construct(ScopeConfigInterface $scopeConfig) - { - $this->scopeConfig = $scopeConfig; - } - - /** - * Get media URL format for catalog images - * - * @param string $scopeType - * @param null|int|string $scopeCode - * @return string - */ - public function getMediaUrlFormat($scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null) - { - return $this->scopeConfig->getValue( - CatalogMediaConfig::XML_PATH_CATALOG_MEDIA_URL_FORMAT, - $scopeType, - $scopeCode - ); - } -} diff --git a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php b/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php deleted file mode 100644 index f24044fc92c95..0000000000000 --- a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Catalog\Model\Config\Source\Web; - -use Magento\Catalog\Model\Config\CatalogMediaConfig; - -/** - * Option provider for catalog media URL format system setting. - */ -class CatalogMediaUrlFormat implements \Magento\Framework\Data\OptionSourceInterface -{ - /** - * Get a list of supported catalog media URL formats. - * - * @codeCoverageIgnore - */ - public function toOptionArray() - { - return [ - [ - 'value' => CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS, - 'label' => __('Image optimization based on query parameters') - ], - ['value' => CatalogMediaConfig::HASH, 'label' => __('Unique hash per image variant (Legacy mode)')] - ]; - } -} diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 7c2a53768fd47..a0be36c5a327c 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -10,11 +10,9 @@ use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Image as MagentoImage; use Magento\Framework\Serialize\SerializerInterface; use Magento\Catalog\Model\Product\Image\ParamsBuilder; -use Magento\Framework\Filesystem\Driver\File as FilesystemDriver; /** * Image operations @@ -103,7 +101,6 @@ class Image extends \Magento\Framework\Model\AbstractModel /** * @var int - * @deprecated unused */ protected $_angle; @@ -202,11 +199,6 @@ class Image extends \Magento\Framework\Model\AbstractModel */ private $serializer; - /** - * @var FilesystemDriver - */ - private $filesystemDriver; - /** * Constructor * @@ -227,8 +219,6 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param array $data * @param SerializerInterface $serializer * @param ParamsBuilder $paramsBuilder - * @param FilesystemDriver $filesystemDriver - * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ @@ -249,8 +239,7 @@ public function __construct( \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], SerializerInterface $serializer = null, - ParamsBuilder $paramsBuilder = null, - FilesystemDriver $filesystemDriver = null + ParamsBuilder $paramsBuilder = null ) { $this->_storeManager = $storeManager; $this->_catalogProductMediaConfig = $catalogProductMediaConfig; @@ -265,7 +254,6 @@ public function __construct( $this->viewAssetPlaceholderFactory = $viewAssetPlaceholderFactory; $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->paramsBuilder = $paramsBuilder ?: ObjectManager::getInstance()->get(ParamsBuilder::class); - $this->filesystemDriver = $filesystemDriver ?: ObjectManager::getInstance()->get(FilesystemDriver::class); } /** @@ -536,7 +524,6 @@ public function resize() * * @param int $angle * @return $this - * @deprecated unused */ public function rotate($angle) { @@ -552,7 +539,6 @@ public function rotate($angle) * * @param int $angle * @return $this - * @deprecated unused */ public function setAngle($angle) { @@ -677,12 +663,7 @@ public function getDestinationSubdir() public function isCached() { $path = $this->imageAsset->getPath(); - try { - $isCached = is_array($this->loadImageInfoFromCache($path)) || $this->filesystemDriver->isExists($path); - } catch (FileSystemException $e) { - $isCached = false; - } - return $isCached; + return is_array($this->loadImageInfoFromCache($path)) || file_exists($path); } /** diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image.php b/app/code/Magento/Catalog/Model/View/Asset/Image.php index da1009ab1125c..c547ec612bb94 100644 --- a/app/code/Magento/Catalog/Model/View/Asset/Image.php +++ b/app/code/Magento/Catalog/Model/View/Asset/Image.php @@ -6,16 +6,11 @@ namespace Magento\Catalog\Model\View\Asset; -use Magento\Catalog\Model\Config\CatalogMediaConfig; use Magento\Catalog\Model\Product\Media\ConfigInterface; use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\EncryptorInterface; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Asset\ContextInterface; use Magento\Framework\View\Asset\LocalInterface; -use Magento\Catalog\Helper\Image as ImageHelper; -use Magento\Framework\App\ObjectManager; -use Magento\Store\Model\StoreManagerInterface; /** * A locally available image file asset that can be referred with a file path @@ -63,21 +58,6 @@ class Image implements LocalInterface */ private $encryptor; - /** - * @var ImageHelper - */ - private $imageHelper; - - /** - * @var CatalogMediaConfig - */ - private $catalogMediaConfig; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - /** * Image constructor. * @@ -86,19 +66,13 @@ class Image implements LocalInterface * @param EncryptorInterface $encryptor * @param string $filePath * @param array $miscParams - * @param ImageHelper $imageHelper - * @param CatalogMediaConfig $catalogMediaConfig - * @param StoreManagerInterface $storeManager */ public function __construct( ConfigInterface $mediaConfig, ContextInterface $context, EncryptorInterface $encryptor, $filePath, - array $miscParams, - ImageHelper $imageHelper = null, - CatalogMediaConfig $catalogMediaConfig = null, - StoreManagerInterface $storeManager = null + array $miscParams ) { if (isset($miscParams['image_type'])) { $this->sourceContentType = $miscParams['image_type']; @@ -111,72 +85,14 @@ public function __construct( $this->filePath = $filePath; $this->miscParams = $miscParams; $this->encryptor = $encryptor; - $this->imageHelper = $imageHelper ?: ObjectManager::getInstance()->get(ImageHelper::class); - $this->catalogMediaConfig = $catalogMediaConfig ?: ObjectManager::getInstance()->get(CatalogMediaConfig::class); - $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); } /** - * Get catalog image URL. - * - * @return string - * @throws LocalizedException + * @inheritdoc */ public function getUrl() { - $mediaUrlFormat = $this->catalogMediaConfig->getMediaUrlFormat(); - switch ($mediaUrlFormat) { - case CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS: - return $this->getUrlWithTransformationParameters(); - case CatalogMediaConfig::HASH: - return $this->context->getBaseUrl() . DIRECTORY_SEPARATOR . $this->getImageInfo(); - default: - throw new LocalizedException( - __("The specified Catalog media URL format '$mediaUrlFormat' is not supported.") - ); - } - } - - /** - * Get image URL with transformation parameters - * - * @return string - */ - private function getUrlWithTransformationParameters() - { - return $this->getOriginalImageUrl() . '?' . http_build_query($this->getImageTransformationParameters()); - } - - /** - * The list of parameters to be used during image transformations (e.g. resizing or applying watermarks). - * - * This method can be used as an extension point. - * - * @return string[] - */ - public function getImageTransformationParameters() - { - return [ - 'width' => $this->miscParams['image_width'], - 'height' => $this->miscParams['image_height'], - 'store' => $this->storeManager->getStore()->getCode(), - 'image-type' => $this->sourceContentType - ]; - } - - /** - * Get URL to the original version of the product image. - * - * @return string - */ - private function getOriginalImageUrl() - { - $originalImageFile = $this->getSourceFile(); - if (!$originalImageFile) { - return $this->imageHelper->getDefaultPlaceholderUrl(); - } else { - return $this->context->getBaseUrl() . $this->getFilePath(); - } + return $this->context->getBaseUrl() . DIRECTORY_SEPARATOR . $this->getImageInfo(); } /** diff --git a/app/code/Magento/Catalog/Observer/ImageResizeAfterProductSave.php b/app/code/Magento/Catalog/Observer/ImageResizeAfterProductSave.php index 54b655a217a08..91d2868afab8c 100644 --- a/app/code/Magento/Catalog/Observer/ImageResizeAfterProductSave.php +++ b/app/code/Magento/Catalog/Observer/ImageResizeAfterProductSave.php @@ -10,11 +10,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\App\State; use Magento\MediaStorage\Service\ImageResize; -use Magento\Catalog\Model\Config\CatalogMediaConfig; -/** - * Resize product images after the product is saved - */ class ImageResizeAfterProductSave implements ObserverInterface { /** @@ -27,26 +23,17 @@ class ImageResizeAfterProductSave implements ObserverInterface */ private $state; - /** - * @var CatalogMediaConfig - */ - private $catalogMediaConfig; - /** * Product constructor. - * * @param ImageResize $imageResize * @param State $state - * @param CatalogMediaConfig $catalogMediaConfig */ public function __construct( ImageResize $imageResize, - State $state, - CatalogMediaConfig $catalogMediaConfig + State $state ) { $this->imageResize = $imageResize; $this->state = $state; - $this->catalogMediaConfig = $catalogMediaConfig; } /** @@ -57,12 +44,6 @@ public function __construct( */ public function execute(\Magento\Framework\Event\Observer $observer) { - $catalogMediaUrlFormat = $this->catalogMediaConfig->getMediaUrlFormat(); - if ($catalogMediaUrlFormat == CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS) { - // Skip image resizing on the Magento side when it is offloaded to a web server or CDN - return; - } - /** @var $product \Magento\Catalog\Model\Product */ $product = $observer->getEvent()->getProduct(); diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php new file mode 100644 index 0000000000000..9a2199859a1df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php @@ -0,0 +1,442 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Helper\Form\Gallery; + +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery\Content; +use Magento\Catalog\Model\Entity\Attribute; +use Magento\Catalog\Model\Product; +use Magento\Framework\Phrase; +use Magento\MediaStorage\Helper\File\Storage\Database; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ContentTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileSystemMock; + + /** + * @var \Magento\Framework\Filesystem\Directory\Read|\PHPUnit_Framework_MockObject_MockObject + */ + protected $readMock; + + /** + * @var Content|\PHPUnit_Framework_MockObject_MockObject + */ + protected $content; + + /** + * @var \Magento\Catalog\Model\Product\Media\Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mediaConfigMock; + + /** + * @var \Magento\Framework\Json\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $jsonEncoderMock; + + /** + * @var \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery|\PHPUnit_Framework_MockObject_MockObject + */ + protected $galleryMock; + + /** + * @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject + */ + protected $imageHelper; + + /** + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + protected $databaseMock; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + public function setUp() + { + $this->fileSystemMock = $this->createPartialMock( + \Magento\Framework\Filesystem::class, + ['stat', 'getDirectoryRead'] + ); + $this->readMock = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + $this->galleryMock = $this->createMock(\Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery::class); + $this->mediaConfigMock = $this->createPartialMock( + \Magento\Catalog\Model\Product\Media\Config::class, + ['getMediaUrl', 'getMediaPath'] + ); + $this->jsonEncoderMock = $this->getMockBuilder(\Magento\Framework\Json\EncoderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->databaseMock = $this->getMockBuilder(Database::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->content = $this->objectManager->getObject( + \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery\Content::class, + [ + 'mediaConfig' => $this->mediaConfigMock, + 'jsonEncoder' => $this->jsonEncoderMock, + 'filesystem' => $this->fileSystemMock, + 'fileStorageDatabase' => $this->databaseMock + ] + ); + } + + public function testGetImagesJson() + { + $url = [ + ['file_1.jpg', 'url_to_the_image/image_1.jpg'], + ['file_2.jpg', 'url_to_the_image/image_2.jpg'] + ]; + $mediaPath = [ + ['file_1.jpg', 'catalog/product/image_1.jpg'], + ['file_2.jpg', 'catalog/product/image_2.jpg'] + ]; + + $sizeMap = [ + ['catalog/product/image_1.jpg', ['size' => 399659]], + ['catalog/product/image_2.jpg', ['size' => 879394]] + ]; + + $imagesResult = [ + [ + 'value_id' => '2', + 'file' => 'file_2.jpg', + 'media_type' => 'image', + 'position' => '0', + 'url' => 'url_to_the_image/image_2.jpg', + 'size' => 879394 + ], + [ + 'value_id' => '1', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '1', + 'url' => 'url_to_the_image/image_1.jpg', + 'size' => 399659 + ] + ]; + + $images = [ + 'images' => [ + [ + 'value_id' => '1', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '1' + ] , + [ + 'value_id' => '2', + 'file' => 'file_2.jpg', + 'media_type' => 'image', + 'position' => '0' + ] + ] + ]; + + $this->content->setElement($this->galleryMock); + $this->galleryMock->expects($this->once())->method('getImages')->willReturn($images); + $this->fileSystemMock->expects($this->once())->method('getDirectoryRead')->willReturn($this->readMock); + + $this->mediaConfigMock->expects($this->any())->method('getMediaUrl')->willReturnMap($url); + $this->mediaConfigMock->expects($this->any())->method('getMediaPath')->willReturnMap($mediaPath); + $this->readMock->expects($this->any())->method('stat')->willReturnMap($sizeMap); + $this->jsonEncoderMock->expects($this->once())->method('encode')->willReturnCallback('json_encode'); + + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + + $this->assertSame(json_encode($imagesResult), $this->content->getImagesJson()); + } + + public function testGetImagesJsonWithoutImages() + { + $this->content->setElement($this->galleryMock); + $this->galleryMock->expects($this->once())->method('getImages')->willReturn(null); + + $this->assertSame('[]', $this->content->getImagesJson()); + } + + public function testGetImagesJsonWithException() + { + $this->imageHelper = $this->getMockBuilder(\Magento\Catalog\Helper\Image::class) + ->disableOriginalConstructor() + ->setMethods(['getDefaultPlaceholderUrl']) + ->getMock(); + + $this->objectManager->setBackwardCompatibleProperty( + $this->content, + 'imageHelper', + $this->imageHelper + ); + + $placeholderUrl = 'url_to_the_placeholder/placeholder.jpg'; + + $imagesResult = [ + [ + 'value_id' => '2', + 'file' => 'file_2.jpg', + 'media_type' => 'image', + 'position' => '0', + 'url' => 'url_to_the_placeholder/placeholder.jpg', + 'size' => 0 + ], + [ + 'value_id' => '1', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '1', + 'url' => 'url_to_the_placeholder/placeholder.jpg', + 'size' => 0 + ] + ]; + + $images = [ + 'images' => [ + [ + 'value_id' => '1', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '1' + ], + [ + 'value_id' => '2', + 'file' => 'file_2.jpg', + 'media_type' => 'image', + 'position' => '0' + ] + ] + ]; + + $this->content->setElement($this->galleryMock); + $this->galleryMock->expects($this->once())->method('getImages')->willReturn($images); + $this->fileSystemMock->expects($this->any())->method('getDirectoryRead')->willReturn($this->readMock); + $this->mediaConfigMock->expects($this->any())->method('getMediaUrl'); + $this->mediaConfigMock->expects($this->any())->method('getMediaPath'); + + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + + $this->readMock->expects($this->any())->method('stat')->willReturnOnConsecutiveCalls( + $this->throwException( + new \Magento\Framework\Exception\FileSystemException(new Phrase('test')) + ), + $this->throwException( + new \Magento\Framework\Exception\FileSystemException(new Phrase('test')) + ) + ); + $this->imageHelper->expects($this->any())->method('getDefaultPlaceholderUrl')->willReturn($placeholderUrl); + $this->jsonEncoderMock->expects($this->once())->method('encode')->willReturnCallback('json_encode'); + + $this->assertSame(json_encode($imagesResult), $this->content->getImagesJson()); + } + + /** + * Test GetImageTypes() will return value for given attribute from data persistor. + * + * @return void + */ + public function testGetImageTypesFromDataPersistor() + { + $attributeCode = 'thumbnail'; + $value = 'testImageValue'; + $scopeLabel = 'testScopeLabel'; + $label = 'testLabel'; + $name = 'testName'; + $expectedTypes = [ + $attributeCode => [ + 'code' => $attributeCode, + 'value' => $value, + 'label' => $label, + 'name' => $name, + ], + ]; + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $product->expects($this->once()) + ->method('getData') + ->with($this->identicalTo($attributeCode)) + ->willReturn(null); + $mediaAttribute = $this->getMediaAttribute($label, $attributeCode); + $product->expects($this->once()) + ->method('getMediaAttributes') + ->willReturn([$mediaAttribute]); + $this->galleryMock->expects($this->exactly(2)) + ->method('getDataObject') + ->willReturn($product); + $this->galleryMock->expects($this->once()) + ->method('getImageValue') + ->with($this->identicalTo($attributeCode)) + ->willReturn($value); + $this->galleryMock->expects($this->once()) + ->method('getScopeLabel') + ->with($this->identicalTo($mediaAttribute)) + ->willReturn($scopeLabel); + $this->galleryMock->expects($this->once()) + ->method('getAttributeFieldName') + ->with($this->identicalTo($mediaAttribute)) + ->willReturn($name); + $this->getImageTypesAssertions($attributeCode, $scopeLabel, $expectedTypes); + } + + /** + * Test GetImageTypes() will return value for given attribute from product. + * + * @return void + */ + public function testGetImageTypesFromProduct() + { + $attributeCode = 'thumbnail'; + $value = 'testImageValue'; + $scopeLabel = 'testScopeLabel'; + $label = 'testLabel'; + $name = 'testName'; + $expectedTypes = [ + $attributeCode => [ + 'code' => $attributeCode, + 'value' => $value, + 'label' => $label, + 'name' => $name, + ], + ]; + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $product->expects($this->once()) + ->method('getData') + ->with($this->identicalTo($attributeCode)) + ->willReturn($value); + $mediaAttribute = $this->getMediaAttribute($label, $attributeCode); + $product->expects($this->once()) + ->method('getMediaAttributes') + ->willReturn([$mediaAttribute]); + $this->galleryMock->expects($this->exactly(2)) + ->method('getDataObject') + ->willReturn($product); + $this->galleryMock->expects($this->never()) + ->method('getImageValue'); + $this->galleryMock->expects($this->once()) + ->method('getScopeLabel') + ->with($this->identicalTo($mediaAttribute)) + ->willReturn($scopeLabel); + $this->galleryMock->expects($this->once()) + ->method('getAttributeFieldName') + ->with($this->identicalTo($mediaAttribute)) + ->willReturn($name); + $this->getImageTypesAssertions($attributeCode, $scopeLabel, $expectedTypes); + } + + /** + * Perform assertions. + * + * @param string $attributeCode + * @param string $scopeLabel + * @param array $expectedTypes + * @return void + */ + private function getImageTypesAssertions(string $attributeCode, string $scopeLabel, array $expectedTypes) + { + $this->content->setElement($this->galleryMock); + $result = $this->content->getImageTypes(); + $scope = $result[$attributeCode]['scope']; + $this->assertSame($scopeLabel, $scope->getText()); + unset($result[$attributeCode]['scope']); + $this->assertSame($expectedTypes, $result); + } + + /** + * Get media attribute mock. + * + * @param string $label + * @param string $attributeCode + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMediaAttribute(string $label, string $attributeCode) + { + $frontend = $this->getMockBuilder(Product\Attribute\Frontend\Image::class) + ->disableOriginalConstructor() + ->getMock(); + $frontend->expects($this->once()) + ->method('getLabel') + ->willReturn($label); + $mediaAttribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $mediaAttribute->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + $mediaAttribute->expects($this->once()) + ->method('getFrontend') + ->willReturn($frontend); + + return $mediaAttribute; + } + + /** + * Test GetImagesJson() calls MediaStorage functions to obtain image from DB prior to stat call + * + * @return void + */ + public function testGetImagesJsonMediaStorageMode() + { + $images = [ + 'images' => [ + [ + 'value_id' => '0', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '0' + ] + ] + ]; + + $mediaPath = [ + ['file_1.jpg', 'catalog/product/image_1.jpg'] + ]; + + $this->content->setElement($this->galleryMock); + + $this->galleryMock->expects($this->once()) + ->method('getImages') + ->willReturn($images); + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->willReturn($this->readMock); + $this->mediaConfigMock->expects($this->any()) + ->method('getMediaPath') + ->willReturnMap($mediaPath); + + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(false)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(true)); + + $this->databaseMock->expects($this->once()) + ->method('saveFileToFilesystem') + ->with('catalog/product/image_1.jpg'); + + $this->content->getImagesJson(); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php new file mode 100644 index 0000000000000..6552e85440008 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model; + +class ImageUploaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\ImageUploader + */ + private $imageUploader; + + /** + * Core file storage database + * + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + private $coreFileStorageDatabaseMock; + + /** + * Media directory object (writable). + * + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $mediaDirectoryMock; + + /** + * Media directory object (writable). + * + * @var \Magento\Framework\Filesystem\Directory\WriteInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $mediaWriteDirectoryMock; + + /** + * Uploader factory + * + * @var \Magento\MediaStorage\Model\File\UploaderFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $uploaderFactoryMock; + + /** + * Store manager + * + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; + + /** + * Base tmp path + * + * @var string + */ + private $baseTmpPath; + + /** + * Base path + * + * @var string + */ + private $basePath; + + /** + * Allowed extensions + * + * @var array + */ + private $allowedExtensions; + + /** + * Allowed mime types + * + * @var array + */ + private $allowedMimeTypes; + + protected function setUp() + { + $this->coreFileStorageDatabaseMock = $this->createMock( + \Magento\MediaStorage\Helper\File\Storage\Database::class + ); + $this->mediaDirectoryMock = $this->createMock( + \Magento\Framework\Filesystem::class + ); + $this->mediaWriteDirectoryMock = $this->createMock( + \Magento\Framework\Filesystem\Directory\WriteInterface::class + ); + $this->mediaDirectoryMock->expects($this->any())->method('getDirectoryWrite')->willReturn( + $this->mediaWriteDirectoryMock + ); + $this->uploaderFactoryMock = $this->createMock( + \Magento\MediaStorage\Model\File\UploaderFactory::class + ); + $this->storeManagerMock = $this->createMock( + \Magento\Store\Model\StoreManagerInterface::class + ); + $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->baseTmpPath = 'base/tmp/'; + $this->basePath = 'base/real/'; + $this->allowedExtensions = ['.jpg']; + $this->allowedMimeTypes = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png']; + + $this->imageUploader = + new \Magento\Catalog\Model\ImageUploader( + $this->coreFileStorageDatabaseMock, + $this->mediaDirectoryMock, + $this->uploaderFactoryMock, + $this->storeManagerMock, + $this->loggerMock, + $this->baseTmpPath, + $this->basePath, + $this->allowedExtensions, + $this->allowedMimeTypes + ); + } + + public function testSaveFileToTmpDir() + { + $fileId = 'file.jpg'; + $allowedMimeTypes = [ + 'image/jpg', + 'image/jpeg', + 'image/gif', + 'image/png', + ]; + /** @var \Magento\MediaStorage\Model\File\Uploader|\PHPUnit_Framework_MockObject_MockObject $uploader */ + $uploader = $this->createMock(\Magento\MediaStorage\Model\File\Uploader::class); + $this->uploaderFactoryMock->expects($this->once())->method('create')->willReturn($uploader); + $uploader->expects($this->once())->method('setAllowedExtensions')->with($this->allowedExtensions); + $uploader->expects($this->once())->method('setAllowRenameFiles')->with(true); + $this->mediaWriteDirectoryMock->expects($this->once())->method('getAbsolutePath')->with($this->baseTmpPath) + ->willReturn($this->basePath); + $uploader->expects($this->once())->method('save')->with($this->basePath) + ->willReturn(['tmp_name' => $this->baseTmpPath, 'file' => $fileId, 'path' => $this->basePath]); + $uploader->expects($this->atLeastOnce())->method('checkMimeType')->with($allowedMimeTypes)->willReturn(true); + $storeMock = $this->createPartialMock( + \Magento\Store\Model\Store::class, + ['getBaseUrl'] + ); + $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); + $storeMock->expects($this->once())->method('getBaseUrl'); + $this->coreFileStorageDatabaseMock->expects($this->once())->method('saveFile'); + + $result = $this->imageUploader->saveFileToTmpDir($fileId); + + $this->assertArrayNotHasKey('path', $result); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php new file mode 100644 index 0000000000000..e73a2f30e2b10 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\View\Asset\Image; + +use Magento\Catalog\Model\Product\Media\ConfigInterface; +use Magento\Catalog\Model\View\Asset\Image\Context; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; + +/** + * Class ContextTest + */ +class ContextTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Context + */ + protected $model; + + /** + * @var WriteInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mediaDirectory; + + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mediaConfig; + + /** + * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + protected $filesystem; + + protected function setUp() + { + $this->mediaConfig = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass(); + $this->mediaConfig->expects($this->any())->method('getBaseMediaPath')->willReturn('catalog/product'); + $this->mediaDirectory = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->mediaDirectory->expects($this->once())->method('create')->with('catalog/product'); + $this->filesystem = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filesystem->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($this->mediaDirectory); + $this->model = new Context( + $this->mediaConfig, + $this->filesystem + ); + } + + public function testGetPath() + { + $path = '/var/www/html/magento2ce/pub/media/catalog/product'; + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with('catalog/product') + ->willReturn($path); + + $this->assertEquals($path, $this->model->getPath()); + } + + public function testGetUrl() + { + $baseUrl = 'http://localhost/pub/media/catalog/product'; + $this->mediaConfig->expects($this->once())->method('getBaseMediaUrl')->willReturn($baseUrl); + + $this->assertEquals($baseUrl, $this->model->getBaseUrl()); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php new file mode 100644 index 0000000000000..6832d5b3399d7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php @@ -0,0 +1,213 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\View\Asset; + +use Magento\Catalog\Model\Product\Media\ConfigInterface; +use Magento\Catalog\Model\View\Asset\Image; +use Magento\Framework\Encryption\Encryptor; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Asset\ContextInterface; +use Magento\Framework\View\Asset\Repository; + +/** + * Class ImageTest + */ +class ImageTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\View\Asset\Image + */ + protected $model; + + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mediaConfig; + + /** + * @var EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $encryptor; + + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $context; + + /** + * @var Repository|\PHPUnit_Framework_MockObject_MockObject + */ + private $assetRepo; + + private $objectManager; + + protected function setUp() + { + $this->mediaConfig = $this->createMock(ConfigInterface::class); + $this->encryptor = $this->createMock(EncryptorInterface::class); + $this->context = $this->createMock(ContextInterface::class); + $this->assetRepo = $this->createMock(Repository::class); + $this->objectManager = new ObjectManager($this); + $this->model = $this->objectManager->getObject( + Image::class, + [ + 'mediaConfig' => $this->mediaConfig, + 'imageContext' => $this->context, + 'encryptor' => $this->encryptor, + 'filePath' => '/somefile.png', + 'assetRepo' => $this->assetRepo, + 'miscParams' => [ + 'image_width' => 100, + 'image_height' => 50, + 'constrain_only' => false, + 'keep_aspect_ratio' => false, + 'keep_frame' => true, + 'keep_transparency' => false, + 'background' => '255,255,255', + 'image_type' => 'image', //thumbnail,small_image,image,swatch_image,swatch_thumb + 'quality' => 80, + 'angle' => null + ] + ] + ); + } + + public function testModuleAndContentAndContentType() + { + $contentType = 'image'; + $this->assertEquals($contentType, $this->model->getContentType()); + $this->assertEquals($contentType, $this->model->getSourceContentType()); + $this->assertNull($this->model->getContent()); + $this->assertEquals('cache', $this->model->getModule()); + } + + public function testGetFilePath() + { + $this->assertEquals('/somefile.png', $this->model->getFilePath()); + } + + public function testGetSoureFile() + { + $this->mediaConfig->expects($this->once())->method('getBaseMediaPath')->willReturn('catalog/product'); + $this->assertEquals('catalog/product/somefile.png', $this->model->getSourceFile()); + } + + public function testGetContext() + { + $this->assertInstanceOf(ContextInterface::class, $this->model->getContext()); + } + + /** + * @param string $filePath + * @param array $miscParams + * @param string $readableParams + * @dataProvider getPathDataProvider + */ + public function testGetPath($filePath, $miscParams, $readableParams) + { + $imageModel = $this->objectManager->getObject( + Image::class, + [ + 'mediaConfig' => $this->mediaConfig, + 'context' => $this->context, + 'encryptor' => $this->encryptor, + 'filePath' => $filePath, + 'assetRepo' => $this->assetRepo, + 'miscParams' => $miscParams + ] + ); + $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product'; + $hashPath = 'somehash'; + $this->context->method('getPath')->willReturn($absolutePath); + $this->encryptor->expects(static::once()) + ->method('hash') + ->with($readableParams, $this->anything()) + ->willReturn($hashPath); + static::assertEquals( + $absolutePath . '/cache/'. $hashPath . $filePath, + $imageModel->getPath() + ); + } + + /** + * @param string $filePath + * @param array $miscParams + * @param string $readableParams + * @dataProvider getPathDataProvider + */ + public function testGetUrl($filePath, $miscParams, $readableParams) + { + $imageModel = $this->objectManager->getObject( + Image::class, + [ + 'mediaConfig' => $this->mediaConfig, + 'context' => $this->context, + 'encryptor' => $this->encryptor, + 'filePath' => $filePath, + 'assetRepo' => $this->assetRepo, + 'miscParams' => $miscParams + ] + ); + $absolutePath = 'http://localhost/pub/media/catalog/product'; + $hashPath = 'somehash'; + $this->context->expects(static::once())->method('getBaseUrl')->willReturn($absolutePath); + $this->encryptor->expects(static::once()) + ->method('hash') + ->with($readableParams, $this->anything()) + ->willReturn($hashPath); + static::assertEquals( + $absolutePath . '/cache/' . $hashPath . $filePath, + $imageModel->getUrl() + ); + } + + /** + * @return array + */ + public function getPathDataProvider() + { + return [ + [ + '/some_file.png', + [], //default value for miscParams, + 'h:empty_w:empty_q:empty_r:empty_nonproportional_noframe_notransparency_notconstrainonly_nobackground', + ], + [ + '/some_file_2.png', + [ + 'image_type' => 'thumbnail', + 'image_height' => 75, + 'image_width' => 75, + 'keep_aspect_ratio' => true, + 'keep_frame' => true, + 'keep_transparency' => true, + 'constrain_only' => true, + 'background' => [233,1,0], + 'angle' => null, + 'quality' => 80, + ], + 'h:75_w:75_proportional_frame_transparency_doconstrainonly_rgb233,1,0_r:empty_q:80', + ], + [ + '/some_file_3.png', + [ + 'image_type' => 'thumbnail', + 'image_height' => 75, + 'image_width' => 75, + 'keep_aspect_ratio' => false, + 'keep_frame' => false, + 'keep_transparency' => false, + 'constrain_only' => false, + 'background' => [233,1,0], + 'angle' => 90, + 'quality' => 80, + ], + 'h:75_w:75_nonproportional_noframe_notransparency_notconstrainonly_rgb233,1,0_r:90_q:80', + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php index bd08a39fb2bed..009cd690d4cd4 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php @@ -99,6 +99,9 @@ public function testGet() ->method('create') ->willReturn($image); + $imageHelper->expects($this->once()) + ->method('getResizedImageInfo') + ->willReturn([11, 11]); $this->state->expects($this->once()) ->method('emulateAreaCode') ->with( @@ -108,14 +111,12 @@ public function testGet() ) ->willReturn($imageHelper); - $width = 5; - $height = 10; $imageHelper->expects($this->once()) ->method('getHeight') - ->willReturn($height); + ->willReturn(10); $imageHelper->expects($this->once()) ->method('getWidth') - ->willReturn($width); + ->willReturn(10); $imageHelper->expects($this->once()) ->method('getLabel') ->willReturn('Label'); @@ -131,10 +132,10 @@ public function testGet() ->with(); $image->expects($this->once()) ->method('setResizedHeight') - ->with($height); + ->with(11); $image->expects($this->once()) ->method('setResizedWidth') - ->with($width); + ->with(11); $productRenderInfoDto->expects($this->once()) ->method('setImages') diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php index 52773b4580256..09c9782fc0e32 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php @@ -9,7 +9,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** - * Column with thumbnail images + * Class Thumbnail * * @api * @since 100.0.2 @@ -20,16 +20,6 @@ class Thumbnail extends \Magento\Ui\Component\Listing\Columns\Column const ALT_FIELD = 'name'; - /** - * @var \Magento\Catalog\Helper\Image - */ - private $imageHelper; - - /** - * @var \Magento\Framework\UrlInterface - */ - private $urlBuilder; - /** * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php index b822a5e3ef88a..b4acb93dcd14f 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php @@ -25,7 +25,7 @@ use Magento\Catalog\Model\Product\Attribute\Source\Status; /** - * Related products modifier + * Class Related * * @api * @@ -143,7 +143,7 @@ public function __construct( } /** - * @inheritdoc + * {@inheritdoc} * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -182,7 +182,7 @@ public function modifyMeta(array $meta) } /** - * @inheritdoc + * {@inheritdoc} * @since 101.0.0 */ public function modifyData(array $data) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index 45383ed51f6fc..d8f76c40e8fad 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -118,14 +118,18 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ [$product, $imageCode, (int) $productRender->getStoreId(), $image] ); + try { + $resizedInfo = $helper->getResizedImageInfo(); + } catch (NotLoadInfoImageException $exception) { + $resizedInfo = [$helper->getWidth(), $helper->getHeight()]; + } + $image->setCode($imageCode); - $height = $helper->getHeight(); - $image->setHeight($height); - $width = $helper->getWidth(); - $image->setWidth($width); + $image->setHeight($helper->getHeight()); + $image->setWidth($helper->getWidth()); $image->setLabel($helper->getLabel()); - $image->setResizedHeight($height); - $image->setResizedWidth($width); + $image->setResizedHeight($resizedInfo[1]); + $image->setResizedWidth($resizedInfo[0]); $images[] = $image; } diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index f59990cdcea96..80b323cfdb250 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -208,13 +208,6 @@ <source_model>Magento\Catalog\Model\Config\Source\LayoutList</source_model> </field> </group> - <group id="url"> - <field id="catalog_media_url_format" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> - <label>Catalog media URL format</label> - <source_model>Magento\Catalog\Model\Config\Source\Web\CatalogMediaUrlFormat</source_model> - <comment><![CDATA[Images should be optimized based on query parameters by your CDN or web server. Use the legacy mode for backward compatibility. <a href="https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options">Learn more</a> about catalog URL formats.<br/><br/><strong style="color:red">Warning!</strong> If you switch back to legacy mode, you must <a href="https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-images.html#resize-catalog-images">use the CLI to regenerate images</a>.]]></comment> - </field> - </group> </section> <section id="system" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <class>separator-top</class> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 68289904db0cf..59fc4b6d947d9 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -80,11 +80,6 @@ <thumbnail_position>stretch</thumbnail_position> </watermark> </design> - <web> - <url> - <catalog_media_url_format>hash</catalog_media_url_format> - </url> - </web> <general> <validator_data> <input_types> diff --git a/app/code/Magento/Checkout/CustomerData/DefaultItem.php b/app/code/Magento/Checkout/CustomerData/DefaultItem.php index 23d5827dc1916..21580d1275d0c 100644 --- a/app/code/Magento/Checkout/CustomerData/DefaultItem.php +++ b/app/code/Magento/Checkout/CustomerData/DefaultItem.php @@ -10,7 +10,7 @@ use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** - * Default item in checkout customer data + * Default item */ class DefaultItem extends AbstractItem { @@ -78,7 +78,7 @@ public function __construct( } /** - * @inheritdoc + * {@inheritdoc} */ protected function doGetItemData() { @@ -121,8 +121,6 @@ protected function getOptionList() } /** - * Get product for thumbnail - * * @return \Magento\Catalog\Model\Product * @codeCoverageIgnore */ @@ -132,8 +130,6 @@ protected function getProductForThumbnail() } /** - * Get product - * * @return \Magento\Catalog\Model\Product * @codeCoverageIgnore */ diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index 87585e4bf327f..fdf49d6765a29 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -31,7 +31,7 @@ use Magento\Ui\Component\Form\Element\Multiline; /** - * Default Config Provider for checkout + * Default Config Provider * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index d592a004e111a..4ed84829c2ad0 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -84,11 +84,7 @@ public function __construct( protected function configure() { $this->setName('catalog:images:resize') - ->setDescription( - 'Creates resized product images ' . - '(Not relevant when image resizing is offloaded from Magento. ' . - 'See https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options )' - ) + ->setDescription('Creates resized product images') ->setDefinition($this->getOptionsList()); } diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index 2f6b57a8650c4..ae54289d4b1c9 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -68,7 +68,7 @@ public function __construct( } /** - * @inheritdoc + * {@inheritdoc} */ public function getSectionData() { @@ -80,8 +80,6 @@ public function getSectionData() } /** - * Get counter - * * @return string */ protected function getCounter() @@ -158,6 +156,7 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) * * @param \Magento\Catalog\Model\Product $product * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function getImageData($product) { @@ -165,11 +164,27 @@ protected function getImageData($product) $helper = $this->imageHelperFactory->create() ->init($product, 'wishlist_sidebar_block'); + $template = 'Magento_Catalog/product/image_with_borders'; + + try { + $imagesize = $helper->getResizedImageInfo(); + } catch (NotLoadInfoImageException $exception) { + $imagesize = [$helper->getWidth(), $helper->getHeight()]; + } + + $width = $helper->getFrame() + ? $helper->getWidth() + : $imagesize[0]; + + $height = $helper->getFrame() + ? $helper->getHeight() + : $imagesize[1]; + return [ - 'template' => 'Magento_Catalog/product/image_with_borders', + 'template' => $template, 'src' => $helper->getUrl(), - 'width' => $helper->getWidth(), - 'height' => $helper->getHeight(), + 'width' => $width, + 'height' => $height, 'alt' => $helper->getLabel(), ]; } diff --git a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php index 3ef2833ded21f..325922f0bc4e3 100644 --- a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php @@ -193,6 +193,9 @@ public function testGetSectionData() $this->catalogImageHelperMock->expects($this->any()) ->method('getFrame') ->willReturn(true); + $this->catalogImageHelperMock->expects($this->once()) + ->method('getResizedImageInfo') + ->willReturn([]); $this->wishlistHelperMock->expects($this->once()) ->method('getProductUrl') @@ -391,6 +394,9 @@ public function testGetSectionDataWithTwoItems() $this->catalogImageHelperMock->expects($this->any()) ->method('getFrame') ->willReturn(true); + $this->catalogImageHelperMock->expects($this->exactly(2)) + ->method('getResizedImageInfo') + ->willReturn([]); $this->wishlistHelperMock->expects($this->exactly(2)) ->method('getProductUrl') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php index a3545e4a39e80..9bcdb00eebe7c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php @@ -120,23 +120,9 @@ public function testGetGalleryImagesJsonWithoutImages(): void $this->assertImages(reset($result), $this->placeholderExpectation); } - /** - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoConfigFixture default/web/url/catalog_media_url_format image_optimization_parameters - * @magentoDbIsolation enabled - * @return void - */ - public function testGetGalleryImagesJsonWithoutImagesWithImageOptimizationParametersInUrl(): void - { - $this->block->setData('product', $this->getProduct()); - $result = $this->serializer->unserialize($this->block->getGalleryImagesJson()); - $this->assertImages(reset($result), $this->placeholderExpectation); - } - /** * @dataProvider galleryDisabledImagesDataProvider * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php - * @magentoConfigFixture default/web/url/catalog_media_url_format hash * @magentoDbIsolation enabled * @param array $images * @param array $expectation @@ -155,7 +141,6 @@ public function testGetGalleryImagesJsonWithDisabledImage(array $images, array $ * @dataProvider galleryDisabledImagesDataProvider * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php * @magentoDataFixture Magento/Store/_files/second_store.php - * @magentoConfigFixture default/web/url/catalog_media_url_format hash * @magentoDbIsolation disabled * @param array $images * @param array $expectation @@ -188,8 +173,6 @@ public function galleryDisabledImagesDataProvider(): array } /** - * Test default image generation format. - * * @dataProvider galleryImagesDataProvider * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php * @magentoDbIsolation enabled @@ -247,95 +230,10 @@ public function galleryImagesDataProvider(): array ]; } - /** - * @dataProvider galleryImagesWithImageOptimizationParametersInUrlDataProvider - * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php - * @magentoConfigFixture default/web/url/catalog_media_url_format image_optimization_parameters - * @magentoDbIsolation enabled - * @param array $images - * @param array $expectation - * @return void - */ - public function testGetGalleryImagesJsonWithImageOptimizationParametersInUrl( - array $images, - array $expectation - ): void { - $product = $this->getProduct(); - $this->setGalleryImages($product, $images); - $this->block->setData('product', $this->getProduct()); - [$firstImage, $secondImage] = $this->serializer->unserialize($this->block->getGalleryImagesJson()); - [$firstExpectedImage, $secondExpectedImage] = $expectation; - $this->assertImages($firstImage, $firstExpectedImage); - $this->assertImages($secondImage, $secondExpectedImage); - } - - /** - * @return array - */ - public function galleryImagesWithImageOptimizationParametersInUrlDataProvider(): array - { - - $imageExpectation = [ - 'thumb' => '/m/a/magento_image.jpg?width=88&height=110&store=default&image-type=thumbnail', - 'img' => '/m/a/magento_image.jpg?width=700&height=700&store=default&image-type=image', - 'full' => '/m/a/magento_image.jpg?store=default&image-type=image', - 'caption' => 'Image Alt Text', - 'position' => '1', - 'isMain' => false, - 'type' => 'image', - 'videoUrl' => null, - ]; - - $thumbnailExpectation = [ - 'thumb' => '/m/a/magento_thumbnail.jpg?width=88&height=110&store=default&image-type=thumbnail', - 'img' => '/m/a/magento_thumbnail.jpg?width=700&height=700&store=default&image-type=image', - 'full' => '/m/a/magento_thumbnail.jpg?store=default&image-type=image', - 'caption' => 'Thumbnail Image', - 'position' => '2', - 'isMain' => false, - 'type' => 'image', - 'videoUrl' => null, - ]; - - return [ - 'with_main_image' => [ - 'images' => [ - '/m/a/magento_image.jpg' => [], - '/m/a/magento_thumbnail.jpg' => ['main' => true], - ], - 'expectation' => [ - $imageExpectation, - array_merge($thumbnailExpectation, ['isMain' => true]), - ], - ], - 'without_main_image' => [ - 'images' => [ - '/m/a/magento_image.jpg' => [], - '/m/a/magento_thumbnail.jpg' => [], - ], - 'expectation' => [ - array_merge($imageExpectation, ['isMain' => true]), - $thumbnailExpectation, - ], - ], - 'with_changed_position' => [ - 'images' => [ - '/m/a/magento_image.jpg' => ['position' => '2'], - '/m/a/magento_thumbnail.jpg' => ['position' => '1'], - ], - 'expectation' => [ - array_merge($thumbnailExpectation, ['position' => '1']), - array_merge($imageExpectation, ['position' => '2', 'isMain' => true]), - ], - ], - ]; - } - /** * @dataProvider galleryImagesOnStoreViewDataProvider * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php * @magentoDataFixture Magento/Store/_files/second_store.php - * @magentoConfigFixture default/web/url/catalog_media_url_format hash * @magentoDbIsolation disabled * @param array $images * @param array $expectation diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/ResourceModel/Catalog/ProductTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/ResourceModel/Catalog/ProductTest.php index 7d5e919880d3b..d6388b188a5fd 100644 --- a/dev/tests/integration/testsuite/Magento/Sitemap/Model/ResourceModel/Catalog/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/ResourceModel/Catalog/ProductTest.php @@ -52,7 +52,6 @@ public function testGetCollectionNone() * 3) Check thumbnails when no thumbnail selected * * @magentoConfigFixture default_store sitemap/product/image_include all - * @magentoConfigFixture default/web/url/catalog_media_url_format hash */ public function testGetCollectionAll() { @@ -121,7 +120,6 @@ public function testGetCollectionAll() * 3) Check thumbnails when no thumbnail selected * * @magentoConfigFixture default_store sitemap/product/image_include base - * @magentoConfigFixture default/web/url/catalog_media_url_format hash */ public function testGetCollectionBase() { diff --git a/lib/internal/Magento/Framework/Image.php b/lib/internal/Magento/Framework/Image.php index 64cd009a84a3c..b3867c0197b79 100644 --- a/lib/internal/Magento/Framework/Image.php +++ b/lib/internal/Magento/Framework/Image.php @@ -49,7 +49,7 @@ public function open() $this->_adapter->checkDependencies(); if (!file_exists($this->_fileName)) { - throw new \RuntimeException("File '{$this->_fileName}' does not exist."); + throw new \Exception("File '{$this->_fileName}' does not exist."); } $this->_adapter->open($this->_fileName); @@ -85,7 +85,6 @@ public function save($destination = null, $newFileName = null) * @param int $angle * @access public * @return void - * @deprecated unused */ public function rotate($angle) { @@ -95,7 +94,7 @@ public function rotate($angle) /** * Crop an image. * - * @param int $top Default value is 0 + * @param int $top Default value is 0 * @param int $left Default value is 0 * @param int $right Default value is 0 * @param int $bottom Default value is 0 @@ -195,7 +194,7 @@ public function quality($value) * @param int $watermarkImageOpacity Watermark image opacity. * @param bool $repeat Enable or disable watermark brick. * @access public - * @throws \RuntimeException + * @throws \Exception * @return void */ public function watermark( @@ -206,7 +205,7 @@ public function watermark( $repeat = false ) { if (!file_exists($watermarkImage)) { - throw new \RuntimeException("Required file '{$watermarkImage}' does not exists."); + throw new \Exception("Required file '{$watermarkImage}' does not exists."); } $this->_adapter->watermark($watermarkImage, $positionX, $positionY, $watermarkImageOpacity, $repeat); } @@ -233,19 +232,16 @@ public function getImageType() return $this->_adapter->getImageType(); } - // phpcs:disable Magento2.CodeAnalysis.EmptyBlock /** * Process * - * @access public, + * @access public * @return void */ public function process() { } - // phpcs:enable Magento2.CodeAnalysis.EmptyBlock - // phpcs:disable Magento2.CodeAnalysis.EmptyBlock /** * Instruction * @@ -255,7 +251,6 @@ public function process() public function instruction() { } - // phpcs:enable Magento2.CodeAnalysis.EmptyBlock /** * Set image background color diff --git a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php index 88dbd69405471..b06f2f9e62397 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php @@ -41,6 +41,9 @@ abstract class AbstractAdapter implements AdapterInterface const POSITION_CENTER = 'center'; + /** + * Default font size + */ const DEFAULT_FONT_SIZE = 15; /** @@ -201,7 +204,6 @@ abstract public function resize($width = null, $height = null); * * @param int $angle * @return void - * @deprecated unused */ abstract public function rotate($angle); diff --git a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php index 736686968b374..b31ed5c773495 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php @@ -28,8 +28,6 @@ interface AdapterInterface public function getColorAt($x, $y); /** - * Render image and return its binary contents - * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -101,7 +99,6 @@ public function crop($top = 0, $left = 0, $right = 0, $bottom = 0); /** * Save image to specific path. - * * If some folders of path does not exist they will be created * * @param null|string $destination @@ -116,7 +113,6 @@ public function save($destination = null, $newName = null); * * @param int $angle * @return void - * @deprecated unused */ public function rotate($angle); } diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index df236faf8173b..6a7a11846334d 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -401,7 +401,6 @@ public function resize($frameWidth = null, $frameHeight = null) * * @param int $angle * @return void - * @deprecated unused */ public function rotate($angle) { diff --git a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php index a08d83d33b0ef..cd49f283d33a7 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php @@ -5,11 +5,6 @@ */ namespace Magento\Framework\Image\Adapter; -/** - * Wrapper for Imagick image processing PHP Extension. - * - * @link https://www.php.net/manual/en/book.imagick.php - */ class ImageMagick extends \Magento\Framework\Image\Adapter\AbstractAdapter { /** @@ -82,11 +77,7 @@ public function open($filename) try { $this->_imageHandler = new \Imagick($this->_fileName); } catch (\ImagickException $e) { - throw new \RuntimeException( - sprintf('Unsupported image format. File: %s', $this->_fileName), - $e->getCode(), - $e - ); + throw new \Exception(sprintf('Unsupported image format. File: %s', $this->_fileName), $e->getCode(), $e); } $this->backgroundColor(); @@ -95,7 +86,6 @@ public function open($filename) /** * Save image to specific path. - * * If some folders of path does not exist they will be created * * @param null|string $destination @@ -134,8 +124,6 @@ protected function _applyOptions() } /** - * Render image binary content and return it. - * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -207,7 +195,6 @@ public function resize($frameWidth = null, $frameHeight = null) * * @param int $angle * @return void - * @deprecated unused */ public function rotate($angle) { @@ -346,7 +333,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = ); } } catch (\ImagickException $e) { - throw new \RuntimeException('Unable to create watermark.', $e->getCode(), $e); + throw new \Exception('Unable to create watermark.', $e->getCode(), $e); } // merge layers @@ -359,12 +346,12 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = * Checks required dependencies * * @return void - * @throws \RuntimeException If some of dependencies are missing + * @throws \Exception If some of dependencies are missing */ public function checkDependencies() { if (!class_exists('\Imagick', false)) { - throw new \RuntimeException("Required PHP extension 'Imagick' was not loaded."); + throw new \Exception("Required PHP extension 'Imagick' was not loaded."); } } diff --git a/nginx.conf.sample b/nginx.conf.sample index f045edb46a1c2..9219400f6aacd 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -26,9 +26,6 @@ ## ## In production mode, you should uncomment the 'expires' directive in the /static/ location block -# Modules can be loaded only at the very beginning of the Nginx config file, please move the line below to the main config file -# load_module /etc/nginx/modules/ngx_http_image_filter_module.so; - root $MAGE_ROOT/pub; index index.php; @@ -137,28 +134,6 @@ location /static/ { } location /media/ { - -## The following section allows to offload image resizing from Magento instance to the Nginx. -## Catalog image URL format should be set accordingly. -## See https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options -# location ~* ^/media/catalog/.* { -# -# # Replace placeholders and uncomment the line below to serve product images from public S3 -# # See examples of S3 authentication at https://github.com/anomalizer/ngx_aws_auth -# # proxy_pass https://<bucket-name>.<region-name>.amazonaws.com; -# -# set $width "-"; -# set $height "-"; -# if ($arg_width != '') { -# set $width $arg_width; -# } -# if ($arg_height != '') { -# set $height $arg_height; -# } -# image_filter resize $width $height; -# image_filter_jpeg_quality 90; -# } - try_files $uri $uri/ /get.php$is_args$args; location ~ ^/media/theme_customization/.*\.xml { From 48447b87a6652a4b6f152cd81b5b6356c5cf9298 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 10 Mar 2020 13:55:05 -0500 Subject: [PATCH 312/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index 9c38e300e3f69..114b11a4f67c3 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "b43323e51d27b75292ee62f357c7a6b98a7ccb03" + "reference": "5ecf5becf631dc5e7cb4534574434d8fffbc2317" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/b43323e51d27b75292ee62f357c7a6b98a7ccb03", - "reference": "b43323e51d27b75292ee62f357c7a6b98a7ccb03", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5ecf5becf631dc5e7cb4534574434d8fffbc2317", + "reference": "5ecf5becf631dc5e7cb4534574434d8fffbc2317", "shasum": "" }, "require": { @@ -7515,13 +7515,14 @@ "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "ext-curl": "*", + "ext-dom": "*", "ext-json": "*", "ext-openssl": "*", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", - "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", + "php": "~7.2.0||~7.3.0", "php-webdriver/webdriver": "^1.8.0", "symfony/process": "^2.8 || ^3.1 || ^4.0", "vlucas/phpdotenv": "^2.4" @@ -7575,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-06T19:37:11+00:00" + "time": "2020-03-10T17:35:25+00:00" }, { "name": "mikey179/vfsstream", From b09f0ec6260025b2a50b27563644ce672436e294 Mon Sep 17 00:00:00 2001 From: Stanislav Ilnytskyi <stailx1@gmail.com> Date: Tue, 10 Mar 2020 19:57:37 +0100 Subject: [PATCH 313/734] test added --- dev/tests/js/jasmine/tests/lib/mage/translate.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/lib/mage/translate.test.js b/dev/tests/js/jasmine/tests/lib/mage/translate.test.js index dc6c6ce7eb966..a29dbab63232b 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/translate.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/translate.test.js @@ -33,7 +33,8 @@ define([ translation = { 'Hello World!': 'Hallo Welt!', - 'Some text with symbols!-+"%#*': 'Ein Text mit Symbolen!-+"%#*' + 'Some text with symbols!-+"%#*': 'Ein Text mit Symbolen!-+"%#*', + 'Text with empty value': '' }; $.mage.translate.add(translation); From 6d814c3325d830ac8ebb0e1253718751d28f123a Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 10 Mar 2020 20:53:53 -0500 Subject: [PATCH 314/734] MC-32278: Position sort does not work in GraphQl. - add apply to preserve order --- .../Products/DataProvider/ProductSearch.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index ff845f4796763..c35bd1fe0608a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -7,16 +7,16 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider; +use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\Catalog\Model\ResourceModel\Product\Collection; -use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; use Magento\Framework\Api\SearchResultsInterface; -use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; /** * Product field data provider for product search, used for GraphQL resolver processing. @@ -88,6 +88,14 @@ public function getList( //Join search results $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); + //Additional applier to keep sorting from search + $items = []; + foreach ($searchResult->getItems() as $item) { + $items[] = $item->getId(); + } + $orderList = join(',', $items); + $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); + $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); $collection->load(); $this->collectionPostProcessor->process($collection, $attributes); From ef13e6b341d837967ff6461ca8186e81417fcff0 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 10 Mar 2020 21:08:19 -0500 Subject: [PATCH 315/734] MC-32278: Position sort does not work in GraphQl. - adding test --- .../GraphQl/Catalog/ProductSearchTest.php | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 9ac5f6959d12e..5605f5719bc9f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -13,6 +13,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\CategoryLinkManagement; +use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Eav\Model\Config; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -463,7 +464,7 @@ private function getDefaultAttributeOptionValue(string $attributeCode) : string } /** - * Full text search for Products and then filter the results by custom attribute ( sort is by defaulty by relevance) + * Full text search for Products and then filter the results by custom attribute (default sort is relevance) * * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -555,7 +556,7 @@ public function testSearchAndFilterByCustomAttribute() ); } - // Validate the price layer of aggregations from the response + // Validate the price layer of aggregations from the response $this->assertResponseFields( $response['products']['aggregations'][0], [ @@ -695,7 +696,7 @@ public function testFilterByCategoryIdAndCustomAttribute() //Validate the number of categories/sub-categories that contain the products with the custom attribute $this->assertCount(6, $actualCategoriesFromResponse); - $expectedCategoryInAggregrations = + $expectedCategoryInAggregations = [ [ 'count' => 2, @@ -732,9 +733,9 @@ public function testFilterByCategoryIdAndCustomAttribute() ], ]; // presort expected and actual results as different search engines have different orders - usort($expectedCategoryInAggregrations, [$this, 'compareLabels']); + usort($expectedCategoryInAggregations, [$this, 'compareLabels']); usort($actualCategoriesFromResponse, [$this, 'compareLabels']); - $categoryInAggregations = array_map(null, $expectedCategoryInAggregrations, $actualCategoriesFromResponse); + $categoryInAggregations = array_map(null, $expectedCategoryInAggregations, $actualCategoriesFromResponse); //Validate the categories and sub-categories data in the filter layer foreach ($categoryInAggregations as $index => $categoryAggregationsData) { @@ -1118,6 +1119,52 @@ public function testFilterWithinSpecificPriceRangeSortedByNameDesc() $this->assertEquals(4, $response['products']['page_info']['page_size']); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/category_with_three_products.php + */ + public function testSortByPosition() + { + // Get category ID for filtering + /** @var Collection $categoryCollection */ + $categoryCollection = Bootstrap::getObjectManager()->get(Collection::class); + $category = $categoryCollection->addFieldToFilter('name', 'Category 999')->getFirstItem(); + $categoryId = $category->getId(); + + $queryAsc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: ASC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultAsc = $this->graphQlQuery($queryAsc); + $this->assertArrayNotHasKey('errors', $resultAsc); + $productsAsc = array_column($resultAsc['products']['items'], 'sku'); + $expectedProductsAsc = ['simple1000', 'simple1001', 'simple1002']; + $this->assertEquals($expectedProductsAsc, $productsAsc); + + $queryDesc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: DESC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultDesc = $this->graphQlQuery($queryDesc); + $this->assertArrayNotHasKey('errors', $resultDesc); + $productsDesc = array_column($resultAsc['products']['items'], 'sku'); + $expectedProductsDesc = array_reverse($expectedProductsAsc); + $this->assertEquals($expectedProductsDesc, $productsDesc); + } + /** * pageSize = total_count and current page = 2 * expected - error is thrown From f91aff175c6a3e4701e69601a68d3333da702e0e Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Wed, 11 Mar 2020 10:06:20 +0200 Subject: [PATCH 316/734] 26749: Saving CMS Page Title from REST web API makes content empty --- app/code/Magento/Cms/Model/PageRepository.php | 24 ++- app/code/Magento/Cms/etc/di.xml | 1 + .../Magento/Cms/Api/PageRepositoryTest.php | 195 ++++++++++++------ 3 files changed, 157 insertions(+), 63 deletions(-) diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 72f07771f59d4..2de44b6691274 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -9,19 +9,21 @@ use Magento\Cms\Api\Data; use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page\IdentityMap; +use Magento\Cms\Model\ResourceModel\Page as ResourcePage; +use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\HydratorInterface; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Reflection\DataObjectProcessor; -use Magento\Cms\Model\ResourceModel\Page as ResourcePage; -use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Store\Model\StoreManagerInterface; /** - * Class PageRepository + * @inheritdoc + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PageRepository implements PageRepositoryInterface @@ -76,6 +78,11 @@ class PageRepository implements PageRepositoryInterface */ private $identityMap; + /** + * @var HydratorInterface + */ + private $hydrator; + /** * @param ResourcePage $resource * @param PageFactory $pageFactory @@ -87,6 +94,7 @@ class PageRepository implements PageRepositoryInterface * @param StoreManagerInterface $storeManager * @param CollectionProcessorInterface $collectionProcessor * @param IdentityMap|null $identityMap + * @param HydratorInterface|null $hydrator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,7 +107,8 @@ public function __construct( DataObjectProcessor $dataObjectProcessor, StoreManagerInterface $storeManager, CollectionProcessorInterface $collectionProcessor = null, - ?IdentityMap $identityMap = null + ?IdentityMap $identityMap = null, + ?HydratorInterface $hydrator = null ) { $this->resource = $resource; $this->pageFactory = $pageFactory; @@ -111,6 +120,7 @@ public function __construct( $this->storeManager = $storeManager; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); + $this->hydrator = $hydrator ?: ObjectManager::getInstance()->get(HydratorInterface::class); } /** @@ -150,8 +160,13 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $storeId = $this->storeManager->getStore()->getId(); $page->setStoreId($storeId); } + $pageId = $page->getId(); + try { $this->validateLayoutUpdate($page); + if ($pageId) { + $page = $this->hydrator->hydrate($this->getById($pageId), $this->hydrator->extract($page)); + } $this->resource->save($page); $this->identityMap->add($page); } catch (\Exception $exception) { @@ -248,6 +263,7 @@ private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( + // phpstan:ignore "Class Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor not found." \Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor::class ); } diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 869bd22e20548..7fc8268eea5e0 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -193,6 +193,7 @@ <type name="Magento\Cms\Model\PageRepository"> <arguments> <argument name="collectionProcessor" xsi:type="object">Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor</argument> + <argument name="hydrator" xsi:type="object">Magento\Framework\EntityManager\AbstractModelHydrator</argument> </arguments> </type> <virtualType name="Magento\Cms\Model\Api\SearchCriteria\CollectionProcessor\BlockFilterProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor"> diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index 015eb067e4c8e..bff2ba3ce7aac 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -6,14 +6,18 @@ namespace Magento\Cms\Api; use Magento\Authorization\Model\Role; -use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RulesFactory; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\Data\PageInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Webapi\Rest\Request; use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -25,34 +29,39 @@ */ class PageRepositoryTest extends WebapiAbstract { - const SERVICE_NAME = 'cmsPageRepositoryV1'; - const SERVICE_VERSION = 'V1'; - const RESOURCE_PATH = '/V1/cmsPage'; + private const PAGE_TITLE = 'Page title'; + private const PAGE_TITLE_NEW = 'Page title new'; + private const PAGE_CONTENT = '<h1>Some content</h1>'; + private const PAGE_IDENTIFIER_PREFIX = 'page-'; + + private const SERVICE_NAME = 'cmsPageRepositoryV1'; + private const SERVICE_VERSION = 'V1'; + private const RESOURCE_PATH = '/V1/cmsPage'; /** - * @var \Magento\Cms\Api\Data\PageInterfaceFactory + * @var PageInterfaceFactory */ - protected $pageFactory; + private $pageFactory; /** - * @var \Magento\Cms\Api\PageRepositoryInterface + * @var PageRepositoryInterface */ - protected $pageRepository; + private $pageRepository; /** - * @var \Magento\Framework\Api\DataObjectHelper + * @var DataObjectHelper */ - protected $dataObjectHelper; + private $dataObjectHelper; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor + * @var DataObjectProcessor */ - protected $dataObjectProcessor; + private $dataObjectProcessor; /** - * @var \Magento\Cms\Api\Data\PageInterface|null + * @var PageInterface|null */ - protected $currentPage; + private $currentPage; /** * @var RoleFactory @@ -70,39 +79,49 @@ class PageRepositoryTest extends WebapiAbstract private $adminTokens; /** - * Execute per test initialization. + * @var array + */ + private $createdPages = []; + + /** + * @inheritdoc */ public function setUp() { - $this->pageFactory = Bootstrap::getObjectManager()->create(\Magento\Cms\Api\Data\PageInterfaceFactory::class); - $this->pageRepository = Bootstrap::getObjectManager()->create(\Magento\Cms\Api\PageRepositoryInterface::class); - $this->dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); - $this->dataObjectProcessor = Bootstrap::getObjectManager() - ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); + $this->pageFactory = Bootstrap::getObjectManager()->create(PageInterfaceFactory::class); + $this->pageRepository = Bootstrap::getObjectManager()->create(PageRepositoryInterface::class); + $this->dataObjectHelper = Bootstrap::getObjectManager()->create(DataObjectHelper::class); + $this->dataObjectProcessor = Bootstrap::getObjectManager()->create(DataObjectProcessor::class); $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); } /** - * Clear temporary data + * @inheritdoc */ - public function tearDown() + protected function tearDown(): void { if ($this->currentPage) { $this->pageRepository->delete($this->currentPage); $this->currentPage = null; } + + foreach ($this->createdPages as $page) { + $this->pageRepository->delete($page); + } } /** - * Test get \Magento\Cms\Api\Data\PageInterface + * Test get page + * + * @return void */ - public function testGet() + public function testGet(): void { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -111,7 +130,7 @@ public function testGet() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $this->currentPage->getId(), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -129,13 +148,15 @@ public function testGet() } /** - * Test create \Magento\Cms\Api\Data\PageInterface + * Test create page + * + * @return void */ - public function testCreate() + public function testCreate(): void { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -143,7 +164,7 @@ public function testCreate() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -152,7 +173,8 @@ public function testCreate() ], ]; - $requestData = ['page' => [ + $requestData = [ + 'page' => [ PageInterface::IDENTIFIER => $pageDataObject->getIdentifier(), PageInterface::TITLE => $pageDataObject->getTitle(), ], @@ -170,10 +192,10 @@ public function testCreate() */ public function testUpdate() { - $pageTitle = 'Page title'; - $newPageTitle = 'New Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $newPageTitle = self::PAGE_TITLE_NEW; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -181,17 +203,17 @@ public function testUpdate() $this->dataObjectHelper->populateWithArray( $this->currentPage, [PageInterface::TITLE => $newPageTitle], - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); $pageData = $this->dataObjectProcessor->buildOutputDataArray( $this->currentPage, - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -207,15 +229,69 @@ public function testUpdate() $this->assertEquals($pageData->getTitle(), $newPageTitle); } + /** + * Test update page one field + * + * @return void + */ + public function testUpdateOneField(): void + { + $pageTitle = self::PAGE_TITLE; + $content = self::PAGE_CONTENT; + $newPageTitle = self::PAGE_TITLE_NEW; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + + /** @var PageInterface $pageDataObject */ + $pageDataObject = $this->pageFactory->create(); + $pageDataObject->setTitle($pageTitle) + ->setIdentifier($pageIdentifier) + ->setContent($content); + $this->currentPage = $this->pageRepository->save($pageDataObject); + $pageId = $this->currentPage->getId(); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $pageId, + 'httpMethod' => Request::HTTP_METHOD_PUT, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + + $data = [ + 'page' => [ + 'title' => $newPageTitle, + ], + ]; + + if (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP) { + $data['page'] += [ + 'id' => $pageId, + 'identifier' => $pageIdentifier, + ]; + } + + $page = $this->_webApiCall($serviceInfo, $data); + + $this->assertArrayHasKey('title', $page); + $this->assertEquals($page['title'], $newPageTitle); + + $this->assertArrayHasKey('content', $page); + $this->assertEquals($page['content'], $content); + } + /** * Test delete \Magento\Cms\Api\Data\PageInterface * @expectedException \Magento\Framework\Exception\NoSuchEntityException */ public function testDelete() { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -224,7 +300,7 @@ public function testDelete() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $this->currentPage->getId(), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + 'httpMethod' => Request::HTTP_METHOD_DELETE, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -291,7 +367,7 @@ public function testSearch() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . "/search" . '?' . http_build_query($requestData), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -314,11 +390,12 @@ public function testSearch() */ public function testCreateSamePage() { - $pageIdentifier = 'page-' . uniqid(); + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); $pageId = $this->createPageWithIdentifier($pageIdentifier); $this->deletePageByIdentifier($pageId); - $this->createPageWithIdentifier($pageIdentifier); + $id = $this->createPageWithIdentifier($pageIdentifier); + $this->currentPage = $this->pageRepository->getById($id); } /** @@ -341,14 +418,14 @@ private function prepareCmsPages() $pagesData['third'][PageInterface::IS_ACTIVE] = true; foreach ($pagesData as $key => $pageData) { - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $this->dataObjectHelper->populateWithArray( $pageDataObject, $pageData, - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); - $result[$key] = $this->pageRepository->save($pageDataObject); + $this->createdPages[] = $result[$key] = $this->pageRepository->save($pageDataObject); } return $result; @@ -364,7 +441,7 @@ private function createPageWithIdentifier($identifier) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -372,10 +449,10 @@ private function createPageWithIdentifier($identifier) 'operation' => self::SERVICE_NAME . 'Save', ], ]; - $requestData = ['page' => - [ + $requestData = [ + 'page' => [ PageInterface::IDENTIFIER => $identifier, - PageInterface::TITLE => 'Page title', + PageInterface::TITLE => self::PAGE_TITLE, ], ]; @@ -393,7 +470,7 @@ private function deletePageByIdentifier($pageId) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $pageId, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + 'httpMethod' => Request::HTTP_METHOD_DELETE, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -433,7 +510,7 @@ public function testSaveDesign(): void $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, 'token' => $token, ], 'soap' => [ @@ -446,7 +523,7 @@ public function testSaveDesign(): void $requestData = [ 'page' => [ PageInterface::IDENTIFIER => $id, - PageInterface::TITLE => 'Page title', + PageInterface::TITLE => self::PAGE_TITLE, PageInterface::CUSTOM_THEME => 1 ], ]; From fdbf8ad927543151f83ba39bcf66fe3de4df9c8c Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Mar 2020 10:33:18 -0500 Subject: [PATCH 317/734] MC-32278: Position sort does not work in GraphQl. - fixing if --- .../Resolver/Products/DataProvider/ProductSearch.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index c35bd1fe0608a..c611ea04868bb 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -89,12 +89,14 @@ public function getList( $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); //Additional applier to keep sorting from search - $items = []; - foreach ($searchResult->getItems() as $item) { - $items[] = $item->getId(); + if (!empty($searchResult->getItems())) { + $items = []; + foreach ($searchResult->getItems() as $item) { + $items[] = $item->getId(); + } + $orderList = join(',', $items); + $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); } - $orderList = join(',', $items); - $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); $collection->load(); From 8ed85fceafa35388a7c5cb04a08ecc85f760c881 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 11 Mar 2020 10:38:52 -0500 Subject: [PATCH 318/734] MC-32201: Reorder functionality --- .../Model/Product/Option/DateType.php | 3 + .../SalesGraphQl/Model/Resolver/Reorder.php | 160 ++++++++++++++++++ .../Magento/SalesGraphQl/etc/schema.graphqls | 14 ++ .../Magento/GraphQl/Sales/ReorderTest.php | 129 ++++++++++++++ ...r_item_with_product_and_custom_options.php | 1 + ...r_item_with_product_and_custom_options.php | 8 + ...th_product_and_custom_options_rollback.php | 8 + 7 files changed, 323 insertions(+) create mode 100644 app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php index cd582ffda9244..b3eac723c05e5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php +++ b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php @@ -45,6 +45,9 @@ private function formatValues($values) { if (isset($values[$this->getOption()->getId()])) { $value = $values[$this->getOption()->getId()]; + if (isset($value['date']) || isset($value['day'], $value['month'], $value['year'])) { + return $values; + } $dateTime = \DateTime::createFromFormat(DateTime::DATETIME_PHP_FORMAT, $value); if ($dateTime === false) { diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php new file mode 100644 index 0000000000000..7855503662584 --- /dev/null +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -0,0 +1,160 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesGraphQl\Model\Resolver; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; +use Magento\Sales\Model\OrderFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Sales\Helper\Reorder as ReorderHelper; + +/** + * ReOrder customer order + */ +class Reorder implements ResolverInterface +{ + /** + * @var OrderFactory + */ + private $orderFactory; + + /** + * @var \Magento\Checkout\Model\CartFactory + */ + private $cartFactory; + + /** + * @var \Magento\Quote\Api\CartManagementInterface + */ + private $cartManagement; + + /** + * @var ReorderHelper + */ + private $reorderHelper; + + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * @var CreateEmptyCartForCustomer + */ + private $createEmptyCartForCustomer; + + /** + * @param OrderFactory $orderFactory + * @param \Magento\Checkout\Model\CartFactory $cartFactory + * @param \Magento\Quote\Api\CartManagementInterface $cartManagement + * @param ReorderHelper $reorderHelper + * @param \Psr\Log\LoggerInterface $logger + * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer + */ + public function __construct( + OrderFactory $orderFactory, + \Magento\Checkout\Model\CartFactory $cartFactory, + \Magento\Quote\Api\CartManagementInterface $cartManagement, + ReorderHelper $reorderHelper, + \Psr\Log\LoggerInterface $logger, + CreateEmptyCartForCustomer $createEmptyCartForCustomer + ) { + $this->orderFactory = $orderFactory; + $this->cartFactory = $cartFactory; + $this->cartManagement = $cartManagement; + $this->reorderHelper = $reorderHelper; + $this->logger = $logger; + $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var ContextInterface $context */ + if (false === $context->getExtensionAttributes()->getIsCustomer()) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } + + $currentUserId = $context->getUserId(); + $orderNumber = $args['orderNumber']; + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, 1); + + if (!$order->getId()) { + throw new GraphQlInputException(__('Cannot find order with number "%1"', $orderNumber)); + } + if ($order->getCustomerId() != $currentUserId) { + throw new GraphQlInputException(__('Order with number "%1" do not belong current customer', $orderNumber)); + } + if (!$this->reorderHelper->canReorder($order->getId())) { + throw new GraphQlInputException(__('Reorder is not available.')); + } + + try { + $cart = $this->cartManagement->getCartForCustomer($currentUserId); + } catch (NoSuchEntityException $e) { + $this->createEmptyCartForCustomer->execute($currentUserId, null); + $cart = $this->cartManagement->getCartForCustomer($currentUserId); + } + $cartModel = $this->cartFactory->create(); + $cartModel->setQuote($cart); + + $lineItemsErrors = []; + $items = $order->getItemsCollection(); + foreach ($items as $item) { + try { + $cartModel->addOrderItem($item); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); + } catch (\Throwable $e) { + $this->logger->critical($e); + $this->addLineItemError( + $lineItemsErrors, + $item, + __('We can\'t add this item to your shopping cart right now.') . $e->getMessage() + ); + } + } + $cartModel->save(); + + return [ + 'cart' => [ + 'model' => $cart, + ], + 'errors' => $lineItemsErrors + + ]; + } + + /** + * Add order line item error + * + * @param array $errors + * @param \Magento\Sales\Model\Order\Item $item + * @param string $message + * @return void + */ + private function addLineItemError(&$errors, \Magento\Sales\Model\Order\Item $item, string $message): void + { + $errors[] = [ + 'sku' => $item->getSku() ?? ($item->getProduct() ? $item->getProduct()->getSku() : ''), + 'message' => $message, + ]; + } +} diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index a687ee59031ea..f025085395efb 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -17,3 +17,17 @@ type CustomerOrder @doc(description: "Order mapping fields") { type CustomerOrders { items: [CustomerOrder] @doc(description: "Array of orders") } + +type Mutation { + addAllOrderItemsToCart(orderNumber: String!): ReOrderOutput @doc(description:"Add all the products from Customer order to the Cart") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") +} + +type ReOrderOutput @doc(description: "ReOrder output") { + cart: Cart! + errors:[ReOrderError] +} + +type ReOrderError @doc(description: "Cart line item error") { + sku: String! + message: String! +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php new file mode 100644 index 0000000000000..4b7a001f8ea6f --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder + */ +class ReorderTest extends GraphQlAbstract +{ + private const CUSTOMER_ID = 1; + private const ORDER_NUMBER = '100000001'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(\Magento\Quote\Api\CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + */ + public function testReorderMutation() + { + $query = $this->getQuery(self::ORDER_NUMBER); + + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $response = $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->assertResponseFields( + $response['addAllOrderItemsToCart'] ?? [], + [ + 'cart' => [ + 'email' => $currentEmail, + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ] + ], + ], + 'errors' => [] + ] + ); + + } + + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testReorderWithoutAuthorisedCustomer() + { + $query = $this->getQuery(self::ORDER_NUMBER); + $this->graphQlMutation($query); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + $query = + <<<MUTATION +mutation { + addAllOrderItemsToCart(orderNumber: "{$orderNumber}") { + errors { + sku + message + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + } + } + } +} +MUTATION; + return $query; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php index f94aa03a9f832..c53becc3193d4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php @@ -48,6 +48,7 @@ /** @var \Magento\Sales\Model\Order\Item $orderItem */ $orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); $orderItem->setProductId($product->getId()); +$orderItem->setSku($product->getSku()); $orderItem->setQtyOrdered(1); $orderItem->setBasePrice($product->getPrice()); $orderItem->setPrice($product->getPrice()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php new file mode 100644 index 0000000000000..f7ceda14df13c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php @@ -0,0 +1,8 @@ +<?php + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/order_item_with_product_and_custom_options.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php new file mode 100644 index 0000000000000..5b0bf62721096 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Catalog/_files/order_item_with_product_and_custom_options_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; From 8a66448d1ac1c3c11073a1e091a714c45250ec3c Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Wed, 11 Mar 2020 12:18:51 -0500 Subject: [PATCH 319/734] MC-31986: Add support for ES 7 to 2.4-develop - fix search engines sorting issue. --- app/code/Magento/Elasticsearch/etc/di.xml | 2 +- app/code/Magento/Elasticsearch6/etc/di.xml | 2 +- app/code/Magento/Elasticsearch7/etc/di.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index fa8686b6262df..5ce0c55fb454a 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -163,7 +163,7 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+ (Deprecated)</item> + <item sortOrder="10" name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+ (Deprecated)</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index f7b22c05027b1..19d3b709cb2ea 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -17,7 +17,7 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.x</item> + <item sortOrder="20" name="elasticsearch6" xsi:type="string">Elasticsearch 6.x</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Elasticsearch7/etc/di.xml b/app/code/Magento/Elasticsearch7/etc/di.xml index 1e480894bc630..a35fef8c66f0d 100644 --- a/app/code/Magento/Elasticsearch7/etc/di.xml +++ b/app/code/Magento/Elasticsearch7/etc/di.xml @@ -17,7 +17,7 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch7" xsi:type="string">Elasticsearch 7.0+</item> + <item sortOrder="30" name="elasticsearch7" xsi:type="string">Elasticsearch 7.0+</item> </argument> </arguments> </type> From ed76a247b3eab1c57cea1ad926df3c8a6c692ae9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Mar 2020 12:38:15 -0500 Subject: [PATCH 320/734] MC-32278: Position sort does not work in GraphQl. - testing just order applier --- .../Model/Resolver/Products/DataProvider/ProductSearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index c611ea04868bb..cfca99ceb7b1a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -86,7 +86,7 @@ public function getList( $collection = $this->collectionFactory->create(); //Join search results - $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); + //$this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); //Additional applier to keep sorting from search if (!empty($searchResult->getItems())) { From ab2f9d2bb51765b4ff825cb5114cf14a6b128d85 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Mar 2020 12:41:20 -0500 Subject: [PATCH 321/734] MC-32278: Position sort does not work in GraphQl. - testing Daniel applier --- .../VisibilityStatusProcessor.php | 16 ++++++++++++++++ .../Products/DataProvider/ProductSearch.php | 18 +++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php index 30174a94aaba0..f88e11be22b90 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessor; +use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaInterface; @@ -18,6 +19,20 @@ */ class VisibilityStatusProcessor implements CollectionProcessorInterface { + /** + * @var Visibility + */ + private $visibility; + + /** + * @param Visibility $visibility + */ + public function __construct(Visibility $visibility) + { + $this->visibility = $visibility; + + } + /** * {@inheritdoc} */ @@ -28,6 +43,7 @@ public function process( ): Collection { $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner'); $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); + $collection->setVisibility($this->visibility->getVisibleInSiteIds()); return $collection; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index cfca99ceb7b1a..20bdc58aec182 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -86,17 +86,17 @@ public function getList( $collection = $this->collectionFactory->create(); //Join search results - //$this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); + $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); //Additional applier to keep sorting from search - if (!empty($searchResult->getItems())) { - $items = []; - foreach ($searchResult->getItems() as $item) { - $items[] = $item->getId(); - } - $orderList = join(',', $items); - $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); - } +// if (!empty($searchResult->getItems())) { +// $items = []; +// foreach ($searchResult->getItems() as $item) { +// $items[] = $item->getId(); +// } +// $orderList = join(',', $items); +// $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); +// } $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); $collection->load(); From 826fea9363fbe90628dcec0a681c9532ddbccd68 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 11 Mar 2020 13:18:50 -0500 Subject: [PATCH 322/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 114b11a4f67c3..54e59a9c31c2b 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "5ecf5becf631dc5e7cb4534574434d8fffbc2317" + "reference": "c37ba96a1caa59755b50593638b01f591e597251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5ecf5becf631dc5e7cb4534574434d8fffbc2317", - "reference": "5ecf5becf631dc5e7cb4534574434d8fffbc2317", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/c37ba96a1caa59755b50593638b01f591e597251", + "reference": "c37ba96a1caa59755b50593638b01f591e597251", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-10T17:35:25+00:00" + "time": "2020-03-11T17:07:43+00:00" }, { "name": "mikey179/vfsstream", From 8bbebf38a52474069a672b93bd9d5ecca6cf1852 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 20:04:12 +0100 Subject: [PATCH 323/734] Avoid executing original `execute` method when no permission to do so --- .../Customer/Controller/Plugin/Account.php | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index b7352873269e9..6974ce92daed1 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -7,8 +7,10 @@ namespace Magento\Customer\Controller\Plugin; +use Closure; use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Model\Session; +use Magento\Framework\App\ActionFlag; use Magento\Framework\App\ActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; @@ -33,53 +35,61 @@ class Account * @var array */ private $allowedActions = []; + /** + * @var ActionFlag + */ + private $actionFlag; /** * @param RequestInterface $request * @param Session $customerSession + * @param ActionFlag $actionFlag * @param array $allowedActions List of actions that are allowed for not authorized users */ public function __construct( RequestInterface $request, Session $customerSession, + ActionFlag $actionFlag, array $allowedActions = [] ) { $this->session = $customerSession; $this->allowedActions = $allowedActions; $this->request = $request; + $this->actionFlag = $actionFlag; } /** - * Dispatch actions allowed for not authorized users + * Executes original method if allowed, otherwise - redirects to log in * - * @param AccountInterface $subject - * @return void + * @param AccountInterface $controllerAction + * @param Closure $proceed + * @return ResultInterface|ResponseInterface|void */ - public function beforeExecute(AccountInterface $subject) + public function aroundExecute(AccountInterface $controllerAction, Closure $proceed) { - $action = strtolower($this->request->getActionName()); - $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i'; - - if (!preg_match($pattern, $action)) { - if (!$this->session->authenticate()) { - $subject->getActionFlag()->set('', ActionInterface::FLAG_NO_DISPATCH, true); - } - } else { + if ($this->isActionAllowed()) { $this->session->setNoReferer(true); + $response = $proceed(); + $this->session->unsNoReferer(false); + + return $response; + } + + if (!$this->session->authenticate()) { + $this->actionFlag->set('', ActionInterface::FLAG_NO_DISPATCH, true); } } /** - * Remove No-referer flag from customer session + * Validates whether currently requested action is one of the allowed * - * @param AccountInterface $subject - * @param ResponseInterface|ResultInterface $result - * @return ResponseInterface|ResultInterface - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return bool */ - public function afterExecute(AccountInterface $subject, $result) + private function isActionAllowed(): bool { - $this->session->unsNoReferer(false); - return $result; + $action = strtolower($this->request->getActionName()); + $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i'; + + return (bool)preg_match($pattern, $action); } } From ac124da9a77e0ed8bed6c582a111bb7ba1dc1a2c Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 11 Mar 2020 15:07:21 -0500 Subject: [PATCH 324/734] MC-32201: Reorder functionality - MC-32362: Reorder interface --- .../Sales/Api/Data/Reorder/LineItemError.php | 48 ++++++ .../Sales/Api/Data/Reorder/ReorderOutput.php | 50 +++++++ .../Magento/Sales/Api/ReorderInterface.php | 21 +++ .../Controller/AbstractController/Reorder.php | 47 +++--- .../Magento/Sales/Model/Order/Reorder.php | 139 ++++++++++++++++++ app/code/Magento/Sales/etc/di.xml | 1 + .../SalesGraphQl/Model/Resolver/Reorder.php | 120 ++++----------- 7 files changed, 304 insertions(+), 122 deletions(-) create mode 100644 app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php create mode 100644 app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php create mode 100644 app/code/Magento/Sales/Api/ReorderInterface.php create mode 100644 app/code/Magento/Sales/Model/Order/Reorder.php diff --git a/app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php b/app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php new file mode 100644 index 0000000000000..b9608feb037b4 --- /dev/null +++ b/app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Api\Data\Reorder; + +/** + * DTO represent Cart line item error + */ +class LineItemError +{ + /** + * @var string + */ + private $sku; + + /** + * @var string + */ + private $message; + + /** + * @param string $sku + * @param string $message + */ + public function __construct(string $sku, string $message) + { + $this->sku = $sku; + $this->message = $message; + } + + /** + * @return string + */ + public function getSku(): string + { + return $this->sku; + } + + /** + * @return string + */ + public function getMessage(): string + { + return $this->message; + } +} diff --git a/app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php b/app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php new file mode 100644 index 0000000000000..c11f98215883c --- /dev/null +++ b/app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Api\Data\Reorder; + +use Magento\Quote\Api\Data\CartInterface; + +/** + * DTO represent output for \Magento\Sales\Api\ReorderInterface + */ +class ReorderOutput +{ + /** + * @var CartInterface + */ + private $cart; + + /** + * @var array + */ + private $lineItemErrors; + + /** + * @param CartInterface $cart + * @param array $lineItemErrors + */ + public function __construct(CartInterface $cart, array $lineItemErrors) + { + $this->cart = $cart; + $this->lineItemErrors = $lineItemErrors; + } + + /** + * @return CartInterface + */ + public function getCart(): CartInterface + { + return $this->cart; + } + + /** + * @return LineItemError[] + */ + public function getLineItemErrors(): array + { + return $this->lineItemErrors; + } +} diff --git a/app/code/Magento/Sales/Api/ReorderInterface.php b/app/code/Magento/Sales/Api/ReorderInterface.php new file mode 100644 index 0000000000000..e38cee521fe3f --- /dev/null +++ b/app/code/Magento/Sales/Api/ReorderInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Api; + +use Magento\Sales\Api\Data\Reorder\ReorderOutput; + +/** + * Allows customer to quickly reorder previously added products and put them to the Cart + */ +interface ReorderInterface +{ + /** + * @param string $incrementOrderId + * @param string $storeId + * @return ReorderOutput + */ + public function execute(string $incrementOrderId, string $storeId): ReorderOutput; +} diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index f53ecaa625bf5..f789d9b3caeb8 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -9,9 +9,11 @@ namespace Magento\Sales\Controller\AbstractController; use Magento\Framework\App\Action; -use Magento\Framework\Registry; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Registry; +use Magento\Sales\Api\ReorderInterface; use Magento\Sales\Helper\Reorder as ReorderHelper; /** @@ -30,9 +32,9 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface protected $_coreRegistry; /** - * @var ReorderHelper + * @var \Magento\Sales\Api\ReorderInterface */ - private $reorderHelper; + private $reorder; /** * Constructor @@ -46,12 +48,13 @@ public function __construct( Action\Context $context, OrderLoaderInterface $orderLoader, Registry $registry, - ReorderHelper $reorderHelper = null + ReorderHelper $reorderHelper = null, + \Magento\Sales\Api\ReorderInterface $reOrder = null ) { $this->orderLoader = $orderLoader; $this->_coreRegistry = $registry; - $this->reorderHelper = $reorderHelper ?: ObjectManager::getInstance()->get(ReorderHelper::class); parent::__construct($context); + $this->reorder = $reOrder ?: ObjectManager::getInstance()->get(ReorderInterface::class); } /** @@ -66,37 +69,27 @@ public function execute() return $result; } $order = $this->_coreRegistry->registry('current_order'); + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - if (!$this->reorderHelper->canReorder($order->getId())) { - $this->messageManager->addErrorMessage(__("Reorder is not available.")); + try { + $reorderOutput = $this->reorder->execute($order->getIncrementId(), $order->getStoreId()); + } catch (LocalizedException $localizedException) { + $this->messageManager->addErrorMessage($localizedException->getMessage()); return $resultRedirect->setPath('checkout/cart'); } - /* @var $cart \Magento\Checkout\Model\Cart */ - $cart = $this->_objectManager->get(\Magento\Checkout\Model\Cart::class); - $items = $order->getItemsCollection(); - foreach ($items as $item) { - try { - $cart->addOrderItem($item); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - if ($this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true)) { - $this->messageManager->addNoticeMessage($e->getMessage()); - } else { - $this->messageManager->addErrorMessage($e->getMessage()); - } - return $resultRedirect->setPath('*/*/history'); - } catch (\Exception $e) { - $this->messageManager->addExceptionMessage( - $e, - __('We can\'t add this item to your shopping cart right now.') - ); - return $resultRedirect->setPath('checkout/cart'); + $errors = $reorderOutput->getLineItemErrors(); + if (!empty($errors)) { + $useNotice = $this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true); + foreach ($errors as $error) { + $useNotice + ? $this->messageManager->addNoticeMessage($error->getMessage()) + : $this->messageManager->addErrorMessage($error->getMessage()); } } - $cart->save(); return $resultRedirect->setPath('checkout/cart'); } } diff --git a/app/code/Magento/Sales/Model/Order/Reorder.php b/app/code/Magento/Sales/Model/Order/Reorder.php new file mode 100644 index 0000000000000..2acb8158b376b --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Reorder.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order; + +use Magento\Framework\Exception\InputException; +use Magento\Sales\Api\ReorderInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; +use Magento\Sales\Model\OrderFactory; +use Magento\Sales\Helper\Reorder as ReorderHelper; + +/** + * @inheritdoc + */ +class Reorder implements ReorderInterface +{ + /** + * @var OrderFactory + */ + private $orderFactory; + + /** + * @var \Magento\Checkout\Model\CartFactory + */ + private $cartFactory; + + /** + * @var \Magento\Quote\Api\CartManagementInterface + */ + private $cartManagement; + + /** + * @var ReorderHelper + */ + private $reorderHelper; + + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * @var CreateEmptyCartForCustomer + */ + private $createEmptyCartForCustomer; + + /** + * @param OrderFactory $orderFactory + * @param \Magento\Checkout\Model\CartFactory $cartFactory + * @param \Magento\Quote\Api\CartManagementInterface $cartManagement + * @param ReorderHelper $reorderHelper + * @param \Psr\Log\LoggerInterface $logger + * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer + */ + public function __construct( + OrderFactory $orderFactory, + \Magento\Checkout\Model\CartFactory $cartFactory, + \Magento\Quote\Api\CartManagementInterface $cartManagement, + ReorderHelper $reorderHelper, + \Psr\Log\LoggerInterface $logger, + CreateEmptyCartForCustomer $createEmptyCartForCustomer + ) { + $this->orderFactory = $orderFactory; + $this->cartFactory = $cartFactory; + $this->cartManagement = $cartManagement; + $this->reorderHelper = $reorderHelper; + $this->logger = $logger; + $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; + } + + /** + * @inheritdoc + * @throws InputException + * @throws NoSuchEntityException + */ + public function execute(string $incrementOrderId, string $storeId): \Magento\Sales\Api\Data\Reorder\ReorderOutput + { + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($incrementOrderId, $storeId); + + if (!$order->getId()) { + throw new NoSuchEntityException( + __('Cannot find order with number "%1" in store "%2"', $incrementOrderId, $storeId) + ); + } + if (!$this->reorderHelper->canReorder($order->getId())) { + throw new InputException(__('Reorder is not available.')); + } + + $customerId = $order->getCustomerId(); + + try { + $cart = $this->cartManagement->getCartForCustomer($customerId); + } catch (NoSuchEntityException $e) { + $this->createEmptyCartForCustomer->execute($customerId); + $cart = $this->cartManagement->getCartForCustomer($customerId); + } + $cartModel = $this->cartFactory->create(); + $cartModel->setQuote($cart); + + $lineItemsErrors = []; + $items = $order->getItemsCollection(); + foreach ($items as $item) { + try { + $cartModel->addOrderItem($item); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); + } catch (\Throwable $e) { + $this->logger->critical($e); + $this->addLineItemError( + $lineItemsErrors, + $item, + __('We can\'t add this item to your shopping cart right now.') + ); + } + } + $cartModel->save(); + + return new \Magento\Sales\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); + } + + /** + * Add order line item error + * + * @param array $errors + * @param \Magento\Sales\Model\Order\Item $item + * @param string $message + * @return void + */ + private function addLineItemError(&$errors, \Magento\Sales\Model\Order\Item $item, $message): void + { + $errors[] = new \Magento\Sales\Api\Data\Reorder\LineItemError( + $item->getProduct() ? $item->getProduct()->getSku() : $item->getSku() ?? '', + $message + ); + } +} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index b4dadfa944a5b..17fd3773be70e 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -117,6 +117,7 @@ <preference for="Magento\Sales\Api\RefundInvoiceInterface" type="Magento\Sales\Model\RefundInvoice"/> <preference for="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface" type="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider" /> <preference for="Magento\Sales\Model\ConfigInterface" type="Magento\Sales\Model\Config" /> + <preference for="Magento\Sales\Api\ReorderInterface" type="Magento\Sales\Model\Order\Reorder" /> <type name="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider"> <arguments> <argument name="providers" xsi:type="array"> diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index 7855503662584..c0f741611d3b9 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -7,16 +7,14 @@ namespace Magento\SalesGraphQl\Model\Resolver; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\GraphQl\Model\Query\ContextInterface; -use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; +use Magento\Sales\Api\Data\Reorder\LineItemError; use Magento\Sales\Model\OrderFactory; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Sales\Helper\Reorder as ReorderHelper; /** * ReOrder customer order @@ -29,52 +27,20 @@ class Reorder implements ResolverInterface private $orderFactory; /** - * @var \Magento\Checkout\Model\CartFactory + * @var \Magento\Sales\Api\ReorderInterface */ - private $cartFactory; - - /** - * @var \Magento\Quote\Api\CartManagementInterface - */ - private $cartManagement; - - /** - * @var ReorderHelper - */ - private $reorderHelper; - - /** - * @var \Psr\Log\LoggerInterface - */ - private $logger; - - /** - * @var CreateEmptyCartForCustomer - */ - private $createEmptyCartForCustomer; + private $reorder; /** + * @param \Magento\Sales\Api\ReorderInterface $reorder * @param OrderFactory $orderFactory - * @param \Magento\Checkout\Model\CartFactory $cartFactory - * @param \Magento\Quote\Api\CartManagementInterface $cartManagement - * @param ReorderHelper $reorderHelper - * @param \Psr\Log\LoggerInterface $logger - * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer */ public function __construct( - OrderFactory $orderFactory, - \Magento\Checkout\Model\CartFactory $cartFactory, - \Magento\Quote\Api\CartManagementInterface $cartManagement, - ReorderHelper $reorderHelper, - \Psr\Log\LoggerInterface $logger, - CreateEmptyCartForCustomer $createEmptyCartForCustomer + \Magento\Sales\Api\ReorderInterface $reorder, + OrderFactory $orderFactory ) { $this->orderFactory = $orderFactory; - $this->cartFactory = $cartFactory; - $this->cartManagement = $cartManagement; - $this->reorderHelper = $reorderHelper; - $this->logger = $logger; - $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; + $this->reorder = $reorder; } /** @@ -93,68 +59,32 @@ public function resolve( } $currentUserId = $context->getUserId(); - $orderNumber = $args['orderNumber']; - $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, 1); + $orderNumber = $args['orderNumber'] ?? ''; + $storeId = (string)$context->getExtensionAttributes()->getStore()->getId(); - if (!$order->getId()) { - throw new GraphQlInputException(__('Cannot find order with number "%1"', $orderNumber)); - } - if ($order->getCustomerId() != $currentUserId) { - throw new GraphQlInputException(__('Order with number "%1" do not belong current customer', $orderNumber)); - } - if (!$this->reorderHelper->canReorder($order->getId())) { - throw new GraphQlInputException(__('Reorder is not available.')); - } + $reorderOutput = $this->reorder->execute($orderNumber, $storeId); - try { - $cart = $this->cartManagement->getCartForCustomer($currentUserId); - } catch (NoSuchEntityException $e) { - $this->createEmptyCartForCustomer->execute($currentUserId, null); - $cart = $this->cartManagement->getCartForCustomer($currentUserId); - } - $cartModel = $this->cartFactory->create(); - $cartModel->setQuote($cart); + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); - $lineItemsErrors = []; - $items = $order->getItemsCollection(); - foreach ($items as $item) { - try { - $cartModel->addOrderItem($item); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); - } catch (\Throwable $e) { - $this->logger->critical($e); - $this->addLineItemError( - $lineItemsErrors, - $item, - __('We can\'t add this item to your shopping cart right now.') . $e->getMessage() - ); - } + if ($order->getCustomerId() != $currentUserId) { + throw new GraphQlInputException( + __('Order with number "%1" doesn\'t belong current customer', $orderNumber) + ); } - $cartModel->save(); return [ 'cart' => [ - 'model' => $cart, + 'model' => $reorderOutput->getCart(), ], - 'errors' => $lineItemsErrors - - ]; - } - - /** - * Add order line item error - * - * @param array $errors - * @param \Magento\Sales\Model\Order\Item $item - * @param string $message - * @return void - */ - private function addLineItemError(&$errors, \Magento\Sales\Model\Order\Item $item, string $message): void - { - $errors[] = [ - 'sku' => $item->getSku() ?? ($item->getProduct() ? $item->getProduct()->getSku() : ''), - 'message' => $message, + 'errors' => \array_map( + function(LineItemError $error) { + return [ + 'sku' => $error->getSku(), + 'message' => $error->getMessage(), + ]; + }, + $reorderOutput->getLineItemErrors() + ) ]; } } From 501e370f7dcfd98bfd068b6af6fa3344676e27d3 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 21:27:30 +0100 Subject: [PATCH 325/734] Fix implementation and Unit Tests --- .../Customer/Controller/Plugin/Account.php | 20 ++---- .../Unit/Controller/Plugin/AccountTest.php | 61 ++++++------------- 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index 6974ce92daed1..7d0b954523fa6 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -35,27 +35,20 @@ class Account * @var array */ private $allowedActions = []; - /** - * @var ActionFlag - */ - private $actionFlag; /** * @param RequestInterface $request * @param Session $customerSession - * @param ActionFlag $actionFlag * @param array $allowedActions List of actions that are allowed for not authorized users */ public function __construct( RequestInterface $request, Session $customerSession, - ActionFlag $actionFlag, array $allowedActions = [] ) { + $this->request = $request; $this->session = $customerSession; $this->allowedActions = $allowedActions; - $this->request = $request; - $this->actionFlag = $actionFlag; } /** @@ -68,16 +61,11 @@ public function __construct( public function aroundExecute(AccountInterface $controllerAction, Closure $proceed) { if ($this->isActionAllowed()) { - $this->session->setNoReferer(true); - $response = $proceed(); - $this->session->unsNoReferer(false); - - return $response; + return $proceed(); } - if (!$this->session->authenticate()) { - $this->actionFlag->set('', ActionInterface::FLAG_NO_DISPATCH, true); - } + /** @FIXME Move Authentication and redirect out of Session model */ + $this->session->authenticate(); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php index 7dd376d57bdb0..8986675d963a4 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Test\Unit\Controller\Plugin; +use Closure; use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Controller\Plugin\Account; use Magento\Customer\Model\Session; @@ -83,40 +84,38 @@ protected function setUp() /** * @param string $action * @param array $allowedActions - * @param boolean $isActionAllowed + * @param boolean $isAllowed * @param boolean $isAuthenticated * * @dataProvider beforeExecuteDataProvider */ - public function testBeforeExecute($action, $allowedActions, $isActionAllowed, $isAuthenticated) + public function testAroundExecuteInterruptsOriginalCallWhenNotAllowed(string $action, array $allowedActions, bool $isAllowed, bool $isAuthenticated) { + /** @var callable|MockObject $proceedMock */ + $proceedMock = $this->getMockBuilder(\stdClass::class) + ->setMethods(['__invoke']) + ->getMock(); + + $closureMock = Closure::fromCallable($proceedMock); + $this->requestMock->expects($this->once()) ->method('getActionName') ->willReturn($action); - if ($isActionAllowed) { - $this->sessionMock->expects($this->once()) - ->method('setNoReferer') - ->with(true) - ->willReturnSelf(); + if ($isAllowed) { + $proceedMock->expects($this->once())->method('__invoke')->willReturn($this->resultMock); } else { - $this->sessionMock->expects($this->once()) - ->method('authenticate') - ->willReturn($isAuthenticated); - if (!$isAuthenticated) { - $this->actionMock->expects($this->once()) - ->method('getActionFlag') - ->willReturn($this->actionFlagMock); - - $this->actionFlagMock->expects($this->once()) - ->method('set') - ->with('', ActionInterface::FLAG_NO_DISPATCH, true) - ->willReturnSelf(); - } + $proceedMock->expects($this->never())->method('__invoke'); } $plugin = new Account($this->requestMock, $this->sessionMock, $allowedActions); - $plugin->beforeExecute($this->actionMock); + $result = $plugin->aroundExecute($this->actionMock, $closureMock); + + if ($isAllowed) { + $this->assertSame($this->resultMock, $result); + } else { + $this->assertNull($result); + } } /** @@ -157,24 +156,4 @@ public function beforeExecuteDataProvider() ], ]; } - - public function testAfterExecute() - { - $this->sessionMock->expects($this->once()) - ->method('unsNoReferer') - ->with(false) - ->willReturnSelf(); - - $plugin = (new ObjectManager($this))->getObject( - Account::class, - [ - 'session' => $this->sessionMock, - 'allowedActions' => ['testaction'] - ] - ); - $this->assertSame( - $this->resultMock, - $plugin->afterExecute($this->actionMock, $this->resultMock, $this->requestMock) - ); - } } From 79a3a1a1ad97dcd04a96a37625541b19881d1583 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 21:33:51 +0100 Subject: [PATCH 326/734] Fix logical issue --- app/code/Magento/Customer/Controller/Plugin/Account.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index 7d0b954523fa6..ee927525f2315 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -60,12 +60,10 @@ public function __construct( */ public function aroundExecute(AccountInterface $controllerAction, Closure $proceed) { - if ($this->isActionAllowed()) { + /** @FIXME Move Authentication and redirect out of Session model */ + if ($this->isActionAllowed() || $this->session->authenticate()) { return $proceed(); } - - /** @FIXME Move Authentication and redirect out of Session model */ - $this->session->authenticate(); } /** From 302392701655d6458d8c976b8491a96a16eb97bb Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 21:50:47 +0100 Subject: [PATCH 327/734] Code Review changes --- app/code/Magento/Customer/Controller/AbstractAccount.php | 2 +- app/code/Magento/Customer/Controller/Plugin/Account.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/AbstractAccount.php b/app/code/Magento/Customer/Controller/AbstractAccount.php index 36a96521cc6d1..4f2c80711d292 100644 --- a/app/code/Magento/Customer/Controller/AbstractAccount.php +++ b/app/code/Magento/Customer/Controller/AbstractAccount.php @@ -12,7 +12,7 @@ * AbstractAccount class is deprecated, in favour of Composition approach to build Controllers * * @SuppressWarnings(PHPMD.NumberOfChildren) - * @deprecated 2.4.0 + * @deprecated * @see \Magento\Customer\Controller\AccountInterface */ abstract class AbstractAccount extends Action implements AccountInterface diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index ee927525f2315..f02b333b70f7a 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -10,8 +10,6 @@ use Closure; use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Model\Session; -use Magento\Framework\App\ActionFlag; -use Magento\Framework\App\ActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; From b5fda4c69bf1268f35e2758355e764c11a02a807 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 21:56:52 +0100 Subject: [PATCH 328/734] Code style --- app/code/Magento/Customer/Controller/Plugin/Account.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index f02b333b70f7a..bbdb58d626108 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -22,7 +22,7 @@ class Account /** * @var Session */ - protected $session; + private $session; /** * @var RequestInterface @@ -55,6 +55,7 @@ public function __construct( * @param AccountInterface $controllerAction * @param Closure $proceed * @return ResultInterface|ResponseInterface|void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundExecute(AccountInterface $controllerAction, Closure $proceed) { From 1917781506ff523a94514fa63a0a04637ef51f05 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 11 Mar 2020 22:55:29 +0100 Subject: [PATCH 329/734] Fix code style --- .../Customer/Test/Unit/Controller/Plugin/AccountTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php index 8986675d963a4..01ff1ced05ff9 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php @@ -85,12 +85,14 @@ protected function setUp() * @param string $action * @param array $allowedActions * @param boolean $isAllowed - * @param boolean $isAuthenticated * * @dataProvider beforeExecuteDataProvider */ - public function testAroundExecuteInterruptsOriginalCallWhenNotAllowed(string $action, array $allowedActions, bool $isAllowed, bool $isAuthenticated) - { + public function testAroundExecuteInterruptsOriginalCallWhenNotAllowed( + string $action, + array $allowedActions, + bool $isAllowed + ) { /** @var callable|MockObject $proceedMock */ $proceedMock = $this->getMockBuilder(\stdClass::class) ->setMethods(['__invoke']) From dfcf474d83c05620e6fa3010ae25632cb820174e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Mar 2020 17:29:18 -0500 Subject: [PATCH 330/734] MC-32278: Position sort does not work in GraphQl. - add category collection handling --- .../VisibilityStatusProcessor.php | 18 +----------------- .../Products/DataProvider/ProductSearch.php | 10 ---------- .../Model/Resolver/Products/Query/Search.php | 16 +++++----------- .../FilterProcessor/CategoryFilter.php | 1 + 4 files changed, 7 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php index f88e11be22b90..e31bc98837053 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php @@ -7,7 +7,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessor; -use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaInterface; @@ -19,20 +18,6 @@ */ class VisibilityStatusProcessor implements CollectionProcessorInterface { - /** - * @var Visibility - */ - private $visibility; - - /** - * @param Visibility $visibility - */ - public function __construct(Visibility $visibility) - { - $this->visibility = $visibility; - - } - /** * {@inheritdoc} */ @@ -42,8 +27,7 @@ public function process( array $attributeNames ): Collection { $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner'); - $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); - $collection->setVisibility($this->visibility->getVisibleInSiteIds()); + //$collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); return $collection; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 20bdc58aec182..49fb06293a29a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -88,16 +88,6 @@ public function getList( //Join search results $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); - //Additional applier to keep sorting from search -// if (!empty($searchResult->getItems())) { -// $items = []; -// foreach ($searchResult->getItems() as $item) { -// $items[] = $item->getId(); -// } -// $orderList = join(',', $items); -// $collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); -// } - $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); $collection->load(); $this->collectionPostProcessor->process($collection, $attributes); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php index 8377cd9baa5b4..bf3c6eb1a314c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php @@ -9,12 +9,12 @@ use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Api\Search\SearchCriteriaInterface; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory; -use Magento\Search\Api\SearchInterface; +use Magento\Framework\Api\Search\SearchCriteriaInterface; use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Search\Api\SearchInterface; use Magento\Search\Model\Search\PageSizeProvider; /** @@ -107,13 +107,9 @@ public function getResult( $searchCriteria->setCurrentPage(0); $itemsResults = $this->search->search($searchCriteria); - //Create copy of search criteria without conditions (conditions will be applied by joining search result) - $searchCriteriaCopy = $this->searchCriteriaFactory->create() - ->setSortOrders($searchCriteria->getSortOrders()) - ->setPageSize($realPageSize) - ->setCurrentPage($realCurrentPage); + $searchCriteria->setPageSize($realPageSize)->setCurrentPage($realCurrentPage); - $searchResults = $this->productsProvider->getList($searchCriteriaCopy, $itemsResults, $queryFields); + $searchResults = $this->productsProvider->getList($searchCriteria, $itemsResults, $queryFields); //possible division by 0 if ($realPageSize) { @@ -121,8 +117,6 @@ public function getResult( } else { $maxPages = 0; } - $searchCriteria->setPageSize($realPageSize); - $searchCriteria->setCurrentPage($realCurrentPage); $productArray = []; /** @var \Magento\Catalog\Model\Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php index 92888a2775e17..f709f8cd6eb72 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php @@ -61,6 +61,7 @@ public function apply(Filter $filter, AbstractDb $collection) $category = $this->categoryFactory->create(); $this->categoryResourceModel->load($category, $categoryId); $categoryProducts[$categoryId] = $category->getProductCollection()->getAllIds(); + $collection->addCategoryFilter($category); } $categoryProductIds = array_unique(array_merge(...$categoryProducts)); From 53da3670a98b000d893c560ae1cf18b1d795dc44 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 12 Mar 2020 01:14:39 +0100 Subject: [PATCH 331/734] Deprecate `AdminLoginTest`, introduce `AdminLoginSuccessfulTest` --- .../Test/Mftf/Test/AdminLoginFailedTest.xml | 28 +++++++++++++++++++ .../Mftf/Test/AdminLoginSuccessfulTest.xml | 27 ++++++++++++++++++ .../Backend/Test/Mftf/Test/AdminLoginTest.xml | 2 +- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml new file mode 100644 index 0000000000000..1239fc471f59e --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml @@ -0,0 +1,28 @@ +<?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="AdminLoginFailedTest"> + <annotations> + <features value="Backend"/> + <stories value="Login on the Admin Login page"/> + <title value="Admin should not be able to log into the backend with invalid credentials"/> + <description value="Admin should not be able to log into the backend with invalid credentials"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + </annotations> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"> + <argument name="password" value="INVALID!{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="assertErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml new file mode 100644 index 0000000000000..a8de04f4342de --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml @@ -0,0 +1,27 @@ +<?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="AdminLoginSuccessfulTest"> + <annotations> + <features value="Backend"/> + <stories value="Login on the Admin Login page"/> + <title value="Admin should be able to log into the Magento Admin backend successfully"/> + <description value="Admin should be able to log into the Magento Admin backend successfully"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + </annotations> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="assertLoggedIn"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml index 566328e075600..5f916063cfd1d 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminLoginTest"> + <test name="AdminLoginTest" deprecated="Replaced with AdminLoginSuccessfulTest"> <annotations> <features value="Backend"/> <stories value="Login on the Admin Login page"/> From aa6d0d9f0beaded06eb6fad1fe8689a2a076fa25 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 12 Mar 2020 01:17:48 +0100 Subject: [PATCH 332/734] Replace deprecated reference to Action Group --- app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml index 5f916063cfd1d..09893f5f51e5e 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml @@ -20,7 +20,7 @@ <group value="login"/> </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </test> From 234712b61deb83776b740b5dc9a7d2256f499113 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 11 Mar 2020 19:33:21 -0500 Subject: [PATCH 333/734] MC-32201: Reorder functionality - MC-32362: Reorder interface --- .../Magento/Sales/Model/Order/Reorder.php | 23 ++++++++++++++++++- .../SalesGraphQl/Model/Resolver/Reorder.php | 7 +++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Reorder.php b/app/code/Magento/Sales/Model/Order/Reorder.php index 2acb8158b376b..377a620075dd1 100644 --- a/app/code/Magento/Sales/Model/Order/Reorder.php +++ b/app/code/Magento/Sales/Model/Order/Reorder.php @@ -104,7 +104,7 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal $items = $order->getItemsCollection(); foreach ($items as $item) { try { - $cartModel->addOrderItem($item); + $this->addOrderItem($cartModel, $item); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); } catch (\Throwable $e) { @@ -121,6 +121,27 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal return new \Magento\Sales\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); } + + /** + * Convert order item to quote item + * + * @param \Magento\Checkout\Model\Cart $cartModel + * @param \Magento\Sales\Model\Order\Item $orderItem + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function addOrderItem($cartModel, $orderItem): void + { + /* @var $orderItem \Magento\Sales\Model\Order\Item */ + if ($orderItem->getParentItem() === null) { + $info = $orderItem->getProductOptionByCode('info_buyRequest'); + $info = new \Magento\Framework\DataObject($info); + $info->setQty($orderItem->getQtyOrdered()); + + $cartModel->addProduct($orderItem->getProductId(), $info); + } + } + /** * Add order line item error * diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index c0f741611d3b9..80a11d0ebbcd3 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -62,16 +62,15 @@ public function resolve( $orderNumber = $args['orderNumber'] ?? ''; $storeId = (string)$context->getExtensionAttributes()->getStore()->getId(); - $reorderOutput = $this->reorder->execute($orderNumber, $storeId); - $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); - - if ($order->getCustomerId() != $currentUserId) { + if ((int)$order->getCustomerId() !== $currentUserId) { throw new GraphQlInputException( __('Order with number "%1" doesn\'t belong current customer', $orderNumber) ); } + $reorderOutput = $this->reorder->execute($orderNumber, $storeId); + return [ 'cart' => [ 'model' => $reorderOutput->getCart(), From aad59c10c0011f36815e541b1e9feb2ebbd6c582 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Mar 2020 21:59:43 -0500 Subject: [PATCH 334/734] MC-32278: Position sort does not work in GraphQl. - fix search_term --- .../CollectionProcessor/FilterProcessor.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index 8f0a3fb2baa69..89354baf73e67 100644 --- a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -65,16 +65,18 @@ private function addFilterGroupToCollection( ) { $fields = []; foreach ($filterGroup->getFilters() as $filter) { - $isApplied = false; - $customFilter = $this->getCustomFilterForField($filter->getField()); - if ($customFilter) { - $isApplied = $customFilter->apply($filter, $collection); - } + if ($filter->getField() != 'search_term') { + $isApplied = false; + $customFilter = $this->getCustomFilterForField($filter->getField()); + if ($customFilter) { + $isApplied = $customFilter->apply($filter, $collection); + } - if (!$isApplied) { - $field = $this->getFieldMapping($filter->getField()); - $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; - $fields[] = ['attribute' => $field, $condition => $filter->getValue()]; + if (!$isApplied) { + $field = $this->getFieldMapping($filter->getField()); + $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; + $fields[] = ['attribute' => $field, $condition => $filter->getValue()]; + } } } From 39ade1f9791606927a37386d08a96105e4d18613 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Thu, 12 Mar 2020 09:48:15 +0200 Subject: [PATCH 335/734] added webapi test --- .../Guest/AddSimpleProductToCartTest.php | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 01ae565f00bf6..3bdce6ea98b30 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -10,6 +10,7 @@ use Exception; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; use Magento\TestFramework\TestCase\GraphQlAbstract; /** @@ -79,6 +80,30 @@ public function testAddSimpleProductToCart() self::assertEquals('USD', $rowTotalIncludingTax['currency']); } + /** + * Add out of stock product to cart + * + * @@magentoApiDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @return void + */ + public function testAddProductToCartWithError(): void + { + $disabledProductSku = 'simple3'; + $quantity = 2; + + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $disabledProductSku, $quantity); + + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU simple3 to the shopping cart: ' . + 'Product that you are trying to add is not available.' + ); + + $this->graphQlMutation($query); + } + /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_id" is missing @@ -191,7 +216,7 @@ public function testAddSimpleProductToCustomerCart() private function getQuery(string $maskedQuoteId, string $sku, float $quantity): string { return <<<QUERY -mutation { +mutation { addSimpleProductsToCart( input: { cart_id: "{$maskedQuoteId}" From aec45cc1420fc451c621b676721f03622c8ccca3 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Thu, 12 Mar 2020 10:09:06 +0200 Subject: [PATCH 336/734] Change Way of saving Operations to Bulk during sheduling new Bulk Request --- .../Api/SaveMultipleOperationsInterface.php | 27 ++++++++++ .../Model/MassSchedule.php | 14 ++++- .../Model/OperationManagement.php | 2 +- .../Model/ResourceModel/Operation.php | 6 ++- .../Operation/OperationRepository.php | 2 +- .../Model/SaveMultipleOperations.php | 54 +++++++++++++++++++ .../Magento/AsynchronousOperations/etc/di.xml | 1 + 7 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php create mode 100644 app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php diff --git a/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php b/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php new file mode 100644 index 0000000000000..8563ab6541a0c --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\AsynchronousOperations\Api; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; + +/** + * Interface for saving multiple operations + * + * @api + */ +interface SaveMultipleOperationsInterface +{ + /** + * Save Operations for Bulk + * + * @param OperationInterface[] $operations + * @return void + */ + public function execute(array $operations): void; +} diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index 89d468159c6e9..a892c0c87d48f 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -21,6 +21,7 @@ use Magento\AsynchronousOperations\Model\ResourceModel\Operation\OperationRepository; use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\Encryption\Encryptor; +use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; /** * Class MassSchedule used for adding multiple entities as Operations to Bulk Management with the status tracking @@ -69,6 +70,11 @@ class MassSchedule */ private $encryptor; + /** + * @var SaveMultipleOperationsInterface + */ + private $saveMultipleOperations; + /** * Initialize dependencies. * @@ -80,6 +86,7 @@ class MassSchedule * @param OperationRepository $operationRepository * @param UserContextInterface $userContext * @param Encryptor|null $encryptor + * @param SaveMultipleOperationsInterface $saveMultipleOperations */ public function __construct( IdentityGeneratorInterface $identityService, @@ -89,7 +96,8 @@ public function __construct( LoggerInterface $logger, OperationRepository $operationRepository, UserContextInterface $userContext = null, - Encryptor $encryptor = null + Encryptor $encryptor = null, + SaveMultipleOperationsInterface $saveMultipleOperations ) { $this->identityService = $identityService; $this->itemStatusInterfaceFactory = $itemStatusInterfaceFactory; @@ -99,6 +107,7 @@ public function __construct( $this->operationRepository = $operationRepository; $this->userContext = $userContext ?: ObjectManager::getInstance()->get(UserContextInterface::class); $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(Encryptor::class); + $this->saveMultipleOperations = $saveMultipleOperations; } /** @@ -133,6 +142,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $operations = []; $requestItems = []; + $singleOperationsData = []; $bulkException = new BulkException(); foreach ($entitiesArray as $key => $entityParams) { /** @var \Magento\AsynchronousOperations\Api\Data\ItemStatusInterface $requestItem */ @@ -141,6 +151,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ try { $operation = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); $operations[] = $operation; + $singleOperationsData[] = $operation->getData(); $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); $requestItem->setDataHash( @@ -161,6 +172,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ } } + $this->saveMultipleOperations->execute($singleOperationsData); if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) { throw new LocalizedException( __('Something went wrong while processing the request.') diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php index f204f63ed032b..a95e2eaa909d6 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php @@ -45,7 +45,7 @@ public function __construct( $this->operationFactory = $operationFactory; $this->logger = $logger; } - + /** * @inheritDoc */ diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php index 061d0917e7ab0..a7343001bc441 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php @@ -11,6 +11,10 @@ */ class Operation extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { + + public const TABLE_NAME = "magento_operation"; + public const TABLE_PRIMARY_KEY = "id"; + /** * Initialize banner sales rule resource model * @@ -18,6 +22,6 @@ class Operation extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ protected function _construct() { - $this->_init('magento_operation', 'id'); + $this->_init(self::TABLE_NAME, self::TABLE_PRIMARY_KEY); } } diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php index 54e65cc3470dd..b54a09a83c34d 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php @@ -93,6 +93,6 @@ public function createByTopic($topicName, $entityParams, $groupId) /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ $operation = $this->operationFactory->create($data); - return $this->entityManager->save($operation); + return $operation; } } diff --git a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php new file mode 100644 index 0000000000000..4536e1b54f8ca --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\AsynchronousOperations\Model; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; +use Magento\AsynchronousOperations\Model\ResourceModel\Operation as OperationResource; +use Magento\Framework\Exception\CouldNotSaveException; + +/** + * Implementation for saving multiple operations + */ +class SaveMultipleOperations implements SaveMultipleOperationsInterface +{ + + /** + * @var OperationResource + */ + private $operationResource; + + /** + * BulkSummary constructor. + * + * @param OperationResource $operationResource + */ + public function __construct( + OperationResource $operationResource + ) { + $this->operationResource = $operationResource; + } + + /** + * @inheritDoc + */ + public function execute(array $operations): void + { + try { + $connection = $this->operationResource->getConnection(); + $connection->insertMultiple( + $this->operationResource->getTable(OperationResource::TABLE_NAME), + $operations + ); + } catch (\Exception $exception) { + throw new CouldNotSaveException(__($exception->getMessage())); + } + } + +} diff --git a/app/code/Magento/AsynchronousOperations/etc/di.xml b/app/code/Magento/AsynchronousOperations/etc/di.xml index 171a01cedf289..895659a3bf76e 100644 --- a/app/code/Magento/AsynchronousOperations/etc/di.xml +++ b/app/code/Magento/AsynchronousOperations/etc/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\AsynchronousOperations\Api\Data\BulkSummaryInterface" type="Magento\AsynchronousOperations\Model\BulkSummary" /> + <preference for="Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface" type="Magento\AsynchronousOperations\Model\SaveMultipleOperations" /> <preference for="Magento\AsynchronousOperations\Api\Data\OperationInterface" type="Magento\AsynchronousOperations\Model\Operation" /> <preference for="Magento\AsynchronousOperations\Api\Data\OperationListInterface" type="Magento\AsynchronousOperations\Model\OperationList" /> <preference for="Magento\Framework\Bulk\BulkManagementInterface" type="Magento\AsynchronousOperations\Model\BulkManagement" /> From 4aadde49fa78181763ea38f921a808614635a8bc Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Thu, 12 Mar 2020 11:21:50 +0200 Subject: [PATCH 337/734] Fix statict --- .../AsynchronousOperations/Model/OperationManagement.php | 2 +- .../Model/ResourceModel/Operation.php | 2 +- .../Model/ResourceModel/Operation/OperationRepository.php | 8 +++++--- .../Model/SaveMultipleOperations.php | 1 - 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php index a95e2eaa909d6..74740cba9a6d8 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php @@ -10,7 +10,7 @@ use Magento\Framework\EntityManager\EntityManager; /** - * Class OperationManagement + * Class for managing Bulk Operations */ class OperationManagement implements \Magento\Framework\Bulk\OperationManagementInterface { diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php index a7343001bc441..0eaa5315af614 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php @@ -7,7 +7,7 @@ namespace Magento\AsynchronousOperations\Model\ResourceModel; /** - * Class Operation + * Resource class for Bulk Operations */ class Operation extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php index b54a09a83c34d..e5b282910cf8c 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php @@ -67,9 +67,11 @@ public function __construct( } /** - * @param $topicName - * @param $entityParams - * @param $groupId + * Creating topic for publishing + * + * @param string $topicName + * @param mixed $entityParams + * @param string $groupId * @return mixed */ public function createByTopic($topicName, $entityParams, $groupId) diff --git a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php index 4536e1b54f8ca..a2599b35a8723 100644 --- a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php +++ b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php @@ -50,5 +50,4 @@ public function execute(array $operations): void throw new CouldNotSaveException(__($exception->getMessage())); } } - } From a224e644edb335d1ae454c25bd447d0b9d7f0e7c Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@adobe.com> Date: Thu, 12 Mar 2020 08:46:15 -0500 Subject: [PATCH 338/734] MQE-683: [Deprecation] Only use more nested assertion syntax - Applied update script --- ...minAttributeTextSwatchesCanBeFiledTest.xml | 8 +- ...AndDeveloperConfigInProductionModeTest.xml | 4 +- .../Test/AdminDashboardWithChartsChart.xml | 5 +- .../BraintreeCreditCardOnCheckoutTest.xml | 10 +- .../StorefrontAddBundleOptionsToCartTest.xml | 5 +- .../Mftf/Test/StorefrontAdminEditDataTest.xml | 5 +- .../Test/StorefrontEditBundleProductTest.xml | 5 +- ...eckCustomizableOptionImportActionGroup.xml | 15 ++- .../CheckProductsOrderActionGroup.xml | 10 +- ...ctSpecialPriceOnProductPageActionGroup.xml | 5 +- .../AddOutOfStockProductToCompareListTest.xml | 10 +- .../AdminCloneProductWithDuplicateUrlTest.xml | 20 ++- .../Mftf/Test/AdminCreateCategoryTest.xml | 5 +- ...roductsImageInCaseOfMultipleStoresTest.xml | 10 +- ...edFieldsHaveRequiredFieldIndicatorTest.xml | 25 +++- ...eForProductOptionsWithoutTierPriceTest.xml | 5 +- .../Test/Mftf/Test/EndToEndB2CAdminTest.xml | 40 ++++-- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 90 ++++++++++--- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 45 +++++-- ...goryHighlightedAndProductDisplayedTest.xml | 20 ++- ...heckDefaultNumberProductsToDisplayTest.xml | 12 +- ...ceForDifferentTimezonesForWebsitesTest.xml | 5 +- ...talogPriceRuleWithConditionActionGroup.xml | 5 +- ...tributeIsUndefinedCatalogPriceRuleTest.xml | 20 ++- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 40 ++++-- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 20 ++- ...ltBillingAndShippingAddressActionGroup.xml | 5 +- ...refrontCheckoutCheckProductActionGroup.xml | 5 +- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 110 ++++++++++++---- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 55 ++++++-- .../OnePageCheckoutUsingSignInLinkTest.xml | 5 +- ...OnePageCheckoutWithAllProductTypesTest.xml | 15 ++- ...ateShoppingCartWhileUpdateMinicartTest.xml | 10 +- ...ntOnePageCheckoutDataWhenChangeQtyTest.xml | 45 +++++-- ...tOnCheckoutPageDifferentStoreViewsTest.xml | 30 ++++- ...ConfigurableProductUpdateAttributeTest.xml | 5 +- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 110 ++++++++++++---- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 55 ++++++-- ...minCurrencySymbolIsDisabledActionGroup.xml | 5 +- ...opUpPasswordAutoCompleteOffActionGroup.xml | 4 +- ...rontPasswordAutocompleteOffActionGroup.xml | 4 +- ...aultBillingShippingCustomerAddressTest.xml | 8 +- ...aultBillingShippingCustomerAddressTest.xml | 8 +- ...inSetCustomerDefaultBillingAddressTest.xml | 4 +- ...nSetCustomerDefaultShippingAddressTest.xml | 4 +- ...CountriesRestrictionApplyOnBackendTest.xml | 5 +- .../VerifyDisabledCustomerGroupFieldTest.xml | 4 +- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 70 ++++++++-- .../AdminElasticConnectionTestActionGroup.xml | 5 +- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 85 ++++++++++--- .../Test/AdminCreatingShippingLabelTest.xml | 10 +- ...hMapAssignedConfigProductIsCorrectTest.xml | 20 ++- .../CancelOrdersInOrderSalesReportTest.xml | 5 +- ...rtAdminCreditMemoGrandTotalActionGroup.xml | 5 +- ...rifyCreatedOrderInformationActionGroup.xml | 4 +- ...ectnessInvoicedItemInBundleProductTest.xml | 5 +- ...reateCreditMemoBankTransferPaymentTest.xml | 8 +- ...AdminCreateCreditMemoPartialRefundTest.xml | 8 +- ...CreateCreditMemoWithCashOnDeliveryTest.xml | 8 +- ...nCreateCreditMemoWithPurchaseOrderTest.xml | 8 +- ...minCreateOrderWithDateTimeOptionUITest.xml | 5 +- ...reateOrderWithMinimumAmountEnabledTest.xml | 4 +- ...nMassOrdersCancelCompleteAndClosedTest.xml | 8 +- ...assOrdersCancelProcessingAndClosedTest.xml | 8 +- .../AdminMassOrdersHoldOnCompleteTest.xml | 4 +- ...ssOrdersHoldOnPendingAndProcessingTest.xml | 8 +- ...AdminMassOrdersReleasePendingOrderTest.xml | 4 +- ...MassOrdersUpdateCancelPendingOrderTest.xml | 4 +- .../AdminOrdersReleaseInUnholdStatusTest.xml | 4 +- ...editMemoTotalAfterShippingDiscountTest.xml | 20 ++- .../Test/Mftf/Test/EndToEndB2CAdminTest.xml | 9 +- .../Test/StorefrontPrintOrderGuestTest.xml | 12 +- ...eatedShipmentInShipmentsTabActionGroup.xml | 4 +- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 105 ++++++++++++--- ...tImageColorWhenFilterByColorFilterTest.xml | 10 +- .../AdminTaxCalcWithApplyTaxOnSettingTest.xml | 10 +- ...StorefrontCheckElementColorActionGroup.xml | 5 +- ...lementInTranslateInlineModeActionGroup.xml | 15 ++- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 120 ++++++++++++++---- .../Mftf/Test/DefaultConfigForUPSTypeTest.xml | 5 +- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 95 +++++++++++--- .../Test/EndToEndB2CGuestUserTest.xml | 20 ++- .../Test/EndToEndB2CLoggedInUserTest.xml | 10 +- 83 files changed, 1328 insertions(+), 342 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml index 32201e03f92ec..851d2447dfc15 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml @@ -110,7 +110,11 @@ <grabValueFrom selector="{{AttributeManageSwatchSection.descriptionField('Admin')}}" stepKey="grabDescriptionForAdmin"/> <!-- Check that Swatch and Description fields for Admin are not empty--> - <assertNotEmpty actual="$grabSwatchForAdmin" stepKey="checkSwatchFieldForAdmin"/> - <assertNotEmpty actual="$grabDescriptionForAdmin" stepKey="checkDescriptionFieldForAdmin"/> + <assertNotEmpty stepKey="checkSwatchFieldForAdmin"> + <actualResult type="const">$grabSwatchForAdmin</actualResult> + </assertNotEmpty> + <assertNotEmpty stepKey="checkDescriptionFieldForAdmin"> + <actualResult type="const">$grabDescriptionForAdmin</actualResult> + </assertNotEmpty> </test> </tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml index ae7722b225cdd..16bde998b9fb3 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml @@ -28,7 +28,9 @@ <actionGroup ref="AdminOpenStoreConfigPageActionGroup" stepKey="openStoreConfigPage" /> <scrollTo selector="{{LocaleOptionsSection.sectionHeader}}" stepKey="scrollToLocaleSection" x="0" y="-80" /> <conditionalClick selector="{{LocaleOptionsSection.sectionHeader}}" dependentSelector="{{LocaleOptionsSection.timezone}}" visible="false" stepKey="openLocaleSection"/> - <assertElementContainsAttribute selector="{{LocaleOptionsSection.locale}}" attribute="disabled" stepKey="seeDisabledLocaleDropdown" /> + <assertElementContainsAttribute stepKey="seeDisabledLocaleDropdown"> + <expectedResult selector="{{LocaleOptionsSection.locale}}" attribute="disabled" type="string"></expectedResult> + </assertElementContainsAttribute> <!-- Go to the developer configuration and make sure the redirect to the configuration page takes place --> <actionGroup ref="AdminOpenStoreConfigDeveloperPageActionGroup" stepKey="goToDeveloperConfigPage"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index e82d54280d4e1..84e6b59e01504 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -120,6 +120,9 @@ <seeElement selector="{{AdminDashboardSection.dashboardDiagramAmountsContentTab}}" stepKey="seeDiagramAmountContent"/> <seeElement selector="{{AdminDashboardSection.dashboardDiagramTotals}}" stepKey="seeAmountTotals"/> <dontSeeJsError stepKey="dontSeeJsError"/> - <assertGreaterThan expected="$grabStartQuantity" actual="$grabEndQuantity" stepKey="checkQuantityWasChanged"/> + <assertGreaterThan stepKey="checkQuantityWasChanged"> + <actualResult type="const">$grabEndQuantity</actualResult> + <expectedResult type="const">$grabStartQuantity</expectedResult> + </assertGreaterThan> </test> </tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index 9a1110bfda29a..0747e785c4ae4 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -105,7 +105,13 @@ <grabTextFrom selector="{{CheckoutOrderSummarySection.additionalAddress}}" stepKey="additionalAddress"/> <see userInput="Shipping Address" stepKey="seeShippingAddress"/> <see userInput="Billing Address" stepKey="seeBillingAddress"/> - <assertEquals stepKey="assertValuesAreEqual" actual="$billingAddr" expected="$shippingAddr"/> - <assertNotEquals stepKey="assertValuesAreNotEqual" actual="$billingAddr" expected="$additionalAddress"/> + <assertEquals stepKey="assertValuesAreEqual"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$shippingAddr</expectedResult> + </assertEquals> + <assertNotEquals stepKey="assertValuesAreNotEqual"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$additionalAddress</expectedResult> + </assertNotEquals> </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index 38926ccfbb7d6..296ba31baa589 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -108,7 +108,10 @@ <!--Assert Bundle Product Price--> <grabTextFrom selector="{{StorefrontBundledSection.bundleProductsPrice}}" stepKey="grabProductsPrice"/> - <assertEquals expected='$123.00' expectedType="string" actual="$grabProductsPrice" message="ExpectedPrice" stepKey="assertBundleProductPrice"/> + <assertEquals message="ExpectedPrice" stepKey="assertBundleProductPrice"> + <actualResult type="const">$grabProductsPrice</actualResult> + <expectedResult type="string">$123.00</expectedResult> + </assertEquals> <!--Chose all products from 1st & 3rd options --> <click stepKey="selectProduct1" selector="{{StorefrontBundledSection.productCheckbox('1','1')}}"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index 05100284a3fe9..22b746ce69046 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -103,7 +103,10 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart1"/> <waitForPageLoad stepKey="waitForCartPageLoad1"/> <grabTextFrom selector="{{CheckoutCartProductSection.nthBundleOptionName('1')}}" stepKey="grabTotalAfter"/> - <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$grabTotalAfter}</actualResult> + <expectedResult type="string">{$grabTotalBefore}</expectedResult> + </assertNotEquals> <!-- Delete the bundled product --> <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index 3a40a1b7eeb71..fe6aa4ff1d928 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -115,7 +115,10 @@ <waitForElementVisible stepKey="waitForInfoDropdown2" selector="{{CheckoutCartSummarySection.total}}"/> <waitForPageLoad stepKey="waitForCartPageLoad4"/> <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabTotalAfter"/> - <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$grabTotalAfter}</actualResult> + <expectedResult type="string">{$grabTotalBefore}</expectedResult> + </assertNotEquals> <!-- Delete the bundled product --> <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml index efd0986efca06..d2d03600afbbd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml @@ -20,8 +20,17 @@ <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> - <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> + <assertEquals stepKey="assertOptionTitle"> + <actualResult type="const">$grabOptionTitle</actualResult> + <expectedResult type="string">{{option.title}}</expectedResult> + </assertEquals> + <assertEquals stepKey="assertOptionPrice"> + <actualResult type="const">$grabOptionPrice</actualResult> + <expectedResult type="string">{{option.price}}</expectedResult> + </assertEquals> + <assertEquals stepKey="assertOptionSku"> + <actualResult type="const">$grabOptionSku</actualResult> + <expectedResult type="string">{{option.title}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml index 6f58299fe1446..d804958caad44 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml @@ -25,8 +25,14 @@ <waitForPageLoad stepKey="waitForPageLoad5AfterCacheCleared" time="60"/> <waitForElement selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" time="120" stepKey="waitCompareWidgetLoad" /> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" userInput="alt" stepKey="grabFirstProductName1_1"/> - <assertEquals expected="{{product_1.name}}" actual="($grabFirstProductName1_1)" message="notExpectedOrder" stepKey="compare1"/> + <assertEquals message="notExpectedOrder" stepKey="compare1"> + <actualResult type="const">($grabFirstProductName1_1)</actualResult> + <expectedResult type="const">{{product_1.name}}</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('2')}}" userInput="alt" stepKey="grabFirstProductName2_2"/> - <assertEquals expected="{{product_2.name}}" actual="($grabFirstProductName2_2)" message="notExpectedOrder" stepKey="compare2"/> + <assertEquals message="notExpectedOrder" stepKey="compare2"> + <actualResult type="const">($grabFirstProductName2_2)</actualResult> + <expectedResult type="const">{{product_2.name}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml index cb2753fa64fc6..39184c1c37cc9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml @@ -20,6 +20,9 @@ <waitForPageLoad stepKey="waitForFirstProductPage"/> <waitForElementVisible selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="waitForProductSpecialPrice"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="grabProductSpecialPrice"/> - <assertEquals actual="$grabProductSpecialPrice" expectedType="string" expected="{{specialPrice}}" stepKey="assertProductPriceValuesAreEqual"/> + <assertEquals stepKey="assertProductPriceValuesAreEqual"> + <actualResult type="const">$grabProductSpecialPrice</actualResult> + <expectedResult type="string">{{specialPrice}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index b34e73a7ede9f..777bbcbcf37aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -61,7 +61,10 @@ <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg"/> <grabTextFrom selector="{{StorefrontMessagesSection.success}}" stepKey="grabTextFromSuccessMessage"/> - <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage"/> + <assertEquals stepKey="assertSuccessMessage"> + <actualResult type="const">($grabTextFromSuccessMessage)</actualResult> + <expectedResult type="string">You added product $$product.name$$ to the comparison list.</expectedResult> + </assertEquals> <!--See product in the comparison list--> <comment userInput="See product in the comparison list" stepKey="seeProductInComparisonList"/> <amOnPage url="{{StorefrontProductComparePage.url}}" stepKey="navigateToComparePage"/> @@ -83,7 +86,10 @@ <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg2"/> <grabTextFrom selector="{{StorefrontMessagesSection.success}}" stepKey="grabTextFromSuccessMessage2"/> - <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage2"/> + <assertEquals stepKey="assertSuccessMessage2"> + <actualResult type="const">($grabTextFromSuccessMessage)</actualResult> + <expectedResult type="string">You added product $$product.name$$ to the comparison list.</expectedResult> + </assertEquals> <!--Check that product displays on add to compare widget--> <comment userInput="Check that product displays on add to compare widget" stepKey="checkProdNameOnWidget"/> <seeElement selector="{{StorefrontComparisonSidebarSection.ProductTitleByName($$product.name$$)}}" stepKey="seeProdNameOnCmpWidget"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml index a213f2af900cf..adda0e017cbec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml @@ -41,8 +41,14 @@ <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductFormFirstTime"/> <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSEOSection"/> <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabDuplicatedProductUrlKey"/> - <assertContains expected="$$createSimpleProduct.custom_attributes[url_key]$$" actual="$grabDuplicatedProductUrlKey" stepKey="assertDuplicatedProductUrlKey"/> - <assertContains expectedType="string" expected="-1" actual="$grabDuplicatedProductUrlKey" stepKey="assertDuplicatedProductUrlKey1"/> + <assertContains stepKey="assertDuplicatedProductUrlKey"> + <actualResult type="const">$grabDuplicatedProductUrlKey</actualResult> + <expectedResult type="const">$$createSimpleProduct.custom_attributes[url_key]$$</expectedResult> + </assertContains> + <assertContains stepKey="assertDuplicatedProductUrlKey1"> + <actualResult type="const">$grabDuplicatedProductUrlKey</actualResult> + <expectedResult type="string">-1</expectedResult> + </assertContains> <!--Add duplicated product to the simple product--> <comment userInput="Add duplicated product to the simple product" stepKey="commentAddProduct"/> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToSimpleProductPage"/> @@ -69,8 +75,14 @@ <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openProductSEOSection"/> <waitForElementVisible selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="waitForUrlKeyField"/> <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabSecondDuplicatedProductUrlKey"/> - <assertContains expected="$$createSimpleProduct.custom_attributes[url_key]$$" actual="$grabSecondDuplicatedProductUrlKey" stepKey="assertSecondDuplicatedProductUrlKey"/> - <assertContains expectedType="string" expected="-2" actual="$grabSecondDuplicatedProductUrlKey" stepKey="assertSecondDuplicatedProductUrlKey1"/> + <assertContains stepKey="assertSecondDuplicatedProductUrlKey"> + <actualResult type="const">$grabSecondDuplicatedProductUrlKey</actualResult> + <expectedResult type="const">$$createSimpleProduct.custom_attributes[url_key]$$</expectedResult> + </assertContains> + <assertContains stepKey="assertSecondDuplicatedProductUrlKey1"> + <actualResult type="const">$grabSecondDuplicatedProductUrlKey</actualResult> + <expectedResult type="string">-2</expectedResult> + </assertContains> <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" visible="false" stepKey="openProductRUSSection1"/> <waitForElementVisible selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('related')}}" stepKey="waitForSelectedProductSku"/> <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('related')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeRelatedProductForDuplicated"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml index 44a83f2dbadd4..72a44cb231b46 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml @@ -98,6 +98,9 @@ <!-- Verify that the Layered navigation price step field has the required indicator --> <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <actualResult type="variable">getRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml index 9ed0a8104faa1..bbeddf43aecff 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml @@ -174,13 +174,19 @@ <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.name$$)}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad0"/> <grabAttributeFrom userInput="src" selector="{{StorefrontCategoryMainSection.mediaDescription($$createProduct.name$$)}}" stepKey="grabAttributeFromImage"/> - <assertContains expectedType="string" expected="{{ProductImage.filename}}" actual="$grabAttributeFromImage" stepKey="assertProductImageAbsence"/> + <assertContains stepKey="assertProductImageAbsence"> + <actualResult type="const">$grabAttributeFromImage</actualResult> + <expectedResult type="string">{{ProductImage.filename}}</expectedResult> + </assertContains> <!--Open Storefront on newly created store view and assert image absence--> <amOnPage url="$grabStoreViewCode" stepKey="navigateToHomePageOfSpecificStore"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSubCategory.name$$)}}" stepKey="clickCategory"/> <waitForPageLoad stepKey="waitForCategoryPageLoad1"/> <grabAttributeFrom userInput="src" selector="{{StorefrontCategoryMainSection.mediaDescription($$createProduct.name$$)}}" stepKey="grabAttributeFromImage2"/> - <assertContains expectedType="string" expected="small_image" actual="$grabAttributeFromImage2" stepKey="assertProductImageAbsence2"/> + <assertContains stepKey="assertProductImageAbsence2"> + <actualResult type="const">$grabAttributeFromImage2</actualResult> + <expectedResult type="string">small_image</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml index 45c2c9d379033..d2822a53b1a73 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml @@ -28,10 +28,16 @@ <!-- Verify that the Category Name field has the required field name indicator --> <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicator}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <actualResult type="variable">getRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicatorColor}}" stepKey="getRequiredFieldIndicatorColor"/> - <assertEquals expected="rgb(226, 38, 38)" expectedType="string" actualType="variable" actual="getRequiredFieldIndicatorColor" message="pass" stepKey="assertRequiredFieldIndicator2"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator2"> + <actualResult type="variable">getRequiredFieldIndicatorColor</actualResult> + <expectedResult type="string">rgb(226, 38, 38)</expectedResult> + </assertEquals> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPage"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="addProductDropdown"/> @@ -39,16 +45,25 @@ <!-- Verify that the Product Name and Sku fields have the required field name indicator --> <executeJS function="{{AdminProductFormSection.RequiredNameIndicator}}" stepKey="productNameRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productNameRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator3"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator3"> + <actualResult type="variable">productNameRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <executeJS function="{{AdminProductFormSection.RequiredSkuIndicator}}" stepKey="productSkuRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productSkuRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator4"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator4"> + <actualResult type="variable">productSkuRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <!-- Verify that the CMS page have the required field name indicator next to Page Title --> <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPage"/> <executeJS function="{{CmsNewPagePageBasicFieldsSection.RequiredFieldIndicator}}" stepKey="pageTitleRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="pageTitleRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator5"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator5"> + <actualResult type="variable">pageTitleRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index b451c06176bc3..0ce506fe1918d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -93,6 +93,9 @@ <waitForPageLoad stepKey="waitForStoreFrontLoad"/> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> - <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + <assertNotContains stepKey="assertNotEquals"> + <actualResult type="const">$grabGrabPriceClass</actualResult> + <expectedResult type="string">.price-box .price-tier_price</expectedResult> + </assertNotContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 0400de5227cf3..43208f336dbcd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -48,7 +48,10 @@ <argument name="product" value="SimpleProduct"/> </actionGroup> <grabAttributeFrom selector="{{AdminProductGridSection.productThumbnail('1')}}" userInput="src" stepKey="getSimpleProductThumbnail"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$getSimpleProductThumbnail" stepKey="simpleThumbnailIsNotDefault"/> + <assertNotRegExp stepKey="simpleThumbnailIsNotDefault"> + <actualResult type="const">$getSimpleProductThumbnail</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="seeSimpleProductInGrid"> <argument name="product" value="SimpleProduct"/> </actionGroup> @@ -114,9 +117,18 @@ <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getMinimumPriceInGrid"/> <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortDescForFilter"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getMaximumPriceInGrid"/> - <assertRegExp expected="'/\$[0-9]{2}\.[0-9]{2}/'" actual="$getMinimumPriceInGrid" stepKey="assertMinimumPriceIsCorrect"/> - <assertRegExp expected="'/\$[0-9]{2}\.[0-9]{2}/'" actual="$getMaximumPriceInGrid" stepKey="assertMaximumPriceIsCorrect"/> - <assertLessThan expected="$getMaximumPriceInGrid" actual="$getMinimumPriceInGrid" stepKey="checkPriceSortCorrect"/> + <assertRegExp stepKey="assertMinimumPriceIsCorrect"> + <actualResult type="const">$getMinimumPriceInGrid</actualResult> + <expectedResult type="const">'/\$[0-9]{2}\.[0-9]{2}/'</expectedResult> + </assertRegExp> + <assertRegExp stepKey="assertMaximumPriceIsCorrect"> + <actualResult type="const">$getMaximumPriceInGrid</actualResult> + <expectedResult type="const">'/\$[0-9]{2}\.[0-9]{2}/'</expectedResult> + </assertRegExp> + <assertLessThan stepKey="checkPriceSortCorrect"> + <actualResult type="const">$getMinimumPriceInGrid</actualResult> + <expectedResult type="const">$getMaximumPriceInGrid</expectedResult> + </assertLessThan> <!--Filter by status--> <actionGroup ref="FilterProductGridByEnabledStatusActionGroup" stepKey="filterGridByEnabledProducts"/> <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" parameterArray="[1,20]" stepKey="seeEnabledProductsNotEmpty"/> @@ -135,23 +147,35 @@ <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getFirstPriceSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'Price')}}" stepKey="getSecondPriceSortAsc"/> - <assertLessThanOrEqual expected="$getSecondPriceSortAsc" actual="$getFirstPriceSortAsc" stepKey="checkPriceAscSortCorrect"/> + <assertLessThanOrEqual stepKey="checkPriceAscSortCorrect"> + <actualResult type="const">$getFirstPriceSortAsc</actualResult> + <expectedResult type="const">$getSecondPriceSortAsc</expectedResult> + </assertLessThanOrEqual> <!--Sort Descending--> <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getFirstPriceSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'Price')}}" stepKey="getSecondPriceSortDesc"/> - <assertGreaterThanOrEqual expected="$getSecondPriceSortDesc" actual="$getFirstPriceSortDesc" stepKey="checkPriceDescSortCorrect"/> + <assertGreaterThanOrEqual stepKey="checkPriceDescSortCorrect"> + <actualResult type="const">$getFirstPriceSortDesc</actualResult> + <expectedResult type="const">$getSecondPriceSortDesc</expectedResult> + </assertGreaterThanOrEqual> <!--By Product ID--> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultSortingId"/> <!--Sort Ascending--> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'ID')}}" stepKey="getFirstProductIdSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'ID')}}" stepKey="getSecondProductIdSortAsc"/> - <assertLessThan expected="$getSecondProductIdSortAsc" actual="$getFirstProductIdSortAsc" stepKey="checkProductIdAscSortCorrect"/> + <assertLessThan stepKey="checkProductIdAscSortCorrect"> + <actualResult type="const">$getFirstProductIdSortAsc</actualResult> + <expectedResult type="const">$getSecondProductIdSortAsc</expectedResult> + </assertLessThan> <!--Sort Descending--> <click selector="{{AdminProductGridSection.columnHeader('ID')}}" stepKey="clickIdHeaderToSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'ID')}}" stepKey="getFirstProductIdSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'ID')}}" stepKey="getSecondProductIdSortDesc"/> - <assertGreaterThan expected="$getSecondProductIdSortDesc" actual="$getFirstProductIdSortDesc" stepKey="checkProductIdDescSortCorrect"/> + <assertGreaterThan stepKey="checkProductIdDescSortCorrect"> + <actualResult type="const">$getFirstProductIdSortDesc</actualResult> + <expectedResult type="const">$getSecondProductIdSortDesc</expectedResult> + </assertGreaterThan> <!--Adding column works--> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultColumns"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index f7ebb090124d6..a89f49b3db346 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -81,7 +81,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> @@ -89,7 +92,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 1 --> <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> @@ -100,7 +106,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 2 --> <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> @@ -112,7 +121,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> <!-- Step 4: User compares products --> @@ -130,7 +142,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> @@ -138,7 +153,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> @@ -156,7 +174,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -182,7 +203,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product2 on comparison page --> <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> @@ -190,7 +214,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Clear comparison sidebar --> <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> @@ -274,7 +301,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> @@ -282,7 +312,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 1 --> <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> @@ -293,7 +326,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 2 --> <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> @@ -305,7 +341,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> <!-- Step 4: User compares products --> @@ -323,7 +362,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> @@ -331,7 +373,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> @@ -349,7 +394,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -375,7 +423,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product2 on comparison page --> <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> @@ -383,7 +434,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Clear comparison sidebar --> <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 74c6da1c47f60..4eb5b843eeb35 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -66,7 +66,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc" after="browseAssertCategoryProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault" after="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault" after="browseGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" after="browseAssertSimpleProduct1ImageNotDefault"/> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2" after="commentCheckSimpleProduct2InCategory"> @@ -74,7 +77,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc" after="browseAssertCategoryProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault" after="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault" after="browseGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 1 --> <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> @@ -85,7 +91,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc" after="browseAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault" after="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault" after="browseGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 2 --> <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" after="browseAssertSimpleProduct1PageImageNotDefault"/> @@ -97,7 +106,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc" after="browseAssertProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault" after="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault" after="browseGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> <!-- Step 4: User compares products --> @@ -115,7 +127,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc" after="compareAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault" after="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault" after="compareGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1" after="compareAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" after="compareClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page" after="waitForCompareSimpleProduct1loaded"> @@ -123,7 +138,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc" after="compareAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault" after="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault" after="compareGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare" after="compareAssertSimpleProduct2PageImageNotDefault"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> @@ -141,7 +159,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc" after="compareAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault" after="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault" after="compareGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare" after="compareAssertSimpleProduct2ImageNotDefault"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -166,14 +187,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison" after="compareAssertSimpleProduct1InComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison" after="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison" after="compareGrabSimpleProduct1ImageSrcInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product2 on comparison page --> <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison" after="compareAssertSimpleProduct1ImageNotDefaultInComparison"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison" after="compareAssertSimpleProduct2InComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison" after="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison" after="compareGrabSimpleProduct2ImageSrcInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Clear comparison sidebar --> <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml index b8a9a9cb3e0e6..00f9608d07c3e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml @@ -60,9 +60,15 @@ <!--Check if current category is highlighted and the others are not--> <comment userInput="Check if current category is highlighted and the others are not" stepKey="checkCateg1NameIsHighlighted"/> <grabAttributeFrom selector="{{AdminCategorySidebarTreeSection.categoryHighlighted($$category1.name$$)}}" userInput="class" stepKey="grabCategory1Class"/> - <assertContains expectedType="string" expected="active" actual="$grabCategory1Class" stepKey="assertCategory1IsHighlighted"/> + <assertContains stepKey="assertCategory1IsHighlighted"> + <actualResult type="const">$grabCategory1Class</actualResult> + <expectedResult type="string">active</expectedResult> + </assertContains> <executeJS function="return document.querySelectorAll('{{AdminCategorySidebarTreeSection.categoryNotHighlighted}}').length" stepKey="highlightedAmount"/> - <assertEquals expectedType="int" expected="1" actual="$highlightedAmount" stepKey="assertRestCategories1IsNotHighlighted"/> + <assertEquals stepKey="assertRestCategories1IsNotHighlighted"> + <actualResult type="const">$highlightedAmount</actualResult> + <expectedResult type="int">1</expectedResult> + </assertEquals> <!--See products in the category page--> <comment userInput="See products in the category page" stepKey="seeProductsInCategoryPage"/> <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($product1.name$)}}" stepKey="seeProduct1InCategoryPage"/> @@ -74,9 +80,15 @@ <!--Check if current category is highlighted and the others are not--> <comment userInput="Check if current category is highlighted and the others are not" stepKey="checkCateg2NameIsHighlighted"/> <grabAttributeFrom selector="{{AdminCategorySidebarTreeSection.categoryHighlighted($$category2.name$$)}}" userInput="class" stepKey="grabCategory2Class"/> - <assertContains expectedType="string" expected="active" actual="$grabCategory2Class" stepKey="assertCategory2IsHighlighted"/> + <assertContains stepKey="assertCategory2IsHighlighted"> + <actualResult type="const">$grabCategory2Class</actualResult> + <expectedResult type="string">active</expectedResult> + </assertContains> <executeJS function="return document.querySelectorAll('{{AdminCategorySidebarTreeSection.categoryNotHighlighted}}').length" stepKey="highlightedAmount2"/> - <assertEquals expectedType="int" expected="1" actual="$highlightedAmount2" stepKey="assertRestCategories1IsNotHighlighted2"/> + <assertEquals stepKey="assertRestCategories1IsNotHighlighted2"> + <actualResult type="const">$highlightedAmount2</actualResult> + <expectedResult type="int">1</expectedResult> + </assertEquals> <!--Assert products in second category page--> <comment userInput="Assert products in second category page" stepKey="commentAssertProducts"/> <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($product3.name$)}}" stepKey="seeProduct3InCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml index dc053bb990685..aacce53819178 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml @@ -197,10 +197,16 @@ <!-- 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" /> + <assertElementContainsAttribute stepKey="assertPerPageFirstValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">12</expectedResult> + </assertElementContainsAttribute> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageSecondValue" /> - <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="24" stepKey="assertPerPageSecondValue" /> + <assertElementContainsAttribute stepKey="assertPerPageSecondValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">24</expectedResult> + </assertElementContainsAttribute> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageThirdValue" /> - <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="36" stepKey="assertPerPageThirdValue" /> + <assertElementContainsAttribute stepKey="assertPerPageThirdValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">36</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 59f0b2f5dd76e..75ec0a4567b7d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -72,7 +72,10 @@ <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="grabSpecialPrice"/> - <assertEquals expected='$15.00' expectedType="string" actual="$grabSpecialPrice" stepKey="assertSpecialPrice"/> + <assertEquals stepKey="assertSpecialPrice"> + <actualResult type="const">$grabSpecialPrice</actualResult> + <expectedResult type="string">$15.00</expectedResult> + </assertEquals> <!--Reset timezone--> <amOnPage url="{{GeneralConfigurationPage.url}}" stepKey="goToGeneralConfigReset"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml index eebc1175f1894..9e21ea186f67b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml @@ -23,6 +23,9 @@ <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> <generateDate date="now" format="Y-m-d" stepKey="today" after="assertDate"/> <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> - <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> + <assertEquals stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"> + <actualResult type="const">$grabTextFromSelectedAttribute</actualResult> + <expectedResult type="const">$today</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml index 0df73c99595a6..fdd9b4788d315 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml @@ -89,13 +89,19 @@ <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToFirstProductPage"/> <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabFirstProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabFirstProductUpdatedPrice)" stepKey="assertFirstProductUpdatedPrice"/> + <assertEquals stepKey="assertFirstProductUpdatedPrice"> + <actualResult type="const">($grabFirstProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Check Catalog Price Rule for second product--> <amOnPage url="{{StorefrontProductPage.url($$createSecondProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSecondProductPage"/> <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabSecondProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabFirstProductUpdatedPrice)" stepKey="assertSecondProductUpdatedPrice"/> + <assertEquals stepKey="assertSecondProductUpdatedPrice"> + <actualResult type="const">($grabFirstProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Delete previous attribute and Catalog Price Rule--> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> @@ -132,12 +138,18 @@ <amOnPage url="{{StorefrontProductPage.url($$createThirdProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToThirdProductPage"/> <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabThirdProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabThirdProductUpdatedPrice)" stepKey="assertThirdProductUpdatedPrice"/> + <assertEquals stepKey="assertThirdProductUpdatedPrice"> + <actualResult type="const">($grabThirdProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Check Catalog Price Rule for forth product--> <amOnPage url="{{StorefrontProductPage.url($$createForthProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToForthProductPage"/> <waitForPageLoad stepKey="waitForForthProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabForthProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabForthProductUpdatedPrice)" stepKey="assertForthProductUpdatedPrice"/> + <assertEquals stepKey="assertForthProductUpdatedPrice"> + <actualResult type="const">($grabForthProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 57940e39e9281..e347803ae3feb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -28,7 +28,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> @@ -36,7 +39,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with common part of product names --> <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> @@ -56,14 +62,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Search simple product2 --> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with non-existent product name --> <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> @@ -94,7 +106,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> @@ -102,7 +117,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with common part of product names --> <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> @@ -122,14 +140,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Search simple product2 --> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with non-existent product name --> <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index cd3dec912a3c1..51d9973117c6f 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -28,7 +28,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> @@ -36,7 +39,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with common part of product names --> <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> @@ -56,14 +62,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Search simple product2 --> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with non-existent product name --> <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml index c91314f1c1bc2..88b517c1cbe25 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml @@ -20,6 +20,9 @@ <!--Make sure that shipping and billing addresses are different--> <see userInput="Shipping Address" stepKey="seeShippingAddress"/> <see userInput="Billing Address" stepKey="seeBillingAddress"/> - <assertEquals stepKey="assert" actual="$billingAddr" expected="$shippingAddr"/> + <assertEquals stepKey="assert"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$shippingAddr</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml index 520db653a5c59..b7fda2b8b0345 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml @@ -19,6 +19,9 @@ <see selector="{{CheckoutCartProductSection.productName}}" userInput="{{product.name}}" stepKey="seeProductName"/> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName(product.name)}}" stepKey="grabProductQty"/> - <assertEquals expected="{{cartItem.qty}}" actual="$grabProductQty" stepKey="assertQtyShoppingCart"/> + <assertEquals stepKey="assertQtyShoppingCart"> + <actualResult type="const">$grabProductQty</actualResult> + <expectedResult type="const">{{cartItem.qty}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 7002479279a78..93bb2acdcacfe 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -24,7 +24,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> @@ -32,7 +35,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct1$$"/> <argument name="productCount" value="1"/> @@ -51,7 +57,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> @@ -66,7 +75,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> @@ -74,7 +86,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -82,7 +97,10 @@ <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> @@ -90,7 +108,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check products in cart --> <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> @@ -111,7 +132,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> @@ -119,7 +143,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in cart --> <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> @@ -131,7 +158,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> @@ -139,7 +169,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> <!-- Step 6: Check out --> @@ -212,7 +245,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> @@ -220,7 +256,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct1$$"/> <argument name="productCount" value="1"/> @@ -239,7 +278,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> @@ -254,7 +296,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> @@ -262,7 +307,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -270,7 +318,10 @@ <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> @@ -278,7 +329,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check products in cart --> <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> @@ -299,7 +353,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> @@ -307,7 +364,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in cart --> <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> @@ -319,7 +379,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> @@ -327,7 +390,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> <!-- Step 6: Check out --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 6df859c9972c3..f47b536106400 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -24,7 +24,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> @@ -32,7 +35,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct1$$"/> <argument name="productCount" value="1"/> @@ -51,7 +57,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> @@ -66,7 +75,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> @@ -74,7 +86,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -82,7 +97,10 @@ <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> @@ -90,7 +108,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check products in cart --> <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> @@ -111,7 +132,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> @@ -119,7 +143,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in cart --> <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> @@ -131,7 +158,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> @@ -139,7 +169,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> <!-- Step 7: Check out --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml index 43ee1c8dd3de4..6b7feb485c812 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml @@ -88,6 +88,9 @@ <!-- Assert that shipping and billing address are the same --> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" stepKey="shippingAddress"/> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.billingAddress}}" stepKey="billingAddress"/> - <assertEquals stepKey="assertAddress" actual="$billingAddress" expected="$shippingAddress"/> + <assertEquals stepKey="assertAddress"> + <actualResult type="const">$billingAddress</actualResult> + <expectedResult type="const">$shippingAddress</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 2b2316b20396e..4e2d5ddbb17e9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -207,7 +207,10 @@ <!-- Assert that addresses on order page the same --> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" stepKey="shippingAddressOrderPage"/> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.billingAddress}}" stepKey="billingAddressOrderPage"/> - <assertEquals actual="$billingAddressOrderPage" expected="$shippingAddressOrderPage" stepKey="assertAddressOrderPage"/> + <assertEquals stepKey="assertAddressOrderPage"> + <actualResult type="const">$billingAddressOrderPage</actualResult> + <expectedResult type="const">$shippingAddressOrderPage</expectedResult> + </assertEquals> <!-- Assert order total --> <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="navigateToCustomerDashboardPage"/> @@ -222,7 +225,13 @@ <!-- Asserts that addresses in address book equal to addresses in order --> <grabTextFrom selector="{{CheckoutOrderSummarySection.shippingAddress}}" stepKey="shippingAddress"/> <grabTextFrom selector="{{CheckoutOrderSummarySection.billingAddress}}" stepKey="billingAddress"/> - <assertEquals actual="$shippingAddress" expected="$shippingAddressOrderPage" stepKey="assertShippingAddress"/> - <assertEquals actual="$billingAddress" expected="$billingAddressOrderPage" stepKey="assertBillingAddress"/> + <assertEquals stepKey="assertShippingAddress"> + <actualResult type="const">$shippingAddress</actualResult> + <expectedResult type="const">$shippingAddressOrderPage</expectedResult> + </assertEquals> + <assertEquals stepKey="assertBillingAddress"> + <actualResult type="const">$billingAddress</actualResult> + <expectedResult type="const">$billingAddressOrderPage</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml index f17716af8fd56..f7ae9d29ada55 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml @@ -39,7 +39,10 @@ <!--Go to Shopping cart and check Qty--> <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCart"/> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyShoppingCart"/> - <assertEquals expected="1" actual="$grabQtyShoppingCart" stepKey="assertQtyShoppingCart"/> + <assertEquals stepKey="assertQtyShoppingCart"> + <actualResult type="const">$grabQtyShoppingCart</actualResult> + <expectedResult type="const">1</expectedResult> + </assertEquals> <!--Open minicart and change Qty--> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> @@ -51,6 +54,9 @@ <!--Check Qty in shopping cart after updating--> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyShoppingCart1"/> - <assertEquals expected="5" actual="$grabQtyShoppingCart1" stepKey="assertQtyShoppingCart1"/> + <assertEquals stepKey="assertQtyShoppingCart1"> + <actualResult type="const">$grabQtyShoppingCart1</actualResult> + <expectedResult type="const">5</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index e335caa2ddb64..9b536882dc64b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -72,7 +72,10 @@ <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCart"/> <waitForAjaxLoad stepKey="waitForAjaxLoad"/> <grabValueFrom selector="{{CheckoutCartProductSection.qty($$createProduct.name$$)}}" stepKey="grabQty"/> - <assertEquals expected="2" actual="$grabQty" stepKey="assertQty"/> + <assertEquals stepKey="assertQty"> + <actualResult type="const">$grabQty</actualResult> + <expectedResult type="const">2</expectedResult> + </assertEquals> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <!--Check that form is filled with customer data--> @@ -85,13 +88,37 @@ <grabValueFrom selector="{{CheckoutShippingSection.postcode}}" stepKey="grabPostcode1"/> <grabValueFrom selector="{{CheckoutShippingSection.telephone}}" stepKey="grabTelephone1"/> - <assertEquals expected="$grabEmail" actual="$grabEmail1" stepKey="assertEmail"/> - <assertEquals expected="$grabFirstName" actual="$grabFirstName1" stepKey="assertFirstName"/> - <assertEquals expected="$grabLastName" actual="$grabLastName1" stepKey="assertLastName"/> - <assertEquals expected="$grabStreet" actual="$grabStreet1" stepKey="assertStreet"/> - <assertEquals expected="$grabCity" actual="$grabCity1" stepKey="assertCity"/> - <assertEquals expected="$grabRegion" actual="$grabRegion1" stepKey="assertRegion"/> - <assertEquals expected="$grabPostcode" actual="$grabPostcode1" stepKey="assertPostcode"/> - <assertEquals expected="$grabTelephone" actual="$grabTelephone1" stepKey="assertTelephone"/> + <assertEquals stepKey="assertEmail"> + <actualResult type="const">$grabEmail1</actualResult> + <expectedResult type="const">$grabEmail</expectedResult> + </assertEquals> + <assertEquals stepKey="assertFirstName"> + <actualResult type="const">$grabFirstName1</actualResult> + <expectedResult type="const">$grabFirstName</expectedResult> + </assertEquals> + <assertEquals stepKey="assertLastName"> + <actualResult type="const">$grabLastName1</actualResult> + <expectedResult type="const">$grabLastName</expectedResult> + </assertEquals> + <assertEquals stepKey="assertStreet"> + <actualResult type="const">$grabStreet1</actualResult> + <expectedResult type="const">$grabStreet</expectedResult> + </assertEquals> + <assertEquals stepKey="assertCity"> + <actualResult type="const">$grabCity1</actualResult> + <expectedResult type="const">$grabCity</expectedResult> + </assertEquals> + <assertEquals stepKey="assertRegion"> + <actualResult type="const">$grabRegion1</actualResult> + <expectedResult type="const">$grabRegion</expectedResult> + </assertEquals> + <assertEquals stepKey="assertPostcode"> + <actualResult type="const">$grabPostcode1</actualResult> + <expectedResult type="const">$grabPostcode</expectedResult> + </assertEquals> + <assertEquals stepKey="assertTelephone"> + <actualResult type="const">$grabTelephone1</actualResult> + <expectedResult type="const">$grabTelephone</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 1fff7501f578d..ab67d3f8d989f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -69,22 +69,40 @@ <!--Check product name in Minicart--> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> <grabTextFrom selector="{{StorefrontMinicartSection.productName}}" stepKey="grabProductNameMinicart"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameMinicart" stepKey="assertProductNameMinicart"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameMinicart" stepKey="assertProductNameMinicart1"/> + <assertContains stepKey="assertProductNameMinicart"> + <actualResult type="const">$grabProductNameMinicart</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameMinicart1"> + <actualResult type="const">$grabProductNameMinicart</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> <!--Check product name in Shopping Cart page--> <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="clickViewAndEdit"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <grabTextFrom selector="{{CheckoutCartProductSection.productName}}" stepKey="grabProductNameCart"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameCart" stepKey="assertProductNameCart"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameCart" stepKey="assertProductNameCart1"/> + <assertContains stepKey="assertProductNameCart"> + <actualResult type="const">$grabProductNameCart</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameCart1"> + <actualResult type="const">$grabProductNameCart</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> <!--Proceed to checkout and check product name in Order Summary area--> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="proceedToCheckout"/> <waitForPageLoad stepKey="waitForShippingPageLoad"/> <click selector="{{CheckoutShippingGuestInfoSection.itemInCart}}" stepKey="clickItemInCart"/> <grabTextFrom selector="{{CheckoutShippingGuestInfoSection.productName}}" stepKey="grabProductNameShipping"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameShipping" stepKey="assertProductNameShipping"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameShipping" stepKey="assertProductNameShipping1"/> + <assertContains stepKey="assertProductNameShipping"> + <actualResult type="const">$grabProductNameShipping</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameShipping1"> + <actualResult type="const">$grabProductNameShipping</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml index 59cb7216ed264..752cc5e307f60 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml @@ -139,7 +139,10 @@ <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad2"/> <grabTextFrom stepKey="getCurrentOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> - <assertNotEquals expected="{$getBeforeOption}" expectedType="string" actual="{$getCurrentOption}" actualType="string" stepKey="assertNotEquals"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$getCurrentOption}</actualResult> + <expectedResult type="string">{$getBeforeOption}</expectedResult> + </assertNotEquals> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 1c99cd722cf86..af9f3df588351 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -94,7 +94,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Configurable Product --> <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> @@ -107,7 +110,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to cart --> <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> @@ -123,7 +129,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> @@ -132,7 +141,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> @@ -141,7 +153,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="productCount" value="3"/> @@ -155,7 +170,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> @@ -166,7 +184,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check configurable product in cart --> <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> @@ -179,7 +200,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> @@ -189,7 +213,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> @@ -199,7 +226,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> <argument name="productVar" value="$$createConfigProduct$$"/> </actionGroup> @@ -218,7 +248,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> </test> <test name="EndToEndB2CGuestUserMysqlTest"> <before> @@ -306,7 +339,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Configurable Product --> <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> @@ -319,7 +355,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to cart --> <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> @@ -335,7 +374,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> @@ -344,7 +386,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> @@ -353,7 +398,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="productCount" value="3"/> @@ -367,7 +415,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> @@ -378,7 +429,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check configurable product in cart --> <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> @@ -391,7 +445,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> @@ -401,7 +458,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> @@ -411,7 +471,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> <argument name="productVar" value="$$createConfigProduct$$"/> </actionGroup> @@ -430,6 +493,9 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index e1c031f11aadf..85c852d09e916 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -96,7 +96,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Configurable Product --> <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> @@ -109,7 +112,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to cart --> <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> @@ -125,7 +131,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> @@ -134,7 +143,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> @@ -143,7 +155,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="productCount" value="3"/> @@ -157,7 +172,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> @@ -168,7 +186,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check configurable product in cart --> <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> @@ -181,7 +202,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> @@ -191,7 +215,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> @@ -201,7 +228,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> <argument name="productVar" value="$$createConfigProduct$$"/> </actionGroup> @@ -220,6 +250,9 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> </test> </tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml index b8bcb2b0b9cb7..bc2314316d8bd 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml @@ -14,6 +14,9 @@ </arguments> <grabAttributeFrom selector="{{AdminCurrencySymbolsGridSection.currencyElement(currency)}}" userInput="disabled" stepKey="grabDisabledAttribute"/> - <assertEquals expected='true' expectedType="string" actual="$grabDisabledAttribute" stepKey="assertInputIsDisabled"/> + <assertEquals stepKey="assertInputIsDisabled"> + <actualResult type="const">$grabDisabledAttribute</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml index c5c9c0a6c9cf8..b29ff9ad3efeb 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml @@ -14,6 +14,8 @@ </annotations> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.password}}" stepKey="waitPasswordFieldVisible"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" expectedValue="off" stepKey="assertAuthorizationPopupPasswordAutocompleteOff"/> + <assertElementContainsAttribute stepKey="assertAuthorizationPopupPasswordAutocompleteOff"> + <expectedResult selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" type="string">off</expectedResult> + </assertElementContainsAttribute> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml index 1b2ec80f87340..6e54222d69b76 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml @@ -14,6 +14,8 @@ </annotations> <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> + <assertElementContainsAttribute stepKey="assertSignInPasswordAutocompleteOff"> + <expectedResult selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" type="string">off</expectedResult> + </assertElementContainsAttribute> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml index 5600b6088cfe5..d48838bc0f14f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml @@ -63,7 +63,11 @@ <see userInput="{{US_Address_TX.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsChanged"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> + <assertElementContainsAttribute stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml index 4f69a9bbfb695..7ec932ab750b9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml @@ -63,7 +63,11 @@ <see userInput="{{US_Address_TX.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsChanged"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> + <assertElementContainsAttribute stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml index b0de96782f0f5..96a614da0b379 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml @@ -52,6 +52,8 @@ <see userInput="{{US_Address_NY_Not_Default_Address.street[0]}}" selector="{{AdminCustomerAddressesDefaultBillingSection.addressDetails}}" stepKey="assertDefaultBillingAddressIsSet"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml index dfeb868959d54..ae26519d9618e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml @@ -52,6 +52,8 @@ <see userInput="{{US_Address_NY_Not_Default_Address.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsSet"/> <click selector="{{AdminCustomerAddressesDefaultShippingSection.editButton}}" stepKey="clickEditDefaultShippingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultShippingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml index 7cd69b4e17472..60caaf64f05b7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml @@ -114,6 +114,9 @@ <waitForPageLoad stepKey="waitForCustomersGrid"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomersGrid"/> <executeJS function="var len = document.querySelectorAll('{{AdminCustomerFiltersSection.countryOptions}}').length; return len-1;" stepKey="countriesAmount2"/> - <assertEquals expected="($countriesAmount)" actual="($countriesAmount2)" stepKey="assertCountryAmounts"/> + <assertEquals stepKey="assertCountryAmounts"> + <actualResult type="const">($countriesAmount2)</actualResult> + <expectedResult type="const">($countriesAmount)</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml index 0f98184aafb4f..e4605ba437772 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml @@ -33,6 +33,8 @@ <!-- 4. Perform all assertions --> <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> - <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> + <assertElementContainsAttribute stepKey="checkIfGroupNameIsDisabled"> + <expectedResult selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" type="string">true</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index f5e1e8ef0c8ec..cb6bdc18b1f9f 100644 --- a/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,32 +15,74 @@ <conditionalClick selector="{{AdminShippingMethodDHLSection.carriersDHLTab}}" dependentSelector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" visible="false" stepKey="expandDHLTab"/> <waitForElementVisible selector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" stepKey="waitDHLTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" userInput="disabled" stepKey="grabDHLActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLActiveDisabled" stepKey="assertDHLActiveDisabled"/> + <assertEquals stepKey="assertDHLActiveDisabled"> + <actualResult type="const">$grabDHLActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLTitle}}" userInput="disabled" stepKey="grabDHLTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLTitleDisabled" stepKey="assertDHLTitleDisabled"/> + <assertEquals stepKey="assertDHLTitleDisabled"> + <actualResult type="const">$grabDHLTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLAccessId}}" userInput="disabled" stepKey="grabDHLAccessIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLAccessIdDisabled" stepKey="assertDHLAccessIdDisabled"/> + <assertEquals stepKey="assertDHLAccessIdDisabled"> + <actualResult type="const">$grabDHLAccessIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLPassword}}" userInput="disabled" stepKey="grabDHLPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLPasswordDisabled" stepKey="assertDHLPasswordDisabled"/> + <assertEquals stepKey="assertDHLPasswordDisabled"> + <actualResult type="const">$grabDHLPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLAccount}}" userInput="disabled" stepKey="grabDHLAccountDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLAccountDisabled" stepKey="assertDHLAccountDisabled"/> + <assertEquals stepKey="assertDHLAccountDisabled"> + <actualResult type="const">$grabDHLAccountDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLContentType}}" userInput="disabled" stepKey="grabDHLContentTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLContentTypeDisabled" stepKey="assertDHLContentTypeDisabled"/> + <assertEquals stepKey="assertDHLContentTypeDisabled"> + <actualResult type="const">$grabDHLContentTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLHandlingType}}" userInput="disabled" stepKey="grabDHLHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLHandlingTypeDisabled" stepKey="assertDHLHandlingTypeDisabled"/> + <assertEquals stepKey="assertDHLHandlingTypeDisabled"> + <actualResult type="const">$grabDHLHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLHandlingAction}}" userInput="disabled" stepKey="grabDHLHandlingDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLHandlingDisabled" stepKey="assertDHLHandlingDisabled"/> + <assertEquals stepKey="assertDHLHandlingDisabled"> + <actualResult type="const">$grabDHLHandlingDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLDivideOrderWeight}}" userInput="disabled" stepKey="grabDHLDivideOrderWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLDivideOrderWeightDisabled" stepKey="assertDHLDivideOrderWeightDisabled"/> + <assertEquals stepKey="assertDHLDivideOrderWeightDisabled"> + <actualResult type="const">$grabDHLDivideOrderWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLUnitOfMeasure}}" userInput="disabled" stepKey="grabDHLUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLUnitOfMeasureDisabled" stepKey="assertDHLUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertDHLUnitOfMeasureDisabled"> + <actualResult type="const">$grabDHLUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSize}}" userInput="disabled" stepKey="grabDHLSizeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSizeDisabled" stepKey="assertDHLSizeDisabled"/> + <assertEquals stepKey="assertDHLSizeDisabled"> + <actualResult type="const">$grabDHLSizeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLNonDocAllowedMethod}}" userInput="disabled" stepKey="grabDHLNonDocAllowedMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLNonDocAllowedMethodDisabled" stepKey="assertDHLNonDocAllowedMethodDisabled"/> + <assertEquals stepKey="assertDHLNonDocAllowedMethodDisabled"> + <actualResult type="const">$grabDHLNonDocAllowedMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSmartPostHubId}}" userInput="disabled" stepKey="grabDHLSmartPostHubIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSmartPostHubIdDisabled" stepKey="assertDHLSmartPostHubIdDisabled"/> + <assertEquals stepKey="assertDHLSmartPostHubIdDisabled"> + <actualResult type="const">$grabDHLSmartPostHubIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSpecificErrMsg}}" userInput="disabled" stepKey="grabDHLSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSpecificErrMsgDisabled" stepKey="assertDHLSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertDHLSpecificErrMsgDisabled"> + <actualResult type="const">$grabDHLSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml index d00c1c59a0f8d..0c51142ac0aae 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml @@ -19,6 +19,9 @@ <click selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="clickOnTestConnectionButton"/> <waitForPageLoad stepKey="waitForConnectionEstablishment"/> <grabTextFrom selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" stepKey="grabConnectionStatus"/> - <assertEquals expected="{{AdminElasticsearch6TestConnectionMessageData.successMessage}}" expectedType="string" actual="$grabConnectionStatus" stepKey="assertThatConnectionSuccessful"/> + <assertEquals stepKey="assertThatConnectionSuccessful"> + <actualResult type="const">$grabConnectionStatus</actualResult> + <expectedResult type="string">{{AdminElasticsearch6TestConnectionMessageData.successMessage}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index f599d7ca223ae..c2678153d13f2 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,38 +15,89 @@ <conditionalClick selector="{{AdminShippingMethodFedExSection.carriersFedExTab}}" dependentSelector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" visible="false" stepKey="expandFedExTab"/> <waitForElementVisible selector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" stepKey="waitFedExTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" userInput="disabled" stepKey="grabFedExActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExActiveDisabled" stepKey="assertFedExActiveDisabled"/> + <assertEquals stepKey="assertFedExActiveDisabled"> + <actualResult type="const">$grabFedExActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExTitle}}" userInput="disabled" stepKey="grabFedExTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExTitleDisabled" stepKey="assertFedExTitleDisabled"/> + <assertEquals stepKey="assertFedExTitleDisabled"> + <actualResult type="const">$grabFedExTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExAccountId}}" userInput="disabled" stepKey="grabFedExAccountIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExAccountIdDisabled" stepKey="assertFedExAccountIdDisabled"/> + <assertEquals stepKey="assertFedExAccountIdDisabled"> + <actualResult type="const">$grabFedExAccountIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExMeterNumber}}" userInput="disabled" stepKey="grabFedExMeterNumberDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExMeterNumberDisabled" stepKey="assertFedExMeterNumberDisabled"/> + <assertEquals stepKey="assertFedExMeterNumberDisabled"> + <actualResult type="const">$grabFedExMeterNumberDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExKey}}" userInput="disabled" stepKey="grabFedExKeyDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExKeyDisabled" stepKey="assertFedExKeyDisabled"/> + <assertEquals stepKey="assertFedExKeyDisabled"> + <actualResult type="const">$grabFedExKeyDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExPassword}}" userInput="disabled" stepKey="grabFedExPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExPasswordDisabled" stepKey="assertFedExPasswordDisabled"/> + <assertEquals stepKey="assertFedExPasswordDisabled"> + <actualResult type="const">$grabFedExPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSandboxMode}}" userInput="disabled" stepKey="grabFedExSandboxDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSandboxDisabled" stepKey="assertFedExSandboxDisabled"/> + <assertEquals stepKey="assertFedExSandboxDisabled"> + <actualResult type="const">$grabFedExSandboxDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExShipmentRequestType}}" userInput="disabled" stepKey="grabFedExShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExShipmentRequestTypeDisabled" stepKey="assertFedExShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertFedExShipmentRequestTypeDisabled"> + <actualResult type="const">$grabFedExShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExPackaging}}" userInput="disabled" stepKey="grabFedExPackagingDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExPackagingDisabled" stepKey="assertFedExPackagingDisabled"/> + <assertEquals stepKey="assertFedExPackagingDisabled"> + <actualResult type="const">$grabFedExPackagingDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExDropoff}}" userInput="disabled" stepKey="grabFedExDropoffDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExDropoffDisabled" stepKey="assertFedExDropoffDisabled"/> + <assertEquals stepKey="assertFedExDropoffDisabled"> + <actualResult type="const">$grabFedExDropoffDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExUnitOfMeasure}}" userInput="disabled" stepKey="grabFedExUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExUnitOfMeasureDisabled" stepKey="assertFedExUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertFedExUnitOfMeasureDisabled"> + <actualResult type="const">$grabFedExUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExMaxPackageWeight}}" userInput="disabled" stepKey="grabFedExMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExMaxPackageWeightDisabled" stepKey="assertFedExMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertFedExMaxPackageWeightDisabled"> + <actualResult type="const">$grabFedExMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExHandlingType}}" userInput="disabled" stepKey="grabFedExHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExHandlingTypeDisabled" stepKey="assertFedExHandlingTypeDisabled"/> + <assertEquals stepKey="assertFedExHandlingTypeDisabled"> + <actualResult type="const">$grabFedExHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExHandlingAction}}" userInput="disabled" stepKey="grabFedExHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExHandlingActionDisabled" stepKey="assertFedExHandlingActionDisabled"/> + <assertEquals stepKey="assertFedExHandlingActionDisabled"> + <actualResult type="const">$grabFedExHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSpecificErrMsg}}" userInput="disabled" stepKey="grabFedExSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSpecificErrMsgDisabled" stepKey="assertFedExSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFedExSpecificErrMsgDisabled"> + <actualResult type="const">$grabFedExSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExAllowSpecific}}" userInput="disabled" stepKey="grabFedExAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExAllowSpecificDisabled" stepKey="assertFedExAllowSpecificDisabled"/> + <assertEquals stepKey="assertFedExAllowSpecificDisabled"> + <actualResult type="const">$grabFedExAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSpecificCountry}}" userInput="disabled" stepKey="grabFedExSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSpecificCountryDisabled" stepKey="assertFedExSpecificCountryDisabled"/> + <assertEquals stepKey="assertFedExSpecificCountryDisabled"> + <actualResult type="const">$grabFedExSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index c0b602e772b54..297333140b0c7 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -124,7 +124,13 @@ <seeElement selector="{{AdminShipmentTrackingInformationShippingSection.shippingNumber}}" stepKey="seeShippingNumberElement"/> <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethod}}" stepKey="grabShippingMethod"/> <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethodTitle}}" stepKey="grabShippingMethodTitle"/> - <assertEquals actual="$grabShippingMethod" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodIsFedEx"/> - <assertEquals actual="$grabShippingMethodTitle" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodTitleIsFedEx"/> + <assertEquals stepKey="assertShippingMethodIsFedEx"> + <actualResult type="const">$grabShippingMethod</actualResult> + <expectedResult type="string">Federal Express</expectedResult> + </assertEquals> + <assertEquals stepKey="assertShippingMethodTitleIsFedEx"> + <actualResult type="const">$grabShippingMethodTitle</actualResult> + <expectedResult type="string">Federal Express</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index 72443a41d67f4..58c717cc50fd8 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -134,28 +134,40 @@ <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToConfigProductPage"/> <waitForPageLoad stepKey="waitForLoadConfigProductPage"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabMapPrice"/> - <assertEquals expected='$66.00' expectedType="string" actual="($grabMapPrice)" stepKey="assertMapPrice"/> + <assertEquals stepKey="assertMapPrice"> + <actualResult type="const">($grabMapPrice)</actualResult> + <expectedResult type="string">$66.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLink"/> <!--Check msrp for second child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption2.value$$" stepKey="selectSecondOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabSecondProductMapPrice"/> - <assertEquals expected='$66.00' expectedType="string" actual="($grabSecondProductMapPrice)" stepKey="assertSecondProductMapPrice"/> + <assertEquals stepKey="assertSecondProductMapPrice"> + <actualResult type="const">($grabSecondProductMapPrice)</actualResult> + <expectedResult type="string">$66.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForSecondProduct"/> <!--Check msrp for first child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectFirstOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad1"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabFirstProductMapPrice"/> - <assertEquals expected='$55.00' expectedType="string" actual="($grabFirstProductMapPrice)" stepKey="assertFirstProductMapPrice"/> + <assertEquals stepKey="assertFirstProductMapPrice"> + <actualResult type="const">($grabFirstProductMapPrice)</actualResult> + <expectedResult type="string">$55.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForFirstProduct"/> <!--Check price for third child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption3.value$$" stepKey="selectThirdOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad2"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="grabThirdProductMapPrice"/> - <assertEquals expected='$70.00' expectedType="string" actual="($grabThirdProductMapPrice)" stepKey="assertThirdProductMapPrice"/> + <assertEquals stepKey="assertThirdProductMapPrice"> + <actualResult type="const">($grabThirdProductMapPrice)</actualResult> + <expectedResult type="string">$70.00</expectedResult> + </assertEquals> <dontSeeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForThirdProduct"/> </test> </tests> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index 0435ab6b7be49..41b917c4c1371 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -89,6 +89,9 @@ <grabTextFrom selector="{{GeneratedReportSection.ordersCount}}" stepKey="grabCanceledOrdersAny"/> <!-- Compare canceled orders price --> - <assertEquals expected="{$grabCanceledOrdersSpecified}" expectedType="string" actual="{$grabCanceledOrdersAny}" actualType="string" stepKey="assertEquals"/> + <assertEquals stepKey="assertEquals"> + <actualResult type="string">{$grabCanceledOrdersAny}</actualResult> + <expectedResult type="string">{$grabCanceledOrdersSpecified}</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml index f318e76ce74a8..471e3aa80835e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml @@ -16,6 +16,9 @@ <argument name="expectedGrandTotal" type="string" defaultValue="$123.00"/> </arguments> <grabTextFrom selector="{{AdminCreditMemoTotalSection.grandTotal}}" stepKey="getGrandTotal"/> - <assertEquals expected='{{expectedGrandTotal}}' actualType="variable" expectedType="string" actual="getGrandTotal" stepKey="assertGrandTotalValue"/> + <assertEquals stepKey="assertGrandTotalValue"> + <actualResult type="variable">getGrandTotal</actualResult> + <expectedResult type="string">{{expectedGrandTotal}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml index 9a62771fb54cd..490496c1f1e9e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml @@ -16,6 +16,8 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index e55cdfeb284b4..48eebfb579c27 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -90,6 +90,9 @@ <!--Verify invoiced items qty in ship tab--> <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="goToShipment"/> <grabTextFrom selector="{{AdminShipmentItemsSection.itemQtyInvoiced('1')}}" stepKey="grabInvoicedItemQty"/> - <assertEquals expected="5" expectedType="string" actual="$grabInvoicedItemQty" stepKey="assertInvoicedItemsQty"/> + <assertEquals stepKey="assertInvoicedItemsQty"> + <actualResult type="const">$grabInvoicedItemQty</actualResult> + <expectedResult type="string">5</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index de92d80546733..a20e9efd614fd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -69,7 +69,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -96,7 +98,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 2cacfe934427c..f4b2c3ad0a819 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -64,7 +64,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -90,7 +92,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoTotalSection.grandTotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index e9954de55afbc..e0e7234fc9d24 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -70,7 +70,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -94,7 +96,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 2d5b2d3c66906..fd186fd051fbf 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -73,7 +73,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -97,7 +99,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml index 677969fd00d91..c689869d35c30 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml @@ -45,7 +45,10 @@ <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> <waitForAjaxLoad stepKey="waitForAjaxLoad"/> <executeJS function="{{AdminProductCustomizableOptionsSection.requiredFieldIndicator}}" stepKey="dateTimeRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="dateTimeRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator"> + <actualResult type="variable">dateTimeRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index bfaf31007b10a..da427c9085084 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -71,7 +71,9 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> - <assertNotEmpty actual="$orderId" stepKey="assertOrderIdIsNotEmpty"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty"> + <actualResult type="const">$orderId</actualResult> + </assertNotEmpty> <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml index 803e24b3423aa..ca2a35eef5ae6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create Shipment for first Order --> <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipmentForFirstOrder"/> @@ -53,7 +55,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create CreditMemo for second Order --> <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml index c230dc41f0d2e..c11ea49893734 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create Invoice for first Order --> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> @@ -53,7 +55,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create CreditMemo for second Order --> <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml index 14b61af59eaed..f583f46f19c7c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Create Shipment for Order --> <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml index 5ac803cb666b4..f334a77da6550 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create second order --> <actionGroup ref="CreateOrderActionGroup" stepKey="createSecondOrder"> @@ -50,7 +52,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create Invoice for second Order --> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml index 4b8df455d545d..437487e3d7e40 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Navigate to backend: Go to Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml index 84d5426bd44e3..87d3a864e0820 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml @@ -41,7 +41,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Navigate to backend: Go to Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml index 83998990c70ed..9eaa3a87e873a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Hold Order --> <click selector="{{AdminOrderDetailsMainActionsSection.hold}}" stepKey="pushButtonHold"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 8c80c1e9ee6d3..97d9ad9dbeb59 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -115,11 +115,17 @@ <see selector="{{AdminInvoiceTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeShippingAndHandling"/> <scrollTo selector="{{AdminInvoiceTotalSection.total('Shipping')}}" stepKey="scrollToInvoiceTotals"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.total('Shipping')}}" stepKey="grabShippingCost"/> - <assertEquals expected='$5.00' expectedType="string" actual="($grabShippingCost)" message="ExpectedShipping" stepKey="assertShippingAndHandling"/> + <assertEquals message="ExpectedShipping" stepKey="assertShippingAndHandling"> + <actualResult type="const">($grabShippingCost)</actualResult> + <expectedResult type="string">$5.00</expectedResult> + </assertEquals> <see selector="{{AdminInvoiceTotalSection.total('Discount')}}" userInput="-$15.00" stepKey="seeShippingAndHandling2"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.total('Discount')}}" stepKey="grabInvoiceDiscount"/> - <assertEquals expected='-$15.00' expectedType="string" actual="($grabInvoiceDiscount)" message="ExpectedDiscount" stepKey="assertDiscountValue"/> + <assertEquals message="ExpectedDiscount" stepKey="assertDiscountValue"> + <actualResult type="const">($grabInvoiceDiscount)</actualResult> + <expectedResult type="string">-$15.00</expectedResult> + </assertEquals> <see selector="{{AdminInvoiceTotalSection.grandTotal}}" userInput="$113.00" stepKey="seeCorrectGrandTotal"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabInvoiceGrandTotal" after="seeCorrectGrandTotal"/> @@ -137,9 +143,15 @@ <!-- Verify Refund Totals --> <see selector="{{AdminCreditMemoTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeRefundSubTotal"/> <grabTextFrom selector="{{AdminCreditMemoTotalSection.total('Discount')}}" stepKey="grabRefundDiscountValue"/> - <assertEquals expected='-$15.00' expectedType="string" actual="($grabRefundDiscountValue)" message="notExpectedDiscountOnRefundPage" stepKey="assertDiscountValue1"/> + <assertEquals message="notExpectedDiscountOnRefundPage" stepKey="assertDiscountValue1"> + <actualResult type="const">($grabRefundDiscountValue)</actualResult> + <expectedResult type="string">-$15.00</expectedResult> + </assertEquals> <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabRefundGrandTotal"/> - <assertEquals expected="($grabInvoiceGrandTotal)" actual="($grabRefundGrandTotal)" message="RefundGrandTotalMatchesWithInvoiceGrandTotal" stepKey="compareRefundGrandTotalAndInvoiceGrandTotal"/> + <assertEquals message="RefundGrandTotalMatchesWithInvoiceGrandTotal" stepKey="compareRefundGrandTotalAndInvoiceGrandTotal"> + <actualResult type="const">($grabRefundGrandTotal)</actualResult> + <expectedResult type="const">($grabInvoiceGrandTotal)</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 6a1cefae7553d..6ba1c3ac3deec 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -72,7 +72,9 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation" after="assertOrderIdIsNotEmpty"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> @@ -235,7 +237,10 @@ <click selector="{{AdminDataGridTableSection.columnHeader('Status')}}" stepKey="clickStatusToSortAsc" after="sortOrderGridByStatusComment"/> <grabTextFrom selector="{{AdminDataGridTableSection.gridCell('1', 'Status')}}" stepKey="getOrderStatusFirstRow" after="clickStatusToSortAsc"/> <grabTextFrom selector="{{AdminDataGridTableSection.gridCell('2', 'Status')}}" stepKey="getOrderStatusSecondRow" after="getOrderStatusFirstRow"/> - <assertGreaterThanOrEqual expected="$getOrderStatusFirstRow" actual="$getOrderStatusSecondRow" stepKey="checkStatusSortOrderAsc" after="getOrderStatusSecondRow"/> + <assertGreaterThanOrEqual stepKey="checkStatusSortOrderAsc" after="getOrderStatusSecondRow"> + <actualResult type="const">$getOrderStatusSecondRow</actualResult> + <expectedResult type="const">$getOrderStatusFirstRow</expectedResult> + </assertGreaterThanOrEqual> <!--@TODO improve sort assertion and check price and date column when MQE-690 is resolved--> <!--Use paging on order grid--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 04dadd95f9f43..e0fcd77ec6243 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -116,7 +116,9 @@ <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminConfigurableProductFormSection.currentAttribute}}" stepKey="grabAttribute"/> - <assertNotEmpty actual="$grabAttribute" stepKey="assertNotEmpty"/> + <assertNotEmpty stepKey="assertNotEmpty"> + <actualResult type="const">$grabAttribute</actualResult> + </assertNotEmpty> <!-- Create bundle Product --> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> @@ -149,7 +151,9 @@ <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminProductFormBundleSection.currentBundleOption}}" stepKey="grabBundleOption"/> - <assertNotEmpty actual="$grabBundleOption" stepKey="assertBundleOptionNotEmpty"/> + <assertNotEmpty stepKey="assertBundleOptionNotEmpty"> + <actualResult type="const">$grabBundleOption</actualResult> + </assertNotEmpty> <!-- Create sales rule --> <createData entity="ActiveSalesRuleCoupon50" stepKey="createCartPriceRule"/> @@ -234,7 +238,9 @@ <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" /> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Find the Order on frontend > Navigate to: Orders and Returns --> <amOnPage url="{{StorefrontGuestOrderSearchPage.url}}" stepKey="amOnOrdersAndReturns"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml index 1a7d3355e4ee4..7c252ca8c14c5 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml @@ -13,6 +13,8 @@ <click stepKey="navigateToShipmentsTab" selector="{{AdminOrderDetailsOrderViewSection.shipments}}"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> - <assertNotEmpty actual="$grabShipmentId" stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"/> + <assertNotEmpty stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"> + <actualResult type="const">$grabShipmentId</actualResult> + </assertNotEmpty> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index c3d9b243c27c7..08064ae59d793 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -31,54 +31,117 @@ <conditionalClick selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateTab}}" dependentSelector="{{AdminShippingMethodFlatRateSection.carriersFlatRateActive}}" visible="false" stepKey="expandFlatRateTab"/> <waitForElementVisible selector="{{AdminShippingMethodFlatRateSection.carriersEnableFlatRateActive}}" stepKey="waitForFlatRateTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersEnableFlatRateActive}}" userInput="disabled" stepKey="grabFlatRateActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateActiveDisabled" stepKey="assertFlatRateActiveDisabled"/> + <assertEquals stepKey="assertFlatRateActiveDisabled"> + <actualResult type="const">$grabFlatRateActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateTitle}}" userInput="disabled" stepKey="grabFlatRateTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateTitleDisabled" stepKey="assertFlatRateTitleDisabled"/> + <assertEquals stepKey="assertFlatRateTitleDisabled"> + <actualResult type="const">$grabFlatRateTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateName}}" userInput="disabled" stepKey="grabFlatRateNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateNameDisabled" stepKey="assertFlatRateNameDisabled"/> + <assertEquals stepKey="assertFlatRateNameDisabled"> + <actualResult type="const">$grabFlatRateNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateSpecificErrMsg}}" userInput="disabled" stepKey="grabFlatRateSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateSpecificErrMsgDisabled" stepKey="assertFlatRateSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFlatRateSpecificErrMsgDisabled"> + <actualResult type="const">$grabFlatRateSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateAllowSpecific}}" userInput="disabled" stepKey="grabFlatRateAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateAllowSpecificDisabled" stepKey="assertFlatRateAllowSpecificDisabled"/> + <assertEquals stepKey="assertFlatRateAllowSpecificDisabled"> + <actualResult type="const">$grabFlatRateAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateSpecificCountry}}" userInput="disabled" stepKey="grabFlatRateSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateSpecificCountryDisabled" stepKey="assertFlatRateSpecificCountryDisabled"/> + <assertEquals stepKey="assertFlatRateSpecificCountryDisabled"> + <actualResult type="const">$grabFlatRateSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <!--Assert configuration are disabled in Free Shipping section--> <comment userInput="Assert configuration are disabled in Free Shipping section" stepKey="commentSeeDisabledFreeShippingConfigs"/> <conditionalClick selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSectionHead}}" dependentSelector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" visible="false" stepKey="expandFreeShippingTab"/> <waitForElementVisible selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" stepKey="waitForFreeShippingTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" userInput="disabled" stepKey="grabFreeShippingActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingActiveDisabled" stepKey="assertFreeShippingActiveDisabled"/> + <assertEquals stepKey="assertFreeShippingActiveDisabled"> + <actualResult type="const">$grabFreeShippingActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingTitle}}" userInput="disabled" stepKey="grabFreeShippingTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingTitleDisabled" stepKey="assertFreeShippingTitleDisabled"/> + <assertEquals stepKey="assertFreeShippingTitleDisabled"> + <actualResult type="const">$grabFreeShippingTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingName}}" userInput="disabled" stepKey="grabFreeShippingNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingNameDisabled" stepKey="assertFreeShippingNameDisabled"/> + <assertEquals stepKey="assertFreeShippingNameDisabled"> + <actualResult type="const">$grabFreeShippingNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSpecificErrMsg}}" userInput="disabled" stepKey="grabFreeShippingSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingSpecificErrMsgDisabled" stepKey="assertFreeShippingSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFreeShippingSpecificErrMsgDisabled"> + <actualResult type="const">$grabFreeShippingSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingAllowSpecific}}" userInput="disabled" stepKey="grabFreeShippingAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingAllowSpecificDisabled" stepKey="assertFreeShippingAllowSpecificDisabled"/> + <assertEquals stepKey="assertFreeShippingAllowSpecificDisabled"> + <actualResult type="const">$grabFreeShippingAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSpecificCountry}}" userInput="disabled" stepKey="grabFreeShippingSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingSpecificCountryDisabled" stepKey="assertFreeShippingSpecificCountryDisabled"/> + <assertEquals stepKey="assertFreeShippingSpecificCountryDisabled"> + <actualResult type="const">$grabFreeShippingSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <!--Assert configuration are disabled in Table Rates section--> <comment userInput="Assert configuration are disabled in Table Rates section" stepKey="commentSeeDisabledTableRatesConfigs"/> <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTableRateTab"/> <waitForElementVisible selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" stepKey="waitForTableRateTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" userInput="disabled" stepKey="grabTableRateActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateActiveDisabled" stepKey="assertTableRateActiveDisabled"/> + <assertEquals stepKey="assertTableRateActiveDisabled"> + <actualResult type="const">$grabTableRateActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTitle}}" userInput="disabled" stepKey="grabTableRateTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateTitleDisabled" stepKey="assertTableRateTitleDisabled"/> + <assertEquals stepKey="assertTableRateTitleDisabled"> + <actualResult type="const">$grabTableRateTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateName}}" userInput="disabled" stepKey="grabTableRateNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateNameDisabled" stepKey="assertTableRateNameDisabled"/> + <assertEquals stepKey="assertTableRateNameDisabled"> + <actualResult type="const">$grabTableRateNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateConditionName}}" userInput="disabled" stepKey="grabTableRateConditionNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateConditionNameDisabled" stepKey="assertTableRateConditionNameDisabled"/> + <assertEquals stepKey="assertTableRateConditionNameDisabled"> + <actualResult type="const">$grabTableRateConditionNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateIncludeVirtualPrice}}" userInput="disabled" stepKey="grabTableRateIncludeVirtualPriceDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateIncludeVirtualPriceDisabled" stepKey="assertTableRateIncludeVirtualPriceDisabled"/> + <assertEquals stepKey="assertTableRateIncludeVirtualPriceDisabled"> + <actualResult type="const">$grabTableRateIncludeVirtualPriceDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateHandlingType}}" userInput="disabled" stepKey="grabTableRateHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateHandlingTypeDisabled" stepKey="assertTableRateHandlingTypeDisabled"/> + <assertEquals stepKey="assertTableRateHandlingTypeDisabled"> + <actualResult type="const">$grabTableRateHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateSpecificErrMsg}}" userInput="disabled" stepKey="grabTableRateSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateSpecificErrMsgDisabled" stepKey="assertTableRateSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertTableRateSpecificErrMsgDisabled"> + <actualResult type="const">$grabTableRateSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateAllowSpecific}}" userInput="disabled" stepKey="grabTableRateAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateAllowSpecificDisabled" stepKey="assertTableRateAllowSpecificDisabled"/> + <assertEquals stepKey="assertTableRateAllowSpecificDisabled"> + <actualResult type="const">$grabTableRateAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateSpecificCountry}}" userInput="disabled" stepKey="grabTableRateSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateSpecificCountryDisabled" stepKey="assertTableRateSpecificCountryDisabled"/> + <assertEquals stepKey="assertTableRateSpecificCountryDisabled"> + <actualResult type="const">$grabTableRateSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index 18e9f82e74121..5a93defc6ae19 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -81,7 +81,10 @@ <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption1.default_label)}}" stepKey="waitForOption"/> <click selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption1.default_label)}}" stepKey="clickFirstOption"/> <grabAttributeFrom selector="{{StorefrontCategoryMainSection.productImage}}" userInput="src" stepKey="grabFirstOptionImg"/> - <assertContains expectedType="string" expected="{{MagentoLogo.filename}}" actualType="variable" actual="$grabFirstOptionImg" stepKey="assertProductFirstOptionImage"/> + <assertContains stepKey="assertProductFirstOptionImage"> + <actualResult type="variable">$grabFirstOptionImg</actualResult> + <expectedResult type="string">{{MagentoLogo.filename}}</expectedResult> + </assertContains> <click selector="{{StorefrontCategorySidebarSection.removeFilter}}" stepKey="removeSideBarFilter"/> <actionGroup ref="SelectStorefrontSideBarAttributeOption" stepKey="selectStorefrontProductAttributeForSecondOption"> <argument name="categoryName" value="$$createCategory.name$$"/> @@ -90,6 +93,9 @@ <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="waitForSecondOption"/> <click selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="clickSecondOption"/> <grabAttributeFrom selector="{{StorefrontCategoryMainSection.productImage}}" userInput="src" stepKey="grabSecondOptionImg"/> - <assertContains expectedType="string" expected="{{TestImageNew.filename}}" actualType="variable" actual="$grabSecondOptionImg" stepKey="assertProductSecondOptionImage"/> + <assertContains stepKey="assertProductSecondOptionImage"> + <actualResult type="variable">$grabSecondOptionImg</actualResult> + <expectedResult type="string">{{TestImageNew.filename}}</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml index 64d047c1a4dff..8cd85ee0ca969 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml @@ -69,12 +69,18 @@ <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="getShippingMethods"/> <waitForPageLoad stepKey="waitForApplyingShippingMethods"/> <grabTextFrom selector="{{AdminOrderFormTotalSection.subtotalRow('3')}}" stepKey="grabTaxCost"/> - <assertEquals expected='$6.00' expectedType="string" actual="($grabTaxCost)" stepKey="assertTax"/> + <assertEquals stepKey="assertTax"> + <actualResult type="const">($grabTaxCost)</actualResult> + <expectedResult type="string">$6.00</expectedResult> + </assertEquals> <scrollTo selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="scrollToSubmitButton"/> <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitElementToBeVisble"/> <click selector="{{AdminOrderFormItemsSection.customPriceCheckbox}}" stepKey="clickOnCustomPriceCheckbox"/> <fillField selector="{{AdminOrderFormItemsSection.customPriceField}}" userInput="{{SimpleProductNameWithDoubleQuote.price}}" stepKey="changePrice"/> <click selector="{{AdminOrderFormItemsSection.updateItemsAndQuantities}}" stepKey="updateItemsAndQunatities"/> - <assertEquals expected='$6.00' expectedType="string" actual="($grabTaxCost)" stepKey="assertTaxAfterCustomPrice"/> + <assertEquals stepKey="assertTaxAfterCustomPrice"> + <actualResult type="const">($grabTaxCost)</actualResult> + <expectedResult type="string">$6.00</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml index 66e98d5e41527..c4c0f01441f0b 100644 --- a/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml @@ -19,6 +19,9 @@ </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"/> + <assertEquals message="pass" stepKey="assertElementColor"> + <actualResult type="variable">getElementColor</actualResult> + <expectedResult type="string">{{color}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml index 2bc783f128c33..6128aeeb8918c 100644 --- a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml @@ -17,9 +17,18 @@ <executeJS function="return window.getComputedStyle(document.querySelector('{{elementSelector}}')).getPropertyValue('outline-style')" stepKey="getBorderType"/> <executeJS function="return window.getComputedStyle(document.querySelector('{{elementSelector}}')).getPropertyValue('outline-width')" stepKey="getBorderWidth"/> - <assertContains actual="$getBorderColor" expected="{{TranslateInlineModeStyleData.borderColor}}" expectedType="string" stepKey="assertBorderColor"/> - <assertContains actual="$getBorderType" expected="{{TranslateInlineModeStyleData.borderType}}" expectedType="string" stepKey="assertBorderType"/> - <assertContains actual="$getBorderWidth" expected="{{TranslateInlineModeStyleData.borderWidth}}" expectedType="string" stepKey="assertBorderWidth"/> + <assertContains stepKey="assertBorderColor"> + <actualResult type="const">$getBorderColor</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderColor}}</expectedResult> + </assertContains> + <assertContains stepKey="assertBorderType"> + <actualResult type="const">$getBorderType</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderType}}</expectedResult> + </assertContains> + <assertContains stepKey="assertBorderWidth"> + <actualResult type="const">$getBorderWidth</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderWidth}}</expectedResult> + </assertContains> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index 126586669afd2..f339d0a5b7028 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,52 +15,124 @@ <conditionalClick selector="{{AdminShippingMethodsUpsSection.carriersUpsTab}}" dependentSelector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" visible="false" stepKey="expandUPSTab"/> <waitForElementVisible selector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" stepKey="waitUPSTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" userInput="disabled" stepKey="grabUPSActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSActiveDisabled" stepKey="assertUPSActiveDisabled"/> + <assertEquals stepKey="assertUPSActiveDisabled"> + <actualResult type="const">$grabUPSActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTypeSystem}}" userInput="disabled" stepKey="grabUPSTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTypeDisabled" stepKey="assertUPSTypeDisabled"/> + <assertEquals stepKey="assertUPSTypeDisabled"> + <actualResult type="const">$grabUPSTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAccountLive}}" userInput="disabled" stepKey="grabUPSAccountLiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAccountLiveDisabled" stepKey="assertUPSAccountLiveDisabled"/> + <assertEquals stepKey="assertUPSAccountLiveDisabled"> + <actualResult type="const">$grabUPSAccountLiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSGatewayXMLUrl}}" userInput="disabled" stepKey="grabUPSGatewayXMLUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSGatewayXMLUrlDisabled" stepKey="assertUPSGatewayXMLUrlDisabled"/> + <assertEquals stepKey="assertUPSGatewayXMLUrlDisabled"> + <actualResult type="const">$grabUPSGatewayXMLUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSModeXML}}" userInput="disabled" stepKey="grabUPSModeXMLDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSModeXMLDisabled" stepKey="assertUPSModeXMLDisabled"/> + <assertEquals stepKey="assertUPSModeXMLDisabled"> + <actualResult type="const">$grabUPSModeXMLDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSOriginShipment}}" userInput="disabled" stepKey="grabUPSOriginShipmentDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSOriginShipmentDisabled" stepKey="assertUPSOriginShipmentDisabled"/> + <assertEquals stepKey="assertUPSOriginShipmentDisabled"> + <actualResult type="const">$grabUPSOriginShipmentDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTitle}}" userInput="disabled" stepKey="grabUPSTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTitleDisabled" stepKey="assertUPSTitleDisabled"/> + <assertEquals stepKey="assertUPSTitleDisabled"> + <actualResult type="const">$grabUPSTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSNegotiatedActive}}" userInput="disabled" stepKey="grabUPSNegotiatedActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSNegotiatedActiveDisabled" stepKey="assertUPSNegotiatedActiveDisabled"/> + <assertEquals stepKey="assertUPSNegotiatedActiveDisabled"> + <actualResult type="const">$grabUPSNegotiatedActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSIncludeTaxes}}" userInput="disabled" stepKey="grabUPSIncludeTaxesDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSIncludeTaxesDisabled" stepKey="assertUPSIncludeTaxesDisabled"/> + <assertEquals stepKey="assertUPSIncludeTaxesDisabled"> + <actualResult type="const">$grabUPSIncludeTaxesDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSShipmentRequestType}}" userInput="disabled" stepKey="grabUPSShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSShipmentRequestTypeDisabled" stepKey="assertUPSShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertUPSShipmentRequestTypeDisabled"> + <actualResult type="const">$grabUPSShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSContainer}}" userInput="disabled" stepKey="grabUPSContainerDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSContainerDisabled" stepKey="assertUPSContainerDisabled"/> + <assertEquals stepKey="assertUPSContainerDisabled"> + <actualResult type="const">$grabUPSContainerDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSDestType}}" userInput="disabled" stepKey="grabUPSDestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSDestTypeDisabled" stepKey="assertUPSDestTypeDisabled"/> + <assertEquals stepKey="assertUPSDestTypeDisabled"> + <actualResult type="const">$grabUPSDestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTrackingXmlUrl}}" userInput="disabled" stepKey="grabUPSTrackingXmlUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTrackingXmlUrlDisabled" stepKey="assertUPSTrackingXmlUrlDisabled"/> + <assertEquals stepKey="assertUPSTrackingXmlUrlDisabled"> + <actualResult type="const">$grabUPSTrackingXmlUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSUnitOfMeasure}}" userInput="disabled" stepKey="grabUPSUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSUnitOfMeasureDisabled" stepKey="assertUPSUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertUPSUnitOfMeasureDisabled"> + <actualResult type="const">$grabUPSUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSMaxPackageWeight}}" userInput="disabled" stepKey="grabUPSMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSMaxPackageWeightDisabled" stepKey="assertUPSMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertUPSMaxPackageWeightDisabled"> + <actualResult type="const">$grabUPSMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSPickup}}" userInput="disabled" stepKey="grabUPSPickupDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSPickupDisabled" stepKey="assertUPSPickupDisabled"/> + <assertEquals stepKey="assertUPSPickupDisabled"> + <actualResult type="const">$grabUPSPickupDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSMinPackageWeight}}" userInput="disabled" stepKey="grabUPSMinPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSMinPackageWeightDisabled" stepKey="assertUPSMinPackageWeightDisabled"/> + <assertEquals stepKey="assertUPSMinPackageWeightDisabled"> + <actualResult type="const">$grabUPSMinPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSHandlingType}}" userInput="disabled" stepKey="grabUPSHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSHandlingTypeDisabled" stepKey="assertUPSHandlingTypeDisabled"/> + <assertEquals stepKey="assertUPSHandlingTypeDisabled"> + <actualResult type="const">$grabUPSHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSHandlingAction}}" userInput="disabled" stepKey="grabUPSHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSHandlingActionDisabled" stepKey="assertUPSHandlingActionDisabled"/> + <assertEquals stepKey="assertUPSHandlingActionDisabled"> + <actualResult type="const">$grabUPSHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAllowedMethods}}" userInput="disabled" stepKey="grabUPSAllowedMethodsDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAllowedMethodsDisabled" stepKey="assertUPSAllowedMethodsDisabled"/> + <assertEquals stepKey="assertUPSAllowedMethodsDisabled"> + <actualResult type="const">$grabUPSAllowedMethodsDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSFreeMethod}}" userInput="disabled" stepKey="grabUPSFreeMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSFreeMethodDisabled" stepKey="assertUPSFreeMethodDisabled"/> + <assertEquals stepKey="assertUPSFreeMethodDisabled"> + <actualResult type="const">$grabUPSFreeMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSSpecificErrMsg}}" userInput="disabled" stepKey="grabUPSSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSSpecificErrMsgDisabled" stepKey="assertUPSSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertUPSSpecificErrMsgDisabled"> + <actualResult type="const">$grabUPSSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAllowSpecific}}" userInput="disabled" stepKey="grabUPSAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAllowSpecificDisabled" stepKey="assertUPSAllowSpecificDisabled"/> + <assertEquals stepKey="assertUPSAllowSpecificDisabled"> + <actualResult type="const">$grabUPSAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSSpecificCountry}}" userInput="disabled" stepKey="grabUPSSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSSpecificCountryDisabled" stepKey="assertUPSSpecificCountryDisabled"/> + <assertEquals stepKey="assertUPSSpecificCountryDisabled"> + <actualResult type="const">$grabUPSSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml index bd278c9dbe975..f4f30d4317a57 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml @@ -42,6 +42,9 @@ <!-- Assert that selected UPS type by default is 'United Parcel Service XML' --> <comment userInput="Check that selected UPS type by default is 'United Parcel Service XML'" stepKey="assertDefUpsType"/> <grabTextFrom selector="{{AdminShippingMethodsUpsSection.selectedUpsType}}" stepKey="grabSelectedOptionText"/> - <assertEquals expected='United Parcel Service XML' expectedType="string" actual="($grabSelectedOptionText)" stepKey="assertDefaultUpsType"/> + <assertEquals stepKey="assertDefaultUpsType"> + <actualResult type="const">($grabSelectedOptionText)</actualResult> + <expectedResult type="string">United Parcel Service XML</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index cd77861fccd58..4d4f84c41e1d4 100644 --- a/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,42 +15,99 @@ <conditionalClick selector="{{AdminShippingMethodUSPSSection.carriersUSPSTab}}" dependentSelector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" visible="false" stepKey="expandUSPSTab"/> <waitForElementVisible selector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" stepKey="waitUSPSTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" userInput="disabled" stepKey="grabUSPSActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSActiveDisabled" stepKey="assertUSPSActiveDisabled"/> + <assertEquals stepKey="assertUSPSActiveDisabled"> + <actualResult type="const">$grabUSPSActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSGatewayXMLUrl}}" userInput="disabled" stepKey="grabUSPSGatewayXMLUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSGatewayXMLUrlDisabled" stepKey="assertUSPSGatewayXMLUrlDisabled"/> + <assertEquals stepKey="assertUSPSGatewayXMLUrlDisabled"> + <actualResult type="const">$grabUSPSGatewayXMLUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSGatewaySecureUrl}}" userInput="disabled" stepKey="grabUSPSGatewaySecureUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSGatewaySecureUrlDisabled" stepKey="assertUSPSGatewaySecureUrlDisabled"/> + <assertEquals stepKey="assertUSPSGatewaySecureUrlDisabled"> + <actualResult type="const">$grabUSPSGatewaySecureUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSTitle}}" userInput="disabled" stepKey="grabUSPSTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSTitleDisabled" stepKey="assertUSPSTitleDisabled"/> + <assertEquals stepKey="assertUSPSTitleDisabled"> + <actualResult type="const">$grabUSPSTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSUserId}}" userInput="disabled" stepKey="grabUSPSUserIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSUserIdDisabled" stepKey="assertUSPSUserIdDisabled"/> + <assertEquals stepKey="assertUSPSUserIdDisabled"> + <actualResult type="const">$grabUSPSUserIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSPassword}}" userInput="disabled" stepKey="grabUSPSPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSPasswordDisabled" stepKey="assertUSPSPasswordDisabled"/> + <assertEquals stepKey="assertUSPSPasswordDisabled"> + <actualResult type="const">$grabUSPSPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSShipmentRequestType}}" userInput="disabled" stepKey="grabUSPSShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSShipmentRequestTypeDisabled" stepKey="assertUSPSShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertUSPSShipmentRequestTypeDisabled"> + <actualResult type="const">$grabUSPSShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSContainer}}" userInput="disabled" stepKey="grabUSPSContainerDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSContainerDisabled" stepKey="assertUSPSContainerDisabled"/> + <assertEquals stepKey="assertUSPSContainerDisabled"> + <actualResult type="const">$grabUSPSContainerDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSize}}" userInput="disabled" stepKey="grabUSPSSizeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSizeDisabled" stepKey="assertUSPSSizeDisabled"/> + <assertEquals stepKey="assertUSPSSizeDisabled"> + <actualResult type="const">$grabUSPSSizeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSDestType}}" userInput="disabled" stepKey="grabUSPSDestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSDestTypeDisabled" stepKey="assertUSPSDestTypeDisabled"/> + <assertEquals stepKey="assertUSPSDestTypeDisabled"> + <actualResult type="const">$grabUSPSDestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSMachinable}}" userInput="disabled" stepKey="grabUSPSMachinableDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSMachinableDisabled" stepKey="assertUSPSMachinableDisabled"/> + <assertEquals stepKey="assertUSPSMachinableDisabled"> + <actualResult type="const">$grabUSPSMachinableDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSMaxPackageWeight}}" userInput="disabled" stepKey="grabUSPSMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSMaxPackageWeightDisabled" stepKey="assertUSPSMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertUSPSMaxPackageWeightDisabled"> + <actualResult type="const">$grabUSPSMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSHandlingType}}" userInput="disabled" stepKey="grabUSPSHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSHandlingTypeDisabled" stepKey="assertUSPSHandlingTypeDisabled"/> + <assertEquals stepKey="assertUSPSHandlingTypeDisabled"> + <actualResult type="const">$grabUSPSHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSHandlingAction}}" userInput="disabled" stepKey="grabUSPSHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSHandlingActionDisabled" stepKey="assertUSPSHandlingActionDisabled"/> + <assertEquals stepKey="assertUSPSHandlingActionDisabled"> + <actualResult type="const">$grabUSPSHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSAllowedMethods}}" userInput="disabled" stepKey="grabUSPSAllowedMethodsDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSAllowedMethodsDisabled" stepKey="assertUSPSAllowedMethodsDisabled"/> + <assertEquals stepKey="assertUSPSAllowedMethodsDisabled"> + <actualResult type="const">$grabUSPSAllowedMethodsDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSFreeMethod}}" userInput="disabled" stepKey="grabUSPSFreeMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSFreeMethodDisabled" stepKey="assertUSPSFreeMethodDisabled"/> + <assertEquals stepKey="assertUSPSFreeMethodDisabled"> + <actualResult type="const">$grabUSPSFreeMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSpecificErrMsg}}" userInput="disabled" stepKey="grabUSPSSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSpecificErrMsgDisabled" stepKey="assertUSPSSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertUSPSSpecificErrMsgDisabled"> + <actualResult type="const">$grabUSPSSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSAllowSpecific}}" userInput="disabled" stepKey="grabUSPSAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSAllowSpecificDisabled" stepKey="assertUSPSAllowSpecificDisabled"/> + <assertEquals stepKey="assertUSPSAllowSpecificDisabled"> + <actualResult type="const">$grabUSPSAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSpecificCountry}}" userInput="disabled" stepKey="grabUSPSSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSpecificCountryDisabled" stepKey="assertUSPSSpecificCountryDisabled"/> + <assertEquals stepKey="assertUSPSSpecificCountryDisabled"> + <actualResult type="const">$grabUSPSSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml index d38bf90a3a16d..b3ab8669a1b6b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml @@ -17,7 +17,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> @@ -25,7 +28,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> </test> <test name="EndToEndB2CGuestUserMysqlTest"> <!-- Search configurable product --> @@ -36,7 +42,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> @@ -44,6 +53,9 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml index f616e46d6f692..e25d3e0d728f6 100644 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml @@ -17,7 +17,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> @@ -25,6 +28,9 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> </test> </tests> From e7e2b447139d1b820ee920f147c5cb0390682844 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@adobe.com> Date: Thu, 12 Mar 2020 09:20:20 -0500 Subject: [PATCH 339/734] MQE-683: [Deprecation] Only use more nested assertion syntax - Lock file update --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 54e59a9c31c2b..3a39be3fa4327 100644 --- a/composer.lock +++ b/composer.lock @@ -2251,11 +2251,6 @@ "MIT" ], "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - }, { "name": "Marijn Huizendveld", "email": "marijn.huizendveld@gmail.com" @@ -2263,6 +2258,11 @@ { "name": "Thibaud Fabre", "email": "thibaud@aztech.io" + }, + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" } ], "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "c37ba96a1caa59755b50593638b01f591e597251" + "reference": "5990abb646462e760d4afa4dcbabe62a9d81b839" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/c37ba96a1caa59755b50593638b01f591e597251", - "reference": "c37ba96a1caa59755b50593638b01f591e597251", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5990abb646462e760d4afa4dcbabe62a9d81b839", + "reference": "5990abb646462e760d4afa4dcbabe62a9d81b839", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-11T17:07:43+00:00" + "time": "2020-03-12T14:12:32+00:00" }, { "name": "mikey179/vfsstream", From 190e0511a322cd5f260b6d11b1d4e2e679070eef Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 12 Mar 2020 09:36:47 -0500 Subject: [PATCH 340/734] MC-32278: Position sort does not work in GraphQl. - fix price_dynamic_algorithm --- .../Api/SearchCriteria/CollectionProcessor/FilterProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index 89354baf73e67..03c33c29d134c 100644 --- a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -65,7 +65,7 @@ private function addFilterGroupToCollection( ) { $fields = []; foreach ($filterGroup->getFilters() as $filter) { - if ($filter->getField() != 'search_term') { + if ($filter->getField() != 'search_term' && $filter->getField() != 'price_dynamic_algorithm') { $isApplied = false; $customFilter = $this->getCustomFilterForField($filter->getField()); if ($customFilter) { From 58b25cab794b4fcdac3e3c9c95b37a66946d7d74 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@adobe.com> Date: Thu, 12 Mar 2020 10:03:06 -0500 Subject: [PATCH 341/734] MQE-683: [Deprecation] Only use more nested assertion syntax - Indent fixes --- .../Mftf/Test/AdminCreateCategoryTest.xml | 58 +++++++++---------- .../Test/StorefrontPrintOrderGuestTest.xml | 8 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml index 72a44cb231b46..3ebc074623d8a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml @@ -70,35 +70,35 @@ <seeOptionIsSelected selector="{{CategoryDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="see2ColumnsSelected" /> </test> <test name="AdminCategoryFormDisplaySettingsUIValidationTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Category should not be saved once layered navigation price step field is left empty"/> - <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-95797"/> - <group value="category"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> - <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> - <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> - <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> - <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> - <!-- Verify that the Layered navigation price step field has the required indicator --> - <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> - <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Category should not be saved once layered navigation price step field is left empty"/> + <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-95797"/> + <group value="category"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> + <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> + <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> + <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> + <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> + <!-- Verify that the Layered navigation price step field has the required indicator --> + <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> + <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> <actualResult type="variable">getRequiredFieldIndicator</actualResult> <expectedResult type="string">"*"</expectedResult> </assertEquals> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index e0fcd77ec6243..80a6510de7551 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -117,8 +117,8 @@ </actionGroup> <grabTextFrom selector="{{AdminConfigurableProductFormSection.currentAttribute}}" stepKey="grabAttribute"/> <assertNotEmpty stepKey="assertNotEmpty"> - <actualResult type="const">$grabAttribute</actualResult> - </assertNotEmpty> + <actualResult type="const">$grabAttribute</actualResult> + </assertNotEmpty> <!-- Create bundle Product --> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> @@ -152,8 +152,8 @@ </actionGroup> <grabTextFrom selector="{{AdminProductFormBundleSection.currentBundleOption}}" stepKey="grabBundleOption"/> <assertNotEmpty stepKey="assertBundleOptionNotEmpty"> - <actualResult type="const">$grabBundleOption</actualResult> - </assertNotEmpty> + <actualResult type="const">$grabBundleOption</actualResult> + </assertNotEmpty> <!-- Create sales rule --> <createData entity="ActiveSalesRuleCoupon50" stepKey="createCartPriceRule"/> From 756237e6f1c82355ee2317d01cf9d7ceb624be9b Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@adobe.com> Date: Thu, 12 Mar 2020 10:39:40 -0500 Subject: [PATCH 342/734] MQE-683: [Deprecation] Only use more nested assertion syntax - Fixed additional edge cases --- ...AdminCreateDropdownProductAttributeTest.xml | 6 ++++-- ...gCategoryProductsUsingScopeSelectorTest.xml | 18 ++++++++++++------ ...oWYSIWYGWithCatalogCategoryLinkTypeTest.xml | 7 ++++--- ...ToWYSIWYGWithCatalogProductLinkTypeTest.xml | 7 ++++--- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml index 4b69123635852..b53babbd4e520 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml @@ -66,7 +66,9 @@ <!-- Check attribute data --> <grabValueFrom selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('2')}}" stepKey="secondOptionAdminLabel"/> - <assertEquals actual="$secondOptionAdminLabel" expected="'Fish & Chips'" - stepKey="assertSecondOption"/> + <assertEquals stepKey="assertSecondOption"> + <actualResult type="const">$secondOptionAdminLabel</actualResult> + <expectedResult type="const">'Fish & Chips'</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index a6f34af9f5315..a0dbb7cea5e16 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -106,8 +106,10 @@ <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection"/> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory"/> - <assertRegExp expected="/\(4\)$/" expectedType="string" actual="$grabTextFromCategory" actualType="variable" - message="wrongCountProductOnAllStoreViews" stepKey="checkCountProducts"/> + <assertRegExp message="wrongCountProductOnAllStoreViews" stepKey="checkCountProducts"> + <actualResult type="variable">$grabTextFromCategory</actualResult> + <expectedResult type="string">/\(4\)$/</expectedResult> + </assertRegExp> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="seeProductName"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" @@ -124,8 +126,10 @@ </actionGroup> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory1"/> - <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory1" actualType="variable" - message="wrongCountProductOnWebsite1" stepKey="checkCountProducts1"/> + <assertRegExp message="wrongCountProductOnWebsite1" stepKey="checkCountProducts1"> + <actualResult type="variable">$grabTextFromCategory1</actualResult> + <expectedResult type="string">/\(2\)$/</expectedResult> + </assertRegExp> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection1"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> @@ -144,8 +148,10 @@ <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection2"/> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory2"/> - <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory2" actualType="variable" - message="wrongCountProductOnWebsite2" stepKey="checkCountProducts2"/> + <assertRegExp message="wrongCountProductOnWebsite2" stepKey="checkCountProducts2"> + <actualResult type="variable">$grabTextFromCategory2</actualResult> + <expectedResult type="string">/\(2\)$/</expectedResult> + </assertRegExp> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 5d745c625ac10..c740237b98f34 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -70,9 +70,10 @@ <see userInput="Hello CMS Page!" stepKey="seeContent2"/> <!--see widget on Storefront--> <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> - <assertRegExp expected="|$$createPreReqCategory.name$$.html|i" - expectedType="string" actual="$dataHref" actualType="variable" - stepKey="seeProductLinkInCategory"/> + <assertRegExp stepKey="seeProductLinkInCategory"> + <actualResult type="variable">$dataHref</actualResult> + <expectedResult type="string">|$$createPreReqCategory.name$$.html|i</expectedResult> + </assertRegExp> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index 940c1979710e1..7dc56f93d6b79 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -76,9 +76,10 @@ <waitForPageLoad stepKey="wait8" /> <!--see widget on Storefront--> <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> - <assertRegExp expected="|$$createPreReqCategory.name$$/$$createPreReqProduct.name$$.html|i" - expectedType="string" actual="$dataHref" actualType="variable" - stepKey="seeProductLinkInCategory"/> + <assertRegExp stepKey="seeProductLinkInCategory"> + <actualResult type="variable">$dataHref</actualResult> + <expectedResult type="string">|$$createPreReqCategory.name$$/$$createPreReqProduct.name$$.html|i</expectedResult> + </assertRegExp> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> From 8ed463763bd23f6b62da6313c251bbf093e294e0 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 12 Mar 2020 10:43:37 -0500 Subject: [PATCH 343/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 3a39be3fa4327..ca2be945e33c4 100644 --- a/composer.lock +++ b/composer.lock @@ -2251,6 +2251,11 @@ "MIT" ], "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + }, { "name": "Marijn Huizendveld", "email": "marijn.huizendveld@gmail.com" @@ -2258,11 +2263,6 @@ { "name": "Thibaud Fabre", "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" } ], "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "5990abb646462e760d4afa4dcbabe62a9d81b839" + "reference": "a4d08e0ca984846f532a6f7b71c7ac01b7e7c287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5990abb646462e760d4afa4dcbabe62a9d81b839", - "reference": "5990abb646462e760d4afa4dcbabe62a9d81b839", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/a4d08e0ca984846f532a6f7b71c7ac01b7e7c287", + "reference": "a4d08e0ca984846f532a6f7b71c7ac01b7e7c287", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-12T14:12:32+00:00" + "time": "2020-03-12T14:26:36+00:00" }, { "name": "mikey179/vfsstream", From b7dce0e92038ee8e895115c0a21f13cc6d2a49f7 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 12 Mar 2020 11:04:23 -0500 Subject: [PATCH 344/734] MC-32278: Position sort does not work in GraphQl. - fix more attributes --- .../SearchCriteria/CollectionProcessor/FilterProcessor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index 03c33c29d134c..616e642f0079a 100644 --- a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -65,7 +65,10 @@ private function addFilterGroupToCollection( ) { $fields = []; foreach ($filterGroup->getFilters() as $filter) { - if ($filter->getField() != 'search_term' && $filter->getField() != 'price_dynamic_algorithm') { + if ($filter->getField() != 'search_term' + && $filter->getField() != 'price_dynamic_algorithm' + && $filter->getField() != 'price.to' + && $filter->getField() != 'price.from') { $isApplied = false; $customFilter = $this->getCustomFilterForField($filter->getField()); if ($customFilter) { From be6ea502a6371c513190ba2e363226a12b98555f Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 12 Mar 2020 11:41:03 -0500 Subject: [PATCH 345/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index ca2be945e33c4..5cdc9a1eac6aa 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "a4d08e0ca984846f532a6f7b71c7ac01b7e7c287" + "reference": "9383730e43898d9807e00b8039684dd3686a9e91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/a4d08e0ca984846f532a6f7b71c7ac01b7e7c287", - "reference": "a4d08e0ca984846f532a6f7b71c7ac01b7e7c287", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/9383730e43898d9807e00b8039684dd3686a9e91", + "reference": "9383730e43898d9807e00b8039684dd3686a9e91", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-12T14:26:36+00:00" + "time": "2020-03-12T16:34:50+00:00" }, { "name": "mikey179/vfsstream", From 2d8721912c321c2098db2e458ec1a0c6c1155658 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Thu, 12 Mar 2020 18:41:35 +0200 Subject: [PATCH 346/734] changes requested; add out stock product to cart test --- .../Guest/AddSimpleProductToCartTest.php | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 3bdce6ea98b30..e5953e7b7ad72 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -80,24 +80,48 @@ public function testAddSimpleProductToCart() self::assertEquals('USD', $rowTotalIncludingTax['currency']); } + /** + * Add disabled product to cart + * + * @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @return void + */ + public function testAddDisabledProductToCart(): void + { + $sku = 'simple3'; + $quantity = 2; + + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $sku, $quantity); + + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU ' . $sku . ' to the shopping cart: ' . + 'Product that you are trying to add is not available.' + ); + + $this->graphQlMutation($query); + } + /** * Add out of stock product to cart * - * @@magentoApiDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual_out_of_stock.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @return void */ - public function testAddProductToCartWithError(): void + public function testAddOutOfStockProductToCart(): void { - $disabledProductSku = 'simple3'; + $sku = 'virtual-product-out'; $quantity = 2; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->getQuery($maskedQuoteId, $disabledProductSku, $quantity); + $query = $this->getQuery($maskedQuoteId, $sku, $quantity); $this->expectException(ResponseContainsErrorsException::class); $this->expectExceptionMessage( - 'Could not add the product with SKU simple3 to the shopping cart: ' . + 'Could not add the product with SKU ' . $sku . ' to the shopping cart: ' . 'Product that you are trying to add is not available.' ); From aaf50094ad8d14b652bce64f2ee81c4472b7ab8d Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 12 Mar 2020 12:48:18 -0500 Subject: [PATCH 347/734] MC-31986: Add support for ES 7 to 2.4-develop -fix after CR --- .../Adapter/DataMapper/ProductDataMapper.php | 451 ------------- .../DataMapper/ProductDataMapperProxy.php | 54 -- .../Adapter/DataMapper/DataMapperResolver.php | 92 --- .../Model/Adapter/Elasticsearch.php | 11 - .../Magento/Elasticsearch/Model/Config.php | 1 - .../Model/Client/ElasticsearchTest.php | 601 ++++++++++++++++++ .../Model/Adapter/Container/AttributeTest.php | 237 ------- .../Unit/Model/Adapter/ElasticsearchTest.php | 8 - app/code/Magento/Elasticsearch/etc/config.xml | 7 - app/code/Magento/Elasticsearch/etc/di.xml | 14 - app/code/Magento/Elasticsearch6/etc/di.xml | 17 - .../FieldName/Resolver/DefaultResolver.php | 8 +- app/code/Magento/Elasticsearch7/etc/di.xml | 16 +- .../Magento/Elasticsearch7/etc/module.xml | 3 - .../Controller/Advanced/ResultTest.php | 3 +- .../Controller/Result/IndexTest.php | 2 +- .../Fulltext/Action/DataProviderTest.php | 3 +- .../Model/QuickSearchTest.php | 4 +- .../SearchAdapter/AdapterTest.php | 5 +- .../Test/Legacy/_files/obsolete_classes.php | 9 +- .../Framework/ObjectManager/etc/config.xsd | 1 + 21 files changed, 625 insertions(+), 922 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php delete mode 100644 app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php create mode 100644 app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php delete mode 100644 app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php deleted file mode 100644 index 91bde497c612b..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php +++ /dev/null @@ -1,451 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Elasticsearch\Model\Adapter\Container\Attribute as AttributeContainer; -use Magento\Elasticsearch\Model\Adapter\Document\Builder; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Api\Data\GroupInterface; -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Model\Adapter\FieldType\Date as DateFieldType; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; - -/** - * Don't use this product data mapper class. - * - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class ProductDataMapper implements DataMapperInterface -{ - /** - * Attribute code for image - */ - const MEDIA_ROLE_IMAGE = 'image'; - - /** - * Attribute code for small image - */ - const MEDIA_ROLE_SMALL_IMAGE = 'small_image'; - - /** - * Attribute code for thumbnail - */ - const MEDIA_ROLE_THUMBNAIL = 'thumbnail'; - - /** - * Attribute code for swatches - */ - const MEDIA_ROLE_SWATCH_IMAGE = 'swatch_image'; - - /** - * @var Builder - */ - private $builder; - - /** - * @var AttributeContainer - */ - private $attributeContainer; - - /** - * @var Index - */ - private $resourceIndex; - - /** - * @var FieldMapperInterface - */ - private $fieldMapper; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var DateFieldType - */ - private $dateFieldType; - - /** - * Media gallery roles - * - * @var array - */ - protected $mediaGalleryRoles; - - /** - * @var AttributeProvider - */ - private $attributeAdapterProvider; - - /** - * @var ResolverInterface - */ - private $fieldNameResolver; - - /** - * Construction for DocumentDataMapper - * - * @param Builder $builder - * @param AttributeContainer $attributeContainer - * @param Index $resourceIndex - * @param FieldMapperInterface $fieldMapper - * @param StoreManagerInterface $storeManager - * @param DateFieldType $dateFieldType - * @param AttributeProvider $attributeAdapterProvider - * @param ResolverInterface $fieldNameResolver - */ - public function __construct( - Builder $builder, - AttributeContainer $attributeContainer, - Index $resourceIndex, - FieldMapperInterface $fieldMapper, - StoreManagerInterface $storeManager, - DateFieldType $dateFieldType, - AttributeProvider $attributeAdapterProvider, - ResolverInterface $fieldNameResolver - ) { - $this->builder = $builder; - $this->attributeContainer = $attributeContainer; - $this->resourceIndex = $resourceIndex; - $this->fieldMapper = $fieldMapper; - $this->storeManager = $storeManager; - $this->dateFieldType = $dateFieldType; - $this->attributeAdapterProvider = $attributeAdapterProvider; - $this->fieldNameResolver = $fieldNameResolver; - - $this->mediaGalleryRoles = [ - self::MEDIA_ROLE_IMAGE, - self::MEDIA_ROLE_SMALL_IMAGE, - self::MEDIA_ROLE_THUMBNAIL, - self::MEDIA_ROLE_SWATCH_IMAGE - ]; - } - - /** - * Prepare index data for using in search engine metadata. - * - * @param int $productId - * @param array $indexData - * @param int $storeId - * @param array $context - * @return array|false - */ - public function map($productId, array $indexData, $storeId, $context = []) - { - $this->builder->addField('store_id', $storeId); - if (count($indexData)) { - $productIndexData = $this->resourceIndex->getFullProductIndexData($productId, $indexData); - } - - foreach ($productIndexData as $attributeCode => $value) { - // Prepare processing attribute info - if (strpos($attributeCode, '_value') !== false) { - $this->builder->addField($attributeCode, $value); - continue; - } - $attribute = $this->attributeContainer->getAttribute($attributeCode); - if (!$attribute || - in_array( - $attributeCode, - [ - 'price', - 'media_gallery', - 'tier_price', - 'quantity_and_stock_status', - 'media_gallery', - 'giftcard_amounts' - ] - ) - ) { - continue; - } - $attribute->setStoreId($storeId); - $value = $this->checkValue($value, $attribute, $storeId); - $this->builder->addField( - $this->fieldMapper->getFieldName( - $attributeCode, - $context - ), - $value - ); - } - $this->processAdvancedAttributes($productId, $productIndexData, $storeId); - - return $this->builder->build(); - } - - /** - * Process advanced attribute values - * - * @param int $productId - * @param array $productIndexData - * @param int $storeId - * @return void - */ - protected function processAdvancedAttributes($productId, array $productIndexData, $storeId) - { - $mediaGalleryRoles = array_fill_keys($this->mediaGalleryRoles, ''); - $productPriceIndexData = $this->attributeContainer->getAttribute('price') - ? $this->resourceIndex->getPriceIndexData([$productId], $storeId) - : []; - $productCategoryIndexData = $this->resourceIndex->getFullCategoryProductIndexData( - $storeId, - [$productId => $productId] - ); - foreach ($productIndexData as $attributeCode => $value) { - if (in_array($attributeCode, $this->mediaGalleryRoles)) { - $mediaGalleryRoles[$attributeCode] = $value; - } elseif ($attributeCode == 'tier_price') { - $this->builder->addFields($this->getProductTierPriceData($value)); - } elseif ($attributeCode == 'quantity_and_stock_status') { - $this->builder->addFields($this->getQtyAndStatus($value)); - } elseif ($attributeCode == 'media_gallery') { - $this->builder->addFields( - $this->getProductMediaGalleryData( - $value, - $mediaGalleryRoles - ) - ); - } - } - $this->builder->addFields($this->getProductPriceData($productId, $storeId, $productPriceIndexData)); - $this->builder->addFields($this->getProductCategoryData($productId, $productCategoryIndexData)); - } - - /** - * Check value. - * - * @param mixed $value - * @param Attribute $attribute - * @param string $storeId - * @return array|mixed|null|string - */ - protected function checkValue($value, $attribute, $storeId) - { - if (in_array($attribute->getBackendType(), ['datetime', 'timestamp']) - || $attribute->getFrontendInput() === 'date') { - return $this->dateFieldType->formatDate($storeId, $value); - } elseif ($attribute->getFrontendInput() === 'multiselect') { - return str_replace(',', ' ', $value); - } else { - return $value; - } - } - - /** - * Prepare tier price data for product - * - * @param array $data - * @return array - */ - protected function getProductTierPriceData($data) - { - $result = []; - if (!empty($data)) { - $i = 0; - foreach ($data as $tierPrice) { - $result['tier_price_id_' . $i] = $tierPrice['price_id']; - $result['tier_website_id_' . $i] = $tierPrice['website_id']; - $result['tier_all_groups_' . $i] = $tierPrice['all_groups']; - $result['tier_cust_group_' . $i] = $tierPrice['cust_group'] == GroupInterface::CUST_GROUP_ALL - ? '' : $tierPrice['cust_group']; - $result['tier_price_qty_' . $i] = $tierPrice['price_qty']; - $result['tier_website_price_' . $i] = $tierPrice['website_price']; - $result['tier_price_' . $i] = $tierPrice['price']; - $i++; - } - } - - return $result; - } - - /** - * Prepare media gallery data for product - * - * @param array $media - * @param array $roles - * @return array - */ - protected function getProductMediaGalleryData($media, $roles) - { - $result = []; - - if (!empty($media['images'])) { - $i = 0; - foreach ($media['images'] as $data) { - if ($data['media_type'] === 'image') { - $result['image_file_' . $i] = $data['file']; - $result['image_position_' . $i] = $data['position']; - $result['image_disabled_' . $i] = $data['disabled']; - $result['image_label_' . $i] = $data['label']; - $result['image_title_' . $i] = $data['label']; - $result['image_base_image_' . $i] = $this->getMediaRoleImage($data['file'], $roles); - $result['image_small_image_' . $i] = $this->getMediaRoleSmallImage($data['file'], $roles); - $result['image_thumbnail_' . $i] = $this->getMediaRoleThumbnail($data['file'], $roles); - $result['image_swatch_image_' . $i] = $this->getMediaRoleSwatchImage($data['file'], $roles); - } else { - $result['video_file_' . $i] = $data['file']; - $result['video_position_' . $i] = $data['position']; - $result['video_disabled_' . $i] = $data['disabled']; - $result['video_label_' . $i] = $data['label']; - $result['video_title_' . $i] = $data['video_title']; - $result['video_base_image_' . $i] = $this->getMediaRoleImage($data['file'], $roles); - $result['video_small_image_' . $i] = $this->getMediaRoleSmallImage($data['file'], $roles); - $result['video_thumbnail_' . $i] = $this->getMediaRoleThumbnail($data['file'], $roles); - $result['video_swatch_image_' . $i] = $this->getMediaRoleSwatchImage($data['file'], $roles); - $result['video_url_' . $i] = $data['video_url']; - $result['video_description_' . $i] = $data['video_description']; - $result['video_metadata_' . $i] = $data['video_metadata']; - $result['video_provider_' . $i] = $data['video_provider']; - } - $i++; - } - } - return $result; - } - - /** - * Get media role image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_IMAGE] ? '1' : '0'; - } - - /** - * Get media role small image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleSmallImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_SMALL_IMAGE] ? '1' : '0'; - } - - /** - * Get media role thumbnail. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleThumbnail($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_THUMBNAIL] ? '1' : '0'; - } - - /** - * Get media role swatch image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleSwatchImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_SWATCH_IMAGE] ? '1' : '0'; - } - - /** - * Prepare quantity and stock status for product - * - * @param array $data - * @return array - */ - protected function getQtyAndStatus($data) - { - $result = []; - if (!is_array($data)) { - $result['is_in_stock'] = $data ? 1 : 0; - $result['qty'] = $data; - } else { - $result['is_in_stock'] = $data['is_in_stock'] ? 1 : 0; - $result['qty'] = $data['qty']; - } - return $result; - } - - /** - * Prepare price index for product - * - * @param int $productId - * @param int $storeId - * @param array $priceIndexData - * @return array - */ - protected function getProductPriceData($productId, $storeId, array $priceIndexData) - { - $result = []; - if (array_key_exists($productId, $priceIndexData)) { - $productPriceIndexData = $priceIndexData[$productId]; - foreach ($productPriceIndexData as $customerGroupId => $price) { - $fieldName = $this->fieldMapper->getFieldName( - 'price', - ['customerGroupId' => $customerGroupId, 'websiteId' => $storeId] - ); - $result[$fieldName] = sprintf('%F', $price); - } - } - return $result; - } - - /** - * Prepare category index data for product - * - * @param int $productId - * @param array $categoryIndexData - * @return array - */ - protected function getProductCategoryData($productId, array $categoryIndexData) - { - $result = []; - $categoryIds = []; - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - $result = $indexData; - } - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - foreach ($indexData as $categoryData) { - $categoryIds[] = (int)$categoryData['id']; - } - if (count($categoryIds)) { - $result = ['category_ids' => implode(' ', $categoryIds)]; - $positionAttribute = $this->attributeAdapterProvider->getByAttributeCode('position'); - $categoryNameAttribute = $this->attributeAdapterProvider->getByAttributeCode('category_name'); - foreach ($indexData as $data) { - $categoryPositionKey = $this->fieldNameResolver->getFieldName( - $positionAttribute, - ['categoryId' => $data['id']] - ); - $categoryNameKey = $this->fieldNameResolver->getFieldName( - $categoryNameAttribute, - ['categoryId' => $data['id']] - ); - $result[$categoryPositionKey] = $data['position']; - $result[$categoryNameKey] = $data['name']; - } - } - } - return $result; - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php deleted file mode 100644 index 0fc6100979f21..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper; - -use Magento\AdvancedSearch\Model\Client\ClientResolver; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; - -/** - * Proxy for product data mappers - */ -class ProductDataMapperProxy implements DataMapperInterface -{ - /** - * @var ClientResolver - */ - private $clientResolver; - - /** - * @var DataMapperInterface[] - */ - private $dataMappers; - - /** - * CategoryFieldsProviderProxy constructor. - * @param ClientResolver $clientResolver - * @param DataMapperInterface[] $dataMappers - */ - public function __construct( - ClientResolver $clientResolver, - array $dataMappers - ) { - $this->clientResolver = $clientResolver; - $this->dataMappers = $dataMappers; - } - - /** - * @return DataMapperInterface - */ - private function getDataMapper() - { - return $this->dataMappers[$this->clientResolver->getCurrentEngine()]; - } - - /** - * @inheritdoc - */ - public function map($entityId, array $entityIndexData, $storeId, $context = []) - { - return $this->getDataMapper()->map($entityId, $entityIndexData, $storeId, $context); - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php deleted file mode 100644 index dbb37f3363b3d..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\DataMapper; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Model\Config; - -/** - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class DataMapperResolver implements DataMapperInterface -{ - /** - * Object Manager instance - * - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var string[] - */ - private $dataMappers; - - /** - * Data Mapper instance - * - * @var DataMapperInterface - */ - private $dataMapperEntity; - - /** - * @param ObjectManagerInterface $objectManager - * @param string[] $dataMappers - */ - public function __construct( - ObjectManagerInterface $objectManager, - array $dataMappers = [] - ) { - $this->objectManager = $objectManager; - $this->dataMappers = $dataMappers; - } - - /** - * {@inheritdoc} - */ - public function map( - $entityId, - array $entityIndexData, - $storeId, - $context = [] - ) { - $entityType = isset($context['entityType']) ? $context['entityType'] : Config::ELASTICSEARCH_TYPE_DEFAULT; - return $this->getEntity($entityType)->map($entityId, $entityIndexData, $storeId, $context); - } - - /** - * Get instance of current data mapper - * - * @param string $entityType - * @return DataMapperInterface - * @throws \Exception - */ - private function getEntity($entityType = '') - { - if (empty($this->dataMapperEntity)) { - if (empty($entityType)) { - throw new \Exception( - 'No entity type given' - ); - } - if (!isset($this->dataMappers[$entityType])) { - throw new \LogicException( - 'There is no such data mapper: ' . $entityType - ); - } - $dataMapperClass = $this->dataMappers[$entityType]; - $this->dataMapperEntity = $this->objectManager->create($dataMapperClass); - if (!($this->dataMapperEntity instanceof DataMapperInterface)) { - throw new \InvalidArgumentException( - 'Data mapper must implement \Magento\Elasticsearch\Model\Adapter\DataMapperInterface' - ); - } - } - return $this->dataMapperEntity; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index 0640b61f9551e..5ab6669a34cc4 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -29,12 +29,6 @@ class Elasticsearch /**#@-*/ protected $connectionManager; - /** - * @var DataMapperInterface - * @deprecated 100.2.0 Will be replaced with BatchDataMapperInterface - */ - protected $documentDataMapper; - /** * @var \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver */ @@ -76,10 +70,7 @@ class Elasticsearch private $batchDocumentDataMapper; /** - * Elasticsearch constructor. - * * @param \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager - * @param DataMapperInterface $documentDataMapper * @param FieldMapperInterface $fieldMapper * @param \Magento\Elasticsearch\Model\Config $clientConfig * @param Index\BuilderInterface $indexBuilder @@ -91,7 +82,6 @@ class Elasticsearch */ public function __construct( \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager, - DataMapperInterface $documentDataMapper, FieldMapperInterface $fieldMapper, \Magento\Elasticsearch\Model\Config $clientConfig, \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder, @@ -101,7 +91,6 @@ public function __construct( $options = [] ) { $this->connectionManager = $connectionManager; - $this->documentDataMapper = $documentDataMapper; $this->fieldMapper = $fieldMapper; $this->clientConfig = $clientConfig; $this->indexBuilder = $indexBuilder; diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index 962c19595c4c0..3766d825aefb5 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -67,7 +67,6 @@ class Config implements ClientOptionsInterface private $engineList; /** - * Config constructor. * @param ScopeConfigInterface $scopeConfig * @param ClientResolver $clientResolver * @param EngineResolverInterface $engineResolver diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php new file mode 100644 index 0000000000000..69442631a87bf --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php @@ -0,0 +1,601 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Client; + +use Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Test elasticsearch client methods. + */ +class ElasticsearchTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch + */ + protected $model; + + /** + * @var \Elasticsearch\Client|\PHPUnit_Framework_MockObject_MockObject + */ + protected $elasticsearchClientMock; + + /** + * @var \Elasticsearch\Namespaces\IndicesNamespace|\PHPUnit_Framework_MockObject_MockObject + */ + protected $indicesMock; + + /** + * @var ObjectManagerHelper + */ + protected $objectManager; + + /** + * Setup + * + * @return void + */ + protected function setUp(): void + { + $this->elasticsearchClientMock = $this->getMockBuilder(\Elasticsearch\Client::class) + ->setMethods( + [ + 'indices', + 'ping', + 'bulk', + 'search', + 'scroll', + 'suggest', + 'info', + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $this->indicesMock = $this->getMockBuilder(\Elasticsearch\Namespaces\IndicesNamespace::class) + ->setMethods( + [ + 'exists', + 'getSettings', + 'create', + 'delete', + 'putMapping', + 'deleteMapping', + 'stats', + 'updateAliases', + 'existsAlias', + 'getAlias', + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $this->elasticsearchClientMock->expects($this->any()) + ->method('indices') + ->willReturn($this->indicesMock); + $this->elasticsearchClientMock->expects($this->any()) + ->method('ping') + ->willReturn(true); + $this->elasticsearchClientMock->expects($this->any()) + ->method('info') + ->willReturn(['version' => ['number' => '5.0.0']]); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + \Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions(), + 'elasticsearchClient' => $this->elasticsearchClientMock + ] + ); + } + + /** + * Test ping functionality + */ + public function testPing() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(true); + $this->assertEquals(true, $this->model->ping()); + } + + /** + * Test validation of connection parameters + */ + public function testTestConnection() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(true); + $this->assertEquals(true, $this->model->testConnection()); + } + + /** + * Test validation of connection parameters returns false + */ + public function testTestConnectionFalse() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(false); + $this->assertEquals(true, $this->model->testConnection()); + } + + /** + * Test bulkQuery() method + */ + public function testBulkQuery() + { + $this->elasticsearchClientMock->expects($this->once()) + ->method('bulk') + ->with([]); + $this->model->bulkQuery([]); + } + + /** + * Test createIndex() method, case when such index exists + */ + public function testCreateIndexExists() + { + $this->indicesMock->expects($this->once()) + ->method('create') + ->with( + [ + 'index' => 'indexName', + 'body' => [], + ] + ); + $this->model->createIndex('indexName', []); + } + + /** + * Test deleteIndex() method. + */ + public function testDeleteIndex() + { + $this->indicesMock->expects($this->once()) + ->method('delete') + ->with(['index' => 'indexName']); + $this->model->deleteIndex('indexName'); + } + + /** + * Test isEmptyIndex() method. + */ + public function testIsEmptyIndex() + { + $indexName = 'magento2_index'; + $stats['indices'][$indexName]['primaries']['docs']['count'] = 0; + + $this->indicesMock->expects($this->once()) + ->method('stats') + ->with(['index' => $indexName, 'metric' => 'docs']) + ->willReturn($stats); + $this->assertTrue($this->model->isEmptyIndex($indexName)); + } + + /** + * Test isEmptyIndex() method returns false. + */ + public function testIsEmptyIndexFalse() + { + $indexName = 'magento2_index'; + $stats['indices'][$indexName]['primaries']['docs']['count'] = 1; + + $this->indicesMock->expects($this->once()) + ->method('stats') + ->with(['index' => $indexName, 'metric' => 'docs']) + ->willReturn($stats); + $this->assertFalse($this->model->isEmptyIndex($indexName)); + } + + /** + * Test updateAlias() method with new index. + */ + public function testUpdateAlias() + { + $alias = 'alias1'; + $index = 'index1'; + + $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $index]]; + + $this->indicesMock->expects($this->once()) + ->method('updateAliases') + ->with($params); + $this->model->updateAlias($alias, $index); + } + + /** + * Test updateAlias() method with new and old index. + */ + public function testUpdateAliasRemoveOldIndex() + { + $alias = 'alias1'; + $newIndex = 'index1'; + $oldIndex = 'indexOld'; + + $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]]; + $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; + + $this->indicesMock->expects($this->once()) + ->method('updateAliases') + ->with($params); + $this->model->updateAlias($alias, $newIndex, $oldIndex); + } + + /** + * Test indexExists() method, case when no such index exists + */ + public function testIndexExists() + { + $this->indicesMock->expects($this->once()) + ->method('exists') + ->with(['index' => 'indexName']) + ->willReturn(true); + $this->model->indexExists('indexName'); + } + + /** + * Tests existsAlias() method checking for alias. + */ + public function testExistsAlias() + { + $alias = 'alias1'; + $params = ['name' => $alias]; + $this->indicesMock->expects($this->once()) + ->method('existsAlias') + ->with($params) + ->willReturn(true); + $this->assertTrue($this->model->existsAlias($alias)); + } + + /** + * Tests existsAlias() method checking for alias and index. + */ + public function testExistsAliasWithIndex() + { + $alias = 'alias1'; + $index = 'index1'; + $params = ['name' => $alias, 'index' => $index]; + $this->indicesMock->expects($this->once()) + ->method('existsAlias') + ->with($params) + ->willReturn(true); + $this->assertTrue($this->model->existsAlias($alias, $index)); + } + + /** + * Test getAlias() method. + */ + public function testGetAlias() + { + $alias = 'alias1'; + $params = ['name' => $alias]; + $this->indicesMock->expects($this->once()) + ->method('getAlias') + ->with($params) + ->willReturn([]); + $this->assertEquals([], $this->model->getAlias($alias)); + } + + /** + * Test createIndexIfNotExists() method, case when operation fails + * @expectedException \Exception + */ + public function testCreateIndexFailure() + { + $this->indicesMock->expects($this->once()) + ->method('create') + ->with( + [ + 'index' => 'indexName', + 'body' => [], + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->createIndex('indexName', []); + } + + /** + * Test testAddFieldsMapping() method + */ + public function testAddFieldsMapping() + { + $this->indicesMock->expects($this->once()) + ->method('putMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + 'body' => [ + 'product' => [ + '_all' => [ + 'enabled' => true, + 'type' => 'text', + ], + 'properties' => [ + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'integer', + 'index' => true + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => true, + ], + ], + ], + ], + ], + ], + ] + ); + $this->model->addFieldsMapping( + [ + 'name' => [ + 'type' => 'text', + ], + ], + 'indexName', + 'product' + ); + } + + /** + * Test testAddFieldsMapping() method + * @expectedException \Exception + */ + public function testAddFieldsMappingFailure() + { + $this->indicesMock->expects($this->once()) + ->method('putMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + 'body' => [ + 'product' => [ + '_all' => [ + 'enabled' => true, + 'type' => 'text', + ], + 'properties' => [ + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'integer', + 'index' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => true, + ], + ], + ] + ], + ], + ], + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->addFieldsMapping( + [ + 'name' => [ + 'type' => 'text', + ], + ], + 'indexName', + 'product' + ); + } + + /** + * Test deleteMapping() method + */ + public function testDeleteMapping() + { + $this->indicesMock->expects($this->once()) + ->method('deleteMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + ] + ); + $this->model->deleteMapping( + 'indexName', + 'product' + ); + } + + /** + * Ensure that configuration returns correct url. + * + * @param array $options + * @param string $expectedResult + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + * @dataProvider getOptionsDataProvider + */ + public function testBuildConfig(array $options, $expectedResult): void + { + $buildConfig = new Elasticsearch($options); + $config = $this->getPrivateMethod(Elasticsearch::class, 'buildConfig'); + $result = $config->invoke($buildConfig, $options); + $this->assertEquals($expectedResult, $result['hosts'][0]); + } + + /** + * Return private method for elastic search class. + * + * @param $className + * @param $methodName + * @return \ReflectionMethod + * @throws \ReflectionException + */ + private function getPrivateMethod($className, $methodName) + { + $reflector = new \ReflectionClass($className); + $method = $reflector->getMethod($methodName); + $method->setAccessible(true); + + return $method; + } + + /** + * Test deleteMapping() method + * @expectedException \Exception + */ + public function testDeleteMappingFailure() + { + $this->indicesMock->expects($this->once()) + ->method('deleteMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->deleteMapping( + 'indexName', + 'product' + ); + } + + /** + * Test query() method + * @return void + */ + public function testQuery() + { + $query = 'test phrase query'; + $this->elasticsearchClientMock->expects($this->once()) + ->method('search') + ->with([$query]) + ->willReturn([]); + $this->assertEquals([], $this->model->query([$query])); + } + + /** + * Test suggest() method + * @return void + */ + public function testSuggest() + { + $query = 'query'; + $this->elasticsearchClientMock->expects($this->once()) + ->method('suggest') + ->willReturn([]); + $this->assertEquals([], $this->model->suggest($query)); + } + + /** + * Get options data provider. + */ + public function getOptionsDataProvider() + { + return [ + [ + 'without_protocol' => [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'http://localhost:9200' + ], + [ + 'with_protocol' => [ + 'hostname' => 'https://localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'https://localhost:9200' + ] + ]; + } + + /** + * Get elasticsearch client options + * + * @return array + */ + protected function getOptions() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } + + /** + * @return array + */ + protected function getEmptyIndexOption() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'index' => '', + 'timeout' => 15, + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php deleted file mode 100644 index ca5d570d735f5..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php +++ /dev/null @@ -1,237 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\Container; - -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -/** - * Unit test for Magento\Elasticsearch\Model\Adapter\Container\Attribute - */ -class AttributeTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Elasticsearch\Model\Adapter\Container\Attribute - */ - private $attribute; - - /** - * @var Collection|\PHPUnit_Framework_MockObject_MockObject - */ - private $collectionMock; - - /** - * {@inheritdoc} - */ - protected function setUp() - { - $this->collectionMock = $this->getMockBuilder(Collection::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->attribute = $objectManager->getObject( - \Magento\Elasticsearch\Model\Adapter\Container\Attribute::class, - [ - 'attributeCollection' => $this->collectionMock, - ] - ); - } - - /** - * @return void - */ - public function testGetAttributeCodeById() - { - $attributeId = 555; - $attributeCode = 'test_attr_code1'; - $expected = 'test_attr_code1'; - $this->mockAttributeById($attributeId, $attributeCode); - $result = $this->attribute->getAttributeCodeById($attributeId); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetOptionsAttributeCodeById() - { - $attributeId = 'options'; - $expected = 'options'; - $result = $this->attribute->getAttributeCodeById($attributeId); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetAttributeIdByCode() - { - $attributeId = 100; - $attributeCode = 'test_attribute_code'; - $this->mockAttributeByCode($attributeId, $attributeCode); - $result = $this->attribute->getAttributeIdByCode($attributeCode); - $this->assertEquals($attributeId, $result); - } - - /** - * Test getAttributeIdByCode() method. - */ - public function testGetOptionsAttributeIdByCode() - { - $attributeCode = 'options'; - $expected = 'options'; - $result = $this->attribute->getAttributeIdByCode($attributeCode); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetMultipleAttributeIdsByCode() - { - $firstAttributeId = 100; - $firstAttributeCode = 'test_attribute_code_100'; - $this->mockAttributeByCode($firstAttributeId, $firstAttributeCode, 0); - $this->assertEquals($firstAttributeId, $this->attribute->getAttributeIdByCode($firstAttributeCode)); - - $secondAttributeId = 200; - $secondAttributeCode = 'test_attribute_code_200'; - $this->mockAttributeByCode($secondAttributeId, $secondAttributeCode, 0); - $this->assertEquals($secondAttributeId, $this->attribute->getAttributeIdByCode($secondAttributeCode)); - } - - /** - * @return void - */ - public function testGetAttributeByIdTwice() - { - $attributeId = 555; - $attributeCode = 'test_attr_code2'; - $expected = 'test_attr_code2'; - $this->mockAttributeById($attributeId, $attributeCode, 0); - $this->assertEquals($expected, $this->attribute->getAttributeCodeById($attributeId)); - $this->assertEquals($expected, $this->attribute->getAttributeCodeById($attributeId)); - } - - /** - * @return void - */ - public function testGetAttributeByIdCachedInGetAttributeByCode() - { - $attributeId = 100; - $attributeCode = 'test_attribute_code'; - $this->mockAttributeByCode($attributeId, $attributeCode); - $this->assertEquals($attributeId, $this->attribute->getAttributeIdByCode($attributeCode)); - $this->assertEquals($attributeCode, $this->attribute->getAttributeCodeById($attributeId)); - } - - /** - * @return void - */ - public function testGetAttribute() - { - $attributeCode = 'attr_code_120'; - $attribute = $this->createAttributeMock(120, $attributeCode); - $attributes = [ - $attribute - ]; - $this->mockAttributes($attributes); - $this->assertEquals($attribute, $this->attribute->getAttribute($attributeCode)); - } - - /** - * @return void - */ - public function testGetUnknownAttribute() - { - $attributeCode = 'attr_code_120'; - $attributes = [ - $this->createAttributeMock(120, 'attribute_code') - ]; - $this->mockAttributes($attributes); - $this->assertEquals(null, $this->attribute->getAttribute($attributeCode)); - } - - /** - * @return void - */ - public function testGetAttributes() - { - $attributes = [ - 'attr_1_mock' => $this->createAttributeMock(1, 'attr_1_mock'), - 'attr_20_mock' => $this->createAttributeMock(20, 'attr_20_mock'), - 'attr_25_mock' => $this->createAttributeMock(25, 'attr_25_mock'), - 'attr_40_mock' => $this->createAttributeMock(40, 'attr_40_mock'), - 'attr_73_mock' => $this->createAttributeMock(73, 'attr_73_mock'), - 'attr_52_mock' => $this->createAttributeMock(52, 'attr_52_mock'), - 'attr_97_mock' => $this->createAttributeMock(97, 'attr_97_mock'), - ]; - $this->mockAttributes($attributes); - $this->assertEquals($attributes, $this->attribute->getAttributes()); - } - - /** - * @param array $attributes - * @return void - */ - private function mockAttributes(array $attributes) - { - $this->collectionMock->expects($this->once()) - ->method('getIterator') - ->willReturn(new \ArrayIterator($attributes)); - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @param int $sequence - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function mockAttributeById($attributeId, $attributeCode, $sequence = 0) - { - $attribute = $this->createAttributeMock($attributeId, $attributeCode); - $this->collectionMock->expects($this->at($sequence)) - ->method('getItemById') - ->with($attributeId) - ->willReturn($attribute); - return $attribute; - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @param int $sequence - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function mockAttributeByCode($attributeId, $attributeCode, $sequence = 0) - { - $attribute = $this->createAttributeMock($attributeId, $attributeCode); - $this->collectionMock->expects($this->at($sequence)) - ->method('getItemByColumnValue') - ->with('attribute_code', $attributeCode) - ->willReturn($attribute); - return $attribute; - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function createAttributeMock($attributeId, $attributeCode) - { - $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->setMethods(['getAttributeCode', 'getId']) - ->disableOriginalConstructor() - ->getMock(); - $attribute->method('getAttributeCode') - ->willReturn($attributeCode); - $attribute->method('getId') - ->willReturn($attributeId); - return $attribute; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php index 153deb8bff845..e549f254cb235 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php @@ -73,11 +73,6 @@ class ElasticsearchTest extends \PHPUnit\Framework\TestCase */ protected $indexNameResolver; - /** - * @var \Magento\Elasticsearch\Model\Adapter\DataMapperInterface|PHPUnit_Framework_MockObject_MockObject - */ - private $documentDataMapper; - /** * Setup * @@ -91,9 +86,6 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['getConnection']) ->getMock(); - $this->documentDataMapper = $this->getMockBuilder( - \Magento\Elasticsearch\Model\Adapter\DataMapperInterface::class - )->disableOriginalConstructor()->getMock(); $this->fieldMapper = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Elasticsearch/etc/config.xml b/app/code/Magento/Elasticsearch/etc/config.xml index 1d55730fce378..93778cc81c6f4 100644 --- a/app/code/Magento/Elasticsearch/etc/config.xml +++ b/app/code/Magento/Elasticsearch/etc/config.xml @@ -9,13 +9,6 @@ <default> <catalog> <search> -<!-- <elasticsearch_server_hostname>localhost</elasticsearch_server_hostname>--> -<!-- <elasticsearch_server_port>9200</elasticsearch_server_port>--> -<!-- <elasticsearch_index_prefix>magento2</elasticsearch_index_prefix>--> -<!-- <elasticsearch_enable_auth>0</elasticsearch_enable_auth>--> -<!-- <elasticsearch_server_timeout>15</elasticsearch_server_timeout>--> -<!-- <elasticsearch_minimum_should_match></elasticsearch_minimum_should_match>--> - <elasticsearch5_server_hostname>localhost</elasticsearch5_server_hostname> <elasticsearch5_server_port>9200</elasticsearch5_server_port> <elasticsearch5_index_prefix>magento2</elasticsearch5_index_prefix> diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 5ce0c55fb454a..7983ec3f5ceed 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -136,7 +136,6 @@ </argument> </arguments> </type> - <preference for="Magento\Elasticsearch\Model\Adapter\DataMapperInterface" type="Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver" /> <virtualType name="additionalFieldsProviderForElasticsearch" type="Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProvider"> <arguments> <argument name="fieldsProviders" xsi:type="array"> @@ -174,13 +173,6 @@ </argument> </arguments> </type> - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> - <arguments> - <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> - </argument> - </arguments> - </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> <arguments> <argument name="productFieldMappers" xsi:type="array"> @@ -364,12 +356,6 @@ <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> </arguments> </type> - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper"> - <arguments> - <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> - </arguments> - </type> -<!-- todo check if we need this configuration--> <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 19d3b709cb2ea..a1ba8549a47e5 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -29,23 +29,6 @@ </argument> </arguments> </type> - - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> - <arguments> - <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> - </argument> - </arguments> - </type> - - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> - <arguments> - <argument name="productFieldMappers" xsi:type="array"> - <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\ProductFieldMapper</item> - </argument> - </arguments> - </type> - <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> <arguments> <argument name="clientFactories" xsi:type="array"> diff --git a/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php index 8387bcf0296ff..2fe5bc3f4a597 100644 --- a/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php +++ b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -9,8 +9,6 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver - as DefaultFiledNameResolver; /** * Default name resolver for Elasticsearch 7 @@ -18,15 +16,15 @@ class DefaultResolver implements ResolverInterface { /** - * @var DefaultFiledNameResolver + * @var ResolverInterface */ private $baseResolver; /** * DefaultResolver constructor. - * @param DefaultFiledNameResolver $baseResolver + * @param ResolverInterface $baseResolver */ - public function __construct(DefaultFiledNameResolver $baseResolver) + public function __construct(ResolverInterface $baseResolver) { $this->baseResolver = $baseResolver; } diff --git a/app/code/Magento/Elasticsearch7/etc/di.xml b/app/code/Magento/Elasticsearch7/etc/di.xml index a35fef8c66f0d..4f8129f8209f4 100644 --- a/app/code/Magento/Elasticsearch7/etc/di.xml +++ b/app/code/Magento/Elasticsearch7/etc/di.xml @@ -29,15 +29,6 @@ </argument> </arguments> </type> - - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> - <arguments> - <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> - </argument> - </arguments> - </type> - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> <arguments> <argument name="productFieldMappers" xsi:type="array"> @@ -139,7 +130,6 @@ </argument> </arguments> </type> -<!--todo rename!!!--> <virtualType name="\Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> @@ -152,7 +142,11 @@ </argument> </arguments> </virtualType> - + <type name="Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver"> + <arguments> + <argument name="baseResolver" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</argument> + </arguments> + </type> <virtualType name="Magento\Elasticsearch7\Model\Adapter\FieldMapper\ProductFieldMapper" type="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper"> <arguments> diff --git a/app/code/Magento/Elasticsearch7/etc/module.xml b/app/code/Magento/Elasticsearch7/etc/module.xml index c5ad0d70cd7d1..836068b59ed1e 100644 --- a/app/code/Magento/Elasticsearch7/etc/module.xml +++ b/app/code/Magento/Elasticsearch7/etc/module.xml @@ -8,9 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_Elasticsearch7"> <sequence> - <module name="Magento_CatalogSearch"/> - <module name="Magento_Search"/> - <module name="Magento_AdvancedSearch"/> <module name="Magento_Elasticsearch"/> </sequence> </module> diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php index fc24658bb01fa..2c16f5b0b30ff 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php @@ -13,7 +13,7 @@ use Zend\Stdlib\Parameters; /** - * Test cases for catalog advanced search using mysql search engine. + * Test cases for catalog advanced search using search engine. * * @magentoDbIsolation disabled * @magentoAppIsolation enabled @@ -37,7 +37,6 @@ protected function setUp() /** * Advanced search test by difference product attributes. * - * @magentoConfigFixture default/catalog/search/engine mysql * @magentoAppArea frontend * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php index 0068d6cbaa015..279d718131dcf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\TestCase\AbstractController; /** - * Test cases for catalog quick search using mysql search engine. + * Test cases for catalog quick search using search engine. * * @magentoDbIsolation disabled * @magentoAppIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php index c090f4ea0183c..8f3f9b3b19c0f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php @@ -17,7 +17,7 @@ use PHPUnit\Framework\TestCase; /** - * Search products by attribute value using mysql search engine. + * Search products by attribute value search engine. */ class DataProviderTest extends TestCase { @@ -65,7 +65,6 @@ protected function setUp() /** * Search product by custom attribute value. * - * @magentoConfigFixture default/catalog/search/engine mysql * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php * @magentoDbIsolation disabled diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php index 6884be3b04d14..701fb2c17d966 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\TestCase; /** - * Test cases related to find configurable product via quick search using mysql search engine. + * Test cases related to find configurable product via quick search using search engine. * * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php @@ -68,7 +68,6 @@ public function testChildProductsHasNotFoundedByQuery(): void * Assert that child product of configurable will be available by search after * set to product visibility by catalog and search using mysql search engine. * - * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productAvailabilityInSearchByVisibilityDataProvider * * @param int $visibility @@ -113,7 +112,6 @@ public function productAvailabilityInSearchByVisibilityDataProvider(): array /** * Assert that configurable product was found by option value using mysql search engine. * - * @magentoConfigFixture default/catalog/search/engine mysql * * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php index fd0ce8e684665..d3fa3569ac004 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php @@ -20,7 +20,7 @@ class AdapterTest extends \PHPUnit\Framework\TestCase private $adapter; /** - * @var \Magento\Elasticsearch6\Model\Client\Elasticsearch|\PHPUnit\Framework\MockObject\MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit\Framework\MockObject\MockObject */ private $clientMock; @@ -43,7 +43,8 @@ protected function setUp() $contentManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) ->disableOriginalConstructor() ->getMock(); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) + $this->clientMock = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['query', 'testConnection']) ->disableOriginalConstructor() ->getMock(); $contentManager diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 6954983d049a5..03884669c73b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4249,5 +4249,12 @@ ['Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper'], ['Magento\Elasticsearch\Model\Client\Elasticsearch'], ['Magento\Elasticsearch\SearchAdapter\Aggregation\Interval'], - + ['Magento\Elasticsearch\SearchAdapter\Mapper'], + ['Magento\Elasticsearch6\Model\DataProvider\Suggestions'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType'], + ['Magento\Elasticsearch\Model\Adapter\DataMapperInterface'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper'], + ['Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver'], + ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'] ]; diff --git a/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd b/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd index c41e0afbd8054..6ca93d3ab78c3 100644 --- a/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd +++ b/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd @@ -14,6 +14,7 @@ <xs:complexContent> <xs:extension base="argumentType"> <xs:attribute name="translatable" use="optional" type="xs:boolean" /> + <xs:attribute name="sortOrder" use="optional" type="xs:integer"/> </xs:extension> </xs:complexContent> </xs:complexType> From 80a321de0c054ef546478e2b741d6eb1ddb4a78e Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Thu, 12 Mar 2020 15:56:51 -0500 Subject: [PATCH 348/734] MC-32120: Catalog price rules are not included in CartItemPrices - Added Catalogrule graphql module - added api functional tests --- .../Magento/CatalogRuleGraphQl/composer.json | 22 +++++ .../CatalogRuleGraphQl/etc/graphql/events.xml | 15 ++++ .../Magento/CatalogRuleGraphQl/etc/module.xml | 14 +++ .../CatalogRuleGraphQl/registration.php | 9 ++ composer.json | 1 + .../GraphQl/Quote/Guest/CartTotalsTest.php | 87 +++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 app/code/Magento/CatalogRuleGraphQl/composer.json create mode 100644 app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml create mode 100644 app/code/Magento/CatalogRuleGraphQl/etc/module.xml create mode 100644 app/code/Magento/CatalogRuleGraphQl/registration.php diff --git a/app/code/Magento/CatalogRuleGraphQl/composer.json b/app/code/Magento/CatalogRuleGraphQl/composer.json new file mode 100644 index 0000000000000..65d504271a976 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-catalog-rule-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-catalog-rule": "*", + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\CatalogRuleGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml new file mode 100644 index 0000000000000..0d0a1fef5354f --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="catalog_product_get_final_price"> + <observer name="catalogrule" instance="Magento\CatalogRule\Observer\ProcessFrontFinalPriceObserver" /> + </event> + <event name="prepare_catalog_product_collection_prices"> + <observer name="catalogrule" instance="Magento\CatalogRule\Observer\PrepareCatalogProductCollectionPricesObserver" /> + </event> +</config> diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/module.xml b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml new file mode 100644 index 0000000000000..8b4cad2c9f30f --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_CatalogRuleGraphQl"> + <sequence> + <module name="Magento_CatalogRule"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/CatalogRuleGraphQl/registration.php b/app/code/Magento/CatalogRuleGraphQl/registration.php new file mode 100644 index 0000000000000..df4dcd1cb47b6 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRuleGraphQl', __DIR__); diff --git a/composer.json b/composer.json index ac005f9da6a1e..27f8680c30a4c 100644 --- a/composer.json +++ b/composer.json @@ -135,6 +135,7 @@ "magento/module-catalog-inventory": "*", "magento/module-catalog-inventory-graph-ql": "*", "magento/module-catalog-rule": "*", + "magento/module-catalog-rule-graph-ql": "*", "magento/module-catalog-rule-configurable": "*", "magento/module-catalog-search": "*", "magento/module-catalog-url-rewrite": "*", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php index 20022d48e83be..cc34ae4728e5a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php @@ -64,6 +64,90 @@ public function testGetCartTotalsWithTaxApplied() self::assertEquals('USD', $appliedTaxesResponse[0]['amount']['currency']); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(18, $cartItem['prices']['row_total_including_tax']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(18, $pricesResponse['grand_total']['value']); + self::assertEquals(18, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleAndTaxApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(19.35, $cartItem['prices']['row_total_including_tax']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(19.35, $pricesResponse['grand_total']['value']); + self::assertEquals(19.35, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/cart_rule_discount_no_coupon.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleAndCartRuleApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(18, $cartItem['prices']['row_total_including_tax']['value']); + self::assertEquals(9, $cartItem['prices']['total_item_discount']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(9, $pricesResponse['grand_total']['value']); + self::assertEquals(18, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(9, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + /** * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -190,6 +274,9 @@ private function getQuery(string $maskedQuoteId): string value currency } + total_item_discount { + value + } } } prices { From 9c4147afdc17c061ee431bc20fdc41cad9f9e433 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 12 Mar 2020 16:06:40 -0500 Subject: [PATCH 349/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 5cdc9a1eac6aa..2bbcfb9090a9c 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "9383730e43898d9807e00b8039684dd3686a9e91" + "reference": "ab4ccebee272df77996978a909ade95066bcd0f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/9383730e43898d9807e00b8039684dd3686a9e91", - "reference": "9383730e43898d9807e00b8039684dd3686a9e91", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ab4ccebee272df77996978a909ade95066bcd0f6", + "reference": "ab4ccebee272df77996978a909ade95066bcd0f6", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-12T16:34:50+00:00" + "time": "2020-03-12T20:46:06+00:00" }, { "name": "mikey179/vfsstream", From 7c4cbeff59d313076a13d4ef2a83cd135642fa5c Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Thu, 12 Mar 2020 16:44:19 -0500 Subject: [PATCH 350/734] MC-32388: Add url_path as a possible filter in categoryList query - refactor only. no functional change --- .../Model/Category/CategoryFilter.php | 100 ++++++++++-------- .../CategoryFilterAttributesForAst.php | 87 +++++++++++++++ .../Model/Resolver/CategoryList.php | 15 +-- app/code/Magento/CatalogGraphQl/etc/di.xml | 1 + .../GraphQl/Catalog/CategoryListTest.php | 7 +- 5 files changed, 149 insertions(+), 61 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 2c03550404ae0..314edf263aa36 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -7,13 +7,15 @@ namespace Magento\CatalogGraphQl\Model\Category; +use Magento\Catalog\Api\CategoryListInterface; use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\ArgumentApplier\Filter; +use Magento\Search\Model\Query; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; -use Magento\Search\Model\Query; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; /** * Category filter allows to filter collection using 'id, url_key, name' from search criteria. @@ -25,78 +27,86 @@ class CategoryFilter */ private $scopeConfig; + /** + * @var CategoryListInterface + */ + private $categoryList; + + /** + * @var Builder + */ + private $searchCriteriaBuilder; + /** * @param ScopeConfigInterface $scopeConfig + * @param CategoryListInterface $categoryList + * @param Builder $searchCriteriaBuilder */ public function __construct( - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + CategoryListInterface $categoryList, + Builder $searchCriteriaBuilder ) { $this->scopeConfig = $scopeConfig; + $this->categoryList = $categoryList; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** - * Filter for filtering the requested categories id's based on url_key, ids, name in the result. + * Search for categories, return list of ids * - * @param array $args - * @param Collection $categoryCollection + * @param array $criteria * @param StoreInterface $store - * @throws InputException + * @return int[] */ - public function applyFilters(array $args, Collection $categoryCollection, StoreInterface $store) + public function getCategoryIds(array $criteria, StoreInterface $store): array { - $categoryCollection->addAttributeToFilter(CategoryInterface::KEY_IS_ACTIVE, ['eq' => 1]); - foreach ($args['filters'] as $field => $cond) { - foreach ($cond as $condType => $value) { - if ($field === 'ids') { - $categoryCollection->addIdFilter($value); - } else { - $this->addAttributeFilter($categoryCollection, $field, $condType, $value, $store); - } - } + $categoryIds = []; + try { + $criteria[Filter::ARGUMENT_NAME] = $this->formatMatchFilters($criteria['filters'], $store); + } catch (InputException $e) { + //Return empty set when match filter is too short. (matches search api behavior) + return $categoryIds; } - } + $criteria[Filter::ARGUMENT_NAME][CategoryInterface::KEY_IS_ACTIVE] = ['eq' => 1]; - /** - * Add filter to category collection - * - * @param Collection $categoryCollection - * @param string $field - * @param string $condType - * @param string|array $value - * @param StoreInterface $store - * @throws InputException - */ - private function addAttributeFilter($categoryCollection, $field, $condType, $value, $store) - { - if ($condType === 'match') { - $this->addMatchFilter($categoryCollection, $field, $value, $store); - return; + $searchCriteria = $this->searchCriteriaBuilder->build('categoryList', $criteria); + $categories = $this->categoryList->getList($searchCriteria); + foreach ($categories->getItems() as $category) { + $categoryIds[] = (int)$category->getId(); } - $categoryCollection->addAttributeToFilter($field, [$condType => $value]); + return $categoryIds; } /** - * Add match filter to collection + * Format match filters to behave like fuzzy match * - * @param Collection $categoryCollection - * @param string $field - * @param string $value + * @param array $filters * @param StoreInterface $store + * @return array * @throws InputException */ - private function addMatchFilter($categoryCollection, $field, $value, $store) + private function formatMatchFilters(array $filters, StoreInterface $store): array { $minQueryLength = $this->scopeConfig->getValue( Query::XML_PATH_MIN_QUERY_LENGTH, ScopeInterface::SCOPE_STORE, $store ); - $searchValue = str_replace('%', '', $value); - $matchLength = strlen($searchValue); - if ($matchLength < $minQueryLength) { - throw new InputException(__('Invalid match filter')); - } - $categoryCollection->addAttributeToFilter($field, ['like' => "%{$searchValue}%"]); + foreach ($filters as $filter => $condition) { + $conditionType = array_keys($condition)[0]; + if ($conditionType === 'match') { + $searchValue = $condition[$conditionType]; + $matchLength = strlen(trim($searchValue)); + if ($matchLength < $minQueryLength) { + throw new InputException(__('Invalid match filter')); + } + unset($filters[$filter]['match']); + $filters[$filter]['like'] = '%' . $searchValue . '%'; + + } + } + return $filters; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php new file mode 100644 index 0000000000000..a0c7c18474b8a --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Category; + +use Magento\Framework\GraphQl\ConfigInterface; +use Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesInterface; + +/** + * Retrieves attributes for a field for the ast converter + */ +class CategoryFilterAttributesForAst implements FieldEntityAttributesInterface +{ + /** + * Map schema fields to entity attributes + * + * @var array + */ + private $fieldMapping = [ + 'ids' => 'entity_id' + ]; + + /** + * @var array + */ + private $additionalFields = [ + 'is_active' + ]; + + /** + * @var ConfigInterface + */ + private $config; + + /** + * @param ConfigInterface $config + * @param array $additionalFields + * @param array $attributeFieldMapping + */ + public function __construct( + ConfigInterface $config, + array $additionalFields = [], + array $attributeFieldMapping = [] + ) { + $this->config = $config; + $this->additionalFields = array_merge($this->additionalFields, $additionalFields); + $this->fieldMapping = array_merge($this->fieldMapping, $attributeFieldMapping); + } + + /** + * @inheritdoc + * + * Gather attributes for Category filtering + * Example format ['attributeNameInGraphQl' => ['type' => 'String'. 'fieldName' => 'attributeNameInSearchCriteria']] + * + * @return array + */ + public function getEntityAttributes() : array + { + $categoryFilterType = $this->config->getConfigElement('CategoryFilterInput'); + + if (!$categoryFilterType) { + throw new \LogicException(__("CategoryFilterInput type not defined in schema.")); + } + + $fields = []; + foreach ($categoryFilterType->getFields() as $field) { + $fields[$field->getName()] = [ + 'type' => 'String', + 'fieldName' => $this->fieldMapping[$field->getName()] ?? $field->getName(), + ]; + } + + foreach ($this->additionalFields as $additionalField) { + $fields[$additionalField] = [ + 'type' => 'String', + 'fieldName' => $additionalField, + ]; + } + + return $fields; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 6b8949d612829..7379b6957d699 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -8,7 +8,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; -use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -74,20 +73,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($args['filters'])) { $rootCategoryIds[] = (int)$store->getRootCategoryId(); } else { - $categoryCollection = $this->collectionFactory->create(); - try { - $this->categoryFilter->applyFilters($args, $categoryCollection, $store); - } catch (InputException $e) { - return []; - } - - foreach ($categoryCollection as $category) { - $rootCategoryIds[] = (int)$category->getId(); - } + $rootCategoryIds = $this->categoryFilter->getCategoryIds($args, $store); } - $result = $this->fetchCategories($rootCategoryIds, $info); - return $result; + return $this->fetchCategories($rootCategoryIds, $info); } /** diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml index d6f75259e30d7..5fec7bfd4fda7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/di.xml @@ -36,6 +36,7 @@ <arguments> <argument name="attributesInstances" xsi:type="array"> <item name="products" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\FilterArgument\ProductEntityAttributesForAst</item> + <item name="categoryList" xsi:type="object">Magento\CatalogGraphQl\Model\Category\CategoryFilterAttributesForAst</item> </argument> </arguments> </type> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index a86c34e57b2bb..95fba7a9cdd41 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -31,9 +31,10 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @dataProvider filterSingleCategoryDataProvider - * @param $field - * @param $condition - * @param $value + * @param string $field + * @param string $condition + * @param string $value + * @param array $expectedResult */ public function testFilterSingleCategoryByField($field, $condition, $value, $expectedResult) { From 0def9f41afe8983ed029168d6db8fa9f14a8f9fb Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 12 Mar 2020 16:19:22 -0500 Subject: [PATCH 351/734] MC-31986: Add support for ES 7 to 2.4-develop --- .../Model/Adapter/FieldMapper/ProductFieldMapper.php | 1 + app/code/Magento/Elasticsearch6/etc/di.xml | 8 +++++++- .../Magento/Test/Legacy/_files/obsolete_classes.php | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index a3236b5492106..9a556460426f6 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -11,6 +11,7 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; + /** * Class ProductFieldMapper provides field name by attribute code and retrieve all attribute types */ diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index a1ba8549a47e5..641fbd069b627 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -133,7 +133,13 @@ <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> </arguments> </virtualType> - + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> + <arguments> + <argument name="productFieldMappers" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\ProductFieldMapper</item> + </argument> + </arguments> + </type> <virtualType name="elasticsearch6FieldNameResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 03884669c73b9..885a84e790b70 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4250,7 +4250,6 @@ ['Magento\Elasticsearch\Model\Client\Elasticsearch'], ['Magento\Elasticsearch\SearchAdapter\Aggregation\Interval'], ['Magento\Elasticsearch\SearchAdapter\Mapper'], - ['Magento\Elasticsearch6\Model\DataProvider\Suggestions'], ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType'], ['Magento\Elasticsearch\Model\Adapter\DataMapperInterface'], ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy'], From 7a742ff7d669559a5f430f3c50d839de4a3080e0 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 12 Mar 2020 17:32:06 -0500 Subject: [PATCH 352/734] MC-32278: Position sort does not work in GraphQl. - allow only category through --- .../VisibilityStatusProcessor.php | 2 +- .../Model/Resolver/Products/Query/Search.php | 46 ++++++++++++++++++- .../CollectionProcessor/FilterProcessor.php | 23 ++++------ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php index e31bc98837053..30174a94aaba0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/VisibilityStatusProcessor.php @@ -27,7 +27,7 @@ public function process( array $attributeNames ): Collection { $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner'); - //$collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); + $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); return $collection; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php index bf3c6eb1a314c..46357b710b50d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php @@ -16,6 +16,8 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Search\Api\SearchInterface; use Magento\Search\Model\Search\PageSizeProvider; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Search\FilterGroupBuilder; /** * Full text search for catalog using given search criteria. @@ -57,6 +59,12 @@ class Search implements ProductQueryInterface */ private $searchCriteriaBuilder; + /** @var FilterBuilder */ + private $filterBuilder; + + /** @var FilterGroupBuilder */ + private $filterGroupBuilder; + /** * @param SearchInterface $search * @param SearchResultFactory $searchResultFactory @@ -65,6 +73,8 @@ class Search implements ProductQueryInterface * @param FieldSelection $fieldSelection * @param ProductSearch $productsProvider * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param FilterBuilder $filterBuilder + * @param FilterGroupBuilder $filterGroupBuilder */ public function __construct( SearchInterface $search, @@ -73,7 +83,9 @@ public function __construct( SearchCriteriaInterfaceFactory $searchCriteriaFactory, FieldSelection $fieldSelection, ProductSearch $productsProvider, - SearchCriteriaBuilder $searchCriteriaBuilder + SearchCriteriaBuilder $searchCriteriaBuilder, + FilterBuilder $filterBuilder, + FilterGroupBuilder $filterGroupBuilder ) { $this->search = $search; $this->searchResultFactory = $searchResultFactory; @@ -82,6 +94,8 @@ public function __construct( $this->fieldSelection = $fieldSelection; $this->productsProvider = $productsProvider; $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + $this->filterGroupBuilder = $filterGroupBuilder; } /** @@ -109,7 +123,33 @@ public function getResult( $searchCriteria->setPageSize($realPageSize)->setCurrentPage($realCurrentPage); - $searchResults = $this->productsProvider->getList($searchCriteria, $itemsResults, $queryFields); + //Create copy of search criteria without conditions (conditions will be applied by joining search result) + $searchCriteriaCopy = $this->searchCriteriaFactory->create() + ->setSortOrders($searchCriteria->getSortOrders()) + ->setPageSize($realPageSize) + ->setCurrentPage($realCurrentPage); + + $categoryGroup = null; + foreach ($searchCriteria->getFilterGroups() as $filterGroup) { + foreach ($filterGroup->getFilters() as $filter) { + if ($filter->getField() == 'category_id') { + $categoryFilter = $this->filterBuilder + ->setField($filter->getField()) + ->setValue($filter->getValue()) + ->setConditionType($filter->getConditionType()) + ->create(); + + $this->filterGroupBuilder->addFilter($categoryFilter); + $categoryGroup = $this->filterGroupBuilder->create(); + } + } + } + + if ($categoryGroup) { + $searchCriteriaCopy->setFilterGroups([$categoryGroup]); + } + + $searchResults = $this->productsProvider->getList($searchCriteriaCopy, $itemsResults, $queryFields); //possible division by 0 if ($realPageSize) { @@ -117,6 +157,8 @@ public function getResult( } else { $maxPages = 0; } + $searchCriteria->setPageSize($realPageSize); + $searchCriteria->setCurrentPage($realCurrentPage); $productArray = []; /** @var \Magento\Catalog\Model\Product $product */ diff --git a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index 616e642f0079a..8f0a3fb2baa69 100644 --- a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -65,21 +65,16 @@ private function addFilterGroupToCollection( ) { $fields = []; foreach ($filterGroup->getFilters() as $filter) { - if ($filter->getField() != 'search_term' - && $filter->getField() != 'price_dynamic_algorithm' - && $filter->getField() != 'price.to' - && $filter->getField() != 'price.from') { - $isApplied = false; - $customFilter = $this->getCustomFilterForField($filter->getField()); - if ($customFilter) { - $isApplied = $customFilter->apply($filter, $collection); - } + $isApplied = false; + $customFilter = $this->getCustomFilterForField($filter->getField()); + if ($customFilter) { + $isApplied = $customFilter->apply($filter, $collection); + } - if (!$isApplied) { - $field = $this->getFieldMapping($filter->getField()); - $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; - $fields[] = ['attribute' => $field, $condition => $filter->getValue()]; - } + if (!$isApplied) { + $field = $this->getFieldMapping($filter->getField()); + $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; + $fields[] = ['attribute' => $field, $condition => $filter->getValue()]; } } From d208aef2207abd06d9d7ef869397e48987c400ce Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 13 Mar 2020 01:56:48 +0100 Subject: [PATCH 353/734] Proper test isolation, consistent naming in backwards-compatible manner --- ...refrontResetCustomerPasswordFailedTest.xml | 7 ++++++ ...frontResetCustomerPasswordSuccessTest.xml} | 23 ++++++++++--------- .../Test/Mftf/Test/_Deprecated_Test.xml | 11 +++++++++ 3 files changed, 30 insertions(+), 11 deletions(-) rename app/code/Magento/Customer/Test/Mftf/Test/{StorefrontForgotPasswordTest.xml => StorefrontResetCustomerPasswordSuccessTest.xml} (54%) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml index 5d0eec935e192..a1fa866a0675f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml @@ -21,9 +21,16 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 3" stepKey="setProtectionOnEmail"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 30" stepKey="increaseThresholdBetweenRequests"/> <createData stepKey="customer" entity="Simple_US_Customer"/> </before> <after> + <!-- Preferred `Use system value` which is not available from CLI --> + <magentoCLI command="config:set customer/password/password_reset_protection_type 1" stepKey="setDefaultProtection"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 30" stepKey="setDefaultThresholdBetweenRequests"/> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> <deleteData stepKey="deleteCustomer" createDataKey="customer" /> </after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml similarity index 54% rename from app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml rename to app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml index 0c6c7dd9b800c..7ea49f3684afc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCustomerForgotPasswordTest"> + <test name="StorefrontResetCustomerPasswordSuccessTest"> <annotations> <features value="Customer"/> <stories value="Customer Login"/> @@ -21,23 +21,24 @@ </annotations> <before> <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 3" stepKey="setProtectionOnEmail"/> <magentoCLI command="config:set customer/password/max_number_password_reset_requests 30" stepKey="increaseLimit"/> - <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 1" stepKey="reduceTimeout"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 0" stepKey="reduceTimeout"/> <createData stepKey="customer" entity="Simple_US_Customer"/> </before> <after> + <!-- Preferred `Use system value` which is not available from CLI --> <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 1" stepKey="setDefaultProtection"/> <deleteData stepKey="deleteCustomer" createDataKey="customer" /> </after> - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <fillField stepKey="fillEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> - <fillField stepKey="fillPassword" userInput="something" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> - <click stepKey="clickForgotPasswordLink" selector="{{StorefrontCustomerSignInFormSection.forgotPasswordLink}}"/> - <see stepKey="seePageTitle" userInput="Forgot Your Password" selector="{{StorefrontForgotPasswordSection.pageTitle}}"/> - <fillField stepKey="enterEmail" userInput="$$customer.email$$" selector="{{StorefrontForgotPasswordSection.email}}"/> - <click stepKey="clickResetPassword" selector="{{StorefrontForgotPasswordSection.resetMyPasswordButton}}"/> - <seeInCurrentUrl stepKey="seeInSignInPage" url="account/login"/> - <see stepKey="seeSuccessMessage" userInput="If there is an account associated with $$customer.email$$ you will receive an email with a link to reset your password." selector="{{StorefrontCustomerLoginMessagesSection.successMessage}}"/> + <actionGroup ref="StorefrontCustomerResetPasswordActionGroup" stepKey="resetPasswordFirstAttempt"> + <argument name="email" value="$$customer.email$$" /> + </actionGroup> + <actionGroup ref="AssertCustomerResetPasswordActionGroup" stepKey="seePageWithSuccessMessage"> + <argument name="url" value="{{StorefrontCustomerSignInPage.url}}"/> + <argument name="message" value="If there is an account associated with $$customer.email$$ you will receive an email with a link to reset your password."/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml new file mode 100644 index 0000000000000..7c3152730524f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml @@ -0,0 +1,11 @@ +<?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="StorefrontCustomerForgotPasswordTest" extends="StorefrontResetCustomerPasswordSuccessTest" deprecated="Use StorefrontResetCustomerPasswordSuccessTest"/> +</tests> From 3ed4cf7e149092f253e6ffa2a8fffffb8a2d1c30 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 13 Mar 2020 02:56:01 +0100 Subject: [PATCH 354/734] Add "Admin" prefix to Test and ActionGroup --- ...GridCustomerGroupEditByCodeActionGroup.xml | 12 ++++++++++ ...avigateToCustomerGroupPageActionGroup.xml} | 2 +- .../ActionGroup/_Deprecated_ActionGroup.xml | 1 + ...nVerifyDisabledCustomerGroupFieldTest.xml} | 22 +++++++++---------- .../Test/Mftf/Test/_Deprecated_Test.xml | 11 ++++++++++ 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{NavigateCustomerGroupActionGroup.xml => AdminNavigateToCustomerGroupPageActionGroup.xml} (89%) rename app/code/Magento/Customer/Test/Mftf/Test/{VerifyDisabledCustomerGroupFieldTest.xml => AdminVerifyDisabledCustomerGroupFieldTest.xml} (56%) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml new file mode 100644 index 0000000000000..7d1cb80682ae5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridCustomerGroupEditByCodeActionGroup"> + <arguments> + <argument name="customerGroupCode" type="string"/> + </arguments> + + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(customerGroupCode)}}" stepKey="clickOnEditCustomerGroup" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml similarity index 89% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml index 78e9a2f18da95..b782436a20949 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="NavigateToCustomerGroupPage"> + <actionGroup name="AdminNavigateToCustomerGroupPageActionGroup"> <annotations> <description>Goes to the Admin Customer Groups page.</description> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml index 5efcfc0e79b0d..46f45ce3802f1 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml @@ -12,6 +12,7 @@ NOTICE: Action Groups in this file are DEPRECATED and SHOULD NOT BE USED anymore --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCustomerGroupPage" extends="AdminNavigateToCustomerGroupPageActionGroup" deprecated="Use AdminNavigateToCustomerGroupPageActionGroup"/> <actionGroup name="AdminCreateCustomerWithWebSiteAndGroup" deprecated="Use `AdminCreateCustomerWithWebSiteAndGroupActionGroup` instead"> <annotations> <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml similarity index 56% rename from app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml rename to app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml index 0f98184aafb4f..e1342f26809ee 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="VerifyDisabledCustomerGroupFieldTest"> + <test name="AdminVerifyDisabledCustomerGroupFieldTest"> <annotations> <stories value="Check that field is disabled in system Customer Group"/> <title value="Check that field is disabled in system Customer Group"/> @@ -19,20 +19,20 @@ <group value="mtf_migrated"/> </annotations> - <!-- Steps --> - <!-- 1. Login to backend as admin user --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <waitForPageLoad stepKey="waitForAdminPageLoad" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <!-- 2. Navigate to Customers > Customer Groups --> - <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="amOnCustomerGroupPage" /> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearFiltersIfTheySet"/> + <actionGroup ref="AdminNavigateToCustomerGroupPageActionGroup" stepKey="amOnCustomerGroupPage"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="clearFiltersIfTheySet"> + <argument name="customerGroupName" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> - <!-- 3. Select system Customer Group specified in data set from grid --> - <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(NotLoggedInCustomerGroup.code)}}" stepKey="clickOnEditCustomerGroup" /> + <actionGroup ref="AdminGridCustomerGroupEditByCodeActionGroup" stepKey="openCustomerGroup"> + <argument name="customerGroupCode" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> - <!-- 4. Perform all assertions --> <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml new file mode 100644 index 0000000000000..795ce85340f41 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml @@ -0,0 +1,11 @@ +<?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="VerifyDisabledCustomerGroupFieldTest" extends="AdminVerifyDisabledCustomerGroupFieldTest" deprecated="Use AdminVerifyDisabledCustomerGroupFieldTest instead"/> +</tests> From 9d78f60c856b842d30ed6e76c1f882e6cb7f7d46 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Fri, 13 Mar 2020 03:44:30 +0100 Subject: [PATCH 355/734] Add missing copyright --- .../AdminGridCustomerGroupEditByCodeActionGroup.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml index 7d1cb80682ae5..68620931393c4 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml @@ -1,4 +1,9 @@ <?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"> From 158f788ec5a49557104fe1722c7e9551721b5932 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Fri, 13 Mar 2020 08:22:22 +0100 Subject: [PATCH 356/734] MC-26683: Added test case for 'Some of the products are out of stock.' case --- .../Quote/Guest/AddSimpleProductToCartTest.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index e5953e7b7ad72..986de59ff7bd2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Guest; use Exception; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; @@ -107,22 +108,26 @@ public function testAddDisabledProductToCart(): void /** * Add out of stock product to cart * - * @magentoApiDataFixture Magento/Catalog/_files/product_virtual_out_of_stock.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/set_simple_product_out_of_stock.php * @return void + * @throws NoSuchEntityException */ public function testAddOutOfStockProductToCart(): void { - $sku = 'virtual-product-out'; - $quantity = 2; + $sku = 'simple1'; + $quantity = 1; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $sku, $quantity); $this->expectException(ResponseContainsErrorsException::class); $this->expectExceptionMessage( - 'Could not add the product with SKU ' . $sku . ' to the shopping cart: ' . - 'Product that you are trying to add is not available.' + 'Some of the products are out of stock.' ); $this->graphQlMutation($query); From 8673f269870df9bb0580d84bb72b235a1ffc6f91 Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Thu, 12 Mar 2020 16:42:08 +0200 Subject: [PATCH 357/734] Enable Persistent Shopping Cart --- ...sistentShoppingCartSettingsActionGroup.xml | 15 ++++++++ ...hoppingCartOptionsAvailableActionGroup.xml | 18 ++++++++++ ...onfigurationPersistentShoppingCartPage.xml | 14 ++++++++ ...dminPersistentShoppingCartSettingsTest.xml | 34 +++++++++++++++++++ .../AdminPersistentShoppingCartSection.xml | 20 +++++++++++ 5 files changed, 101 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml new file mode 100644 index 0000000000000..811187b9fe95e --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.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="AdminNavigateToPersistentShoppingCartSettingsActionGroup"> + <amOnPage url="{{AdminConfigurationPersistentShoppingCartPage.url}}" stepKey="navigateToPersistencePage"/> + <conditionalClick selector="{{AdminPersistentShoppingCartSection.DefaultLayoutsTab}}" dependentSelector="{{AdminPersistentShoppingCartSection.CheckIfTabExpand}}" visible="true" stepKey="clickTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml new file mode 100644 index 0000000000000..3b75602e6d89c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AssertAdminPersistentShoppingCartOptionsAvailableActionGroup"> + <seeElement stepKey="seeLifetimeInput" selector="{{AdminPersistentShoppingCartSection.persistenceLifeTime}}"/> + <seeElement stepKey="seeRememberMeEnableInput" selector="{{AdminPersistentShoppingCartSection.rememberMeEnable}}"/> + <seeElement stepKey="seeRememberMeDefaultInput" selector="{{AdminPersistentShoppingCartSection.rememberMeDefault}}"/> + <seeElement stepKey="seeClearPersistence" selector="{{AdminPersistentShoppingCartSection.clearPersistenceOnLogout}}"/> + <seeElement stepKey="seePersistShoppingCart" selector="{{AdminPersistentShoppingCartSection.persistShoppingCart}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml new file mode 100644 index 0000000000000..f7e579c57e21c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigurationPersistentShoppingCartPage" url="admin/system_config/edit/section/persistent/" module="Customers" area="admin"> + <section name="AdminPersistentShoppingCartSection"/> + </page> +</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml new file mode 100644 index 0000000000000..cf1ed2d63034f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml @@ -0,0 +1,34 @@ +<?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="AdminPersistentShoppingCartSettingsTest"> + <annotations> + <features value="Backend"/> + <stories value="Enable Persistent Shopping cart"/> + <title value="Admin should be able to manage persistent shopping cart settings"/> + <description value="Admin should be able to enable persistent shopping cart in Magento Admin backend and see additional options"/> + <group value="backend"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCLI stepKey="enablePersistentShoppingCart" command="config:set persistent/options/enabled 1"/> + <magentoCLI stepKey="cacheClean" command="cache:clean config"/> + </before> + <after> + <magentoCLI stepKey="disablePersistentShoppingCart" command="config:set persistent/options/enabled 0"/> + <magentoCLI stepKey="cacheClean" command="cache:clean config"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateToPersistentShoppingCartSettingsActionGroup" stepKey="navigateToPersistenceSettings"/> + <actionGroup ref="AssertAdminPersistentShoppingCartOptionsAvailableActionGroup" stepKey="assertOptions"/> + </test> +</tests> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml new file mode 100644 index 0000000000000..7dc0d16e39556 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml @@ -0,0 +1,20 @@ +<?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="AdminPersistentShoppingCartSection"> + <element name="DefaultLayoutsTab" type="button" selector=".entry-edit-head-link"/> + <element name="CheckIfTabExpand" type="button" selector=".entry-edit-head-link:not(.open)"/> + <element name="persistenceLifeTime" type="input" selector="#persistent_options_lifetime"/> + <element name="rememberMeEnable" type="input" selector="#persistent_options_remember_enabled"/> + <element name="rememberMeDefault" type="input" selector="#persistent_options_remember_default"/> + <element name="clearPersistenceOnLogout" type="input" selector="#persistent_options_logout_clear"/> + <element name="persistShoppingCart" type="input" selector="#persistent_options_shopping_cart"/> + </section> +</sections> From b91010e088cc1a5cfc28e901f320b05169b5b86b Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 13 Mar 2020 11:28:25 -0500 Subject: [PATCH 358/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 2bbcfb9090a9c..adcff1bf0e73b 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "ab4ccebee272df77996978a909ade95066bcd0f6" + "reference": "24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ab4ccebee272df77996978a909ade95066bcd0f6", - "reference": "ab4ccebee272df77996978a909ade95066bcd0f6", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac", + "reference": "24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-12T20:46:06+00:00" + "time": "2020-03-13T16:06:57+00:00" }, { "name": "mikey179/vfsstream", From 5ef3a835682f6851f24fce488746128381f8fdac Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 13 Mar 2020 11:36:06 -0500 Subject: [PATCH 359/734] MC-32278: Position sort does not work in GraphQl. - order by second --- .../Catalog/Model/ResourceModel/Product/Collection.php | 2 +- .../CatalogGraphQl/Model/Resolver/Products/Query/Search.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 12e3c6b53d701..7904167808f14 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1717,7 +1717,7 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) // optimize if using cat index $filters = $this->_productLimitationFilters; if (isset($filters['category_id']) || isset($filters['visibility'])) { - $this->getSelect()->order('cat_index.position ' . $dir); + $this->getSelect()->order(['cat_index.position ' . $dir, 'e.entity_id ' . $dir]); } else { $this->getSelect()->order('e.entity_id ' . $dir); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php index 46357b710b50d..66aec9e4acee1 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php @@ -121,8 +121,6 @@ public function getResult( $searchCriteria->setCurrentPage(0); $itemsResults = $this->search->search($searchCriteria); - $searchCriteria->setPageSize($realPageSize)->setCurrentPage($realCurrentPage); - //Create copy of search criteria without conditions (conditions will be applied by joining search result) $searchCriteriaCopy = $this->searchCriteriaFactory->create() ->setSortOrders($searchCriteria->getSortOrders()) @@ -144,7 +142,7 @@ public function getResult( } } } - + //add root category or all root category children if category_id is not defined if ($categoryGroup) { $searchCriteriaCopy->setFilterGroups([$categoryGroup]); } From 239b0ddb3656cf62945f03c18aa3f873c5ca2b91 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 13 Mar 2020 11:54:17 -0500 Subject: [PATCH 360/734] MC-32388: Add url_path as a possible filter in categoryList query --- .../CatalogGraphQl/etc/schema.graphqls | 3 +- .../GraphQl/Catalog/CategoryListTest.php | 108 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index d4b98b311fca4..c0cc2f33c0cb7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -307,8 +307,9 @@ input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { ids: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies the category.") - url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category") + url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category.") name: FilterMatchTypeInput @doc(description: "Filter by the display name of the category.") + url_path: FilterEqualTypeInput @doc(description: "Filter by the URL path for the category.") } input ProductFilterInput @doc(description: "ProductFilterInput is deprecated, use @ProductAttributeFilterInput instead. ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 95fba7a9cdd41..25d29ccf98e86 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -422,6 +422,114 @@ public function testCategoryImageName() $this->assertEquals($expectedImageUrl, $categoryList[0]['image']); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathTopLevelCategory() + { + $urlPath = 'category-1'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {eq: "$urlPath"}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(1, $categoryList); + $this->assertEquals($urlPath, $categoryList[0]['url_path']); + $this->assertEquals('Category 1', $categoryList[0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNestedCategory() + { + $urlPath = 'category-1/category-1-1/category-1-1-1'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {eq: "$urlPath"}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(1, $categoryList); + $this->assertEquals($urlPath, $categoryList[0]['url_path']); + $this->assertEquals('Category 1.1.1', $categoryList[0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathMultipleCategories() + { + $urlPaths = ['category-1/category-1-1', 'inactive', 'movable-position-2']; + $urlPathsString = '"' . implode('", "', $urlPaths) . '"'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {in: [$urlPathsString]}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(2, $categoryList); + $this->assertEquals($urlPaths[0], $categoryList[0]['url_path']); + $this->assertEquals('Category 1.1', $categoryList[0]['name']); + $this->assertEquals($urlPaths[2], $categoryList[1]['url_path']); + $this->assertEquals('Movable Position 2', $categoryList[1]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNoResults() + { + $query = <<<QUERY +{ + categoryList(filters: {url_path: {in: ["not-a-category url path"]}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(0, $categoryList); + } + /** * @return array */ From 94bb238f7e3ee2f0e1f9b06a34751068bc4eb396 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 13 Mar 2020 12:26:50 -0500 Subject: [PATCH 361/734] MC-32278: Position sort does not work in GraphQl. - fix test --- .../testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 5605f5719bc9f..564ff4578018c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -1160,7 +1160,7 @@ public function testSortByPosition() QUERY; $resultDesc = $this->graphQlQuery($queryDesc); $this->assertArrayNotHasKey('errors', $resultDesc); - $productsDesc = array_column($resultAsc['products']['items'], 'sku'); + $productsDesc = array_column($resultDesc['products']['items'], 'sku'); $expectedProductsDesc = array_reverse($expectedProductsAsc); $this->assertEquals($expectedProductsDesc, $productsDesc); } From e3a0dfce9da29b51f57b852e36470238c04421df Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Fri, 13 Mar 2020 12:38:27 -0500 Subject: [PATCH 362/734] MC-32201: Reorder functionality --- .../Magento/GraphQl/Sales/ReorderTest.php | 193 +++++++++++++++++- .../order_with_product_out_of_stock.php | 26 +++ ...der_with_product_out_of_stock_rollback.php | 3 + .../_files/order_with_two_simple_products.php | 139 +++++++++++++ ...rder_with_two_simple_products_rollback.php | 4 + 5 files changed, 356 insertions(+), 9 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 4b7a001f8ea6f..a432e3931d339 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -18,14 +18,34 @@ */ class ReorderTest extends GraphQlAbstract { + /** + * Customer Id + */ private const CUSTOMER_ID = 1; + + /** + * Order Number + */ private const ORDER_NUMBER = '100000001'; + /** + * Incremented order number + */ + private const INCREMENTED_ORDER_NUMBER = '100001001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + /** * @var CustomerTokenServiceInterface */ private $customerTokenService; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -46,17 +66,12 @@ protected function setUp() */ public function testReorderMutation() { - $query = $this->getQuery(self::ORDER_NUMBER); - - $currentEmail = 'customer@example.com'; - $currentPassword = 'password'; - - $response = $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $response = $this->makeReorderForDefaultCustomer(); $this->assertResponseFields( $response['addAllOrderItemsToCart'] ?? [], [ 'cart' => [ - 'email' => $currentEmail, + 'email' => self::CUSTOMER_EMAIL, 'total_quantity' => 1, 'items' => [ [ @@ -70,10 +85,8 @@ public function testReorderMutation() 'errors' => [] ] ); - } - /** * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php * @expectedException \Exception @@ -85,6 +98,149 @@ public function testReorderWithoutAuthorisedCustomer() $this->graphQlMutation($query); } + /** + * Test reorder when simple product is out of stock/disabled/deleted + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_product_out_of_stock.php + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + */ + public function testSimpleProductOutOfStock() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); + + $this->assertProductNotAvailable(); + $this->assertDisabledProduct($productRepository, $product); + $this->assertWithDeletedProduct($productRepository, $product); + } + + /** + * Assert that simple product is not available. + */ + private function assertProductNotAvailable() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'errors' => [ + [ + 'sku' => 'simple', + 'message' => 'Product that you are trying to add is not available.', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 0, + 'items' => [], + ], + ]; + $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + } + + /** + * Assert reorder with disabled product. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + * @return void + */ + private function assertDisabledProduct( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // make product available in stock but disable and make reorder + $product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED); + $productRepository->save($product); + $this->assertProductNotAvailable(); + } + + /** + * Assert reorder with deleted product. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @throws \Magento\Framework\Exception\StateException + * @return void + */ + private function assertWithDeletedProduct( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // delete a product and make reorder + /** @var \Magento\Framework\Registry $registry */ + $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + $productRepository->delete($product); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + $expectedResponse = [ + 'errors' => [ + [ + 'sku' => 'simple', + 'message' => 'The product wasn\'t found. Verify the product and try again.', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 0, + 'items' => [], + ], + ]; + $response = $this->makeReorderForDefaultCustomer(); + $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_simple_products.php + */ + public function notFinishedTestTwoProducts() + { + $response = $this->makeReorderForDefaultCustomer(self::INCREMENTED_ORDER_NUMBER); + + $expectedResponse = [ + 'errors' => [ + [ + 'sku' => 'simple-1', + 'message' => 'We can\'t add this item to your shopping cart right now.', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + } + /** * @param string $email * @param string $password @@ -97,6 +253,25 @@ private function getCustomerAuthHeaders(string $email, string $password): array return ['Authorization' => 'Bearer ' . $customerToken]; } + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + /** * @param string $orderNumber * @return string diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php new file mode 100644 index 0000000000000..2db288ff1da71 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php @@ -0,0 +1,26 @@ +<?php + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set it out of stock +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product as out of stock +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php new file mode 100644 index 0000000000000..b146386192be7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php @@ -0,0 +1,3 @@ +<?php + +require __DIR__ . '/../../../Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php new file mode 100644 index 0000000000000..6edc0ce6ad3a2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php @@ -0,0 +1,139 @@ +<?php + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; +$requestInfo = [ + 'qty' => 1, +]; + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItemSimple = clone $orderItem; +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +//$orderItemSimple->setProductId($simpleProduct->getId()); +//$orderItemSimple->setParentItem($orderItem); +//$orderItemSimple->setStoreId(0); +//$orderItemSimple->setProductType($simpleProduct->getTypeId()); +//$orderItemSimple->setProductOptions(['info_buyRequest' => $requestInfo]); +//$orderItemSimple->setSku($simpleProduct->getSku()); + + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100001001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +//$order->addItem($orderItemSimple); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); + + + + + + + + + + + +/** @var \Magento\Catalog\Model\Product $product */ +/** @var \Magento\Sales\Model\Order $order */ +//$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +//require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + + + + + +/** @var $product \Magento\Catalog\Model\Product */ +//$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +//$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +// ->setAttributeSetId(4) +// ->setWebsiteIds([1]) +// ->setName('Simple Product') +// ->setSku('simple-2') +// ->setPrice(10) +// ->setDescription('Description with <b>html tag</b>') +// ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) +// ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) +// ->setCategoryIds([2]) +// ->set +// ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) +// ->setUrlKey('simple-product-2') +// ->save(); + +// +//$orderItems[] = [ +// 'product_id' => $product->getId(), +// 'base_price' => 123, +// 'order_id' => $order->getId(), +// 'price' => 123, +// 'row_total' => 126, +// 'product_type' => 'simple' +//]; + +/** @var array $orderItemData */ +//foreach ($orderItems as $orderItemData) { +// +// $requestInfo = [ +// 'product' => $orderItemData['product_id'], +// 'qty' => 1, +// ]; +// /** @var $orderItem \Magento\Sales\Model\Order\Item */ +// $orderItem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( +// \Magento\Sales\Model\Order\Item::class +// ); +// $orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +// $orderItem->setData($orderItemData)->save(); +// $order->addItem($orderItem); +//} + + +/** @var OrderRepositoryInterface $orderRepository */ +//$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +//$orderRepository->save($order); + +//$customerIdFromFixture = 1; +///** @var $order \Magento\Sales\Model\Order */ +//$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php new file mode 100644 index 0000000000000..6a0de02b02be8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php @@ -0,0 +1,4 @@ +<?php + +require __DIR__ . '/../../../Magento/Sales/_files/order_item_with_configurable_for_reorder_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; From 9dfeccff6fac75ccc9239e8c65dabaa387577424 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Fri, 13 Mar 2020 19:57:46 +0200 Subject: [PATCH 363/734] Make the "Display Category Filter" field appear after after additional fields for "Price Navigation Step Calculation" --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 30a8ec8a81ec5..4e10453f542bb 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -139,7 +139,7 @@ </field> </group> <group id="layered_navigation"> - <field id="display_category" translate="label" type="select" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="display_category" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Display Category Filter</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> From 106e24426ac1c7437c43d801d2b437560114980a Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Fri, 13 Mar 2020 18:43:37 +0000 Subject: [PATCH 364/734] Load view from indexer object --- .../Grid/Column/Renderer/ScheduleStatus.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php index 4d90d9c178c12..eba7591c17286 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -11,9 +11,8 @@ use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; use Magento\Framework\DataObject; use Magento\Framework\Escaper; -use Magento\Framework\Mview\View; -use Magento\Framework\Mview\ViewInterface; use Magento\Framework\Phrase; +use Magento\Indexer\Model\IndexerFactory; /** * Renderer for 'Schedule Status' column in indexer grid @@ -26,25 +25,25 @@ class ScheduleStatus extends AbstractRenderer private $escaper; /** - * @var ViewInterface + * @var IndexerFactory */ - private $viewModel; + private $indexerFactory; /** * @param Context $context * @param Escaper $escaper - * @param ViewInterface $viewModel + * @param IndexerFactory $indexerFactory * @param array $data */ public function __construct( Context $context, Escaper $escaper, - View $viewModel, + IndexerFactory $indexerFactory, array $data = [] ) { parent::__construct($context, $data); $this->escaper = $escaper; - $this->viewModel = $viewModel; + $this->indexerFactory = $indexerFactory; } /** @@ -61,7 +60,9 @@ public function render(DataObject $row) } try { - $view = $this->viewModel->load($row->getIndexerId()); + $indexer = $this->indexerFactory->create(); + $indexer->load($row->getIndexerId()); + $view = $indexer->getView(); } catch (\InvalidArgumentException $exception) { // No view for this index. return ''; From 764cdf94634311e8468af2c6ddadc75e30e10211 Mon Sep 17 00:00:00 2001 From: Alexander Taranovsky <firster@atwix.com> Date: Fri, 13 Mar 2020 21:21:48 +0200 Subject: [PATCH 365/734] Update AbstractExtensibleObject.php --- lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index bf2967ba564ff..f96e91dfbd45c 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -12,6 +12,7 @@ * * @SuppressWarnings(PHPMD.NumberOfChildren) * @api + * @codeCoverageIgnore * @deprecated * @see \Magento\Framework\Model\AbstractExtensibleModel */ From 987d7138351adedd5f33af3e5525a6dee03cb1ed Mon Sep 17 00:00:00 2001 From: Alexander Taranovsky <firster@atwix.com> Date: Fri, 13 Mar 2020 21:22:07 +0200 Subject: [PATCH 366/734] Update AbstractExtensibleModel.php --- lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index 20669ae20dc7a..3e53dbfdc118d 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -16,6 +16,7 @@ * Implementations may choose to process custom attributes as their persistence requires them to. * @SuppressWarnings(PHPMD.NumberOfChildren) * @api + * @codeCoverageIgnore */ abstract class AbstractExtensibleModel extends AbstractModel implements \Magento\Framework\Api\CustomAttributesDataInterface From 8b63f456e6179f45ac394406eca38244f27da6ae Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Fri, 13 Mar 2020 14:28:34 -0500 Subject: [PATCH 367/734] MC-32120: Catalog price rules are not included in CartItemPrices - Added review fixes --- composer.lock | 4 ++-- .../Magento/GraphQl/Quote/Guest/CartTotalsTest.php | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index 1236d2b0ae82f..09c0b39c65a05 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "522d676db5baf5864a824409c54948fc", + "content-hash": "b0c98d770c12f5b74f5c791a79402064", "packages": [ { "name": "aws/aws-sdk-php", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php index cc34ae4728e5a..135b61849c29a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php @@ -140,6 +140,10 @@ public function testGetCartTotalsWithCatalogRuleAndCartRuleApplied() self::assertEquals(18, $cartItem['prices']['row_total_including_tax']['value']); self::assertEquals(9, $cartItem['prices']['total_item_discount']['value']); + $discount = $cartItem['prices']['discounts'][0]; + self::assertEquals("50% Off for all orders", $discount['label']); + self::assertEquals(9, $discount['amount']['value']); + self::assertArrayHasKey('prices', $response['cart']); $pricesResponse = $response['cart']['prices']; self::assertEquals(9, $pricesResponse['grand_total']['value']); @@ -277,6 +281,12 @@ private function getQuery(string $maskedQuoteId): string total_item_discount { value } + discounts { + label + amount { + value + } + } } } prices { From dbfd32c826c425bba51fbc12cd20c66249833764 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Fri, 13 Mar 2020 14:30:53 -0500 Subject: [PATCH 368/734] MC-32120: Catalog price rules are not included in CartItemPrices - Added README --- app/code/Magento/CatalogRuleGraphQl/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/code/Magento/CatalogRuleGraphQl/README.md diff --git a/app/code/Magento/CatalogRuleGraphQl/README.md b/app/code/Magento/CatalogRuleGraphQl/README.md new file mode 100644 index 0000000000000..8cb1786635582 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/README.md @@ -0,0 +1,4 @@ +# CatalogRuleGraphQl + +**Magento_CatalogRuleGraphQl** module is responsible for applying Catalog Rules, one of the types of price rules in Magento, for GraphQl requests. +Catalog Rules are applied to products before they are added to the cart. \ No newline at end of file From 70c5e12d4ebbf08d35cf8ebb0575826808e887d1 Mon Sep 17 00:00:00 2001 From: Alexander Taranovsky <firster@atwix.com> Date: Fri, 13 Mar 2020 21:34:37 +0200 Subject: [PATCH 369/734] Update AbstractExtensibleObject.php --- lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index f96e91dfbd45c..902709bbedcd3 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -11,8 +11,8 @@ * Base Class for extensible data Objects * * @SuppressWarnings(PHPMD.NumberOfChildren) + * phpcs:disable Magento2.Classes.AbstractApi * @api - * @codeCoverageIgnore * @deprecated * @see \Magento\Framework\Model\AbstractExtensibleModel */ From 852bf03d88da8d5d6cbd09bf6263043490995bb2 Mon Sep 17 00:00:00 2001 From: Alexander Taranovsky <firster@atwix.com> Date: Fri, 13 Mar 2020 21:35:07 +0200 Subject: [PATCH 370/734] Update AbstractExtensibleModel.php --- .../Magento/Framework/Model/AbstractExtensibleModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index 3e53dbfdc118d..5484103cc27ef 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -15,8 +15,8 @@ * This class defines basic data structure of how custom attributes are stored in an ExtensibleModel. * Implementations may choose to process custom attributes as their persistence requires them to. * @SuppressWarnings(PHPMD.NumberOfChildren) + * phpcs:disable Magento2.Classes.AbstractApi * @api - * @codeCoverageIgnore */ abstract class AbstractExtensibleModel extends AbstractModel implements \Magento\Framework\Api\CustomAttributesDataInterface From fff5ce9c09b3d0e9650d7754dff384aad74af7f1 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Fri, 13 Mar 2020 20:35:55 +0100 Subject: [PATCH 371/734] MC-26683: Removed unused set_guest_email.php fixture for test --- .../Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 986de59ff7bd2..3ee27acfa2418 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -111,7 +111,6 @@ public function testAddDisabledProductToCart(): void * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/set_simple_product_out_of_stock.php * @return void From 8846899c4c6d9ead9fe341a832213c420c534b8e Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Fri, 13 Mar 2020 14:44:29 -0500 Subject: [PATCH 372/734] MC-32201: Reorder functionality - MC-32362: Reorder interface --- .../Magento/Sales/Model/Order/Reorder.php | 59 ++++++++++++------- .../Magento/GraphQl/Sales/ReorderTest.php | 3 +- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Reorder.php b/app/code/Magento/Sales/Model/Order/Reorder.php index 377a620075dd1..4f014f452eeb1 100644 --- a/app/code/Magento/Sales/Model/Order/Reorder.php +++ b/app/code/Magento/Sales/Model/Order/Reorder.php @@ -5,7 +5,11 @@ */ namespace Magento\Sales\Model\Order; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Sales\Api\ReorderInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; @@ -23,12 +27,7 @@ class Reorder implements ReorderInterface private $orderFactory; /** - * @var \Magento\Checkout\Model\CartFactory - */ - private $cartFactory; - - /** - * @var \Magento\Quote\Api\CartManagementInterface + * @var CartManagementInterface */ private $cartManagement; @@ -47,28 +46,41 @@ class Reorder implements ReorderInterface */ private $createEmptyCartForCustomer; + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @param OrderFactory $orderFactory - * @param \Magento\Checkout\Model\CartFactory $cartFactory - * @param \Magento\Quote\Api\CartManagementInterface $cartManagement + * @param CartManagementInterface $cartManagement * @param ReorderHelper $reorderHelper * @param \Psr\Log\LoggerInterface $logger * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer + * @param CartRepositoryInterface $cartRepository + * @param ProductRepositoryInterface $productRepository */ public function __construct( OrderFactory $orderFactory, - \Magento\Checkout\Model\CartFactory $cartFactory, - \Magento\Quote\Api\CartManagementInterface $cartManagement, + CartManagementInterface $cartManagement, + CreateEmptyCartForCustomer $createEmptyCartForCustomer, + CartRepositoryInterface $cartRepository, + ProductRepositoryInterface $productRepository, ReorderHelper $reorderHelper, - \Psr\Log\LoggerInterface $logger, - CreateEmptyCartForCustomer $createEmptyCartForCustomer + \Psr\Log\LoggerInterface $logger ) { $this->orderFactory = $orderFactory; - $this->cartFactory = $cartFactory; $this->cartManagement = $cartManagement; + $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; + $this->cartRepository = $cartRepository; + $this->productRepository = $productRepository; $this->reorderHelper = $reorderHelper; $this->logger = $logger; - $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; } /** @@ -92,19 +104,18 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal $customerId = $order->getCustomerId(); try { + /** @var \Magento\Quote\Model\Quote $cart */ $cart = $this->cartManagement->getCartForCustomer($customerId); } catch (NoSuchEntityException $e) { $this->createEmptyCartForCustomer->execute($customerId); $cart = $this->cartManagement->getCartForCustomer($customerId); } - $cartModel = $this->cartFactory->create(); - $cartModel->setQuote($cart); $lineItemsErrors = []; $items = $order->getItemsCollection(); foreach ($items as $item) { try { - $this->addOrderItem($cartModel, $item); + $this->addOrderItem($cart, $item); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); } catch (\Throwable $e) { @@ -116,7 +127,8 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal ); } } - $cartModel->save(); + + $this->cartRepository->save($cart); return new \Magento\Sales\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); } @@ -125,12 +137,12 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal /** * Convert order item to quote item * - * @param \Magento\Checkout\Model\Cart $cartModel + * @param \Magento\Quote\Model\Quote $cart * @param \Magento\Sales\Model\Order\Item $orderItem * @return void * @throws \Magento\Framework\Exception\LocalizedException */ - private function addOrderItem($cartModel, $orderItem): void + private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): void { /* @var $orderItem \Magento\Sales\Model\Order\Item */ if ($orderItem->getParentItem() === null) { @@ -138,7 +150,12 @@ private function addOrderItem($cartModel, $orderItem): void $info = new \Magento\Framework\DataObject($info); $info->setQty($orderItem->getQtyOrdered()); - $cartModel->addProduct($orderItem->getProductId(), $info); + try { + $product = $this->productRepository->getById($orderItem->getProductId(), false, null, true); + } catch (NoSuchEntityException $e) { + throw new LocalizedException(__('Could not find a product with ID "%1"', $orderItem->getProductId())); + } + $cart->addProduct($product, $info); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index a432e3931d339..22e48f2a588af 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -189,6 +189,7 @@ private function assertWithDeletedProduct( $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); + $productId = $product->getId(); $productRepository->delete($product); $registry->unregister('isSecureArea'); @@ -198,7 +199,7 @@ private function assertWithDeletedProduct( 'errors' => [ [ 'sku' => 'simple', - 'message' => 'The product wasn\'t found. Verify the product and try again.', + 'message' => 'Could not find a product with ID "' . $productId . '"', ], ], 'cart' => [ From 55280a6d453f1565a8f8c65021a1535e4ab3a6b3 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 13 Mar 2020 15:20:51 -0500 Subject: [PATCH 373/734] MC-32278: Position sort does not work in GraphQl. - refactor & fix test --- .../Products/DataProvider/ProductSearch.php | 28 +++++-- ...ProductCollectionSearchCriteriaBuilder.php | 79 +++++++++++++++++++ .../Model/Resolver/Products/Query/Search.php | 67 ++-------------- .../GraphQl/Catalog/ProductSearchTest.php | 28 +++++++ 4 files changed, 137 insertions(+), 65 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 49fb06293a29a..50f726953c6d3 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -12,11 +12,13 @@ use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch\ProductCollectionSearchCriteriaBuilder; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchResultsInterface; +use Magento\Framework\App\ObjectManager; /** * Product field data provider for product search, used for GraphQL resolver processing. @@ -48,25 +50,35 @@ class ProductSearch */ private $searchResultApplierFactory; + /** + * @var ProductCollectionSearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + /** * @param CollectionFactory $collectionFactory * @param ProductSearchResultsInterfaceFactory $searchResultsFactory * @param CollectionProcessorInterface $collectionPreProcessor * @param CollectionPostProcessor $collectionPostProcessor * @param SearchResultApplierFactory $searchResultsApplierFactory + * @param ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder */ public function __construct( CollectionFactory $collectionFactory, ProductSearchResultsInterfaceFactory $searchResultsFactory, CollectionProcessorInterface $collectionPreProcessor, CollectionPostProcessor $collectionPostProcessor, - SearchResultApplierFactory $searchResultsApplierFactory + SearchResultApplierFactory $searchResultsApplierFactory, + ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder = null ) { $this->collectionFactory = $collectionFactory; $this->searchResultsFactory = $searchResultsFactory; $this->collectionPreProcessor = $collectionPreProcessor; $this->collectionPostProcessor = $collectionPostProcessor; $this->searchResultApplierFactory = $searchResultsApplierFactory; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->searchCriteriaBuilder = $searchCriteriaBuilder + ?: ObjectManager::getInstance()->get(ProductCollectionSearchCriteriaBuilder::class); } /** @@ -85,15 +97,21 @@ public function getList( /** @var Collection $collection */ $collection = $this->collectionFactory->create(); - //Join search results - $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); + //Create a copy of search criteria without filters to preserve the results from search + $searchCriteriaForCollection = $this->searchCriteriaBuilder->process($searchCriteria); + //Apply CatalogSearch results from search and join table + $this->getSearchResultsApplier( + $searchResult, + $collection, + $this->getSortOrderArray($searchCriteriaForCollection) + )->apply(); - $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); + $this->collectionPreProcessor->process($collection, $searchCriteriaForCollection, $attributes); $collection->load(); $this->collectionPostProcessor->process($collection, $attributes); $searchResults = $this->searchResultsFactory->create(); - $searchResults->setSearchCriteria($searchCriteria); + $searchResults->setSearchCriteria($searchCriteriaForCollection); $searchResults->setItems($collection->getItems()); $searchResults->setTotalCount($searchResult->getTotalCount()); return $searchResults; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php new file mode 100644 index 0000000000000..a5db92db3fc0e --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch; + +use Magento\Catalog\Model\CategoryProductLink; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Search\FilterGroupBuilder; +use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; +use Magento\Framework\Api\SearchCriteriaInterface; + +class ProductCollectionSearchCriteriaBuilder +{ + /** + * @var SearchCriteriaInterfaceFactory + */ + private $searchCriteriaFactory; + + /** @var FilterBuilder */ + private $filterBuilder; + + /** @var FilterGroupBuilder */ + private $filterGroupBuilder; + + /** + * @param CollectionFactory $collectionFactory + * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory + * @param FilterBuilder $filterBuilder + * @param FilterGroupBuilder $filterGroupBuilder + */ + public function __construct( + CollectionFactory $collectionFactory, + SearchCriteriaInterfaceFactory $searchCriteriaFactory, + FilterBuilder $filterBuilder, + FilterGroupBuilder $filterGroupBuilder + ) { + $this->collectionFactory = $collectionFactory; + $this->searchCriteriaFactory = $searchCriteriaFactory; + $this->filterBuilder = $filterBuilder; + $this->filterGroupBuilder = $filterGroupBuilder; + } + + /** + * Build searchCriteria from search for product collection + * + * @param SearchCriteriaInterface $searchCriteria + */ + public function build(SearchCriteriaInterface $searchCriteria): SearchCriteriaInterface + { + //Create a copy of search criteria without filters to preserve the results from search + $searchCriteriaForCollection = $this->searchCriteriaFactory->create() + ->setSortOrders($searchCriteria->getSortOrders()) + ->setPageSize($searchCriteria->getPageSize()) + ->setCurrentPage($searchCriteria->getCurrentPage()); + + //Add category id to enable sorting by position + foreach ($searchCriteria->getFilterGroups() as $filterGroup) { + foreach ($filterGroup->getFilters() as $filter) { + if ($filter->getField() == CategoryProductLink::KEY_CATEGORY_ID) { + $categoryFilter = $this->filterBuilder + ->setField($filter->getField()) + ->setValue($filter->getValue()) + ->setConditionType($filter->getConditionType()) + ->create(); + + $this->filterGroupBuilder->addFilter($categoryFilter); + $categoryGroup = $this->filterGroupBuilder->create(); + $searchCriteriaForCollection->setFilterGroups([$categoryGroup]); + } + } + } + return $searchCriteriaForCollection; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php index 66aec9e4acee1..fbb0e42f2afeb 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php @@ -12,12 +12,9 @@ use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory; use Magento\Framework\Api\Search\SearchCriteriaInterface; -use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Search\Api\SearchInterface; use Magento\Search\Model\Search\PageSizeProvider; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\Search\FilterGroupBuilder; /** * Full text search for catalog using given search criteria. @@ -39,11 +36,6 @@ class Search implements ProductQueryInterface */ private $pageSizeProvider; - /** - * @var SearchCriteriaInterfaceFactory - */ - private $searchCriteriaFactory; - /** * @var FieldSelection */ @@ -59,43 +51,28 @@ class Search implements ProductQueryInterface */ private $searchCriteriaBuilder; - /** @var FilterBuilder */ - private $filterBuilder; - - /** @var FilterGroupBuilder */ - private $filterGroupBuilder; - /** * @param SearchInterface $search * @param SearchResultFactory $searchResultFactory * @param PageSizeProvider $pageSize - * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory * @param FieldSelection $fieldSelection * @param ProductSearch $productsProvider * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param FilterBuilder $filterBuilder - * @param FilterGroupBuilder $filterGroupBuilder */ public function __construct( SearchInterface $search, SearchResultFactory $searchResultFactory, PageSizeProvider $pageSize, - SearchCriteriaInterfaceFactory $searchCriteriaFactory, FieldSelection $fieldSelection, ProductSearch $productsProvider, - SearchCriteriaBuilder $searchCriteriaBuilder, - FilterBuilder $filterBuilder, - FilterGroupBuilder $filterGroupBuilder + SearchCriteriaBuilder $searchCriteriaBuilder ) { $this->search = $search; $this->searchResultFactory = $searchResultFactory; $this->pageSizeProvider = $pageSize; - $this->searchCriteriaFactory = $searchCriteriaFactory; $this->fieldSelection = $fieldSelection; $this->productsProvider = $productsProvider; $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterBuilder = $filterBuilder; - $this->filterGroupBuilder = $filterGroupBuilder; } /** @@ -115,48 +92,18 @@ public function getResult( $realPageSize = $searchCriteria->getPageSize(); $realCurrentPage = $searchCriteria->getCurrentPage(); - // Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround + //Because of limitations of sort and pagination on search API we will query all IDS $pageSize = $this->pageSizeProvider->getMaxPageSize(); $searchCriteria->setPageSize($pageSize); $searchCriteria->setCurrentPage(0); $itemsResults = $this->search->search($searchCriteria); - //Create copy of search criteria without conditions (conditions will be applied by joining search result) - $searchCriteriaCopy = $this->searchCriteriaFactory->create() - ->setSortOrders($searchCriteria->getSortOrders()) - ->setPageSize($realPageSize) - ->setCurrentPage($realCurrentPage); - - $categoryGroup = null; - foreach ($searchCriteria->getFilterGroups() as $filterGroup) { - foreach ($filterGroup->getFilters() as $filter) { - if ($filter->getField() == 'category_id') { - $categoryFilter = $this->filterBuilder - ->setField($filter->getField()) - ->setValue($filter->getValue()) - ->setConditionType($filter->getConditionType()) - ->create(); - - $this->filterGroupBuilder->addFilter($categoryFilter); - $categoryGroup = $this->filterGroupBuilder->create(); - } - } - } - //add root category or all root category children if category_id is not defined - if ($categoryGroup) { - $searchCriteriaCopy->setFilterGroups([$categoryGroup]); - } - - $searchResults = $this->productsProvider->getList($searchCriteriaCopy, $itemsResults, $queryFields); - - //possible division by 0 - if ($realPageSize) { - $maxPages = (int)ceil($searchResults->getTotalCount() / $realPageSize); - } else { - $maxPages = 0; - } + //Address limitations of sort and pagination on search API apply original pagination from GQL query $searchCriteria->setPageSize($realPageSize); $searchCriteria->setCurrentPage($realCurrentPage); + $searchResults = $this->productsProvider->getList($searchCriteria, $itemsResults, $queryFields); + + $totalPages = $realPageSize ? ((int)ceil($searchResults->getTotalCount() / $realPageSize)) : 0; $productArray = []; /** @var \Magento\Catalog\Model\Product $product */ @@ -172,7 +119,7 @@ public function getResult( 'searchAggregation' => $itemsResults->getAggregations(), 'pageSize' => $realPageSize, 'currentPage' => $realCurrentPage, - 'totalPages' => $maxPages, + 'totalPages' => $totalPages, ] ); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 564ff4578018c..1907630325e65 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -1163,6 +1163,34 @@ public function testSortByPosition() $productsDesc = array_column($resultDesc['products']['items'], 'sku'); $expectedProductsDesc = array_reverse($expectedProductsAsc); $this->assertEquals($expectedProductsDesc, $productsDesc); + + //revert position + $productPositions = $category->getProductsPosition(); + $count = 3; + foreach ($productPositions as $productId => $position) { + $productPositions[$productId] = $count; + $count--; + } + + $category->setPostedProducts($productPositions); + $category->save(); + + $queryDesc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: DESC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultDesc = $this->graphQlQuery($queryDesc); + $this->assertArrayNotHasKey('errors', $resultDesc); + $productsDesc = array_column($resultDesc['products']['items'], 'sku'); + $expectedProductsDesc = $expectedProductsAsc; + $this->assertEquals($expectedProductsDesc, $productsDesc); } /** From e9756816e409b0fd6438a517972c43f7360b57c5 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 13 Mar 2020 15:31:55 -0500 Subject: [PATCH 374/734] MC-32278: Position sort does not work in GraphQl. - fix method call --- .../Model/Resolver/Products/DataProvider/ProductSearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 50f726953c6d3..55f81b8067c8b 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -98,7 +98,7 @@ public function getList( $collection = $this->collectionFactory->create(); //Create a copy of search criteria without filters to preserve the results from search - $searchCriteriaForCollection = $this->searchCriteriaBuilder->process($searchCriteria); + $searchCriteriaForCollection = $this->searchCriteriaBuilder->build($searchCriteria); //Apply CatalogSearch results from search and join table $this->getSearchResultsApplier( $searchResult, From 258086beb6f446ec3a2a0b108b4dd6ef98435e6a Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Fri, 13 Mar 2020 19:01:30 -0500 Subject: [PATCH 375/734] MC-32201: Reorder functionality - MC-32362: Reorder interface --- .../Api/Data/Reorder/LineItemError.php | 2 +- .../Api/Data/Reorder/ReorderOutput.php | 4 ++-- .../Magento/{Sales => Quote}/Api/ReorderInterface.php | 4 ++-- .../Model/Order => Quote/Model/Quote}/Reorder.php | 10 +++++----- app/code/Magento/Quote/etc/di.xml | 1 + .../Sales/Controller/AbstractController/Reorder.php | 6 +++--- app/code/Magento/Sales/etc/di.xml | 1 - .../Magento/SalesGraphQl/Model/Resolver/Reorder.php | 9 +++++---- 8 files changed, 19 insertions(+), 18 deletions(-) rename app/code/Magento/{Sales => Quote}/Api/Data/Reorder/LineItemError.php (94%) rename app/code/Magento/{Sales => Quote}/Api/Data/Reorder/ReorderOutput.php (88%) rename app/code/Magento/{Sales => Quote}/Api/ReorderInterface.php (84%) rename app/code/Magento/{Sales/Model/Order => Quote/Model/Quote}/Reorder.php (95%) diff --git a/app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php b/app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php similarity index 94% rename from app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php rename to app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php index b9608feb037b4..e224e85d0b8d7 100644 --- a/app/code/Magento/Sales/Api/Data/Reorder/LineItemError.php +++ b/app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Sales\Api\Data\Reorder; +namespace Magento\Quote\Api\Data\Reorder; /** * DTO represent Cart line item error diff --git a/app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php b/app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php similarity index 88% rename from app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php rename to app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php index c11f98215883c..4e9acdc06596d 100644 --- a/app/code/Magento/Sales/Api/Data/Reorder/ReorderOutput.php +++ b/app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Sales\Api\Data\Reorder; +namespace Magento\Quote\Api\Data\Reorder; use Magento\Quote\Api\Data\CartInterface; /** - * DTO represent output for \Magento\Sales\Api\ReorderInterface + * DTO represent output for \Magento\Quote\Api\ReorderInterface */ class ReorderOutput { diff --git a/app/code/Magento/Sales/Api/ReorderInterface.php b/app/code/Magento/Quote/Api/ReorderInterface.php similarity index 84% rename from app/code/Magento/Sales/Api/ReorderInterface.php rename to app/code/Magento/Quote/Api/ReorderInterface.php index e38cee521fe3f..0fa882fb5213f 100644 --- a/app/code/Magento/Sales/Api/ReorderInterface.php +++ b/app/code/Magento/Quote/Api/ReorderInterface.php @@ -3,9 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Sales\Api; +namespace Magento\Quote\Api; -use Magento\Sales\Api\Data\Reorder\ReorderOutput; +use Magento\Quote\Api\Data\Reorder\ReorderOutput; /** * Allows customer to quickly reorder previously added products and put them to the Cart diff --git a/app/code/Magento/Sales/Model/Order/Reorder.php b/app/code/Magento/Quote/Model/Quote/Reorder.php similarity index 95% rename from app/code/Magento/Sales/Model/Order/Reorder.php rename to app/code/Magento/Quote/Model/Quote/Reorder.php index 4f014f452eeb1..b2918f441508c 100644 --- a/app/code/Magento/Sales/Model/Order/Reorder.php +++ b/app/code/Magento/Quote/Model/Quote/Reorder.php @@ -3,14 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Sales\Model\Order; +namespace Magento\Quote\Model\Quote; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Sales\Api\ReorderInterface; +use Magento\Quote\Api\ReorderInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; use Magento\Sales\Model\OrderFactory; @@ -88,7 +88,7 @@ public function __construct( * @throws InputException * @throws NoSuchEntityException */ - public function execute(string $incrementOrderId, string $storeId): \Magento\Sales\Api\Data\Reorder\ReorderOutput + public function execute(string $incrementOrderId, string $storeId): \Magento\Quote\Api\Data\Reorder\ReorderOutput { $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($incrementOrderId, $storeId); @@ -130,7 +130,7 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Sal $this->cartRepository->save($cart); - return new \Magento\Sales\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); + return new \Magento\Quote\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); } @@ -169,7 +169,7 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi */ private function addLineItemError(&$errors, \Magento\Sales\Model\Order\Item $item, $message): void { - $errors[] = new \Magento\Sales\Api\Data\Reorder\LineItemError( + $errors[] = new \Magento\Quote\Api\Data\Reorder\LineItemError( $item->getProduct() ? $item->getProduct()->getSku() : $item->getSku() ?? '', $message ); diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index f66001e7789cf..45856c754efbd 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -44,6 +44,7 @@ <preference for="Magento\Quote\Api\Data\EstimateAddressInterface" type="Magento\Quote\Model\EstimateAddress" /> <preference for="Magento\Quote\Api\Data\ProductOptionInterface" type="Magento\Quote\Model\Quote\ProductOption" /> <preference for="Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface" type="Magento\Quote\Model\ValidationRules\QuoteValidationComposite\Proxy"/> + <preference for="Magento\Quote\Api\ReorderInterface" type="Magento\Quote\Model\Quote\Reorder" /> <type name="Magento\Webapi\Controller\Rest\ParamsOverrider"> <arguments> <argument name="paramOverriders" xsi:type="array"> diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index f789d9b3caeb8..46f6887fda878 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -13,7 +13,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Registry; -use Magento\Sales\Api\ReorderInterface; +use Magento\Quote\Api\ReorderInterface; use Magento\Sales\Helper\Reorder as ReorderHelper; /** @@ -32,7 +32,7 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface protected $_coreRegistry; /** - * @var \Magento\Sales\Api\ReorderInterface + * @var ReorderInterface */ private $reorder; @@ -49,7 +49,7 @@ public function __construct( OrderLoaderInterface $orderLoader, Registry $registry, ReorderHelper $reorderHelper = null, - \Magento\Sales\Api\ReorderInterface $reOrder = null + ReorderInterface $reOrder = null ) { $this->orderLoader = $orderLoader; $this->_coreRegistry = $registry; diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 17fd3773be70e..b4dadfa944a5b 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -117,7 +117,6 @@ <preference for="Magento\Sales\Api\RefundInvoiceInterface" type="Magento\Sales\Model\RefundInvoice"/> <preference for="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface" type="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider" /> <preference for="Magento\Sales\Model\ConfigInterface" type="Magento\Sales\Model\Config" /> - <preference for="Magento\Sales\Api\ReorderInterface" type="Magento\Sales\Model\Order\Reorder" /> <type name="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider"> <arguments> <argument name="providers" xsi:type="array"> diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index 80a11d0ebbcd3..ca39ad1ae2a66 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -12,7 +12,8 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\GraphQl\Model\Query\ContextInterface; -use Magento\Sales\Api\Data\Reorder\LineItemError; +use Magento\Quote\Api\Data\Reorder\LineItemError; +use Magento\Quote\Api\ReorderInterface; use Magento\Sales\Model\OrderFactory; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -27,16 +28,16 @@ class Reorder implements ResolverInterface private $orderFactory; /** - * @var \Magento\Sales\Api\ReorderInterface + * @var ReorderInterface */ private $reorder; /** - * @param \Magento\Sales\Api\ReorderInterface $reorder + * @param ReorderInterface $reorder * @param OrderFactory $orderFactory */ public function __construct( - \Magento\Sales\Api\ReorderInterface $reorder, + ReorderInterface $reorder, OrderFactory $orderFactory ) { $this->orderFactory = $orderFactory; From 4ecae3093cfc04c0a7d66045e0b54b30c73112b3 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 12 Mar 2020 18:30:52 -0500 Subject: [PATCH 376/734] MC-31117: Can't create new customer account from frontend with required DOB field - Fix wrong date format passed to moment.js --- .../Magento/Customer/Block/Widget/Dob.php | 6 ++- ...dminCustomerShowDateOfBirthActionGroup.xml | 26 +++++++++++ ...reateAccountWithDateOfBirthActionGroup.xml | 20 +++++++++ .../Mftf/Data/AdminCustomerConfigData.xml | 16 +++++++ .../Customer/Test/Mftf/Data/DateData.xml | 25 +++++++++++ .../Section/AdminCustomerConfigSection.xml | 2 + .../StorefrontCustomerCreateFormSection.xml | 1 + ...frontCreateCustomerWithDateOfBirthTest.xml | 43 +++++++++++++++++++ .../Test/Unit/Block/Widget/DobTest.php | 40 +++++++++++++---- .../view/frontend/web/js/validation.js | 9 ++-- lib/web/mage/validation.js | 5 ++- 11 files changed, 177 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Data/DateData.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml diff --git a/app/code/Magento/Customer/Block/Widget/Dob.php b/app/code/Magento/Customer/Block/Widget/Dob.php index e020de79a3a60..90ce9ba210ed2 100644 --- a/app/code/Magento/Customer/Block/Widget/Dob.php +++ b/app/code/Magento/Customer/Block/Widget/Dob.php @@ -9,7 +9,7 @@ use Magento\Framework\Api\ArrayObjectSearch; /** - * Class Dob + * Customer date of birth attribute block * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ @@ -267,7 +267,9 @@ public function getHtmlExtraParams() $validators['validate-date'] = [ 'dateFormat' => $this->getDateFormat() ]; - $validators['validate-dob'] = true; + $validators['validate-dob'] = [ + 'dateFormat' => $this->getDateFormat() + ]; return 'data-validate="' . $this->_escaper->escapeHtml(json_encode($validators)) . '"'; } diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml new file mode 100644 index 0000000000000..009c37c568c44 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml @@ -0,0 +1,26 @@ +<?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="AdminCustomerShowDateOfBirthActionGroup"> + <annotations> + <description>Goes to the customer configuration. Set "Show Date of Birth" with provided value.</description> + </annotations> + <arguments> + <argument name="value" type="string" defaultValue="{{ShowDateOfBirth.optional}}"/> + </arguments> + <amOnPage url="{{AdminCustomerConfigPage.url('#customer_address-link')}}" stepKey="openCustomerConfigPage"/> + <waitForPageLoad stepKey="waitCustomerConfigPage"/> + <scrollTo selector="{{AdminCustomerConfigSection.showDateOfBirth}}" x="0" y="-100" stepKey="scrollToShowDateOfBirth"/> + <uncheckOption selector="{{AdminCustomerConfigSection.showDateOfBirthInherit}}" stepKey="uncheckUseSystem"/> + <selectOption selector="{{AdminCustomerConfigSection.showDateOfBirth}}" userInput="{{value}}" stepKey="fillShowDateOfBirth"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml new file mode 100644 index 0000000000000..22af2752ed1a5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerCreateAccountWithDateOfBirthActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <annotations> + <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Fills birthday field.</description> + </annotations> + <arguments> + <argument name="dob" defaultValue="{{EN_US_DATE.short4DigitYear}}" type="string"/> + </arguments> + <fillField userInput="{{dob}}" selector="{{StorefrontCustomerCreateFormSection.dobField}}" after="fillLastName" stepKey="fillDob"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml new file mode 100644 index 0000000000000..53963b1e95fbd --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.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="ShowDateOfBirth"> + <data key="no">No</data> + <data key="optional">Optional</data> + <data key="required">Required</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml b/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml new file mode 100644 index 0000000000000..5fbb1c279e38e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml @@ -0,0 +1,25 @@ +<?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="EN_US_DATE"> + <data key="short">9/21/93</data> + <data key="short4DigitYear">9/21/1993</data> + <data key="medium">Sep 21, 1993</data> + <data key="long">September 21, 1993</data> + <data key="full">Tuesday, September 21, 1993</data> + </entity> + <entity name="FR_FR_DATE"> + <data key="short">21/09/1993</data> + <data key="short4DigitYear">21/09/1993</data> + <data key="medium">21 sept. 1993</data> + <data key="long">21 septembre 1993</data> + <data key="full">mardi 21 septembre 1993</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml index a934d71397b8c..f8a441e40a792 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml @@ -11,5 +11,7 @@ <element name="accountSharingOptionsTab" type="button" selector="#customer_account_share-head"/> <element name="shareCustomerAccountInherit" type="checkbox" selector="#customer_account_share_scope_inherit"/> <element name="shareCustomerAccount" type="select" selector="#customer_account_share_scope"/> + <element name="showDateOfBirth" type="select" selector="#customer_address_dob_show"/> + <element name="showDateOfBirthInherit" type="select" selector="#customer_address_dob_show_inherit"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml index 9fc26a03b04ee..d1658c19545ff 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -11,6 +11,7 @@ <section name="StorefrontCustomerCreateFormSection"> <element name="firstnameField" type="input" selector="#firstname"/> <element name="lastnameField" type="input" selector="#lastname"/> + <element name="dobField" type="input" selector="#dob"/> <element name="lastnameLabel" type="text" selector="//label[@for='lastname']"/> <element name="signUpForNewsletter" type="checkbox" selector="//div/input[@name='is_subscribed']"/> <element name="emailField" type="input" selector="#email_address"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml new file mode 100644 index 0000000000000..8de446a59ed07 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml @@ -0,0 +1,43 @@ +<?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="StorefrontCreateCustomerWithDateOfBirthTest"> + <annotations> + <features value="Customer"/> + <stories value="Create a Customer via the Storefront"/> + <title value="Customer should be able to create an account with date of birth via the storefront"/> + <description value="Customer should be able to create an account with date of birth via the storefront"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-32413"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="showDateOfBirth"> + <argument name="value" value="{{ShowDateOfBirth.required}}"/> + </actionGroup> + </before> + <after> + <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="hideDateOfBirth"> + <argument name="value" value="{{ShowDateOfBirth.no}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="StorefrontCustomerCreateAccountWithDateOfBirthActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + <argument name="dob" value="{{EN_US_DATE.short4DigitYear}}"/> + </actionGroup> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index 1fd7fc340e542..59660ec345814 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -29,6 +29,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class DobTest extends TestCase { @@ -542,18 +543,30 @@ public function testGetMaxDateRangeWithException() */ public function testGetHtmlExtraParamsWithoutRequiredOption() { + $validation = json_encode( + [ + 'validate-date' => [ + 'dateFormat' => self::DATE_FORMAT + ], + 'validate-dob' => [ + 'dateFormat' => self::DATE_FORMAT + ], + ] + ); $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') - ->will($this->returnValue('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}')); + ->with($validation) + ->will( + $this->returnValue($validation) + ); $this->attribute->expects($this->once()) ->method("isRequired") ->willReturn(false); $this->assertEquals( - $this->_block->getHtmlExtraParams(), - 'data-validate="{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"' + "data-validate=\"$validation\"", + $this->_block->getHtmlExtraParams() ); } @@ -562,22 +575,31 @@ public function testGetHtmlExtraParamsWithoutRequiredOption() */ public function testGetHtmlExtraParamsWithRequiredOption() { + $validation = json_encode( + [ + 'required' => true, + 'validate-date' => [ + 'dateFormat' => self::DATE_FORMAT + ], + 'validate-dob' => [ + 'dateFormat' => self::DATE_FORMAT + ], + ] + ); $this->attribute->expects($this->once()) ->method("isRequired") ->willReturn(true); $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') + ->with($validation) ->will( - $this->returnValue( - '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' - ) + $this->returnValue($validation) ); $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($this->escaper)); $this->assertEquals( - 'data-validate="{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"', + "data-validate=\"$validation\"", $this->_block->getHtmlExtraParams() ); } diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 67a714212026a..181b0f69d6c04 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -1,19 +1,22 @@ define([ 'jquery', 'moment', + 'mageUtils', 'jquery/validate', 'mage/translate' -], function ($, moment) { +], function ($, moment, utils) { 'use strict'; $.validator.addMethod( 'validate-dob', - function (value) { + function (value, element, params) { + var dateFormat = utils.convertToMomentFormat(params.dateFormat); + if (value === '') { return true; } - return moment(value).isBefore(moment()); + return moment(value, dateFormat).isBefore(moment()); }, $.mage.__('The Date of Birth should not be greater than today.') ); diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 18e7b6413bc96..10f9dab6bdd9b 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -6,10 +6,11 @@ define([ 'jquery', 'moment', + 'mageUtils', 'jquery-ui-modules/widget', 'jquery/validate', 'mage/translate' -], function ($, moment) { +], function ($, moment, utils) { 'use strict'; var creditCartTypes, rules, showLabel, originValidateDelegate; @@ -1032,7 +1033,7 @@ define([ ], 'validate-date': [ function (value, params, additionalParams) { - var test = moment(value, additionalParams.dateFormat); + var test = moment(value, utils.convertToMomentFormat(additionalParams.dateFormat)); return $.mage.isEmptyNoTrim(value) || test.isValid(); }, From 01ba4bdc0dd87612309431fb06bb21a623211564 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 14 Mar 2020 02:49:41 +0100 Subject: [PATCH 377/734] Resolved conflicts and restructured Action Groups to be reusable atomically. --- ...ConditionsDeleteTermByNameActionGroup.xml} | 11 +---- ...rmsConditionsEditTermByNameActionGroup.xml | 19 ++++++++ ...ConditionsFillTermEditFormActionGroup.xml} | 9 ++-- ...ConditionsFilterGridByNameActionGroup.xml} | 9 ++-- ...dminTermsConditionsOpenGridActionGroup.xml | 15 ++++++ ...msConditionsOpenNewTermPageActionGroup.xml | 15 ++++++ ...dminTermsConditionsSaveTermActionGroup.xml | 15 ++++++ ...nTermsConditionsUpdateTermActionGroup.xml} | 5 +- ...tAdminTermsConditionsInGridActionGroup.xml | 14 ++++++ ...efrontTermAbsentInCheckoutActionGroup.xml} | 2 +- ...rtStorefrontTermInCheckoutActionGroup.xml} | 2 +- ...ageInMultishippingCheckoutActionGroup.xml} | 2 +- .../AdminCreateActiveHtmlTermEntityTest.xml | 36 +++++++++----- .../AdminCreateActiveTextTermEntityTest.xml | 26 +++------- .../AdminCreateDisabledTextTermEntityTest.xml | 36 +++++++++----- ...abledTextTermOnMultishippingEntityTest.xml | 23 ++++++--- ...oresTermsAndConditionsNavigateMenuTest.xml | 4 +- .../AdminUpdateDisabledHtmlTermEntityTest.xml | 47 +++++++++++++------ .../AdminUpdateDisabledTextTermEntityTest.xml | 33 +++++-------- .../AdminUpdateEnabledTextTermEntityTest.xml | 32 +++++-------- 20 files changed, 220 insertions(+), 135 deletions(-) rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{DeleteTermActionGroup.xml => AdminTermsConditionsDeleteTermByNameActionGroup.xml} (55%) create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{CreateNewTermActionGroup.xml => AdminTermsConditionsFillTermEditFormActionGroup.xml} (75%) rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{AdminAssertTermInGridActionGroup.xml => AdminTermsConditionsFilterGridByNameActionGroup.xml} (68%) create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{UpdateTermActionGroup.xml => AdminTermsConditionsUpdateTermActionGroup.xml} (92%) create mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{StorefrontAssertTermAbsentInCheckoutActionGroup.xml => AssertStorefrontTermAbsentInCheckoutActionGroup.xml} (96%) rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{StorefrontAssertTermInCheckoutActionGroup.xml => AssertStorefrontTermInCheckoutActionGroup.xml} (96%) rename app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/{StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup.xml => AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml} (97%) diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml similarity index 55% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml index 13163e90efdbc..9489fece37008 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/DeleteTermActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml @@ -8,16 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DeleteTermActionGroup"> - <arguments> - <argument name="term"/> - </arguments> - <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> - <waitForPageLoad stepKey="waitForAdminTermsGridPageLoad"/> - <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> - <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> - <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + <actionGroup name="AdminTermsConditionsDeleteTermByNameActionGroup"> <click selector="{{AdminEditTermFormSection.delete}}" stepKey="clickDeleteButton"/> <waitForElementVisible selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="waitForElement"/> <click selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="clickDeleteOkButton"/> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml new file mode 100644 index 0000000000000..8f2e65415ac22 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsEditTermByNameActionGroup" extends="AdminTermsConditionsFilterGridByNameActionGroup"> + <annotations> + <description>Filters Terms and Conditions grid and opens the first result Edit page</description> + </annotations> + + <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml similarity index 75% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml index d420cc155a77c..f32f1b11926a3 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/CreateNewTermActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml @@ -8,12 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateNewTermActionGroup"> + <actionGroup name="AdminTermsConditionsFillTermEditFormActionGroup"> <arguments> <argument name="term"/> </arguments> - <amOnPage url="{{AdminNewTermPage.url}}" stepKey="amOnNewTermPage"/> - <waitForPageLoad stepKey="waitForAdminNewTermPageLoad"/> + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{term.name}}" stepKey="fillFieldConditionName"/> <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{term.isActive}}" stepKey="selectOptionIsActive"/> <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{term.isHtml}}" stepKey="selectOptionIsHtml"/> @@ -21,7 +20,5 @@ <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{term.storeView}}" stepKey="selectOptionStoreView" /> <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{term.checkboxText}}" stepKey="fillFieldCheckboxText"/> <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{term.content}}" stepKey="fillFieldContent"/> - <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> - <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml similarity index 68% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml index 9a855c6f8b5e9..2290d8152473c 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermInGridActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml @@ -8,14 +8,15 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertTermInGridActionGroup"> + <actionGroup name="AdminTermsConditionsFilterGridByNameActionGroup"> + <annotations> + <description>Filters Terms and Conditions grid for name</description> + </annotations> <arguments> <argument name="termName" type="string"/> </arguments> - <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/> <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> - <see selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermInGrid"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml new file mode 100644 index 0000000000000..98a0a04e501fc --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.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="AdminTermsConditionsOpenGridActionGroup"> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml new file mode 100644 index 0000000000000..a6aa97dd269d4 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.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="AdminTermsConditionsOpenNewTermPageActionGroup"> + <amOnPage url="{{AdminNewTermPage.url}}" stepKey="amOnNewTermPage"/> + <waitForPageLoad stepKey="waitForAdminNewTermPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml new file mode 100644 index 0000000000000..457c4495c28e3 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.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="AdminTermsConditionsSaveTermActionGroup"> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml similarity index 92% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml index d95729dfc27f8..b7f92093356fa 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/UpdateTermActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml @@ -17,8 +17,7 @@ <waitForPageLoad stepKey="waitForAdminTermsGridLoad"/> <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> - <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{updateTermData.name}}" stepKey="fillFieldConditionName"/> <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{updateTermData.isActive}}" stepKey="selectOptionIsActive"/> <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{updateTermData.isHtml}}" stepKey="selectOptionIsHtml"/> @@ -29,4 +28,4 @@ <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml new file mode 100644 index 0000000000000..37f2761bf8e9a --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AssertAdminTermsConditionsInGridActionGroup" extends="AdminTermsConditionsFilterGridByNameActionGroup"> + <see selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermInGrid" after="clickSearchButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml similarity index 96% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml index 7be17d8ca69d0..bf0c4f4b5a2c5 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermAbsentInCheckoutActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertTermAbsentInCheckoutActionGroup"> + <actionGroup name="AssertStorefrontTermAbsentInCheckoutActionGroup"> <arguments> <argument name="termCheckboxText" type="string"/> </arguments> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml similarity index 96% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml index 0cf745ce4e04f..bef0591a6e311 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermInCheckoutActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertTermInCheckoutActionGroup"> + <actionGroup name="AssertStorefrontTermInCheckoutActionGroup"> <arguments> <argument name="termCheckboxText" type="string"/> </arguments> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml similarity index 97% rename from app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup.xml rename to app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml index 35ac4826ccfef..c8f49adc30067 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup"> + <actionGroup name="AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup"> <arguments> <argument name="termCheckboxText" type="string"/> </arguments> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml index 1e87d73c26205..c597d3d660dc8 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml @@ -21,32 +21,44 @@ </annotations> <before> <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeHtmlTerm"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="activeHtmlTerm"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> <argument name="termName" value="{{activeHtmlTerm.name}}"/> </actionGroup> + + <openNewTab stepKey="openStorefrontTab"/> <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> + <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{activeHtmlTerm.checkboxText}}"/> </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> </test> </tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml index 2db3377e0e89e..a90c3536ec744 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateActiveTextTermEntityTest"> + <test name="AdminCreateActiveTextTermEntityTest" extends="AdminCreateActiveHtmlTermEntityTest"> <annotations> <features value="CheckoutAgreements"/> <stories value="Checkout agreements"/> @@ -19,33 +19,19 @@ <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> - <before> - <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> <after> - <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeTextTerm"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="activeTextTerm"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> - </actionGroup> - <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> </actionGroup> </test> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml index df666ecab817b..3c0c171fdfe9e 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml @@ -21,32 +21,44 @@ </annotations> <before> <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="disabledTextTerm"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{disabledTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="disabledTextTerm"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> <argument name="termName" value="{{disabledTextTerm.name}}"/> </actionGroup> + + <openNewTab stepKey="openStorefrontTab"/> <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> + <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> <argument name="termCheckboxText" value="{{disabledTextTerm.checkboxText}}"/> </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> </test> </tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml index fec2365431862..c60ef95c8edce 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml @@ -21,27 +21,36 @@ </annotations> <before> <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createdCustomer"/> <createData entity="SimpleTwo" stepKey="createdProduct1"/> <createData entity="SimpleTwo" stepKey="createdProduct2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdCustomer" stepKey="deletedCustomer"/> <deleteData createDataKey="createdProduct1" stepKey="deletedProduct1"/> <deleteData createDataKey="createdProduct2" stepKey="deletedProduct2"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeTextTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="activeTextTerm"/> </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> <argument name="Customer" value="$$createdCustomer$$" /> </actionGroup> @@ -51,7 +60,7 @@ <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProduct2ToTheCart"> <argument name="product" value="$$createdProduct2$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertTermRequireMessageInMultishippingCheckoutActionGroup" stepKey="assertTermInCheckout"> + <actionGroup ref="AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> </actionGroup> </test> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml index 7ffabcfa51215..dced8e8f7a52b 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> @@ -29,7 +29,7 @@ <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> <argument name="submenuUiId" value="{{AdminMenuStoresSettingsTermsAndConditions.dataUiId}}"/> </actionGroup> - <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> + <actionGroup ref="AssertAdminPageTitleActionGroup" stepKey="seePageTitle"> <argument name="title" value="{{AdminMenuStoresSettingsTermsAndConditions.pageTitle}}"/> </actionGroup> </test> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml index 29ed6c5194c38..9779bf2df2b3c 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml @@ -21,34 +21,53 @@ </annotations> <before> <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeTextTerm"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="disabledHtmlTerm"/> </actionGroup> - <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> - <argument name="term" value="disabledHtmlTerm"/> - <argument name="updateTermData" value="activeTextTerm"/> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="activeTextTerm"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveUpdateTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openNewTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> + + <openNewTab stepKey="openStorefrontTab"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> + <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> </test> </tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml index d3d4e805e0ef7..30bdff5c8b24f 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUpdateDisabledTextTermEntityTest"> + <test name="AdminUpdateDisabledTextTermEntityTest" extends="AdminUpdateDisabledHtmlTermEntityTest"> <annotations> <features value="CheckoutAgreements"/> <stories value="Checkout agreements"/> @@ -19,35 +19,26 @@ <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> - <before> - <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> - </before> + <after> - <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeHtmlTerm"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="disabledTextTerm"/> </actionGroup> - <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> - <argument name="term" value="disabledTextTerm"/> - <argument name="updateTermData" value="activeHtmlTerm"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{disabledTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> - <argument name="termName" value="{{activeHtmlTerm.name}}"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="activeHtmlTerm"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> </actionGroup> - <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{activeHtmlTerm.checkboxText}}"/> </actionGroup> </test> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml index 5a761442a5992..bd1ccbc5ae29a 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUpdateEnabledTextTermEntityTest"> + <test name="AdminUpdateEnabledTextTermEntityTest" extends="AdminUpdateDisabledHtmlTermEntityTest"> <annotations> <features value="CheckoutAgreements"/> <stories value="Checkout agreements"/> @@ -19,35 +19,25 @@ <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> - <before> - <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> - <createData entity="SimpleTwo" stepKey="createdProduct"/> - </before> <after> - <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> - <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="disabledHtmlTerm"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> <argument name="term" value="activeTextTerm"/> </actionGroup> - <actionGroup ref="UpdateTermActionGroup" stepKey="updateTerm"> - <argument name="term" value="activeTextTerm"/> - <argument name="updateTermData" value="disabledHtmlTerm"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AdminAssertTermInGridActionGroup" stepKey="assertTermInGrid"> - <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="disabledHtmlTerm"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> - <argument name="product" value="$$createdProduct$$"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> </actionGroup> - <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermInCheckout"> <argument name="termCheckboxText" value="{{disabledHtmlTerm.checkboxText}}"/> </actionGroup> </test> From f421fba8b3cd5df9a31b0d140f128a3bd48d9761 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 14 Mar 2020 03:57:03 +0100 Subject: [PATCH 378/734] Renamed 1 reference too much :-) --- .../Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml index dced8e8f7a52b..3e680ce83f00e 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml @@ -29,7 +29,7 @@ <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> <argument name="submenuUiId" value="{{AdminMenuStoresSettingsTermsAndConditions.dataUiId}}"/> </actionGroup> - <actionGroup ref="AssertAdminPageTitleActionGroup" stepKey="seePageTitle"> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> <argument name="title" value="{{AdminMenuStoresSettingsTermsAndConditions.pageTitle}}"/> </actionGroup> </test> From 25abbc8467dae02fd9fd07f3e6838d98c28558f8 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 14 Mar 2020 03:55:00 +0100 Subject: [PATCH 379/734] Merge latest 2.4-develop --- .../AdminDeleteUserActionGroup.xml | 2 +- .../Magento/User/Test/Mftf/Data/UserData.xml | 2 ++ .../Test/AdminCreateActiveUserEntityTest.xml | 6 ++--- .../AdminCreateInactiveUserEntityTest.xml | 17 +++++++------- .../Test/AdminLockAdminUserEntityTest.xml | 22 ++++++++++--------- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml index 67075eb52a678..82a3a37cdd724 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml @@ -18,7 +18,7 @@ <amOnPage stepKey="amOnAdminUsersPage" url="{{AdminUsersPage.url}}"/> <waitForPageLoad stepKey="waitForAdminUserPageLoad"/> - <click stepKey="openTheUser" selector="{{AdminDeleteUserSection.role(user.name)}}"/> + <click stepKey="openTheUser" selector="{{AdminDeleteUserSection.role(user.username)}}"/> <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteUserSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> <scrollToTopOfPage stepKey="scrollToTop"/> <click stepKey="clickToDeleteRole" selector="{{AdminDeleteUserSection.delete}}"/> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index b8a59f530c533..cf15c77810791 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -156,6 +156,7 @@ <data key="interface_local_label">English (United States)</data> <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> <data key="role">Administrators</data> + <data key="is_active">1</data> <array key="roles"> <item>1</item> </array> @@ -171,6 +172,7 @@ <data key="interface_local_label">English (United States)</data> <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> <data key="role">Administrators</data> + <data key="is_active">0</data> <array key="roles"> <item>1</item> </array> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml index 943f49f271e90..19189d86aafcb 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <after> - <actionGroup ref="LoginAsAdmin" stepKey="adminLogin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> <argument name="user" value="activeAdmin"/> </actionGroup> @@ -28,8 +28,8 @@ </after> <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> - <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> - <argument name="User" value="activeAdmin"/> + <actionGroup ref="AdminCreateUserWithRoleActionGroup" stepKey="createAdminUser"> + <argument name="user" value="activeAdmin"/> <argument name="role" value="roleDefaultAdministrator"/> </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutMasterAdmin"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml index e802b67eb8073..820c7f922784e 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -19,22 +19,23 @@ <group value="user"/> <group value="mtf_migrated"/> </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - </before> - <actionGroup ref="AdminCreateUserWithRoleAndIsInactiveActionGroup" stepKey="createAdminUser"> - <argument name="User" value="inactiveAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminMasterLogin"/> + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="user" value="inactiveAdmin"/> <argument name="role" value="roleDefaultAdministrator"/> </actionGroup> <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="assertAdminIsInGrid"> <argument name="user" value="inactiveAdmin"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToNewAdmin"> - <argument name="adminUser" value="inactiveAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminMasterLogout"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminNewLogin"> + <argument name="username" value="{{inactiveAdmin.username}}"/> + <argument name="password" value="{{inactiveAdmin.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeUserErrorMessage" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminNewLogout"/> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index c367bddc8d999..70b521bf688bc 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -19,16 +19,15 @@ <group value="user"/> <group value="mtf_migrated"/> </annotations> - <before> <magentoCLI command="config:set admin/captcha/enable 0" stepKey="disableAdminCaptcha"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set admin/captcha/enable 1" stepKey="enableAdminCaptcha"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create New User--> @@ -48,23 +47,26 @@ <!-- Log in to Admin Panel with incorrect password specified number of times--> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsDefaultUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFirstAttempt"> - <argument name="adminUser" value="adminUserIncorrectPassword"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserFirstAttempt"> + <argument name="username" value="{{adminUserIncorrectPassword.username}}"/> + <argument name="password" value="{{adminUserIncorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFirstAttempt"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserSecondAttempt"> - <argument name="adminUser" value="adminUserIncorrectPassword"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserSecondAttempt"> + <argument name="username" value="{{adminUserIncorrectPassword.username}}"/> + <argument name="password" value="{{adminUserIncorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorSecondAttempt"/> <!-- Log in to Admin Panel with correct password--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> - <argument name="adminUser" value="adminUserCorrectPassword"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserThirdAttempt"> + <argument name="username" value="{{adminUserCorrectPassword.username}}"/> + <argument name="password" value="{{adminUserCorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> <!--Login as default admin user--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsDefaultAdminUser"/> <!--Delete new User--> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> From 1e8ed89642906a7e20684da2f63cf72f149b52f8 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sun, 15 Mar 2020 03:44:34 +0100 Subject: [PATCH 380/734] Magento_Bundle / Remove `cache:flush` and extract Tests to separate files --- ...ndEditBundleProductOptionsNegativeTest.xml | 122 +++++++ ...CreateAndEditBundleProductSettingsTest.xml | 111 ------ ...alogSearchBundleByDescriptionMysqlTest.xml | 47 +++ ...ceCatalogSearchBundleByDescriptionTest.xml | 51 +++ ...anceCatalogSearchBundleByNameMysqlTest.xml | 47 +++ ...nceCatalogSearchBundleByPriceMysqlTest.xml | 56 +++ .../AdvanceCatalogSearchBundleByPriceTest.xml | 60 ++++ ...earchBundleByShortDescriptionMysqlTest.xml | 47 +++ ...alogSearchBundleByShortDescriptionTest.xml | 51 +++ .../AdvanceCatalogSearchBundleBySkuTest.xml | 46 +++ .../AdvanceCatalogSearchBundleProductTest.xml | 321 +----------------- .../MassEnableDisableBundleProductsTest.xml | 4 +- ...CatalogSearchBundleBySkuWithHyphenTest.xml | 3 +- .../StorefrontBundleAddToCartSuccessTest.xml | 114 +++++++ .../Mftf/Test/StorefrontBundleCartTest.xml | 104 ------ ...undleProductShownInCategoryListAndGrid.xml | 4 +- 16 files changed, 646 insertions(+), 542 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml new file mode 100644 index 0000000000000..8a8e9dd275ee4 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml @@ -0,0 +1,122 @@ +<?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="AdminCreateAndEditBundleProductOptionsNegativeTest"> + <annotations> + <features value="Bundle"/> + <stories value="Modify bundle product in Admin"/> + <title value="Admin should be able to remove any bundle option a bundle product"/> + <description value="Admin should be able to set/edit other product information when creating/editing a bundle product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-224"/> + <skip> + <issueId value="https://github.com/magento/magento2/issues/25468"/> + </skip> + <group value="Catalog"/> + </annotations> + <before> + <!-- Create a Website --> + <createData entity="customWebsite" stepKey="createWebsite"/> + + <!-- Create first simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> + + <!-- Create second simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product --> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + + <!-- Delete the simple product --> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + + <!-- Delete a Website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + + <!-- Log out --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Create new bundle product --> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> + <argument name="productType" value="bundle"/> + </actionGroup> + + <!-- Fill all main fields --> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> + + <!-- Add first bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Add second bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addSecondBundleOption"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{CheckboxOption.title}}"/> + <argument name="inputType" value="{{CheckboxOption.type}}"/> + </actionGroup> + + <!-- Add third bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addThirdBundleOption"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Set product in created Website --> + <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="selectProductInWebsites"> + <argument name="website" value="$createWebsite.website[name]$"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> + + <!-- Open created product --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Remove second option --> + <actionGroup ref="DeleteBundleOptionByIndexActionGroup" stepKey="deleteSecondOption"> + <argument name="deleteIndex" value="1"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveWithTwoOptions"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!-- Delete created bundle product --> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml index b143bd63280ea..8442f9e583cab 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml @@ -136,117 +136,6 @@ <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> - <!-- Delete created bundle product --> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - </test> - <test name="AdminCreateAndEditBundleProductOptionsNegativeTest"> - <annotations> - <features value="Bundle"/> - <stories value="Modify bundle product in Admin"/> - <title value="Admin should be able to remove any bundle option a bundle product"/> - <description value="Admin should be able to set/edit other product information when creating/editing a bundle product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-224"/> - <skip> - <issueId value="https://github.com/magento/magento2/issues/25468"/> - </skip> - <group value="Catalog"/> - </annotations> - <before> - <!-- Create a Website --> - <createData entity="customWebsite" stepKey="createWebsite"/> - - <!-- Create first simple product for a bundle option --> - <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> - - <!-- Create second simple product for a bundle option --> - <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> - - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!-- Delete the simple product --> - <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> - - <!-- Delete the simple product --> - <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> - - <!-- Delete a Website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> - <argument name="websiteName" value="Second Website"/> - </actionGroup> - - <!-- Log out --> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!-- Create new bundle product --> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> - <argument name="productType" value="bundle"/> - </actionGroup> - - <!-- Fill all main fields --> - <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> - - <!-- Add first bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> - <argument name="x" value="0"/> - <argument name="n" value="1"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> - <argument name="inputType" value="{{RadioButtonsOption.type}}"/> - </actionGroup> - - <!-- Add second bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addSecondBundleOption"> - <argument name="x" value="1"/> - <argument name="n" value="2"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{CheckboxOption.title}}"/> - <argument name="inputType" value="{{CheckboxOption.type}}"/> - </actionGroup> - - <!-- Add third bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addThirdBundleOption"> - <argument name="x" value="2"/> - <argument name="n" value="3"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> - <argument name="inputType" value="{{RadioButtonsOption.type}}"/> - </actionGroup> - - <!-- Set product in created Website --> - <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="selectProductInWebsites"> - <argument name="website" value="$createWebsite.website[name]$"/> - </actionGroup> - - <!-- Save product form --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> - - <!-- Open created product --> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - - <!-- Remove second option --> - <actionGroup ref="DeleteBundleOptionByIndexActionGroup" stepKey="deleteSecondOption"> - <argument name="deleteIndex" value="1"/> - </actionGroup> - - <!-- Save product form --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveWithTwoOptions"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - <!-- Delete created bundle product --> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BundleProduct"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..e9525e2a144fb --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml @@ -0,0 +1,47 @@ +<?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="AdvanceCatalogSearchBundleByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20473"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml new file mode 100644 index 0000000000000..95b4e06678af2 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml @@ -0,0 +1,51 @@ +<?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="AdvanceCatalogSearchBundleByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description"/> + <description value="Guest customer should be able to advance search Bundle product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-242"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml new file mode 100644 index 0000000000000..05fe8dd7de27e --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml @@ -0,0 +1,47 @@ +<?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="AdvanceCatalogSearchBundleByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20472"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml new file mode 100644 index 0000000000000..c70e9a95ab532 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml @@ -0,0 +1,56 @@ +<?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="AdvanceCatalogSearchBundleByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20475"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml new file mode 100644 index 0000000000000..f45c9ceba635f --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml @@ -0,0 +1,60 @@ +<?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="AdvanceCatalogSearchBundleByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price"/> + <description value="Guest customer should be able to advance search Bundle product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-251"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..2bb2974b78555 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml @@ -0,0 +1,47 @@ +<?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="AdvanceCatalogSearchBundleByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20474"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml new file mode 100644 index 0000000000000..05e14174d14a5 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml @@ -0,0 +1,51 @@ +<?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="AdvanceCatalogSearchBundleByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description"/> + <description value="Guest customer should be able to advance search Bundle product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-250"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml new file mode 100644 index 0000000000000..eadf7667b010b --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml @@ -0,0 +1,46 @@ +<?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="AdvanceCatalogSearchBundleBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product sku"/> + <description value="Guest customer should be able to advance search Bundle product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-143"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProductUnderscoredSku" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index c6aab0ea54ea2..e6af89181e558 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -36,280 +36,8 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20472"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product sku"/> - <description value="Guest customer should be able to advance search Bundle product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-143"/> - <group value="Bundle"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProductUnderscoredSku" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product description"/> - <description value="Guest customer should be able to advance search Bundle product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-242"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20473"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product short description"/> - <description value="Guest customer should be able to advance search Bundle product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-250"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20474"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product price"/> - <description value="Guest customer should be able to advance search Bundle product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-251"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <magentoCron stepKey="runCronReindex" groups="index"/> </before> <after> <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> @@ -320,49 +48,4 @@ <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> </test> - <test name="AdvanceCatalogSearchBundleByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20475"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 97e509db39fa7..4760570adfa2e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -120,9 +120,7 @@ <click selector="{{AdminProductFiltersSection.disable}}" stepKey="ClickOnDisable"/> <waitForPageLoad stepKey="waitForPageloadToExecute"/> - <!--Clear Cache - reindex - resets products according to enabled/disabled view--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronReindex" groups="index"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> <!--Confirm bundle products have been disabled--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml index d8d6034cd1a21..07be59b998dec 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml @@ -36,8 +36,7 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronReindex" groups="index"/> </before> <after> <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml new file mode 100644 index 0000000000000..9fc19f5c5750f --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml @@ -0,0 +1,114 @@ +<?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="StorefrontBundleAddToCartSuccessTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle product details page"/> + <title value="Customer should be able to add the bundle product to the cart"/> + <description value="Customer should be able to add the bundle product to the cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-232"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Start creating a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add Option One, a "Drop-down" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option One"/> + <argument name="inputType" value="select"/> + </actionGroup> + + <!-- Add Option Two, a "Radio Buttons" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Two"/> + <argument name="inputType" value="radio"/> + </actionGroup> + + <!-- Add Option Three, a "Checkbox" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Three"/> + <argument name="inputType" value="checkbox"/> + </actionGroup> + + <!-- Add Option Four, a "Multi Select" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> + <argument name="x" value="3"/> + <argument name="n" value="4"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Four"/> + <argument name="inputType" value="multi"/> + </actionGroup> + + <!-- Save product and go to storefront --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!-- Select all applicable options --> + <selectOption selector="select.bundle-option-select" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption1"/> + <click selector="input[type='radio']:nth-of-type(1)" stepKey="selectOption2"/> + <checkOption selector="input[type='checkbox']:nth-of-type(1)" stepKey="selectOption3"/> + <selectOption selector="select[multiple='multiple']" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption4"/> + + <!-- Customize and add the bundle product to our cart --> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCart1"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('1')}}" userInput="This is a required field." stepKey="validForm1"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('2')}}" userInput="Please select one of the options." stepKey="validForm2"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="validForm3"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="validForm4"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{BundleProduct.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> + + <!-- Verify cart contents --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> + <waitForPageLoad stepKey="waitForCart"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('1')}}" userInput="Option One" stepKey="seeOption1"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('2')}}" userInput="Option Two" stepKey="seeOption2"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('3')}}" userInput="Option Three" stepKey="seeOption3"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('4')}}" userInput="Option Four" stepKey="seeOption4"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('1')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue1"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('2')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue2"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('3')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue3"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('4')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue4"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index d617ced82074e..ed4a592b0d71e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -124,108 +124,4 @@ <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="error19"/> <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="error20"/> </test> - - <test name="StorefrontBundleAddToCartSuccessTest"> - <annotations> - <features value="Bundle"/> - <stories value="Bundle product details page"/> - <title value="Customer should be able to add the bundle product to the cart"/> - <description value="Customer should be able to add the bundle product to the cart"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-232"/> - <group value="Bundle"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!-- Start creating a bundle product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - - <!-- Add Option One, a "Drop-down" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> - <argument name="x" value="0"/> - <argument name="n" value="1"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option One"/> - <argument name="inputType" value="select"/> - </actionGroup> - - <!-- Add Option Two, a "Radio Buttons" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> - <argument name="x" value="1"/> - <argument name="n" value="2"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Two"/> - <argument name="inputType" value="radio"/> - </actionGroup> - - <!-- Add Option Three, a "Checkbox" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> - <argument name="x" value="2"/> - <argument name="n" value="3"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Three"/> - <argument name="inputType" value="checkbox"/> - </actionGroup> - - <!-- Add Option Four, a "Multi Select" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> - <argument name="x" value="3"/> - <argument name="n" value="4"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Four"/> - <argument name="inputType" value="multi"/> - </actionGroup> - - <!-- Save product and go to storefront --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStorefront"/> - <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> - - <!-- Select all applicable options --> - <selectOption selector="select.bundle-option-select" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption1"/> - <click selector="input[type='radio']:nth-of-type(1)" stepKey="selectOption2"/> - <checkOption selector="input[type='checkbox']:nth-of-type(1)" stepKey="selectOption3"/> - <selectOption selector="select[multiple='multiple']" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption4"/> - - <!-- Customize and add the bundle product to our cart --> - <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCart1"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('1')}}" userInput="This is a required field." stepKey="validForm1"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('2')}}" userInput="Please select one of the options." stepKey="validForm2"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="validForm3"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="validForm4"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{BundleProduct.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - - <!-- Verify cart contents --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> - <waitForPageLoad stepKey="waitForCart"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('1')}}" userInput="Option One" stepKey="seeOption1"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('2')}}" userInput="Option Two" stepKey="seeOption2"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('3')}}" userInput="Option Three" stepKey="seeOption3"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('4')}}" userInput="Option Four" stepKey="seeOption4"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('1')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue1"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('2')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue2"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('3')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue3"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('4')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue4"/> - </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml index 364d4fa68e590..11984bc200e15 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml @@ -76,9 +76,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronReindex" groups="index"/> <!--Go to category page--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> From 4c40dad1ca3582551a2830161e1a57fbd5c83df6 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Sun, 15 Mar 2020 22:44:22 +0700 Subject: [PATCH 381/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" --- app/code/Magento/Downloadable/Model/Link/Builder.php | 6 +++++- .../Downloadable/Test/Unit/Model/Link/BuilderTest.php | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index ff76f7eeda440..5ccf4ffa6fc7e 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -116,7 +116,7 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) $link->setLinkFile($linkFileName); $link->setLinkUrl(null); } - + if (isset($this->data['sample'])) { $link = $this->buildSample($link, $this->data['sample']); } @@ -132,6 +132,10 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) if (isset($this->data['is_unlimited']) && $this->data['is_unlimited']) { $link->setNumberOfDownloads(0); } + + if (isset($this->data['use_default_title']) && $this->data['use_default_title'] == '1') { + $link->setTitle(null); + } $this->resetData(); return $link; diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 26c5ccd90c463..32dce80bed5f3 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -68,7 +68,7 @@ protected function setUp() $this->linkMock = $this->getMockBuilder(LinkInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - + $this->service = $objectManagerHelper->getObject( Builder::class, [ @@ -160,6 +160,9 @@ public function testBuild($data, $expectedPrice) if (isset($data['is_unlimited'])) { $this->linkMock->expects($this->once())->method('setNumberOfDownloads')->with(0); } + if (isset($data['use_default_title']) && $data['use_default_title'] == '1') { + $this->linkMock->expects($this->once())->method('getTitle')->with(null); + } if (isset($data['price'])) { $this->linkMock->expects($this->once())->method('getPrice')->willReturn($data['price']); } else { From f49060be8ffa842445f7f8b2488e37bd6bd4ffbb Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 00:17:27 +0100 Subject: [PATCH 382/734] magento/module-cms Replace inheritance with Composition `cms/page/view` --- app/code/Magento/Cms/Controller/Page/View.php | 54 ++++++++++---- app/code/Magento/Cms/Helper/Page.php | 21 +++--- .../Test/Unit/Controller/Page/ViewTest.php | 71 ++++++++++--------- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Page/View.php b/app/code/Magento/Cms/Controller/Page/View.php index 9d5785450ec71..da35e7a9ea9e4 100644 --- a/app/code/Magento/Cms/Controller/Page/View.php +++ b/app/code/Magento/Cms/Controller/Page/View.php @@ -1,50 +1,78 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Controller\Page; -use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Cms\Helper\Page as PageHelper; use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\Controller\ResultInterface; /** * Custom page for storefront. Needs to be accessible by POST because of the store switching. */ -class View extends Action implements HttpGetActionInterface, HttpPostActionInterface +class View implements HttpGetActionInterface, HttpPostActionInterface { /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ protected $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + * @var RequestInterface + */ + private $request; + + /** + * @var PageHelper + */ + private $pageHelper; + + /** + * @param RequestInterface $request + * @param PageHelper $pageHelper + * @param ForwardFactory $resultForwardFactory */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + RequestInterface $request, + PageHelper $pageHelper, + ForwardFactory $resultForwardFactory ) { + $this->request = $request; + $this->pageHelper = $pageHelper; $this->resultForwardFactory = $resultForwardFactory; - parent::__construct($context); } /** * View CMS page action * - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ public function execute() { - $pageId = $this->getRequest()->getParam('page_id', $this->getRequest()->getParam('id', false)); - $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId); + $resultPage = $this->pageHelper->prepareResultPage($this, $this->getPageId()); if (!$resultPage) { $resultForward = $this->resultForwardFactory->create(); return $resultForward->forward('noroute'); } return $resultPage; } + + /** + * Returns Page ID if provided or null + * + * @return int|null + */ + private function getPageId(): ?int + { + $id = $this->request->getParam('page_id') ?? $this->request->getParam('id'); + + return $id ? (int)$id : null; + } } diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 39b292bf07239..501c2a5b051a2 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -3,22 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Helper; use Magento\Cms\Model\Page\CustomLayoutManagerInterface; use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; use Magento\Cms\Model\Page\IdentityMap; -use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Helper\AbstractHelper; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Result\Page as ResultPage; /** * CMS Page Helper + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ -class Page extends \Magento\Framework\App\Helper\AbstractHelper +class Page extends AbstractHelper { /** * CMS no-route config path @@ -146,11 +151,11 @@ public function __construct( /** * Return result CMS page * - * @param Action $action + * @param ActionInterface $action * @param int $pageId - * @return \Magento\Framework\View\Result\Page|bool + * @return ResultPage|bool */ - public function prepareResultPage(Action $action, $pageId = null) + public function prepareResultPage(ActionInterface $action, $pageId = null) { if ($pageId !== null && $pageId !== $this->_page->getId()) { $delimiterPosition = strrpos($pageId, '|'); @@ -180,7 +185,7 @@ public function prepareResultPage(Action $action, $pageId = null) $this->_design->setDesignTheme($this->_page->getCustomTheme()); } } - /** @var \Magento\Framework\View\Result\Page $resultPage */ + /** @var ResultPage $resultPage */ $resultPage = $this->resultPageFactory->create(); $this->setLayoutType($inRange, $resultPage); $resultPage->addHandle('cms_page_view'); @@ -247,8 +252,8 @@ public function getPageUrl($pageId = null) * Set layout type * * @param bool $inRange - * @param \Magento\Framework\View\Result\Page $resultPage - * @return \Magento\Framework\View\Result\Page + * @param ResultPage $resultPage + * @return ResultPage */ protected function setLayoutType($inRange, $resultPage) { diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php index f15e6ff3e3bf2..e4c2beaa75aa7 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php @@ -3,73 +3,80 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Test\Unit\Controller\Page; -class ViewTest extends \PHPUnit\Framework\TestCase +use Magento\Cms\Controller\Page\View; +use Magento\Cms\Helper\Page as PageHelper; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Result\Page; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ViewTest extends TestCase { + private const STUB_PAGE_ID = 2; + /** - * @var \Magento\Cms\Controller\Page\View + * @var View */ protected $controller; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ - protected $cmsHelperMock; + protected $pageHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject|RequestInterface */ protected $requestMock; /** - * @var \Magento\Framework\Controller\Result\ForwardFactory|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|ForwardFactory */ protected $forwardFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|Forward */ protected $forwardMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|Page */ protected $resultPageMock; - /** - * @var string - */ - protected $pageId = '2'; - protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $objectManager = new ObjectManagerHelper($this); + + $this->resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); - $this->forwardFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\ForwardFactory::class) + $this->forwardFactoryMock = $this->getMockBuilder(ForwardFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->forwardMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Forward::class) + $this->forwardMock = $this->getMockBuilder(Forward::class) ->disableOriginalConstructor() ->getMock(); $this->forwardFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->forwardMock); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->cmsHelperMock = $this->createMock(\Magento\Cms\Helper\Page::class); - $objectManagerMock->expects($this->once())->method('get')->willReturn($this->cmsHelperMock); - $this->controller = $helper->getObject( - \Magento\Cms\Controller\Page\View::class, + $this->requestMock = $this->createMock(RequestInterface::class); + $this->pageHelperMock = $this->createMock(PageHelper::class); + + $this->controller = $objectManager->getObject( + View::class, [ - 'response' => $responseMock, - 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, + 'pageHelper' => $this->pageHelperMock, 'resultForwardFactory' => $this->forwardFactoryMock ] ); @@ -81,13 +88,13 @@ public function testExecuteResultPage() ->method('getParam') ->willReturnMap( [ - ['page_id', $this->pageId, $this->pageId], - ['id', false, $this->pageId] + ['page_id', null, self::STUB_PAGE_ID], + ['id', null, self::STUB_PAGE_ID] ] ); - $this->cmsHelperMock->expects($this->once()) + $this->pageHelperMock->expects($this->once()) ->method('prepareResultPage') - ->with($this->controller, $this->pageId) + ->with($this->controller, self::STUB_PAGE_ID) ->willReturn($this->resultPageMock); $this->assertSame($this->resultPageMock, $this->controller->execute()); } @@ -98,8 +105,8 @@ public function testExecuteResultForward() ->method('getParam') ->willReturnMap( [ - ['page_id', $this->pageId, $this->pageId], - ['id', false, $this->pageId] + ['page_id', null, self::STUB_PAGE_ID], + ['id', null, self::STUB_PAGE_ID] ] ); $this->forwardMock->expects($this->once()) From 4fd4f99410b3fd10069c14f3ab38cd42a063c05f Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 01:19:51 +0100 Subject: [PATCH 383/734] Fix failing Unit Test due to Page ID that can be INT --- app/code/Magento/Cms/Helper/Page.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 501c2a5b051a2..24581cb9ee90e 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -158,7 +158,7 @@ public function __construct( public function prepareResultPage(ActionInterface $action, $pageId = null) { if ($pageId !== null && $pageId !== $this->_page->getId()) { - $delimiterPosition = strrpos($pageId, '|'); + $delimiterPosition = strrpos((string)$pageId, '|'); if ($delimiterPosition) { $pageId = substr($pageId, 0, $delimiterPosition); } From 4a7adebc2060d561626981ba65a66609d2d4ed40 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 03:11:22 +0100 Subject: [PATCH 384/734] We are not ready for `strict_types` in this Class --- app/code/Magento/Cms/Helper/Page.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 24581cb9ee90e..d899a5cea985a 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\Cms\Helper; use Magento\Cms\Model\Page\CustomLayoutManagerInterface; From ebb1fae1439e178463092c38a2448a74942b9e3c Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Mon, 16 Mar 2020 10:17:26 +0200 Subject: [PATCH 385/734] MC-32128: Admin: create shipment for order --- ...th_two_order_items_with_simple_product.php | 88 +++++ ...der_items_with_simple_product_rollback.php | 36 ++ .../Save/CreateShipmentForOrderTest.php | 334 ++++++++++++++++++ 3 files changed, 458 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php new file mode 100644 index 0000000000000..48f5d5dd99f80 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\Data\OrderAddressInterfaceFactory; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\Data\OrderItemInterfaceFactory; +use Magento\Sales\Api\Data\OrderPaymentInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Store\Model\StoreManagerInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/products.php'; + +$addressData = include __DIR__ . '/address_data.php'; + +/** @var OrderAddressInterfaceFactory $addressFactory */ +$addressFactory = $objectManager->get(OrderAddressInterfaceFactory::class); +/** @var OrderPaymentInterfaceFactory $paymentFactory */ +$paymentFactory = $objectManager->get(OrderPaymentInterfaceFactory::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); +/** @var OrderItemInterfaceFactory $orderItemFactory */ +$orderItemFactory = $objectManager->get(OrderItemInterfaceFactory::class); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); + +$billingAddress = $addressFactory->create(['data' => $addressData]); +$billingAddress->setAddressType(Address::TYPE_BILLING); +$shippingAddress = $addressFactory->create(['data' => $addressData]); +$shippingAddress->setAddressType(Address::TYPE_SHIPPING); +$payment = $paymentFactory->create(); +$payment->setMethod('checkmo')->setAdditionalInformation( + [ + 'last_trans_id' => '11122', + 'metadata' => [ + 'type' => 'free', + 'fraudulent' => false, + ] + ] +); + +$defaultStoreId = $storeManager->getStore('default')->getId(); +$order = $orderFactory->create(); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(20) + ->setGrandTotal(20) + ->setBaseSubtotal(20) + ->setBaseGrandTotal(20) + ->setCustomerIsGuest(false) + ->setCustomerId($customer->getId()) + ->setCustomerEmail($customer->getEmail()) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($defaultStoreId) + ->setPayment($payment); + +$orderItem = $orderItemFactory->create(); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(5) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType($product->getTypeId()) + ->setName($product->getName()) + ->setSku($product->getSku()); +$order->addItem($orderItem); + +$orderItem = $orderItemFactory->create(); +$orderItem->setProductId($customDesignProduct->getId()) + ->setQtyOrdered(5) + ->setBasePrice($customDesignProduct->getPrice()) + ->setPrice($customDesignProduct->getPrice()) + ->setRowTotal($customDesignProduct->getPrice()) + ->setProductType($customDesignProduct->getTypeId()) + ->setName($customDesignProduct->getName()) + ->setSku($customDesignProduct->getSku()); +$order->addItem($orderItem); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php new file mode 100644 index 0000000000000..b76b5178d3d25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var CollectionFactory $orderCollectionFactory */ +$orderCollectionFactory = $objectManager->get(CollectionFactory::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$order = $orderCollectionFactory->create() + ->addFieldToFilter(OrderInterface::INCREMENT_ID, '100000001') + ->setPageSize(1) + ->getFirstItem(); +if ($order->getId()) { + $orderRepository->delete($order); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php new file mode 100644 index 0000000000000..20543f808c316 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php @@ -0,0 +1,334 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment\Save; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Shipping\Controller\Adminhtml\Order\Shipment\AbstractShipmentControllerTest; +use Magento\Framework\Escaper; + +/** + * Test cases related to check that shipment creates as expected or proper error message appear. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * + * @see \Magento\Shipping\Controller\Adminhtml\Order\Shipment\Save::execute + */ +class CreateShipmentForOrderTest extends AbstractShipmentControllerTest +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * Assert that shipment created successfully. + * + * @magentoDataFixture Magento/Sales/_files/order_with_two_order_items_with_simple_product.php + * @dataProvider dataForCreateShipmentDataProvider + * + * @param array $dataForBuildPostData + * @param array $expectedShippedQtyBySku + * @return void + */ + public function testCreateOrderShipment(array $dataForBuildPostData, array $expectedShippedQtyBySku): void + { + $postData = $this->createPostData($dataForBuildPostData); + $order = $this->orderRepository->get($postData['order_id']); + $shipment = $this->getShipment($order); + $this->assertNull($shipment->getEntityId()); + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestSuccessfullyPerformed((int)$order->getEntityId()); + $createdShipment = $this->getShipment($order); + $this->assertNotNull($createdShipment->getEntityId()); + $shipmentItems = $createdShipment->getItems(); + $this->assertCount(count($expectedShippedQtyBySku), $shipmentItems); + foreach ($shipmentItems as $shipmentItem) { + $this->assertEquals($expectedShippedQtyBySku[$shipmentItem->getSku()], $shipmentItem->getQty()); + } + } + + /** + * Data for create full or partial order shipment POST data. + * + * @return array + */ + public function dataForCreateShipmentDataProvider(): array + { + return [ + 'create_full_shipment' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create full shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + 'create_partial_shipment' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 3, + 'custom-design-simple-product' => 2, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 3, + 'custom-design-simple-product' => 2, + ], + ], + 'create_shipment_for_one_of_product' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 4, + 'custom-design-simple-product' => 0, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 4, + ], + ], + 'create_shipment_qty_more_that_ordered' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 150, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + 'create_shipment_qty_less_that_zero' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => -10, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'custom-design-simple-product' => 5, + ], + ], + 'create_shipment_without_counts' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + ]; + } + + /** + * Assert that body contains 404 error and forwarded to no-route if + * requested POST data is empty. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithoutPostData(): void + { + $this->performShipmentCreationRequest([]); + $this->assert404NotFound(); + } + + /** + * Assert that if we doesn't send order_id it will forwarded to no-rout and contains error 404. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithoutOrderId(): void + { + $dataToSend = [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 2, + ], + 'comment_text' => 'Create full shipment', + ]; + $postData = $this->createPostData($dataToSend); + unset($postData['order_id']); + $this->performShipmentCreationRequest($postData); + $this->assert404NotFound(); + } + + /** + * Assert that if we send POST data with wrong order item ids we got error message. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithWrongItemsIds(): void + { + $orderId = $this->getOrder('100000001')->getEntityId(); + $postData = [ + 'order_id' => $orderId, + 'shipment' => + [ + 'items' => [ + 678678 => 4 + ], + ], + ]; + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestWithError( + "Shipment Document Validation Error(s):\nYou can't create a shipment without products.", + "admin/order_shipment/new/order_id/{$orderId}" + ); + } + + /** + * Assert that if we send POST data with alphabet order item count we got error message. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithAlphabetQty(): void + { + $dataToSend = [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 'test_letters', + ], + 'comment_text' => 'Create full shipment', + ]; + $postData = $this->createPostData($dataToSend); + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestWithError( + "Shipment Document Validation Error(s):\nYou can't create a shipment without products.", + "admin/order_shipment/new/order_id/{$postData['order_id']}" + ); + } + + /** + * @inheritdoc + */ + public function assert404NotFound() + { + $this->assertEquals('noroute', $this->getRequest()->getControllerName()); + $this->assertContains('404 Error', $this->getResponse()->getBody()); + $this->assertContains('Page not found', $this->getResponse()->getBody()); + } + + /** + * Set POST data and type POST to request + * and perform request by path backend/admin/order_shipment/save. + * + * @param array $postData + * @return void + */ + private function performShipmentCreationRequest(array $postData): void + { + $this->getRequest()->setPostValue($postData) + ->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/admin/order_shipment/save'); + } + + /** + * Assert that after create shipment session message manager + * contain message "The shipment has been created." and redirect to sales/order/view is created. + * + * @param int $orderId + * @return void + */ + private function assertCreateShipmentRequestSuccessfullyPerformed(int $orderId): void + { + $this->assertSessionMessages( + $this->equalTo([(string)__('The shipment has been created.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $orderId)); + } + + /** + * Assert that session message manager contain provided error message and + * redirect created if it was provided. + * + * @param string $message + * @param string|null $redirect + * @param bool $isNeedEscapeMessage + * @return void + */ + private function assertCreateShipmentRequestWithError( + string $message, + ?string $redirect = null, + bool $isNeedEscapeMessage = true + ): void { + $assertMessage = $isNeedEscapeMessage + ? $this->escaper->escapeHtml((string)__($message)) : (string)__($message); + $this->assertSessionMessages( + $this->equalTo([$assertMessage]), + MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringContains($redirect)); + } + + /** + * Create POST data for create shipment for order. + * + * @param array $dataForBuildPostData + * @return array + */ + private function createPostData(array $dataForBuildPostData): array + { + $result = []; + $order = $this->getOrder($dataForBuildPostData['order_increment_id']); + $result['order_id'] = (int)$order->getEntityId(); + $shipment = []; + $shipment['items'] = []; + foreach ($order->getItems() as $orderItem) { + if (!isset($dataForBuildPostData['items_count_to_ship_by_sku'][$orderItem->getSku()])) { + continue; + } + $shipment['items'][$orderItem->getItemId()] + = $dataForBuildPostData['items_count_to_ship_by_sku'][$orderItem->getSku()]; + } + if (empty($shipment['items'])) { + unset($shipment['items']); + } + + $shipment['comment_text'] = $dataForBuildPostData['comment_text']; + $result['shipment'] = $shipment; + + return $result; + } +} From 5932b5904fe7460ad687c96107399089219a3df6 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Mon, 16 Mar 2020 12:24:06 +0200 Subject: [PATCH 386/734] MC-32134: Storefront: Customer wish list on customer profile --- .../Model/GetWishlistByCustomerId.php | 61 +++++ .../Wishlist/Block/Customer/SharingTest.php | 61 +++++ .../Customer/Wishlist/Item/ColumnTest.php | 124 +++++++-- .../Wishlist/Block/Customer/WishlistTest.php | 39 ++- .../Wishlist/Controller/Index/AddTest.php | 189 ++++++++++++++ .../Wishlist/Controller/Index/AllcartTest.php | 110 ++++++++ .../Wishlist/Controller/Index/CartTest.php | 120 +++++++++ .../Wishlist/Controller/Index/IndexTest.php | 82 ++++++ .../Wishlist/Controller/Index/PluginTest.php | 45 +++- .../Wishlist/Controller/Index/RemoveTest.php | 79 ++++++ .../Wishlist/Controller/Index/SendTest.php | 180 +++++++++++++ .../Index/UpdateItemOptionsTest.php | 198 ++++++++++++++ .../Wishlist/Controller/Index/UpdateTest.php | 98 +++++++ .../Magento/Wishlist/Controller/IndexTest.php | 190 -------------- .../Magento/Wishlist/Model/ItemTest.php | 168 +++++++++--- .../Magento/Wishlist/Model/WishlistTest.php | 246 ++++++++++++++---- .../_files/wishlist_shared_rollback.php | 9 + .../wishlist_with_configurable_product.php | 17 ++ ...ist_with_configurable_product_rollback.php | 10 + .../_files/wishlist_with_simple_product.php | 16 ++ .../wishlist_with_simple_product_rollback.php | 9 + 21 files changed, 1735 insertions(+), 316 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php b/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php new file mode 100644 index 0000000000000..93f170466cb40 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Wishlist\Model; + +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; + +/** + * Load wish list by customer id. + */ +class GetWishlistByCustomerId +{ + /** @var WishlistFactory */ + private $wishlistFactory; + + /** + * @param WishlistFactory $wishlistFactory + */ + public function __construct(WishlistFactory $wishlistFactory) + { + $this->wishlistFactory = $wishlistFactory; + } + + /** + * Load wish list by customer id. + * + * @param int $customerId + * @return Wishlist + */ + public function execute(int $customerId): Wishlist + { + return $this->wishlistFactory->create()->loadByCustomerId($customerId, true); + } + + /** + * Get wish list item by sku. + * + * @param int $customerId + * @param string $sku + * @return null|Item + */ + public function getItemBySku(int $customerId, string $sku): ?Item + { + $result = null; + $items = $this->execute($customerId)->getItemCollection()->getItems(); + foreach ($items as $item) { + if ($item->getProduct()->getData('sku') === $sku) { + $result = $item; + break; + } + } + + return $result; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php new file mode 100644 index 0000000000000..a96a7e9b4c7b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Customer; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class test share wish list block. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class SharingTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Sharing */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Sharing::class); + } + + /** + * @return void + */ + public function testDisplayWishListSharingForm(): void + { + $elementsXpath = [ + 'Emails input' => "//form[contains(@class, 'share')]//textarea[@name='emails' and @id='email_address']", + 'Message input' => "//form[contains(@class, 'share')]//textarea[@name='message' and @id='message']", + 'Share button' => "//form[contains(@class, 'share')]//button[contains(@class, 'submit')]" + . "/span[contains(text(), 'Share Wish List')]", + ]; + $blockHtml = $this->block->setTemplate('Magento_Wishlist::sharing.phtml')->toHtml(); + foreach ($elementsXpath as $element => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $blockHtml), + sprintf("%s was not found.", $element) + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php index 668aec616533c..ffce4354dd097 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php @@ -3,44 +3,130 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Wishlist\Block\Customer\Wishlist\Item; -class ColumnTest extends \PHPUnit\Framework\TestCase +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Element\Text; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Block\Customer\Wishlist\Items; +use PHPUnit\Framework\TestCase; + +/** + * Test wish list item column. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ColumnTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $customerSession; + + /** @var LayoutInterface */ + private $layout; + + /** @var Column */ + private $block; + + /** @var GetWishlistByCustomerId */ + private $getWishlistItemsByCustomerId; + /** - * @var \Magento\Framework\View\LayoutInterface + * @inheritdoc */ - protected $_layout = null; + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->addBlock(Column::class, 'test'); + $this->layout->addBlock(Text::class, 'child', 'test'); + $this->getWishlistItemsByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + } /** - * @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column + * @inheritdoc */ - protected $_block = null; - - protected function setUp() + protected function tearDown() { - $this->_layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - ); - $this->_block = $this->_layout->addBlock(\Magento\Wishlist\Block\Customer\Wishlist\Item\Column::class, 'test'); - $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'child', 'test'); + $this->customerSession->setCustomerId(null); + + parent::tearDown(); } /** * @magentoAppIsolation enabled + * + * @return void */ - public function testToHtml() + public function testToHtml(): void { $item = new \StdClass(); - $this->_block->setItem($item); - $this->_block->toHtml(); - $this->assertSame($item, $this->_layout->getBlock('child')->getItem()); + $this->block->setItem($item); + $this->block->toHtml(); + $this->assertSame($item, $this->layout->getBlock('child')->getItem()); } - public function testGetJs() + /** + * @return void + */ + public function testGetJs(): void { $expected = uniqid(); - $this->_layout->getBlock('child')->setJs($expected); - $this->assertEquals($expected, $this->_block->getJs()); + $this->layout->getBlock('child')->setJs($expected); + $this->assertEquals($expected, $this->block->getJs()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testWishListItemButtons(): void + { + $buttons = [ + "Add to Cart button" => "//button[contains(@class, 'tocart')]/span[contains(text(), 'Add to Cart')]", + "Edit button" => "//a[contains(@class, 'edit')]/span[contains(text(), 'Edit')]", + "Remove item button" => "//a[contains(@class, 'delete')]/span[contains(text(), 'Remove item')]", + ]; + $item = $this->getWishlistItemsByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $block = $this->getWishListItemsBlock()->getChildBlock('customer.wishlist.item.inner'); + $blockHtml = $block->setItem($item)->toHtml(); + foreach ($buttons as $buttonName => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $blockHtml), + sprintf("%s wasn't found.", $buttonName) + ); + } + } + + /** + * Get wish list items block. + * + * @return Items + */ + private function getWishListItemsBlock(): Items + { + $page = $this->objectManager->create(PageFactory::class)->create(); + $page->addHandle([ + 'default', + 'wishlist_index_index', + ]); + $page->getLayout()->generateXml(); + + return $page->getLayout()->getBlock('customer.wishlist.items'); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php index 944d2ac6faada..36cd7fe3e4c89 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase; /** - * Class test my wish list on customer account page. + * Class test block wish list on customer account page. * * @magentoAppArea frontend * @magentoDbIsolation enabled @@ -66,9 +66,11 @@ protected function tearDown() public function testDisplayNumberOfItemsInWishList(): void { $this->customerSession->setCustomerId(1); + $pagerBlockHtml = $this->getWishListBlock()->getChildBlock('wishlist_item_pager')->toHtml(); $this->assertEquals( 1, - Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 1), $this->getWishListPagerBlockHtml()) + Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 1), $pagerBlockHtml), + "Element items count wasn't found." ); } @@ -82,27 +84,46 @@ public function testDisplayItemQuantitiesInWishList(): void { $this->markTestSkipped('Test is blocked by issue MC-31595'); $this->customerSession->setCustomerId(1); + $pagerBlockHtml = $this->getWishListBlock()->getChildBlock('wishlist_item_pager')->toHtml(); $this->assertEquals( 1, - Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 3), $this->getWishListPagerBlockHtml()) + Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 3), $pagerBlockHtml), + "Element items count wasn't found." ); } /** - * Get wish list pager block html. + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php * - * @return string + * @return void + */ + public function testDisplayActionButtonsInWishList(): void + { + $buttonsXpath = [ + "//button[contains(@class, 'update') and @type='submit']/span[contains(text(), 'Update Wish List')]", + "//button[contains(@class, 'share') and @type='submit']/span[contains(text(), 'Share Wish List')]", + "//button[contains(@class, 'tocart') and @type='button']/span[contains(text(), 'Add All to Cart')]", + ]; + $this->customerSession->setCustomerId(1); + $blockHtml = $this->getWishListBlock()->toHtml(); + foreach ($buttonsXpath as $xpath) { + $this->assertEquals(1, Xpath::getElementsCountForXpath($xpath, $blockHtml)); + } + } + + /** + * Get wish list block. + * + * @return Wishlist */ - private function getWishListPagerBlockHtml(): string + private function getWishListBlock(): Wishlist { $this->page->addHandle([ 'default', 'wishlist_index_index', ]); $this->page->getLayout()->generateXml(); - /** @var Wishlist $customerWishlistBlock */ - $customerWishlistBlock = $this->page->getLayout()->getBlock('customer.wishlist'); - return $customerWishlistBlock->getChildBlock('wishlist_item_pager')->toHtml(); + return $this->page->getLayout()->getBlock('customer.wishlist'); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php new file mode 100644 index 0000000000000..82ae8e92d2979 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php @@ -0,0 +1,189 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Zend\Stdlib\Parameters; + +/** + * Test for add product to wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + */ +class AddTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php + * + * @return void + */ + public function testAddActionProductNameXss(): void + { + $this->prepareReferer(); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('product-with-xss'); + $escapedProductName = $this->escaper->escapeHtml($product->getName()); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSuccess(1, 1, $escapedProductName); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddConfigurableProductToWishList(): void + { + $this->prepareReferer(); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('Configurable product'); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSuccess(1, 1, $product->getName()); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testAddDisabledProductToWishList(): void + { + $expectedMessage = $this->escaper->escapeHtml("We can't specify a product."); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('simple3'); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddToWishListWithoutParams(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddToWishListRequest([]); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddNotExistingProductToWishList(): void + { + $this->customerSession->setCustomerId(1); + $expectedMessage = $this->escaper->escapeHtml("We can't specify a product."); + $this->performAddToWishListRequest(['product' => 989]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddToNotExistingWishList(): void + { + $expectedMessage = $this->escaper->escapeHtml("The requested Wish List doesn't exist."); + $this->customerSession->setCustomerId(1); + $this->performAddToWishListRequest(['wishlist_id' => 989]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assert404NotFound(); + } + + /** + * Perform request add item to wish list. + * + * @param array $params + * @return void + */ + private function performAddToWishListRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/add'); + } + + /** + * Assert success response and items count. + * + * @param int $customerId + * @param int $itemsCount + * @param string $productName + * @return void + */ + private function assertSuccess(int $customerId, int $itemsCount, string $productName): void + { + $expectedMessage = sprintf("\n%s has been added to your Wish List.", $productName) + . " Click <a href=\"http://localhost/test\">here</a> to continue shopping."; + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $this->assertCount($itemsCount, $wishlist->getItemCollection()); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $wishlist->getId())); + } + + /** + * Prepare referer to test. + * + * @return void + */ + private function prepareReferer(): void + { + $parameters = $this->_objectManager->create(Parameters::class); + $parameters->set('HTTP_REFERER', 'http://localhost/test'); + $this->getRequest()->setServer($parameters); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php new file mode 100644 index 0000000000000..bc589c2791eb5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Checkout\Model\CartFactory; +use Magento\Checkout\Model\Cart as CartModel; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for add all products to cart from wish list. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class AllcartTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var CartModel */ + private $cart; + + /** @var Escaper */ + private $escaper; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->cart = $this->_objectManager->get(CartFactory::class)->create(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_increments.php + * + * @return void + */ + public function testAddProductQtyIncrementToCartFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddAllToCartRequest(); + $wishlistCollection = $this->getWishlistByCustomerId->execute(1)->getItemCollection(); + $this->assertCount(1, $wishlistCollection); + $this->assertCount(0, $this->cart->getQuote()->getItemsCollection()); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $expectedMessage = $this->escaper->escapeHtml( + sprintf('You can buy this product only in quantities of 5 at a time for "%s".', $item->getName()) + ); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * + * @return void + */ + public function testAddAllProductToCartFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddAllToCartRequest(); + $quoteCollection = $this->cart->getQuote()->getItemsCollection(); + $this->assertCount(1, $quoteCollection); + $item = $quoteCollection->getFirstItem(); + $expectedMessage = $this->escaper->escapeHtml( + sprintf('1 product(s) have been added to shopping cart: "%s".', $item->getName()) + ); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS); + } + + /** + * Perform add all products to cart from wish list request. + * + * @return void + */ + private function performAddAllToCartRequest(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/allcart'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php new file mode 100644 index 0000000000000..3681409e40156 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Checkout\Model\CartFactory; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for add product to cart from wish list. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class CartTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var CartFactory */ + private $cartFactory; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->cartFactory = $this->_objectManager->get(CartFactory::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * + * @return void + */ + public function testAddSimpleProductToCart(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $this->performAddToCartRequest(['item' => $item->getId(), 'qty' => 3]); + $message = sprintf('You added %s to your shopping cart.', $item->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + $cart = $this->cartFactory->create(); + $this->assertEquals(1, $cart->getItemsCount()); + $this->assertEquals(3, $cart->getItemsQty()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * + * @return void + */ + public function testAddItemWithNotChosenOptionToCart(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $this->performAddToCartRequest(['item' => $item->getId(), 'qty' => 1]); + $redirectUrl = sprintf("wishlist/index/configure/id/%s/product_id/%s", $item->getId(), $item->getProductId()); + $this->assertRedirect($this->stringContains($redirectUrl)); + $message = 'You need to choose options for your item.'; + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_NOTICE); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testAddNotExistingItemToCart(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddToCartRequest(['item' => 989]); + $this->assertRedirect($this->stringContains('wishlist/index/')); + } + + /** + * Perform request add to cart from wish list. + * + * @param array $params + * @return void + */ + private function performAddToCartRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/cart'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php new file mode 100644 index 0000000000000..b92b2f3e5e85b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test wish list on customer account page. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class IndexTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * Verify wishlist view action + * + * The following is verified: + * - \Magento\Wishlist\Model\ResourceModel\Item\Collection + * - \Magento\Wishlist\Block\Customer\Wishlist + * - \Magento\Wishlist\Block\Customer\Wishlist\Items + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment + * - \Magento\Wishlist\Block\Customer\Wishlist\Button + * - that \Magento\Wishlist\Block\Customer\Wishlist\Item\Options doesn't throw a fatal error + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testItemColumnBlock(): void + { + $this->customerSession->setCustomerId(1); + $this->dispatch('wishlist/index/index'); + $body = $this->getResponse()->getBody(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + '//img[contains(@src, "small_image.jpg") and @alt = "Simple Product"]', + $body + ) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + '//textarea[contains(@name, "description")]', + $body + ) + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php index 5303c9f352b87..7193791bdbe6c 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php @@ -7,12 +7,17 @@ namespace Magento\Wishlist\Controller\Index; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\TestCase\AbstractController; use Magento\Customer\Model\Session as CustomerSession; use Magento\Catalog\Api\ProductRepositoryInterface; /** * Test for wishlist plugin before dispatch + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend */ class PluginTest extends AbstractController { @@ -21,13 +26,20 @@ class PluginTest extends AbstractController */ private $customerSession; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @inheritdoc */ protected function setUp() { parent::setUp(); + $this->customerSession = $this->_objectManager->get(CustomerSession::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); } /** @@ -37,20 +49,22 @@ protected function tearDown() { $this->customerSession->logout(); $this->customerSession = null; + parent::tearDown(); } /** * Test for adding product to wishlist with invalidate credentials * - * @return void * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppArea frontend + * + * @return void */ public function testAddActionProductWithInvalidCredentials(): void { - $this->getRequest()->setMethod('POST'); + $product = $this->productRepository->get('product-with-xss'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'login' => [ @@ -59,14 +73,23 @@ public function testAddActionProductWithInvalidCredentials(): void ], ] ); - - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); - - $product = $productRepository->get('product-with-xss'); - - $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); - + $this->getRequest()->setParams(['product' => $product->getId(), 'nocookie' => 1]); + $this->dispatch('wishlist/index/add'); $this->assertArrayNotHasKey('login', $this->customerSession->getBeforeWishlistRequest()); + $expectedMessage = 'You must login or register to add items to your wishlist.'; + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testWithDisabledWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->dispatch('wishlist/index/index'); + $this->assert404NotFound(); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php new file mode 100644 index 0000000000000..5bd8d006e5fe5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for remove product from wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class RemoveTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testRemoveProductFromWishList(): void + { + $customerId = 1; + $this->customerSession->setCustomerId($customerId); + $item = $this->getWishlistByCustomerId->getItemBySku($customerId, 'simple'); + $this->assertNotNull($item); + $productName = $item->getProduct()->getName(); + $this->getRequest()->setParam('item', $item->getId())->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/remove'); + $message = sprintf("\n%s has been removed from your Wish List.\n", $productName); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertCount(0, $this->getWishlistByCustomerId->execute($customerId)->getItemCollection()); + } + + /** + * @return void + */ + public function testRemoveNotExistingItemFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->getRequest()->setParams(['item' => 989])->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/remove'); + $this->assert404NotFound(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php new file mode 100644 index 0000000000000..6a89be8bdb3b0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php @@ -0,0 +1,180 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\CustomerNameGenerationInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test sending wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class SendTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var CustomerNameGenerationInterface */ + private $customerNameGeneration; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->customerNameGeneration = $this->_objectManager->get(CustomerNameGenerationInterface::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testSendWishList(): void + { + $product = $this->productRepository->get('simple'); + $this->customerSession->setCustomerId(1); + $shareMessage = 'Here\'s what I want for my birthday.'; + $postValues = ['emails' => 'test@example.com', 'message' => $shareMessage]; + $this->dispatchSendWishListRequest($postValues); + $this->assertSessionMessages( + $this->equalTo([(string)__('Your wish list has been shared.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertNotNull($this->transportBuilder->getSentMessage()); + $messageContent = $this->transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + $this->assertContains($shareMessage, $messageContent); + $this->assertContains( + sprintf( + '%s wants to share this Wish List', + $this->customerNameGeneration->getCustomerName($this->customerSession->getCustomerDataObject()) + ), + $messageContent + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//a[contains(@href, '%s')]/strong[contains(text(), '%s')]", + $product->getProductUrl(), + $product->getName() + ), + $messageContent + ) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + "//a[contains(@href, 'wishlist/shared/index/code/fixture_unique_code/')" + . " and contains(text(), 'View all Wish List')]", + $messageContent + ) + ); + } + + /** + * @magentoConfigFixture current_store wishlist/email/number_limit 2 + * + * @return void + */ + public function testSendWishListWithEmailsLimit(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test@example.com, test2@example.com, test3@example.com']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Maximum of 2 emails can be sent.'); + } + + /** + * @magentoConfigFixture current_store wishlist/email/text_limit 10 + * + * @return void + */ + public function testSendWishListWithTextLimit(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test@example.com', 'message' => 'Test message']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Message length must not exceed 10 symbols'); + } + + /** + * @return void + */ + public function testSendWishListWithoutEmails(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => '']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Please enter an email address.'); + } + + /** + * @return void + */ + public function testSendWishListWithInvalidEmail(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test @example.com']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Please enter a valid email address.'); + } + + /** + * Dispatch send wish list request. + * + * @param array $postValues + * @return void + */ + private function dispatchSendWishListRequest(array $postValues): void + { + $this->getRequest()->setPostValue($postValues)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/send'); + } + + /** + * Assert error message and redirect. + * + * @param string $message + * @return void + */ + private function assertResponseWithError(string $message): void + { + $this->assertSessionMessages($this->equalTo([__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/index/share')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php new file mode 100644 index 0000000000000..40646be11f684 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php @@ -0,0 +1,198 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Model\Item; + +/** + * Test for update wish list item. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class UpdateItemOptionsTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Escaper */ + private $escaper; + + /** @var SerializerInterface */ + private $json; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->json = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUpdateItemOptions(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $params = [ + 'id' => $item->getId(), + 'product' => $item->getProductId(), + 'super_attribute' => $this->performConfigurableOption($item->getProduct()), + 'qty' => 5, + ]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $this->assertUpdatedItem( + $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'), + $params + ); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testUpdateItemOptionsWithoutParams(): void + { + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest([]); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testUpdateNotExistingItem(): void + { + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => 989]); + $message = $this->escaper->escapeHtml("We can't specify a product."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testUpdateOutOfStockItem(): void + { + $product = $this->productRepository->get('simple3'); + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => $product->getId()]); + $message = $this->escaper->escapeHtml("We can't specify a product."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php + * + * @return void + */ + public function testUpdateItemNotSpecifyAsWishListItem(): void + { + $product = $this->productRepository->get('simple_ms_out_of_stock'); + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => $product->getId()]); + $message = $this->escaper->escapeHtml("We can't specify a wish list item."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/')); + } + + /** + * Perform request update wish list item. + * + * @param array $params + * @return void + */ + private function performUpdateWishListItemRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/updateItemOptions'); + } + + /** + * Assert updated item in wish list. + * + * @param Item $item + * @param array $expectedData + * @return void + */ + private function assertUpdatedItem(Item $item, array $expectedData): void + { + $this->assertEquals($expectedData['qty'], $item->getQty()); + $buyRequestOption = $this->json->unserialize($item->getOptionByCode('info_buyRequest')->getValue()); + foreach ($expectedData as $key => $value) { + $this->assertEquals($value, $buyRequestOption[$key]); + } + } + + /** + * Perform configurable option to select. + * + * @param ProductInterface $product + * @return array + */ + private function performConfigurableOption(ProductInterface $product): array + { + $configurableOptions = $product->getTypeInstance()->getConfigurableOptions($product); + $attributeId = key($configurableOptions); + $option = reset($configurableOptions[$attributeId]); + + return [$attributeId => $option['value_index']]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php new file mode 100644 index 0000000000000..8126ec71eb7db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for update wish list item. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class UpdateTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testUpdateWishListItem(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $params = ['description' => [$item->getId() => 'Some description.'], 'qty' => [$item->getId() => 5]]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $updatedItem = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($updatedItem); + $this->assertEquals(5, $updatedItem->getQty()); + $this->assertEquals('Some description.', $updatedItem->getDescription()); + } + + /** + * @return void + */ + public function testUpdateWishListItemZeroQty(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $params = ['description' => [$item->getId() => ''], 'qty' => [$item->getId() => 0]]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * Perform update wish list item request. + * + * @param array $params + * @return void + */ + private function performUpdateWishListItemRequest(array $params): void + { + $this->getRequest()->setPostValue($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/update'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php deleted file mode 100644 index d225b40dc39da..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Wishlist\Controller; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class IndexTest extends \Magento\TestFramework\TestCase\AbstractController -{ - /** - * @var \Magento\Customer\Model\Session - */ - protected $_customerSession; - - /** - * @var \Magento\Framework\Message\ManagerInterface - */ - protected $_messages; - - /** - * @var \Magento\Customer\Helper\View - */ - protected $_customerViewHelper; - - protected function setUp() - { - parent::setUp(); - $logger = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->_customerSession = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Customer\Model\Session::class, - [$logger] - ); - /** @var \Magento\Customer\Api\AccountManagementInterface $service */ - $service = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AccountManagementInterface::class - ); - $customer = $service->authenticate('customer@example.com', 'password'); - $this->_customerSession->setCustomerDataAsLoggedIn($customer); - - $this->_customerViewHelper = $this->_objectManager->create(\Magento\Customer\Helper\View::class); - - $this->_messages = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\Message\ManagerInterface::class - ); - } - - protected function tearDown() - { - $this->_customerSession->logout(); - $this->_customerSession = null; - parent::tearDown(); - } - - /** - * Verify wishlist view action - * - * The following is verified: - * - \Magento\Wishlist\Model\ResourceModel\Item\Collection - * - \Magento\Wishlist\Block\Customer\Wishlist - * - \Magento\Wishlist\Block\Customer\Wishlist\Items - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment - * - \Magento\Wishlist\Block\Customer\Wishlist\Button - * - that \Magento\Wishlist\Block\Customer\Wishlist\Item\Options doesn't throw a fatal error - * - * @magentoDataFixture Magento/Wishlist/_files/wishlist.php - */ - public function testItemColumnBlock() - { - $this->dispatch('wishlist/index/index'); - $body = $this->getResponse()->getBody(); - $this->assertEquals( - 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( - '//img[contains(@src, "small_image.jpg") and @alt = "Simple Product"]', - $body - ) - ); - $this->assertEquals( - 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( - '//textarea[contains(@name, "description")]', - $body - ) - ); - } - - /** - * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppArea frontend - */ - public function testAddActionProductNameXss() - { - /** @var \Magento\Framework\Data\Form\FormKey $formKey */ - $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class); - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue( - [ - 'form_key' => $formKey->getFormKey(), - ] - ); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $product = $productRepository->get('product-with-xss'); - - $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); - - $this->assertSessionMessages( - $this->equalTo( - [ - "\n<script>alert("xss");</script> has been added to your Wish List. " - . 'Click <a href="http://localhost/index.php/">here</a> to continue shopping.', - ] - ), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDbIsolation disabled - * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_increments.php - */ - public function testAllcartAction() - { - $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class)->getFormKey(); - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParam('form_key', $formKey); - $this->dispatch('wishlist/index/allcart'); - - /** @var \Magento\Checkout\Model\Cart $cart */ - $cart = $this->_objectManager->get(\Magento\Checkout\Model\Cart::class); - $quoteCount = $cart->getQuote()->getItemsCollection()->count(); - - $this->assertEquals(0, $quoteCount); - $this->assertSessionMessages( - $this->contains( - htmlspecialchars( - 'You can buy this product only in quantities of 5 at a time for "Simple Product".' - ) - ), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } - - /** - * @magentoDataFixture Magento/Wishlist/_files/wishlist.php - */ - public function testSendAction() - { - \Magento\TestFramework\Helper\Bootstrap::getInstance() - ->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - $request = [ - 'form_key' => $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class)->getFormKey(), - 'emails' => 'test@tosend.com', - 'message' => 'message', - 'rss_url' => null, // no rss - ]; - - $this->getRequest()->setPostValue($request); - $this->getRequest()->setMethod('POST'); - - $this->_objectManager->get(\Magento\Framework\Registry::class)->register( - 'wishlist', - $this->_objectManager->get(\Magento\Wishlist\Model\Wishlist::class)->loadByCustomerId(1) - ); - $this->dispatch('wishlist/index/send'); - - /** @var \Magento\TestFramework\Mail\Template\TransportBuilderMock $transportBuilder */ - $transportBuilder = $this->_objectManager->get( - \Magento\TestFramework\Mail\Template\TransportBuilderMock::class - ); - - $actualResult = $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent(); - - $this->assertStringMatchesFormat( - '%A' . $this->_customerViewHelper->getCustomerName($this->_customerSession->getCustomerDataObject()) - . ' wants to share this Wish List%A', - $actualResult - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php index 896b59c7983fe..afd15929ae685 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php @@ -3,57 +3,94 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Wishlist\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Exception as ProductException; +use Magento\Checkout\Model\CartFactory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObjectFactory; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Model\Item\OptionFactory; +use PHPUnit\Framework\TestCase; + /** - * Item test class. + * Tests for wish list item model. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled */ -class ItemTest extends \PHPUnit\Framework\TestCase +class ItemTest extends TestCase { - /** - * @var \Magento\Framework\App\ObjectManager - */ + /** @var ObjectManager */ private $objectManager; - /** - * @var \Magento\Wishlist\Model\Item - */ + /** @var Item */ private $model; + /** @var DataObjectFactory */ + private $dataObjectFactory; + + /** @var OptionFactory */ + private $optionFactory; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var CartFactory */ + private $cartFactory; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ItemFactory */ + private $itemFactory; + /** - * {@inheritDoc} + * @inheritdoc */ public function setUp() { - $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->model = $this->objectManager->get(\Magento\Wishlist\Model\Item::class); + parent::setUp(); + + $this->objectManager = ObjectManager::getInstance(); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); + $this->model = $this->objectManager->get(Item::class); + $this->itemFactory = $this->objectManager->get(ItemFactory::class); + $this->optionFactory = $this->objectManager->get(OptionFactory::class); + $this->getWishlistByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + $this->cartFactory = $this->objectManager->get(CartFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); } /** - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled + * @inheritdoc */ - public function testBuyRequest() + protected function tearDown() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $product = $productRepository->getById(1); + $this->cartFactory->create()->truncate(); - /** @var \Magento\Wishlist\Model\Item\Option $option */ - $option = $this->objectManager->create( - \Magento\Wishlist\Model\Item\Option::class, + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * + * @return void + */ + public function testBuyRequest(): void + { + $product = $this->productRepository->get('simple'); + $option = $this->optionFactory->create( ['data' => ['code' => 'info_buyRequest', 'value' => '{"qty":23}']] ); $option->setProduct($product); $this->model->addOption($option); - - // Assert getBuyRequest method $buyRequest = $this->model->getBuyRequest(); $this->assertEquals($buyRequest->getOriginalQty(), 23); - - // Assert mergeBuyRequest method $this->model->mergeBuyRequest(['qty' => 11, 'additional_data' => 'some value']); $buyRequest = $this->model->getBuyRequest(); $this->assertEquals( @@ -62,18 +99,87 @@ public function testBuyRequest() ); } - public function testSetBuyRequest() + /** + * @return void + */ + public function testSetBuyRequest(): void { - $buyRequest = $this->objectManager->create( - \Magento\Framework\DataObject::class, + $buyRequest = $this->dataObjectFactory->create( ['data' => ['field_1' => 'some data', 'field_2' => 234]] ); - $this->model->setBuyRequest($buyRequest); - - $this->assertEquals( + $this->assertJsonStringEqualsJsonString( '{"field_1":"some data","field_2":234,"id":null}', $this->model->getData('buy_request') ); } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddItemToCart(): void + { + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $cart = $this->cartFactory->create(); + $this->assertTrue($item->addToCart($cart)); + $this->assertCount(1, $cart->getQuote()->getItemsCollection()); + $this->assertCount(1, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddItemToCartAndDeleteFromWishList(): void + { + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $cart = $this->cartFactory->create(); + $item->addToCart($cart, true); + $this->assertCount(1, $cart->getQuote()->getItemsCollection()); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * + * @return void + */ + public function testAddOutOfStockItemToCart(): void + { + $product = $this->productRepository->get('simple-out-of-stock'); + $item = $this->itemFactory->create()->setProduct($product); + $this->expectExceptionObject(new ProductException(__('Product is not salable.'))); + $item->addToCart($this->cartFactory->create()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testAddDisabledItemToCart(): void + { + $product = $this->productRepository->get('simple3'); + $item = $this->itemFactory->create()->setProduct($product); + $this->assertFalse($item->addToCart($this->cartFactory->create())); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + * + * @return void + */ + public function testAddNotVisibleItemToCart(): void + { + $product = $this->productRepository->get('simple_not_visible_1'); + $item = $this->itemFactory->create()->setProduct($product)->setStoreId($product->getStoreId()); + $this->assertFalse($item->addToCart($this->cartFactory->create())); + } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php index b684da05dd254..f12299b32945d 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php @@ -3,63 +3,86 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Wishlist\Model; +use Magento\Bundle\Model\Product\OptionList; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Framework\App\ObjectManager; -use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use PHPUnit\Framework\TestCase; -class WishlistTest extends \PHPUnit\Framework\TestCase +/** + * Tests for wish list model. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class WishlistTest extends TestCase { - /** - * @var ObjectManager - */ + /** @var ObjectManager */ private $objectManager; - /** - * @var Wishlist - */ - private $wishlist; + /** @var WishlistFactory */ + private $wishlistFactory; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var DataObjectFactory */ + private $dataObjectFactory; + + /** @var SerializerInterface */ + private $json; /** - * {@inheritDoc} + * @inheritdoc */ protected function setUp() { $this->objectManager = ObjectManager::getInstance(); - $this->wishlist = $this->objectManager->get(Wishlist::class); + $this->wishlistFactory = $this->objectManager->get(WishlistFactory::class); + $this->getWishlistByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); + $this->json = $this->objectManager->get(SerializerInterface::class); } /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled + * + * @return void */ - public function testAddNewItem() + public function testAddNewItem(): void { $productSku = 'simple'; $customerId = 1; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $this->wishlist->loadByCustomerId($customerId, true); - $this->wishlist->addNewItem( + $product = $this->productRepository->get($productSku); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $wishlist->addNewItem( $product, '{"qty":2}' ); - $this->wishlist->addNewItem( + $wishlist->addNewItem( $product, ['qty' => 3] ); - $this->wishlist->addNewItem( + $wishlist->addNewItem( $product, - new DataObject(['qty' => 4]) + $this->dataObjectFactory->create(['data' => ['qty' => 4]]) ); - $this->wishlist->addNewItem($product); - /** @var Item $wishlistItem */ - $wishlistItem = $this->wishlist->getItemCollection()->getFirstItem(); + $wishlist->addNewItem($product); + $wishlistItem = $this->getWishlistByCustomerId->getItemBySku(1, $productSku); $this->assertInstanceOf(Item::class, $wishlistItem); $this->assertEquals($wishlistItem->getQty(), 10); } @@ -67,58 +90,169 @@ public function testAddNewItem() /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid wishlist item configuration. + * + * @return void */ - public function testAddNewItemInvalidWishlistItemConfiguration() + public function testAddNewItemInvalidWishlistItemConfiguration(): void { $productSku = 'simple'; $customerId = 1; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $this->wishlist->loadByCustomerId($customerId, true); - $this->wishlist->addNewItem( - $product, - '{"qty":2' - ); - $this->wishlist->addNewItem($product); + $product = $this->productRepository->get($productSku); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $this->expectExceptionObject(new \InvalidArgumentException('Invalid wishlist item configuration.')); + $wishlist->addNewItem($product, '{"qty":2'); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testGetItemCollection(): void + { + $productSku = 'simple'; + $item = $this->getWishlistByCustomerId->getItemBySku(1, $productSku); + $this->assertNotNull($item); } /** - * @magentoDbIsolation disabled * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void */ - public function testGetItemCollection() + public function testGetItemCollectionWithDisabledProduct(): void { $productSku = 'simple'; $customerId = 1; + $product = $this->productRepository->get($productSku); + $product->setStatus(ProductStatus::STATUS_DISABLED); + $this->productRepository->save($product); + $this->assertEmpty($this->getWishlistByCustomerId->execute($customerId)->getItemCollection()->getItems()); + } - $this->wishlist->loadByCustomerId($customerId, true); - $itemCollection = $this->wishlist->getItemCollection(); - /** @var \Magento\Wishlist\Model\Item $item */ - $item = $itemCollection->getFirstItem(); - $this->assertEquals($productSku, $item->getProduct()->getSku()); + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddConfigurableProductToWishList(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $configurableOptions = $configurableProduct->getTypeInstance()->getConfigurableOptions($configurableProduct); + $attributeId = key($configurableOptions); + $option = reset($configurableOptions[$attributeId]); + $buyRequest = ['super_attribute' => [$attributeId => $option['value_index']]]; + $wishlist = $this->getWishlistByCustomerId->execute(1); + $wishlist->addNewItem($configurableProduct, $buyRequest); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $this->assertWishListItem($item, $option['sku'], $buyRequest); } /** + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php + * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddBundleProductToWishList(): void + { + $bundleProduct = $this->productRepository->get('fixed_bundle_product_without_discounts'); + $bundleOptionList = $this->objectManager->create(OptionList::class); + $bundleOptions = $bundleOptionList->getItems($bundleProduct); + $option = reset($bundleOptions); + $productLinks = $option->getProductLinks(); + $this->assertNotNull($productLinks[0]); + $buyRequest = ['bundle_option' => [$option->getOptionId() => $productLinks[0]->getId()]]; + $skuWithChosenOption = implode('-', [$bundleProduct->getSku(), $productLinks[0]->getSku()]); + $wishlist = $this->getWishlistByCustomerId->execute(1); + $wishlist->addNewItem($bundleProduct, $buyRequest); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'fixed_bundle_product_without_discounts'); + $this->assertNotNull($item); + $this->assertWishListItem($item, $skuWithChosenOption, $buyRequest); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testAddNotExistingItemToWishList(): void + { + $wishlist = $this->getWishlistByCustomerId->execute(1); + $this->expectExceptionObject(new LocalizedException(__('Cannot specify product.'))); + $wishlist->addNewItem(989); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php + * + * @return void + */ + public function testAddOutOfStockItemToWishList(): void + { + $product = $this->productRepository->get('simple_ms_out_of_stock'); + $wishlist = $this->getWishlistByCustomerId->execute(1); + $this->expectExceptionObject(new LocalizedException(__('Cannot add product without stock to wishlist.'))); + $wishlist->addNewItem($product); + } + + /** * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void */ - public function testGetItemCollectionWithDisabledProduct() + public function testUpdateItemQtyInWishList(): void { - $productSku = 'simple'; - $customerId = 1; + $wishlist = $this->getWishlistByCustomerId->execute(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $buyRequest = $this->dataObjectFactory->create(['data' => ['qty' => 55]]); + $wishlist->updateItem($item->getId(), $buyRequest); + $updatedItem = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertEquals(55, $updatedItem->getQty()); + } - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $product->setStatus(ProductStatus::STATUS_DISABLED); - $productRepository->save($product); + /** + * @return void + */ + public function testUpdateNotExistingItemInWishList(): void + { + $this->expectExceptionObject(new LocalizedException(__('We can\'t specify a wish list item.'))); + $this->wishlistFactory->create()->updateItem(989, []); + } - $this->wishlist->loadByCustomerId($customerId, true); - $itemCollection = $this->wishlist->getItemCollection(); - $this->assertEmpty($itemCollection->getItems()); + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testUpdateNotExistingProductInWishList(): void + { + $wishlist = $this->getWishlistByCustomerId->execute(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $item->getProduct()->setId(null); + $this->expectExceptionObject(new LocalizedException(__('The product does not exist.'))); + $wishlist->updateItem($item, []); + } + + /** + * Assert item in wish list. + * + * @param Item $item + * @param string $itemSku + * @param array $buyRequest + * @return void + */ + private function assertWishListItem(Item $item, string $itemSku, array $buyRequest): void + { + $this->assertEquals($itemSku, $item->getProduct()->getSku()); + $buyRequestOption = $item->getOptionByCode('info_buyRequest'); + $this->assertEquals($buyRequest, $this->json->unserialize($buyRequestOption->getValue())); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php new file mode 100644 index 0000000000000..24bbccd5739f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php new file mode 100644 index 0000000000000..c67c3e32f24bc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Wishlist\Model\WishlistFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php'; + +$wishlistFactory = $objectManager->get(WishlistFactory::class); +$wishlist = $wishlistFactory->create(); +$wishlist->loadByCustomerId($customer->getId(), true); +$product = $productRepository->get('Configurable product'); +$wishlist->addNewItem($product); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php new file mode 100644 index 0000000000000..776d17137db30 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ + . '/../../../Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php new file mode 100644 index 0000000000000..61448c54dd7ab --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Wishlist\Model\WishlistFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$wishlistFactory = $objectManager->get(WishlistFactory::class); +$wishlist = $wishlistFactory->create(); +$wishlist->loadByCustomerId($customer->getId(), true); +$wishlist->addNewItem($product); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php new file mode 100644 index 0000000000000..ffa99feba6652 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; From 2410cbb760e1e88fa5c0681fa35baaf3686dd64a Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Mon, 16 Mar 2020 14:25:19 +0200 Subject: [PATCH 387/734] fix static, test coverage --- .../Adminhtml/Config/Form/Field/YtdStart.php | 38 +++++------- .../Config/Form/Field/YtdStartTest.php | 58 +++++++++++++++++++ 2 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index a64cb14ed9862..b564e582943ac 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -5,14 +5,13 @@ */ namespace Magento\Reports\Block\Adminhtml\Config\Form\Field; +use Magento\Config\Block\System\Config\Form\Field; use Magento\Framework\Data\Form\Element\AbstractElement; /** * Dashboard Year-To-Date Month and Day starts Field Renderer - * - * @author Magento Core Team <core@magentocommerce.com> */ -class YtdStart extends \Magento\Config\Block\System\Config\Form\Field +class YtdStart extends Field { /** * Get Month and Day Element @@ -25,38 +24,29 @@ protected function _getElementHtml(AbstractElement $element) { $_months = []; for ($i = 1; $i <= 12; $i++) { - $month = $this->_localeDate->date(mktime(null, null, null, $i, 1))->format('m'); + $month = $this->_localeDate->date(mktime(null, null, null, $i, 1)) + ->format('m'); $_months[$month] = $month; } ksort($_months); + $_days = []; for ($i = 1; $i <= 31; $i++) { $_days[$i] = $i < 10 ? '0' . $i : $i; } - if ($element->getValue()) { - $values = explode(',', $element->getValue()); - } else { - $values = []; - } - + $values = $element->getValue() ? explode(',', $element->getValue()) : []; $element->setName($element->getName() . '[]'); - $_monthsHtml = $element->setStyle( - 'width:100px;' - )->setValues( - $_months - )->setValue( - isset($values[0]) ? $values[0] : null - )->getElementHtml(); + $_monthsHtml = $element->setStyle('width:100px;') + ->setValues($_months) + ->setValue(isset($values[0]) ? $values[0] : null) + ->getElementHtml(); - $_daysHtml = $element->setStyle( - 'width:50px;' - )->setValues( - $_days - )->setValue( - isset($values[1]) ? $values[1] : null - )->getElementHtml(); + $_daysHtml = $element->setStyle('width:50px;') + ->setValues($_days) + ->setValue(isset($values[1]) ? $values[1] : null) + ->getElementHtml(); return sprintf('%s %s', $_monthsHtml, $_daysHtml); } diff --git a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php new file mode 100644 index 0000000000000..d378b3e7344e1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Reports\Block\Adminhtml\Config\Form\Field; + +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for \Magento\Reports\Block\Adminhtml\Config\Form\Field\YtdStart. + * + * @magentoAppArea adminhtml + */ +class YtdStartTest extends AbstractBackendController +{ + /** + * @var array + */ + private $monthNumbers = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']; + + /** + * Test Get Month and Day Element renderer + * + * @return void + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testGetElementHtml(): void + { + $this->dispatch('backend/admin/system_config/edit/section/reports/'); + $body = $this->getResponse()->getBody(); + + $this->assertContains($this->getOptionsHtml('01'), $body); + } + + /** + * Options html + * + * @param string $selected + * @return string + */ + private function getOptionsHtml(string $selected): string + { + $html = ''; + foreach ($this->monthNumbers as $number) { + $html .= $number === $selected + ? '<option value="' . $selected . '" selected="selected">' . $selected . '</option>' + : '<option value="' . $number . '">' . $number . '</option>'; + + $html .= PHP_EOL; + } + + return $html; + } +} From e7faf0678b8b249c492232aaf4a6b53059683add Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Mon, 16 Mar 2020 14:53:26 +0200 Subject: [PATCH 388/734] Refactoring --- ...wResourcesAccessIntegrationActionGroup.xml | 1 - ...ReAuthorizeTokensIntegrationEntityTest.xml | 64 +++++++++---------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml index 272cf130b8aec..bf4a9798225cc 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminAllowResourcesAccessIntegrationActionGroup.xml @@ -8,7 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAllowResourcesAccessIntegrationActionGroup"> <click stepKey="clickAllowButton" selector="{{AdminNewIntegrationSection.allow}}"/> <waitForPageLoad stepKey="waitForLoading"/> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml index 949c57e081866..bb61527ecab74 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml @@ -18,21 +18,16 @@ <group value="mtf_migrated"/> </annotations> <before> - <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> </actionGroup> - <!-- Click the "Add New Integration" button --> <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> - <!-- Create New Integration --> <actionGroup ref="AdminFillIntegrationFormActionGroup" stepKey="createIntegration"> <argument name="integration" value="defaultIntegrationData"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> - <!-- Click Save And Activate Button --> <actionGroup ref="AdminIntegrationSaveAndActivateActionGroup" stepKey="clickSaveAndActivateButton"/> <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="allowAccess"/> </before> @@ -45,35 +40,34 @@ <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteCreatedIntegration"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <!-- TEST BODY --> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabConsumerKey"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabConsumerSecret"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabAccessToken"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabAccessTokenSecret"/> - <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickDoneButton"/> - <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> - <argument name="name" value="{{defaultIntegrationData.name}}"/> - </actionGroup> - <actionGroup ref="AdminIntegrationClickReauthoriseLinkOnGridActionGroup" stepKey="clickReauthoriseLink"/> - <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickReauthoriseButton"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabAfterReauthorizeConsumerKey"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabAfterReauthorizeConsumerSecret"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabReauthorizedAccessToken"/> - <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabReauthorizedAccessTokenSecret"/> - <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="finishTheProcess"/> - <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> - <argument name="message" value="The integration '{{defaultIntegrationData.name}}' has been re-authorized."/> - </actionGroup> - <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findReAuthorizedIntegration"> - <argument name="name" value="{{defaultIntegrationData.name}}"/> - </actionGroup> - <actionGroup ref="AdminIntegrationOpenExistingEntityActionGroup" stepKey="openIntegrationEntity"/> - <!-- Compare Tokens After Reauthorizing --> - <assertEquals stepKey="assertConsumerKey" expected="($grabConsumerKey)" actual="$grabAfterReauthorizeConsumerKey" actualType="string"/> - <assertEquals stepKey="assertConsumerSecret" expected="($grabConsumerSecret)" actual="$grabAfterReauthorizeConsumerSecret" actualType="string"/> - <assertNotEquals stepKey="assertNotEqualsToken" expected="($grabAccessToken)" actual="$grabReauthorizedAccessToken" actualType="string"/> - <assertNotEquals stepKey="assertNotEqualsTokenSecret" expected="($grabAccessTokenSecret)" actual="$grabReauthorizedAccessTokenSecret" actualType="string"/> + + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabConsumerKey"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabConsumerSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabAccessToken"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabAccessTokenSecret"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickDoneButton"/> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminIntegrationClickReauthoriseLinkOnGridActionGroup" stepKey="clickReauthoriseLink"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="clickReauthoriseButton"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerKey}}" stepKey="grabAfterReauthorizeConsumerKey"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.consumerSecret}}" stepKey="grabAfterReauthorizeConsumerSecret"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessToken}}" stepKey="grabReauthorizedAccessToken"/> + <grabValueFrom selector="{{AdminNewIntegrationSection.accessTokenSecret}}" stepKey="grabReauthorizedAccessTokenSecret"/> + <actionGroup ref="AdminAllowResourcesAccessIntegrationActionGroup" stepKey="finishTheProcess"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The integration '{{defaultIntegrationData.name}}' has been re-authorized."/> + </actionGroup> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findReAuthorizedIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminIntegrationOpenExistingEntityActionGroup" stepKey="openIntegrationEntity"/> + <assertEquals stepKey="assertConsumerKey" expected="($grabConsumerKey)" actual="$grabAfterReauthorizeConsumerKey" actualType="string"/> + <assertEquals stepKey="assertConsumerSecret" expected="($grabConsumerSecret)" actual="$grabAfterReauthorizeConsumerSecret" actualType="string"/> + <assertNotEquals stepKey="assertNotEqualsToken" expected="($grabAccessToken)" actual="$grabReauthorizedAccessToken" actualType="string"/> + <assertNotEquals stepKey="assertNotEqualsTokenSecret" expected="($grabAccessTokenSecret)" actual="$grabReauthorizedAccessTokenSecret" actualType="string"/> </test> </tests> From ce7924aae8e48831b278a0778022b8eb17ca4003 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 16 Mar 2020 15:09:29 +0200 Subject: [PATCH 389/734] MC-32124: Admin: Create/update bundle product --- .../Product/AbstractBundleProductSaveTest.php | 252 ++++++++++++++++ .../Product/DynamicBundleProductTest.php | 269 ++++++++++++++++++ .../Product/FixedBundleProductTest.php | 226 +++++++++++++++ 3 files changed, 747 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php new file mode 100644 index 0000000000000..3ce3181b9c803 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php @@ -0,0 +1,252 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Type; +use Magento\Bundle\Model\ResourceModel\Option\Collection as OptionCollection; +use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Class determine basic logic for bundle product save tests + */ +abstract class AbstractBundleProductSaveTest extends AbstractBackendController +{ + /** @var string */ + protected $productToDelete; + + /** @var ProductRepositoryInterface */ + protected $productRepository; + + /** @var Config */ + private $eavConfig; + + /** @var ProductResource */ + private $productResource; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->eavConfig = $this->_objectManager->get(Config::class); + $this->productResource = $this->_objectManager->get(ProductResource::class); + $this->productToDelete = $this->getStaticProductData()['sku']; + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + if ($this->productToDelete) { + $this->productRepository->deleteById($this->productToDelete); + } + + parent::tearDown(); + } + + /** + * Retrieve default product attribute set id. + * + * @return int + */ + protected function getDefaultAttributeSetId(): int + { + return (int)$this->eavConfig->getEntityType(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * Prepare request + * + * @param array $post + * @param int|null $id + * @return array + */ + protected function prepareRequestData(array $post, ?int $id = null): array + { + $post = $this->preparePostParams($post); + $this->setRequestparams($post, $id); + + return $post; + } + + /** + * Prepare and assert bundle options + * + * @param array $bundleOptions + * @return void + */ + protected function assertBundleOptions(array $bundleOptions): void + { + $mainProduct = $this->productRepository->get($this->getStaticProductData()['sku'], false, null, true); + $optionsCollection = $mainProduct->getTypeInstance()->getOptionsCollection($mainProduct); + $selectionCollection = $mainProduct->getTypeInstance() + ->getSelectionsCollection($optionsCollection->getAllIds(), $mainProduct); + $this->assertOptionsData($bundleOptions, $optionsCollection, $selectionCollection); + } + + /** + * Prepare post params before dispatch + * + * @param array $post + * @return array + */ + private function preparePostParams(array $post): array + { + $post['product'] = $this->getStaticProductData(); + foreach ($post['bundle_options']['bundle_options'] as &$bundleOption) { + $bundleOption = $this->prepareOptionByType($bundleOption['type'], $bundleOption); + $productIdsBySkus = $this->productResource->getProductsIdsBySkus( + array_column($bundleOption['bundle_selections'], 'sku') + ); + foreach ($bundleOption['bundle_selections'] as &$bundleSelection) { + $bundleSelection = $this->prepareSelection($productIdsBySkus, $bundleSelection); + } + } + + return $post; + } + + /** + * Prepare option params + * + * @param string $type + * @param array $option + * @return array + */ + private function prepareOptionByType(string $type, array $option): array + { + $option['required'] = '1'; + $option['delete'] = ''; + $option['title'] = $option['title'] ?? $type . ' Option Title'; + + return $option; + } + + /** + * Prepare selection params + * + * @param array $productIdsBySkus + * @param array $selection + * @return array + */ + private function prepareSelection(array $productIdsBySkus, array $selection): array + { + $staticData = [ + 'price' => '10', + 'selection_qty' => '5', + 'selection_can_change_qty' => '0' + ]; + $selection['product_id'] = $productIdsBySkus[$selection['sku']]; + $selection = array_merge($selection, $staticData); + + return $selection; + } + + /** + * Assert bundle options data + * + * @param array $expectedOptions + * @param OptionCollection $actualOptions + * @param SelectionCollection $selectionCollection + * @return void + */ + private function assertOptionsData( + array $expectedOptions, + OptionCollection $actualOptions, + SelectionCollection $selectionCollection + ): void { + $this->assertCount(count($expectedOptions['bundle_options']), $actualOptions); + foreach ($expectedOptions['bundle_options'] as $expectedOption) { + $optionToCheck = $actualOptions->getItemByColumnValue('title', $expectedOption['title']); + $this->assertNotNull($optionToCheck->getId()); + $selectionToCheck = $selectionCollection->getItemsByColumnValue('option_id', $optionToCheck->getId()); + $this->assertCount(count($expectedOption['bundle_selections']), $selectionToCheck); + $this->assertSelections($expectedOption['bundle_selections'], $selectionToCheck); + unset($expectedOption['delete'], $expectedOption['bundle_selections']); + foreach ($expectedOption as $key => $value) { + $this->assertEquals($value, $optionToCheck->getData($key)); + } + } + } + + /** + * Assert selections data + * + * @param array $expectedSelections + * @param array $actualSelections + * @return void + */ + private function assertSelections(array $expectedSelections, array $actualSelections): void + { + foreach ($expectedSelections as $expectedSelection) { + $actualSelectionToCheck = $this->getSelectionByProductSku($expectedSelection['sku'], $actualSelections); + $this->assertNotNull($actualSelectionToCheck); + foreach ($expectedSelection as $key => $value) { + $this->assertEquals($value, $actualSelectionToCheck->getData($key)); + } + } + } + + /** + * Get selection by product sku + * + * @param string $sku + * @param array $actualSelections + * @return ProductInterface + */ + private function getSelectionByProductSku(string $sku, array $actualSelections): ProductInterface + { + $item = null; + foreach ($actualSelections as $selection) { + if ($selection->getSku() === $sku) { + $item = $selection; + break; + } + } + + return $item; + } + + /** + * Set request parameters + * + * @param array $post + * @param int|null $id + * @return void + */ + private function setRequestParams(array $post, ?int $id): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $params = ['type' => Type::TYPE_CODE, 'set' => $this->getDefaultAttributeSetId()]; + if ($id) { + $params['id'] = $id; + } + $this->getRequest()->setParams($params); + $this->getRequest()->setPostValue('product', $post['product']); + $this->getRequest()->setPostValue('bundle_options', $post['bundle_options']); + } + + /** + * Get main product data + * + * @return array + */ + abstract protected function getStaticProductData(): array; +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php new file mode 100644 index 0000000000000..988baf91981bc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php @@ -0,0 +1,269 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Type\AbstractType; + +/** + * Class checks dynamic bundle product save behavior + * + * @magentoAppArea adminhtml + */ +class DynamicBundleProductTest extends AbstractBundleProductSaveTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider bundleProductDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSave(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function bundleProductDataProvider(): array + { + return [ + 'with_dropdown_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_radio_buttons_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'radio', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_checkbox_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_multiselect_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @dataProvider multiOptionsDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSaveMultiOptions(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function multiOptionsDataProvider(): array + { + return [ + 'with_two_options_few_selections' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider emptyOptionTitleDataProvider + * + * @param array $post + * @return void + */ + public function testProductSaveMissedOptionTitle(array $post): void + { + $this->productToDelete = null; + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo(["The option couldn't be saved."])); + } + + /** + * @return array + */ + public function emptyOptionTitleDataProvider(): array + { + return [ + 'empty_option_title' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'title' => '', + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider updateProductDataProvider + * + * @param array $post + * @return void + */ + public function testUpdateProduct(array $post): void + { + $id = $this->productRepository->get('bundle-product-checkbox-options')->getId(); + $post = $this->prepareRequestData($post, (int)$id); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function updateProductDataProvider(): array + { + return [ + 'update_existing_product' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getStaticProductData(): array + { + return [ + 'sku' => 'bundle-test-product', + 'name' => 'test-bundle', + 'price' => '', + 'sku_type' => '0', + 'price_type' => Price::PRICE_TYPE_DYNAMIC, + 'weight_type' => '0', + 'shipment_type' => AbstractType::SHIPMENT_TOGETHER, + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php new file mode 100644 index 0000000000000..908a96368992d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php @@ -0,0 +1,226 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Type\AbstractType; + +/** + * Class checks fixed bundle product save behavior + * + * @magentoAppArea adminhtml + */ +class FixedBundleProductTest extends AbstractBundleProductSaveTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider fixedBundleProductDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSave(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function fixedBundleProductDataProvider(): array + { + return [ + 'with_dropdown_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_radio_buttons_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'radio', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_checkbox_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_multiselect_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @dataProvider multiOptionsDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSaveMultiOptions(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function multiOptionsDataProvider(): array + { + return [ + 'with_two_options_few_selections' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider updateProductDataProvider + * + * @param array $post + * @return void + */ + public function testUpdateProduct(array $post): void + { + $id = $this->productRepository->get('bundle-product-checkbox-options')->getId(); + $post = $this->prepareRequestData($post, (int)$id); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function updateProductDataProvider(): array + { + return [ + 'update_existing_product' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getStaticProductData(): array + { + return [ + 'sku' => 'bundle-test-product', + 'name' => 'test-bundle', + 'price' => '150', + 'sku_type' => '1', + 'price_type' => Price::PRICE_TYPE_FIXED, + 'weight_type' => '1', + 'shipment_type' => AbstractType::SHIPMENT_TOGETHER, + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + ]; + } +} From c37078cfd3d60608d4f6d653bdfbc5172fcf027e Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Mon, 16 Mar 2020 15:33:49 +0200 Subject: [PATCH 390/734] Refactoring --- .../AdminMarketingDeleteSitemapEntityTest.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml index 3ba13bb76b178..54e7ed211636e 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Navigate to Marketing->Sitemap Page --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> @@ -32,18 +32,18 @@ </actionGroup> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteCreatedSitemap"> - <argument name="filename" value="{{UniqueSitemapName.filename}}"/> - </actionGroup> - <actionGroup ref="AssertSiteMapDeleteSuccessActionGroup" stepKey="assertSuccessMessage"/> - <actionGroup ref="AdminMarketingSitemapSearchActionGroup" stepKey="searchDeletedSitemap"> - <argument name="filename" value="{{UniqueSitemapName.filename}}"/> - </actionGroup> - <actionGroup ref="AssertAdminSitemapIsNotInGridActionGroup" stepKey="dontSeeEntity"> - <argument name="filename" value="{{UniqueSitemapName.filename}}"/> - </actionGroup> + <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteCreatedSitemap"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> + <actionGroup ref="AssertSiteMapDeleteSuccessActionGroup" stepKey="assertSuccessMessage"/> + <actionGroup ref="AdminMarketingSitemapSearchActionGroup" stepKey="searchDeletedSitemap"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> + <actionGroup ref="AssertAdminSitemapIsNotInGridActionGroup" stepKey="dontSeeEntity"> + <argument name="filename" value="{{UniqueSitemapName.filename}}"/> + </actionGroup> </test> </tests> From 04ec5edcedb30b9a74e6811c7fcb478265817e7a Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 16 Mar 2020 15:50:53 +0200 Subject: [PATCH 391/734] MC-32136: Email product to friend --- .../SendFriend/Model/DeleteLogRowsByIp.php | 40 +++ .../SendFriend/Block/ProductViewTest.php | 104 ++++++++ .../Magento/SendFriend/Block/SendTest.php | 124 +++++++--- .../SendFriend/Controller/SendTest.php | 161 +++++++++++++ .../SendFriend/Controller/SendmailTest.php | 190 ++++++++------- .../SendFriend/Model/SendFriendTest.php | 227 ++++++++++++++++++ ...sendfriend_log_record_half_hour_before.php | 20 ++ ...d_log_record_half_hour_before_rollback.php | 13 + 8 files changed, 767 insertions(+), 112 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php b/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php new file mode 100644 index 0000000000000..aecf40b575957 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\SendFriend\Model; + +use Magento\SendFriend\Model\ResourceModel\SendFriend as SendFriendResource; + +/** + * Delete log rows by ip address + */ +class DeleteLogRowsByIp +{ + /** @var SendFriendResource */ + private $sendFriendResource; + + /** + * @param SendFriendResource $sendFriendResource + */ + public function __construct(SendFriendResource $sendFriendResource) + { + $this->sendFriendResource = $sendFriendResource; + } + + /** + * Delete rows from sendfriend_log table by ip address + * + * @param string $ipAddress + * @return void + */ + public function execute(string $ipAddress): void + { + $connection = $this->sendFriendResource->getConnection(); + $condition = $connection->quoteInto('ip = ?', ip2long($ipAddress)); + $connection->delete($this->sendFriendResource->getMainTable(), $condition); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php new file mode 100644 index 0000000000000..daa7b0bab84e3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Block; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks send friend link visibility + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class ProductViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var LayoutInterface */ + private $layout; + + /** @var View */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(View::class); + $this->block->setTemplate('Magento_Catalog::product/view/mailto.phtml'); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->registry->unregister('product'); + } + + /** + * @return void + */ + public function testSendFriendLinkDisabled(): void + { + $this->registerProduct('simple2'); + $this->assertEmpty($this->block->toHtml()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @return void + */ + public function testSendFriendLinkEnabled(): void + { + $product = $this->registerProduct('simple2'); + $html = $this->block->toHtml(); + $this->assertContains('sendfriend/product/send/id/' . $product->getId(), $html); + $this->assertEquals('Email', trim(strip_tags($html))); + } + + /** + * Register product by sku + * + * @param string $sku + * @return ProductInterface + */ + private function registerProduct(string $sku): ProductInterface + { + $product = $this->productRepository->get($sku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + + return $product; + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php index 1c6bfe29f876d..539293480d5bb 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php @@ -3,40 +3,94 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\SendFriend\Block; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; -class SendTest extends \PHPUnit\Framework\TestCase +/** + * Class checks send friend email block + * + * @see \Magento\SendFriend\Block\Send + * + * @magentoAppArea frontend + */ +class SendTest extends TestCase { + /** @var array */ + private $elementsXpath = [ + 'sender name field' => "//input[@name='sender[name]']", + 'sender email field' => "//input[@name='sender[email]']", + 'sender message field' => "//textarea[@name='sender[message]']", + 'recipient name field' => "//input[contains(@name, 'recipients[name]')]", + 'recipient email field' => "//input[contains(@name, 'recipients[email]')]", + 'submit button' => "//button[@type='submit']/span[contains(text(), 'Send Email')]", + 'notice massage' => "//div[@id='max-recipient-message']" + . "/span[contains(text(), 'Maximum 1 email addresses allowed.')]" + ]; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Send */ + private $block; + + /** @var Session */ + private $session; + + /** @var AccountManagementInterface */ + private $accountManagement; + /** - * @var \Magento\SendFriend\Block\Send + * @inheritdoc */ - protected $_block; - protected function setUp() { - $this->_block = Bootstrap::getObjectManager()->create(\Magento\SendFriend\Block\Send::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Send::class); + $this->session = $this->objectManager->get(Session::class); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->session->logout(); } /** + * @dataProvider formDataProvider + * * @param string $field * @param string $value - * @dataProvider formDataProvider - * @covers \Magento\SendFriend\Block\Send::getUserName - * @covers \Magento\SendFriend\Block\Send::getEmail + * @return void */ - public function testGetCustomerFieldFromFormData($field, $value) + public function testGetCustomerFieldFromFormData(string $field, string $value): void { $formData = ['sender' => [$field => $value]]; - $this->_block->setFormData($formData); + $this->block->setFormData($formData); $this->assertEquals(trim($value), $this->_callBlockMethod($field)); } /** * @return array */ - public function formDataProvider() + public function formDataProvider(): array { return [ ['name', 'Customer Form Name'], @@ -45,29 +99,27 @@ public function formDataProvider() } /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @dataProvider customerSessionDataProvider + * + * @magentoAppIsolation enabled + * * @param string $field * @param string $value - * @dataProvider customerSessionDataProvider - * @covers \Magento\SendFriend\Block\Send::getUserName - * @covers \Magento\SendFriend\Block\Send::getEmail - * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void */ - public function testGetCustomerFieldFromSession($field, $value) + public function testGetCustomerFieldFromSession(string $field, string $value): void { - $logger = $this->createMock(\Psr\Log\LoggerInterface::class); - /** @var $session \Magento\Customer\Model\Session */ - $session = Bootstrap::getObjectManager()->create(\Magento\Customer\Model\Session::class, [$logger]); - /** @var \Magento\Customer\Api\AccountManagementInterface $service */ - $service = Bootstrap::getObjectManager()->create(\Magento\Customer\Api\AccountManagementInterface::class); - $customer = $service->authenticate('customer@example.com', 'password'); - $session->setCustomerDataAsLoggedIn($customer); + $customer = $this->accountManagement->authenticate('customer@example.com', 'password'); + $this->session->setCustomerDataAsLoggedIn($customer); $this->assertEquals($value, $this->_callBlockMethod($field)); } /** * @return array */ - public function customerSessionDataProvider() + public function customerSessionDataProvider(): array { return [ ['name', 'John Smith'], @@ -75,19 +127,37 @@ public function customerSessionDataProvider() ]; } + /** + * @magentoConfigFixture current_store sendfriend/email/max_recipients 1 + * + * @return void + */ + public function testBlockAppearance(): void + { + $this->block->setTemplate('Magento_SendFriend::send.phtml'); + $html = preg_replace('#<script(.*?)>#i', '', $this->block->toHtml()); + foreach ($this->elementsXpath as $key => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $html), + sprintf('The %s field is not found on the page', $key) + ); + } + } + /** * Call block method based on form field * * @param string $field * @return null|string */ - protected function _callBlockMethod($field) + protected function _callBlockMethod(string $field): ?string { switch ($field) { case 'name': - return $this->_block->getUserName(); + return $this->block->getUserName(); case 'email': - return $this->_block->getEmail(); + return $this->block->getEmail(); default: return null; } diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php new file mode 100644 index 0000000000000..eccf4ee4c5ef9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Controller; + +use Magento\Customer\Model\Session; +use Magento\SendFriend\Model\SendFriend; +use Magento\TestFramework\Response; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class for send friend send action + * + * @magentoAppArea frontend + * + * @see \Magento\SendFriend\Controller\Product\Send + */ +class SendTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->customerSession->logout(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 0 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSendMailNotAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assert404NotFound(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/allow_guest 0 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testGuestSendMailNotAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assertRedirect($this->stringContains('customer/account/login')); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/allow_guest 1 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testGuestSendMailAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testLoggedInCustomer(): void + { + $this->customerSession->loginById(1); + $this->dispatchWithProductIdParam(6); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testWithoutProductId(): void + { + $this->customerSession->loginById(1); + $this->dispatch('sendfriend/product/send/'); + $this->assert404NotFound(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testToMachSendRequests(): void + { + $this->createSendFriendMock(); + $this->customerSession->loginById(1); + $this->dispatchWithProductIdParam(6); + $this->assertSessionMessages( + $this->equalTo([(string)__('You can\'t send messages more than 5 times an hour.')]) + ); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * Set product id parameter and dispatch controller + * + * @param int $productId + * @return void + */ + private function dispatchWithProductIdParam(int $productId): void + { + $this->getRequest()->setParam('id', $productId); + $this->dispatch('sendfriend/product/send/'); + } + + /** + * Create mock to imitate to mach send requests + * + * @return void + */ + private function createSendFriendMock(): void + { + $mock = $this->createMock(SendFriend::class); + $mock->method('isExceedLimit')->willReturn(true); + $mock->method('getMaxSendsToFriend')->willReturn(5); + $this->_objectManager->addSharedInstance($mock, SendFriend::class); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index 1d6adf52466d2..850225bcee1b2 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -10,79 +10,128 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Customer\Model\Session; -use Magento\Framework\Data\Form\FormKey; -use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\Request; use Magento\TestFramework\TestCase\AbstractController; /** - * Class SendmailTest + * Class checks send mail action + * + * @see \Magento\SendFriend\Controller\Product\Sendmail + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ class SendmailTest extends AbstractController { + private const MESSAGE_PRODUCT_LINK_XPATH = "//a[contains(@href, '%s') and contains(text(), '%s')]"; + + /** @var Session */ + private $session; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** @var array */ + private $staticData = [ + 'sender' => [ + 'name' => 'Test', + 'email' => 'test@example.com', + 'message' => 'Message', + ], + 'recipients' => [ + 'name' => [ + 'Recipient 1', + 'Recipient 2' + ], + 'email' => [ + 'r1@example.com', + 'r2@example.com' + ] + ], + ]; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->session = $this->_objectManager->get(Session::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->session->logout(); + } + /** * Share the product to friend as logged in customer * - * @magentoAppArea frontend - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsLoggedIn() + public function testSendActionAsLoggedIn(): void { - $product = $this->getProduct(); - $this->login(1); + $product = $this->productRepository->get('custom-design-simple-product'); + $this->session->loginById(1); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); - $this->assertSessionMessages( - $this->equalTo(['The link to a friend was sent.']), - MessageInterface::TYPE_SUCCESS - ); + $this->checkSuccess($product); } /** * Share the product to friend as guest customer * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsGuest() + public function testSendActionAsGuest(): void { - $product = $this->getProduct(); + $product = $this->productRepository->get('custom-design-simple-product'); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); - $this->assertSessionMessages( - $this->equalTo(['The link to a friend was sent.']), - MessageInterface::TYPE_SUCCESS - ); + $this->checkSuccess($product); } /** * Share the product to friend as guest customer with invalid post data * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsGuestWithInvalidData() + public function testSendActionAsGuestWithInvalidData(): void { - $product = $this->getProduct(); - $this->prepareRequestData(true); - + $product = $this->productRepository->get('custom-design-simple-product'); + unset($this->staticData['sender']['email']); + $this->prepareRequestData(); $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); $this->assertSessionMessages( - $this->equalTo(['Invalid Sender Email']), + $this->equalTo([(string)__('Invalid Sender Email')]), MessageInterface::TYPE_ERROR ); } @@ -90,82 +139,53 @@ public function testSendActionAsGuestWithInvalidData() /** * Share the product invisible in catalog to friend as guest customer * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + * + * @return void */ - public function testSendInvisibleProduct() + public function testSendInvisibleProduct(): void { - $product = $this->getInvisibleProduct(); + $product = $this->productRepository->get('simple_not_visible_1'); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); $this->assert404NotFound(); } /** - * @return ProductInterface - */ - private function getProduct() - { - return $this->_objectManager->get(ProductRepositoryInterface::class)->get('custom-design-simple-product'); - } - - /** - * @return ProductInterface - */ - private function getInvisibleProduct() - { - return $this->_objectManager->get(ProductRepositoryInterface::class)->get('simple_not_visible_1'); - } - - /** - * Login the user + * Check success session message and email content * - * @param string $customerId Customer to mark as logged in for the session + * @param ProductInterface $product * @return void */ - protected function login($customerId) + private function checkSuccess(ProductInterface $product): void { - /** @var Session $session */ - $session = Bootstrap::getObjectManager() - ->get(Session::class); - $session->loginById($customerId); + $this->assertSessionMessages( + $this->equalTo([(string)__('The link to a friend was sent.')]), + MessageInterface::TYPE_SUCCESS + ); + $message = $this->transportBuilder->getSentMessage(); + $this->assertNotNull($message, 'The message was not sent'); + $content = $message->getBody()->getParts()[0]->getRawContent(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf(self::MESSAGE_PRODUCT_LINK_XPATH, $product->getUrlKey(), $product->getName()), + $content + ), + 'Sent message does not contain product link' + ); } /** - * @param bool $invalidData + * Prepare request before dispatch + * * @return void */ - private function prepareRequestData($invalidData = false) + private function prepareRequestData(): void { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $post = [ - 'sender' => [ - 'name' => 'Test', - 'email' => 'test@example.com', - 'message' => 'Message', - ], - 'recipients' => [ - 'name' => [ - 'Recipient 1', - 'Recipient 2' - ], - 'email' => [ - 'r1@example.com', - 'r2@example.com' - ] - ], - 'form_key' => $formKey->getFormKey(), - ]; - if ($invalidData) { - unset($post['sender']['email']); - } - $this->getRequest()->setMethod(Request::METHOD_POST); - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($this->staticData); } } diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php new file mode 100644 index 0000000000000..52b2ed05baf9e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Model; + +use Laminas\Stdlib\Parameters; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\SendFriend\Helper\Data as SendFriendHelper; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks send friend model behavior + * + * @see \Magento\SendFriend\Model\SendFriend + */ +class SendFriendTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var SendFriend */ + private $sendFriend; + + /** @var CookieManagerInterface */ + private $cookieManager; + + /** @var RequestInterface */ + private $request; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->sendFriend = $this->objectManager->get(SendFriendFactory::class)->create(); + $this->cookieManager = $this->objectManager->get(CookieManagerInterface::class); + $this->request = $this->objectManager->get(RequestInterface::class); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/max_recipients 1 + * + * @dataProvider validateDataProvider + * + * @param array $sender + * @param array $recipients + * @param string|bool $expectedResult + * @return void + */ + public function testValidate(array $sender, array $recipients, $expectedResult): void + { + $this->prepareData($sender, $recipients); + $this->checkResult($expectedResult, $this->sendFriend->validate()); + } + + /** + * @return array + */ + public function validateDataProvider(): array + { + return [ + 'valid_data' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient_name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => true, + ], + 'empty_message' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => '', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => 'Please enter a message.', + ], + 'empty_sender_name' => [ + 'sender' => [ + 'name' => '', + 'email' => 'customer_email@example.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => 'Please enter a sender name.', + ], + 'empty_recipients' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [], + 'email' => [], + ], + 'expected_result' => 'Please specify at least one recipient.', + ], + 'wrong_recipient_email' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + '123123', + ], + ], + 'expected_result' => 'Please enter a correct recipient email address.', + ], + 'to_much_recipients' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + 'second name', + ], + 'email' => [ + 'recipient_email@example.com', + 'recipient2_email@example.com', + ], + ], + 'expected_result' => 'No more than 1 emails can be sent at a time.', + ], + ]; + } + + /** + * @magentoConfigFixture current_store sendfriend/email/check_by 0 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @return void + */ + public function testisExceedLimitByCookies(): void + { + $this->cookieManager->setPublicCookie(SendFriendHelper::COOKIE_NAME, (string)time()); + $this->assertTrue($this->sendFriend->isExceedLimit()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/check_by 1 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @magentoDataFixture Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php + * + * @magentoDbIsolation disabled + * @return void + */ + public function testisExceedLimitByIp(): void + { + $this->markTestSkipped('Blocked by MC-31968'); + $parameters = $this->objectManager->create(Parameters::class); + $parameters->set('REMOTE_ADDR', '127.0.0.1'); + $this->request->setServer($parameters); + $this->assertTrue($this->sendFriend->isExceedLimit()); + } + + /** + * Check result + * + * @param array|bool $expectedResult + * @param array|bool $result + * @return void + */ + private function checkResult($expectedResult, $result): void + { + if ($expectedResult === true) { + $this->assertTrue($result); + } else { + $this->assertEquals($expectedResult, (string)reset($result) ?? ''); + } + } + + /** + * Prepare sender and recipient data + * + * @param array $sender + * @param array $recipients + * @return void + */ + private function prepareData(array $sender, array $recipients): void + { + $this->sendFriend->setSender($sender); + $this->sendFriend->setRecipients($recipients); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php new file mode 100644 index 0000000000000..132cbe97d43ee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SendFriend\Model\ResourceModel\SendFriend as SendFriendResource; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +$ip = ip2long('127.0.0.1'); +$updateDatetime = new \DateTime('-0.5 hours'); +/** @var SendFriendResource $sendFriendResource */ +$sendFriendResource = $objectManager->get(SendFriendResource::class); +$sendFriendResource->addSendItem($ip, $updateDatetime->getTimestamp(), $baseWebsiteId); diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php new file mode 100644 index 0000000000000..9a700f20bf92c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\SendFriend\Model\DeleteLogRowsByIp; + +/** @var DeleteLogRowsByIp $deleteLogRowsByIp */ +$deleteLogRowsByIp = Bootstrap::getObjectManager()->get(DeleteLogRowsByIp::class); +$deleteLogRowsByIp->execute('127.0.0.1'); From 4da150c13e2540a886ddf1cfaf74ba9e2b167eea Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Mon, 16 Mar 2020 16:01:31 +0200 Subject: [PATCH 392/734] Added improvements to category repository (save method) to fix level issue --- .../Catalog/Model/CategoryRepository.php | 1 + ...ntChildCategoryTreeElementsActionGroup.xml | 22 ++++++++++ .../AdminExpandCategoryTreeActionGroup.xml | 19 ++++++++ .../AdminOpenCategoryPageActionGroup.xml | 19 ++++++++ ...yLevelByParentCategoryLevelActionGroup.xml | 22 ++++++++++ .../Catalog/Test/Mftf/Data/CategoryData.xml | 6 +++ .../AdminCategorySidebarTreeSection.xml | 1 + ...inCheckNewCategoryLevelAddedViaApiTest.xml | 44 +++++++++++++++++++ .../Unit/Model/CategoryRepositoryTest.php | 2 +- 9 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php index a8636306f5e5b..0ce52b966c32c 100644 --- a/app/code/Magento/Catalog/Model/CategoryRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryRepository.php @@ -108,6 +108,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category) $parentCategory = $this->get($parentId, $storeId); $existingData['path'] = $parentCategory->getPath(); $existingData['parent_id'] = $parentId; + $existingData['level'] = null; } $category->addData($existingData); try { diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml new file mode 100644 index 0000000000000..b1ed08db05b9a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertParentChildCategoryTreeElementsActionGroup"> + <annotations> + <description>Checks category tree, parent category has child category element.</description> + </annotations> + <arguments> + <argument name="parentCategoryName" type="string" defaultValue="parent"/> + <argument name="childCategoryName" type="string" defaultValue="child"/> + </arguments> + + <seeElement selector="{{AdminCategorySidebarTreeSection.childCategoryUnderParent(parentCategoryName, childCategoryName)}}" stepKey="seeSubcategoryIsUnderParent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml new file mode 100644 index 0000000000000..f2cce9b9a42fe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandCategoryTreeActionGroup"> + <annotations> + <description>Expands category tree.</description> + </annotations> + + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..14c4f5234ba67 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCategoryPageActionGroup"> + <annotations> + <description>Navigates to category page.</description> + </annotations> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml new file mode 100644 index 0000000000000..1830a6abc992e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCategoryLevelByParentCategoryLevelActionGroup"> + <annotations> + <description>Checks category level by parent category level.</description> + </annotations> + <arguments> + <argument name="parentCategoryLevel" type="string" defaultValue="2"/> + <argument name="categoryLevel" type="string" defaultValue="3"/> + </arguments> + + <assertEquals expected="{{parentCategoryLevel}} + 1" actual="{{categoryLevel}}" message="wrongCategoryLevel" stepKey="compareCategoryLevel"/> + </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 f54ce9af83e88..976ed5a3ed17a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -243,4 +243,10 @@ <data key="include_in_menu">true</data> <var key="parent_id" entityType="category" entityKey="id"/> </entity> + <entity name="ApiSubCategoryWithLevelZero" type="category"> + <data key="name" unique="suffix">cat with level 1</data> + <data key="is_active">true</data> + <data key="level">0</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml index 304c34b404ea5..c35e775152ac9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml @@ -16,6 +16,7 @@ <element name="categoryTreeRoot" type="text" selector="div.x-tree-root-node>li.x-tree-node:first-of-type>div.x-tree-node-el:first-of-type" timeout="30"/> <element name="categoryInTree" type="text" selector="//a/span[contains(text(), '{{name}}')]" parameterized="true" timeout="30"/> <element name="categoryInTreeUnderRoot" type="text" selector="//li/ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{name}}')]" parameterized="true"/> + <element name="childCategoryUnderParent" type="text" selector="//li/ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{parentCategoryName}}')]/../../../ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{childCategoryName}}')]" parameterized="true"/> <element name="lastCreatedCategory" type="block" selector=".x-tree-root-ct li li:last-child" /> <element name="treeContainer" type="block" selector=".tree-holder" /> <element name="expandRootCategory" type="text" selector="img.x-tree-elbow-end-plus"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml new file mode 100644 index 0000000000000..427ef6551ce9b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml @@ -0,0 +1,44 @@ +<?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="AdminCheckNewCategoryLevelAddedViaApiTest"> + <annotations> + <stories value="Add parent and child categories via API"/> + <title value="Add parent and child categories via API"/> + <description value="Login as admin, create parent and child categories via API. + Check category level for child category entity based on parent level. + Check category tree: parent element has child element. "/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="ApiCategoryWithChildren" stepKey="createCategoryWithChildrenBlank"/> + <createData entity="ApiSubCategoryWithLevelZero" stepKey="createSubCategoryWithLevelZero"> + <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategoryWithChildrenBlank" stepKey="deleteCategoryWithChildrenBlank"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AssertAdminCategoryLevelByParentCategoryLevelActionGroup" stepKey="assertCategoryLevelByParentCategory"> + <argument name="parentCategoryLevel" value="$createCategoryWithChildrenBlank.level$"/> + <argument name="categoryLevel" value="$createSubCategoryWithLevelZero.level$"/> + </actionGroup> + + <actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="openCategoryPage"/> + <actionGroup ref="AdminExpandCategoryTreeActionGroup" stepKey="expandCategoryTree"/> + <actionGroup ref="AdminAssertParentChildCategoryTreeElementsActionGroup" stepKey="assertParentChildCategoryTreeElements"> + <argument name="parentCategoryName" value="$createCategoryWithChildrenBlank.name$"/> + <argument name="childCategoryName" value="$createSubCategoryWithLevelZero.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php index 864b91b20d017..3799e6e5fa4aa 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php @@ -204,7 +204,7 @@ public function testCreateNewCategory() $parentCategoryId = 15; $newCategoryId = 25; $categoryData = ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category']; - $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15]; + $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15, 'level' => null]; $this->extensibleDataObjectConverterMock ->expects($this->once()) ->method('toNestedArray') From 10501a6b270fb6a0a7217269209c9d3fa00bbd0d Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Mon, 16 Mar 2020 09:03:11 -0500 Subject: [PATCH 393/734] MC-32120: Catalog price rules are not included in CartItemPrices - README content fix --- app/code/Magento/CatalogRuleGraphQl/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRuleGraphQl/README.md b/app/code/Magento/CatalogRuleGraphQl/README.md index 8cb1786635582..6f9761fedecbb 100644 --- a/app/code/Magento/CatalogRuleGraphQl/README.md +++ b/app/code/Magento/CatalogRuleGraphQl/README.md @@ -1,4 +1,3 @@ # CatalogRuleGraphQl -**Magento_CatalogRuleGraphQl** module is responsible for applying Catalog Rules, one of the types of price rules in Magento, for GraphQl requests. -Catalog Rules are applied to products before they are added to the cart. \ No newline at end of file +The *Magento_CatalogRuleGraphQl* module applies catalog rules to products for GraphQL requests. \ No newline at end of file From 6f0fc3497d445703aa0d8b16ab1d852b522c2cae Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Mon, 16 Mar 2020 16:03:51 +0200 Subject: [PATCH 394/734] Refactoring --- ...enNewsletterTemplateFromGridActionGroup.xml | 2 -- ...CreateNewsletterTemplatePageActionGroup.xml | 2 -- ...inMarketingNewsletterTemplateUpdateTest.xml | 18 +++--------------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml index 4e380fe63d251..0341e68cf2b13 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml @@ -8,8 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - - <!--Open Newsletter Template --> <actionGroup name="AdminMarketingOpenNewsletterTemplateFromGridActionGroup"> <click stepKey="openTemplate" selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}"/> </actionGroup> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml index c743bde60172a..6e965279a4cb0 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml @@ -8,8 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - - <!--Click "Add New Template" button--> <actionGroup name="AdminNavigateToCreateNewsletterTemplatePageActionGroup"> <click stepKey="clickAddNewTemplateButton" selector="{{AdminNewsletterGridMainActionsSection.add}}"/> <waitForPageLoad stepKey="waitForNewNewsletterTemplatesPageLoaded"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml index 378dd71e13334..cedb176cf909f 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml @@ -19,15 +19,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <!-- Navigate To MARKETING > Newsletter Template --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterTemplatePage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> <argument name="submenuUiId" value="{{AdminMenuMarketingCommunicationsNewsletterTemplate.dataUiId}}"/> </actionGroup> - <!-- Navigate To Create Newsletter Template Page --> <actionGroup ref="AdminNavigateToCreateNewsletterTemplatePageActionGroup" stepKey="navigateTiCreateNewsletterTemplatePage"/> - <!-- Create Newsletter Template --> <actionGroup ref="AdminMarketingCreateNewsletterTemplateActionGroup" stepKey="createNewsletterTemplate"> <argument name="name" value="{{_defaultNewsletter.name}}"/> <argument name="subject" value="{{_defaultNewsletter.subject}}"/> @@ -35,16 +32,13 @@ <argument name="senderEmail" value="{{_defaultNewsletter.senderEmail}}"/> <argument name="templateContent" value="{{_defaultNewsletter.textAreaContent}}"/> </actionGroup> - <!-- Search Created Newsletter Template on Grid --> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> <argument name="name" value="{{_defaultNewsletter.name}}"/> <argument name="subject" value="{{_defaultNewsletter.subject}}"/> </actionGroup> - <!-- Open Created Newsletter Template --> <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openCreatedNewsletterTemplate"/> </before> <after> - <!-- Delete Updated Newsletter Template --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterGridPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> <argument name="submenuUiId" value="{{AdminMenuMarketingCommunicationsNewsletterTemplate.dataUiId}}"/> @@ -55,10 +49,9 @@ </actionGroup> <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openTemplate"/> <actionGroup ref="AdminMarketingDeleteNewsletterTemplateActionGroup" stepKey="deleteTemplate"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <!-- TEST BODY --> - <!-- Update Created Newsletter Template --> + <actionGroup ref="AdminMarketingCreateNewsletterTemplateActionGroup" stepKey="updateNewsletterTemplate"> <argument name="name" value="{{updatedNewsletter.name}}"/> <argument name="subject" value="{{updatedNewsletter.subject}}"/> @@ -66,22 +59,18 @@ <argument name="senderEmail" value="{{updatedNewsletter.senderEmail}}"/> <argument name="templateContent" value="{{updatedNewsletter.textAreaContent}}"/> </actionGroup> - <!-- Assert Success Message --> <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="The newsletter template has been saved."/> <argument name="messageType" value="success"/> </actionGroup> - <!-- Find Created Newsletter on Grid --> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findCreatedNewsletterTemplate"> <argument name="name" value="{{updatedNewsletter.name}}"/> <argument name="subject" value="{{updatedNewsletter.subject}}"/> </actionGroup> - <!-- Assert Created Newsletter on Grid --> <actionGroup ref="AssertAdminCreatedNewsletterTemplateInGridActionGroup" stepKey="assertNewsletterInGrid"> <argument name="name" value="{{updatedNewsletter.name}}"/> <argument name="subject" value="{{updatedNewsletter.subject}}"/> </actionGroup> - <!-- Assert Newsletter Form --> <actionGroup ref="AdminSearchNewsletterTemplateOnGridActionGroup" stepKey="findUpdatedNewsletterTemplate"> <argument name="name" value="Updated Newsletter Template"/> <argument name="subject" value="Updated Newsletter Subject"/> @@ -94,6 +83,5 @@ <argument name="senderEmail" value="{{updatedNewsletter.senderEmail}}"/> <argument name="templateContent" value="{{updatedNewsletter.textAreaContent}}"/> </actionGroup> - <!-- END TEST BODY --> </test> </tests> From 9ce87f83c60723f082c2616c004e886dd6c2e76e Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Mon, 16 Mar 2020 09:48:53 -0500 Subject: [PATCH 395/734] MC-32120: Catalog price rules are not included in CartItemPrices - Removed trailing comma --- app/code/Magento/CatalogRuleGraphQl/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRuleGraphQl/composer.json b/app/code/Magento/CatalogRuleGraphQl/composer.json index 65d504271a976..2d09ec010aec3 100644 --- a/app/code/Magento/CatalogRuleGraphQl/composer.json +++ b/app/code/Magento/CatalogRuleGraphQl/composer.json @@ -5,7 +5,7 @@ "require": { "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", - "magento/module-catalog-rule": "*", + "magento/module-catalog-rule": "*" }, "license": [ "OSL-3.0", From dc27742d408aa144c85b8ef49318113228c43f1c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 16 Mar 2020 17:15:43 +0200 Subject: [PATCH 396/734] MC-32136: Email product to friend --- .../testsuite/Magento/SendFriend/Model/SendFriendTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php index 52b2ed05baf9e..9a9ef6440261a 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php @@ -7,13 +7,13 @@ namespace Magento\SendFriend\Model; -use Laminas\Stdlib\Parameters; use Magento\Framework\App\RequestInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\SendFriend\Helper\Data as SendFriendHelper; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; +use Zend\Stdlib\Parameters; /** * Class checks send friend model behavior From 53136a261df08fbe9b1a263a6721c765014adb6c Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Mon, 16 Mar 2020 22:48:02 +0700 Subject: [PATCH 397/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Fix unit test for the function that was fixed --- .../Downloadable/Model/Sample/Builder.php | 2 +- .../Test/Unit/Model/Link/BuilderTest.php | 3 ++- .../Test/Unit/Model/Sample/BuilderTest.php | 2 +- dev/tests/unit/phpunit.xml.dist | 20 +++++++++---------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index 7cc7cb640ae74..28ecf79b8a151 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -24,7 +24,7 @@ class Builder * @var Sample */ private $component; - + /** * @var File */ diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 32dce80bed5f3..3c240290594ab 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -161,7 +161,7 @@ public function testBuild($data, $expectedPrice) $this->linkMock->expects($this->once())->method('setNumberOfDownloads')->with(0); } if (isset($data['use_default_title']) && $data['use_default_title'] == '1') { - $this->linkMock->expects($this->once())->method('getTitle')->with(null); + $this->linkMock->expects($this->once())->method('setTitle')->with(null); } if (isset($data['price'])) { $this->linkMock->expects($this->once())->method('getPrice')->willReturn($data['price']); @@ -222,6 +222,7 @@ public function buildProvider() [ 'file' => 'cXVlIHRhbA==', 'type' => 'file', + 'use_default_title' => '1', 'sample' => [ 'file' => 'cXVlIHRhbA==', 'type' => 'file' diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 28ffa6db05e0b..08f260282756b 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -123,7 +123,7 @@ public function testBuild() $this->sampleMock->expects($this->once())->method('setSampleFile')->with($fileName); $this->sampleMock->expects($this->once())->method('setSortOrder')->with(1); $this->service->setData($data); - + $this->service->build($this->sampleMock); } } diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index 94500ff7bdc86..6a126b50a71b8 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -13,17 +13,17 @@ bootstrap="./framework/bootstrap.php" > <testsuite name="Magento_Unit_Tests_App_Code"> - <directory suffix="Test.php">../../../app/code/*/*/Test/Unit</directory> - </testsuite> - <testsuite name="Magento_Unit_Tests_Other"> - <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory> - <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory> - <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory> - <directory suffix="Test.php">../../../vendor/*/module-*/Test/Unit</directory> - <directory suffix="Test.php">../../../vendor/*/framework/Test/Unit</directory> - <directory suffix="Test.php">../../../vendor/*/framework/*/Test/Unit</directory> - <directory suffix="Test.php">../../tests/unit/*/Test/Unit</directory> + <directory suffix="BuilderTest.php">../../../app/code/Magento/Downloadable/Test/Unit</directory> </testsuite> +<!-- <testsuite name="Magento_Unit_Tests_Other">--> +<!-- <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../../vendor/*/module-*/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../../vendor/*/framework/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../../vendor/*/framework/*/Test/Unit</directory>--> +<!-- <directory suffix="Test.php">../../tests/unit/*/Test/Unit</directory>--> +<!-- </testsuite>--> <php> <ini name="date.timezone" value="America/Los_Angeles"/> <ini name="xdebug.max_nesting_level" value="200"/> From 5a2c9b7a6d7a9326b07c154e719c021420a00921 Mon Sep 17 00:00:00 2001 From: Abel Truong <truongngocanh2794@gmail.com> Date: Mon, 16 Mar 2020 22:59:49 +0700 Subject: [PATCH 398/734] Fix bug saving link title incorrectly Update app/code/Magento/Downloadable/Model/Link/Builder.php Co-Authored-By: Eduard Chitoraga <e.chitoraga@atwix.com> --- app/code/Magento/Downloadable/Model/Link/Builder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index 5ccf4ffa6fc7e..ad247127e1701 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -133,7 +133,9 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) $link->setNumberOfDownloads(0); } - if (isset($this->data['use_default_title']) && $this->data['use_default_title'] == '1') { + $useDefaultTitle = $this->data['use_default_title'] ?? false; + + if ($useDefaultTitle) { $link->setTitle(null); } $this->resetData(); From 2a2b98a1a04141dad801b7491588b113bd11f7cf Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Mon, 16 Mar 2020 23:03:52 +0700 Subject: [PATCH 399/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Revert incorrect file --- dev/tests/unit/phpunit.xml.dist | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index 6a126b50a71b8..94500ff7bdc86 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -13,17 +13,17 @@ bootstrap="./framework/bootstrap.php" > <testsuite name="Magento_Unit_Tests_App_Code"> - <directory suffix="BuilderTest.php">../../../app/code/Magento/Downloadable/Test/Unit</directory> + <directory suffix="Test.php">../../../app/code/*/*/Test/Unit</directory> + </testsuite> + <testsuite name="Magento_Unit_Tests_Other"> + <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory> + <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory> + <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory> + <directory suffix="Test.php">../../../vendor/*/module-*/Test/Unit</directory> + <directory suffix="Test.php">../../../vendor/*/framework/Test/Unit</directory> + <directory suffix="Test.php">../../../vendor/*/framework/*/Test/Unit</directory> + <directory suffix="Test.php">../../tests/unit/*/Test/Unit</directory> </testsuite> -<!-- <testsuite name="Magento_Unit_Tests_Other">--> -<!-- <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../../vendor/*/module-*/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../../vendor/*/framework/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../../vendor/*/framework/*/Test/Unit</directory>--> -<!-- <directory suffix="Test.php">../../tests/unit/*/Test/Unit</directory>--> -<!-- </testsuite>--> <php> <ini name="date.timezone" value="America/Los_Angeles"/> <ini name="xdebug.max_nesting_level" value="200"/> From 796255795513a80b6aa8d85b4bc7840185989100 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Mon, 16 Mar 2020 23:07:50 +0700 Subject: [PATCH 400/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Update unit test --- .../Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 3c240290594ab..60a2a048c549c 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -160,7 +160,8 @@ public function testBuild($data, $expectedPrice) if (isset($data['is_unlimited'])) { $this->linkMock->expects($this->once())->method('setNumberOfDownloads')->with(0); } - if (isset($data['use_default_title']) && $data['use_default_title'] == '1') { + $useDefaultTitle = $data['use_default_title'] ?? false; + if ($useDefaultTitle) { $this->linkMock->expects($this->once())->method('setTitle')->with(null); } if (isset($data['price'])) { From 4ddb85cc52fa0c2dca3c6548a799b52f02ce0579 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 16 Mar 2020 12:18:40 -0500 Subject: [PATCH 401/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas -- fix merge conflict --- .../Test/Unit/Controller/RouterTest.php | 324 +++++++++++------- 1 file changed, 199 insertions(+), 125 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index cd2725f218aae..a41d9d38bdcb3 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -6,27 +6,35 @@ namespace Magento\UrlRewrite\Test\Unit\Controller; use Magento\Framework\App\Action\Forward; +use Magento\Framework\App\Action\Redirect; +use Magento\Framework\App\ActionFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Controller\Router; +use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Store\Model\Store; -use PHPUnit\Framework\MockObject\MockObject; use Laminas\Stdlib\ParametersInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Test class for UrlRewrite Controller Router * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RouterTest extends \PHPUnit\Framework\TestCase +class RouterTest extends TestCase { /** - * @var \Magento\UrlRewrite\Controller\Router + * @var Router */ private $router; /** - * @var \Magento\Framework\App\ActionFactory|MockObject + * @var ActionFactory|MockObject */ private $actionFactory; @@ -36,7 +44,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $url; /** - * @var \Magento\Store\Model\StoreManagerInterface|MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; @@ -46,12 +54,12 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $store; /** - * @var \Magento\Framework\App\ResponseInterface|MockObject + * @var ResponseInterface|MockObject */ private $response; /** - * @var \Magento\Framework\App\RequestInterface|MockObject + * @var RequestInterface|MockObject */ private $request; @@ -61,7 +69,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $requestQuery; /** - * @var \Magento\UrlRewrite\Model\UrlFinderInterface|MockObject + * @var UrlFinderInterface|MockObject */ private $urlFinder; @@ -71,24 +79,24 @@ class RouterTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $this->actionFactory = $this->createMock(\Magento\Framework\App\ActionFactory::class); + $this->actionFactory = $this->createMock(ActionFactory::class); $this->url = $this->createMock(UrlInterface::class); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); $this->response = $this->createPartialMock( - \Magento\Framework\App\ResponseInterface::class, + ResponseInterface::class, ['setRedirect', 'sendResponse'] ); $this->requestQuery = $this->createMock(ParametersInterface::class); $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor()->getMock(); $this->request->method('getQuery')->willReturn($this->requestQuery); - $this->urlFinder = $this->createMock(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + $this->urlFinder = $this->createMock(UrlFinderInterface::class); $this->store = $this->getMockBuilder( Store::class )->disableOriginalConstructor()->getMock(); $this->router = $objectManager->getObject( - \Magento\UrlRewrite\Controller\Router::class, + Router::class, [ 'actionFactory' => $this->actionFactory, 'url' => $this->url, @@ -104,9 +112,16 @@ protected function setUp() */ public function testNoRewriteExist() { - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue(null)); - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->request->method('getPathInfo') + ->willReturn(''); + $this->request->method('getRequestString') + ->willReturn(''); + $this->urlFinder->method('findOneByData') + ->willReturn(null); + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->store->method('getId') + ->willReturn(1); $this->assertNull($this->router->match($this->request)); } @@ -118,55 +133,45 @@ public function testRewriteAfterStoreSwitcher() { $initialRequestPath = 'request-path'; $newRequestPath = 'new-request-path'; + $newTargetPath = 'new-target-path'; $oldStoreAlias = 'old-store'; $oldStoreId = 'old-store-id'; $currentStoreId = 'current-store-id'; $rewriteEntityType = 'entity-type'; $rewriteEntityId = 42; - $this->request - ->expects($this->any()) - ->method('getParam') + $this->request->method('getParam') ->with('___from_store') ->willReturn($oldStoreAlias); - $this->request - ->expects($this->any()) - ->method('getPathInfo') + $this->request->method('getPathInfo') + ->willReturn($initialRequestPath); + $this->request->method('getRequestString') ->willReturn($initialRequestPath); $oldStore = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); - $oldStore->expects($this->any()) - ->method('getId') + $oldStore->method('getId') ->willReturn($oldStoreId); - $this->store - ->expects($this->any()) - ->method('getId') + $this->store->method('getId') ->willReturn($currentStoreId); - $this->storeManager - ->expects($this->any()) - ->method('getStore') + $this->storeManager->method('getStore') ->willReturnMap([[$oldStoreAlias, $oldStore], [null, $this->store]]); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor() ->getMock(); - $oldUrlRewrite->expects($this->any()) - ->method('getEntityType') + $oldUrlRewrite->method('getEntityType') ->willReturn($rewriteEntityType); - $oldUrlRewrite->expects($this->any()) - ->method('getEntityId') + $oldUrlRewrite->method('getEntityId') ->willReturn($rewriteEntityId); - $oldUrlRewrite->expects($this->any()) - ->method('getRedirectType') + $oldUrlRewrite->method('getRedirectType') ->willReturn(0); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor() ->getMock(); - $urlRewrite->expects($this->any()) - ->method('getRequestPath') + $urlRewrite->method('getRequestPath') ->willReturn($newRequestPath); - $this->urlFinder - ->expects($this->any()) - ->method('findOneByData') + $urlRewrite->method('getTargetPath') + ->willReturn($newTargetPath); + $this->urlFinder->method('findOneByData') ->willReturnMap( [ [ @@ -190,22 +195,23 @@ public function testRewriteAfterStoreSwitcher() */ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() { - $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); - $this->request->expects($this->any())->method('getParam')->with('___from_store') - ->will($this->returnValue('old-store')); + $this->request->method('getPathInfo')->willReturn('request-path'); + $this->request->method('getRequestString')->willReturn('request-path'); + $this->request->method('getParam')->with('___from_store') + ->willReturn('old-store'); $oldStore = $this->getMockBuilder(Store::class)->disableOriginalConstructor()->getMock(); - $this->storeManager->expects($this->any())->method('getStore') - ->will($this->returnValueMap([['old-store', $oldStore], [null, $this->store]])); - $oldStore->expects($this->any())->method('getId')->will($this->returnValue('old-store-id')); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->storeManager->method('getStore') + ->willReturnMap([['old-store', $oldStore], [null, $this->store]]); + $oldStore->method('getId')->willReturn('old-store-id'); + $this->store->method('getId')->willReturn('current-store-id'); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $oldUrlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('entity-type')); - $oldUrlRewrite->expects($this->any())->method('getEntityId')->will($this->returnValue('entity-id')); - $oldUrlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); + $oldUrlRewrite->method('getEntityType')->willReturn('entity-type'); + $oldUrlRewrite->method('getEntityId')->willReturn('entity-id'); + $oldUrlRewrite->method('getRequestPath')->willReturn('request-path'); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); + $urlRewrite->method('getRequestPath')->willReturn('request-path'); $this->assertNull($this->router->match($this->request)); } @@ -215,41 +221,40 @@ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() */ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() { - $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); - $this->request->expects($this->any())->method('getParam')->with('___from_store') - ->will($this->returnValue('old-store')); + $this->request->method('getPathInfo')->willReturn('request-path'); + $this->request->method('getRequestString')->willReturn('request-path'); + $this->request->method('getParam')->with('___from_store') + ->willReturn('old-store'); $oldStore = $this->getMockBuilder(Store::class)->disableOriginalConstructor()->getMock(); - $this->storeManager->expects($this->any())->method('getStore') - ->will($this->returnValueMap([['old-store', $oldStore], [null, $this->store]])); - $oldStore->expects($this->any())->method('getId')->will($this->returnValue('old-store-id')); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->storeManager->method('getStore') + ->willReturnMap([['old-store', $oldStore], [null, $this->store]]); + $oldStore->method('getId')->willReturn('old-store-id'); + $this->store->method('getId')->willReturn('current-store-id'); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $oldUrlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('entity-type')); - $oldUrlRewrite->expects($this->any())->method('getEntityId')->will($this->returnValue('entity-id')); - $oldUrlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('old-request-path')); + $oldUrlRewrite->method('getEntityType')->willReturn('entity-type'); + $oldUrlRewrite->method('getEntityId')->willReturn('entity-id'); + $oldUrlRewrite->method('getRequestPath')->willReturn('old-request-path'); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('old-request-path')); + $urlRewrite->method('getRequestPath')->willReturn('old-request-path'); - $this->urlFinder->expects($this->any())->method('findOneByData')->will( - $this->returnValueMap( + $this->urlFinder->method('findOneByData')->willReturnMap( + [ + [ + [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], + $oldUrlRewrite, + ], [ [ - [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], - $oldUrlRewrite, - ], - [ - [ - UrlRewrite::ENTITY_TYPE => 'entity-type', - UrlRewrite::ENTITY_ID => 'entity-id', - UrlRewrite::STORE_ID => 'current-store-id', - UrlRewrite::IS_AUTOGENERATED => 1, - ], - $urlRewrite + UrlRewrite::ENTITY_TYPE => 'entity-type', + UrlRewrite::ENTITY_ID => 'entity-id', + UrlRewrite::STORE_ID => 'current-store-id', + UrlRewrite::IS_AUTOGENERATED => 1, ], - ] - ) + $urlRewrite + ], + ] ); $this->assertNull($this->router->match($this->request)); @@ -261,51 +266,107 @@ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() public function testMatchWithRedirect() { $queryParams = []; - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $redirectType = 'redirect-code'; + $requestPath = 'request-path'; + $targetPath = 'target-path'; + $newTargetPath = 'new-target-path'; + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) - ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->response->expects($this->once())->method('setRedirect') - ->with('new-target-path', 'redirect-code'); - $this->request->expects($this->once())->method('getParams')->willReturn($queryParams); - $this->url->expects($this->once())->method('getUrl')->with( - '', - ['_direct' => 'target-path', '_query' => $queryParams] - ) - ->will($this->returnValue('new-target-path')); - $this->request->expects($this->once())->method('setDispatched')->with(true); - $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->disableOriginalConstructor() + ->getMock(); + $urlRewrite->method('getRedirectType')->willReturn($redirectType); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with($newTargetPath, $redirectType); + $this->request->expects($this->once()) + ->method('getParams') + ->willReturn($queryParams); + $this->url->expects($this->once()) + ->method('getUrl') + ->with( + '', + ['_direct' => $targetPath, '_query' => $queryParams] + ) + ->willReturn($newTargetPath); + $this->request->expects($this->once()) + ->method('setDispatched') + ->with(true); + $this->actionFactory->expects($this->once()) + ->method('create') + ->with(Redirect::class); $this->router->match($this->request); } /** - * @return void + * @param string $requestPath + * @param string $targetPath + * @param bool $shouldRedirect + * @dataProvider customInternalRedirectDataProvider */ - public function testMatchWithCustomInternalRedirect() + public function testMatchWithCustomInternalRedirect($requestPath, $targetPath, $shouldRedirect) { $queryParams = []; - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $redirectType = 'redirect-code'; + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) - ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('custom')); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->request->expects($this->any())->method('getParams')->willReturn($queryParams); - $this->response->expects($this->once())->method('setRedirect')->with('a', 'redirect-code'); - $this->url->expects($this->once())->method('getUrl')->with( - '', - ['_direct' => 'target-path', '_query' => $queryParams] - )->willReturn('a'); - $this->request->expects($this->once())->method('setDispatched')->with(true); - $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->disableOriginalConstructor() + ->getMock(); + $urlRewrite->method('getEntityType')->willReturn('custom'); + $urlRewrite->method('getRedirectType')->willReturn($redirectType); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); - $this->router->match($this->request); + if ($shouldRedirect) { + $this->request->method('getParams')->willReturn($queryParams); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with('a', $redirectType); + $this->url->expects($this->once()) + ->method('getUrl') + ->with( + '', + ['_direct' => $targetPath, '_query' => $queryParams] + ) + ->willReturn('a'); + $this->request->expects($this->once()) + ->method('setDispatched') + ->with(true); + $this->actionFactory->expects($this->once()) + ->method('create') + ->with(Redirect::class); + } + + $routerResult = $this->router->match($this->request); + + if (!$shouldRedirect) { + $this->assertNull($routerResult); + } + } + + /** + * @return array + */ + public function customInternalRedirectDataProvider() + { + return [ + ['request-path', 'target-path', true], + ['/', '/', false], + ]; } /** @@ -314,19 +375,27 @@ public function testMatchWithCustomInternalRedirect() */ public function testMatchWithCustomExternalRedirect($targetPath) { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $requestPath = 'request-path'; + $this->storeManager->method('getStore')->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('custom')); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue($targetPath)); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->response->expects($this->once())->method('setRedirect')->with($targetPath, 'redirect-code'); + $urlRewrite->method('getEntityType')->willReturn('custom'); + $urlRewrite->method('getRedirectType')->willReturn('redirect-code'); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with($targetPath, 'redirect-code'); $this->request->expects($this->never())->method('getParams'); $this->url->expects($this->never())->method('getUrl'); $this->request->expects($this->once())->method('setDispatched')->with(true); $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->with(Redirect::class); $this->router->match($this->request); } @@ -347,18 +416,23 @@ public function externalRedirectTargetPathDataProvider() */ public function testMatch() { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $requestPath = 'request-path'; + $this->storeManager->method('getStore')->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue(0)); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); + $urlRewrite->method('getRedirectType')->willReturn(0); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn('target-path'); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); $this->request->expects($this->once())->method('setPathInfo')->with('/target-path'); $this->request->expects($this->once())->method('setAlias') ->with(UrlInterface::REWRITE_REQUEST_PATH_ALIAS, 'request-path'); $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Forward::class); + ->with(Forward::class); $this->router->match($this->request); } From 1b014f8272390816c9a1c7edc038c80c11bbe5f7 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 19:24:11 +0100 Subject: [PATCH 402/734] Integration Tests for Authentication feature on Customer Account --- .../Controller/AuthenticationTest.php | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php new file mode 100644 index 0000000000000..3ccd415a16c4f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Customer\Controller\Plugin\Account as AccountPlugin; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Set of Tests to verify that Authentication methods work properly + */ +class AuthenticationTest extends AbstractController +{ + /** + * Make sure that customized AccountPlugin was reverted. + */ + protected function tearDown() + { + $this->resetAllowedActions(); + parent::tearDown(); + } + + /** + * After changes to `di.xml` and overriding list of allowed actions, unallowed ones should cause redirect. + */ + public function testExpectRedirectResponseWhenDispatchNotAllowedAction() + { + $this->overrideAllowedActions(['notExistingRoute']); + + $this->dispatch('customer/account/create'); + $this->assertRedirect($this->stringContains('customer/account/login')); + } + + /** + * Allowed actions should be displayed + */ + public function testExpectPageDispatchWhenAllowedAction() + { + $this->overrideAllowedActions(['create']); + + $this->dispatch('customer/account/create'); + $this->assertEquals(200, $this->getResponse()->getStatusCode()); + } + + /** + * Overrides list of `allowedActions` for Authorization Plugin + * + * @param string[] $allowedActions + * @see \Magento\Customer\Controller\Plugin\Account + */ + private function overrideAllowedActions(array $allowedActions): void + { + $allowedActions = array_combine($allowedActions, $allowedActions); + $pluginMock = $this->_objectManager->create(AccountPlugin::class, ['allowedActions' => $allowedActions]); + $this->_objectManager->addSharedInstance($pluginMock, AccountPlugin::class); + } + + /** + * Removes all the customizations applied to `allowedActions` + * @see \Magento\Customer\Controller\Plugin\Account + */ + private function resetAllowedActions() + { + $this->_objectManager->removeSharedInstance(AccountPlugin::class); + } +} From 65ac7b743f8e02105bedecc5a2ff7048e53cca0f Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 19:27:30 +0100 Subject: [PATCH 403/734] Invalid name (was `Mock` instead of `Fake`) --- .../Magento/Customer/Controller/AuthenticationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php index 3ccd415a16c4f..83f30e730e683 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php @@ -55,8 +55,8 @@ public function testExpectPageDispatchWhenAllowedAction() private function overrideAllowedActions(array $allowedActions): void { $allowedActions = array_combine($allowedActions, $allowedActions); - $pluginMock = $this->_objectManager->create(AccountPlugin::class, ['allowedActions' => $allowedActions]); - $this->_objectManager->addSharedInstance($pluginMock, AccountPlugin::class); + $pluginFake = $this->_objectManager->create(AccountPlugin::class, ['allowedActions' => $allowedActions]); + $this->_objectManager->addSharedInstance($pluginFake, AccountPlugin::class); } /** From fe21a4348816c95397887ea13690579f39b734c2 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 16 Mar 2020 19:30:23 +0100 Subject: [PATCH 404/734] Rename Test to be more meaningful --- .../Magento/Customer/Controller/AuthenticationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php index 83f30e730e683..822b3ab8f35d1 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php @@ -36,9 +36,9 @@ public function testExpectRedirectResponseWhenDispatchNotAllowedAction() } /** - * Allowed actions should be displayed + * Allowed actions should be rendered normally */ - public function testExpectPageDispatchWhenAllowedAction() + public function testExpectPageResponseWhenAllowedAction() { $this->overrideAllowedActions(['create']); From de842be659583d7ae050738f5966f9300874a97b Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 16 Mar 2020 13:37:42 -0500 Subject: [PATCH 405/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas --- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 2 ++ .../Test/Legacy/_files/blacklist/obsolete_mage.php | 3 ++- .../Magento/Test/Legacy/_files/obsolete_namespaces.php | 1 + .../Code/Test/Unit/Generator/ClassGeneratorTest.php | 9 ++++++--- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index b962e3af6e0aa..48a0eebf0205b 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -142,6 +142,8 @@ public function apply() } $this->domainManager->addDomains($this->whitelist); + + return $this; } /** diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php index 62816cd4e4f76..e4d5407a341b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php @@ -11,5 +11,6 @@ 'lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php', 'dev/tests/integration/testsuite/Magento/Indexer/Model/Config/_files/result.php', 'lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php', - 'lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php' + 'lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php', + 'setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php', ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php index 0d9da334ea3ab..f2411cdad86de 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php @@ -200,4 +200,5 @@ ['Magento\BraintreeTwo', 'Magento\Braintree'], ['Magento\MysqlMq\Model\Resource', 'Magento\MysqlMq\Model\ResourceModel'], ['Magento\BulkOperations', 'Magento\AsynchronousOperations'], + ['Zend', 'Laminas'], ]; diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php index bdcd834527c91..3887744e45126 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator; +/** + * Test for Magento\Framework\Code\Generator\ClassGenerator + */ class ClassGeneratorTest extends \PHPUnit\Framework\TestCase { /**#@+ @@ -333,9 +336,9 @@ public function testNamespaceName($actualNamespace, $expectedNamespace) public function providerNamespaces() { return [ - ['Zend', 'Zend'], - ['\Zend', 'Zend'], - ['\Zend\SomeClass', 'Zend\SomeClass'], + ['Laminas', 'Laminas'], + ['\Laminas', 'Laminas'], + ['\Laminas\SomeClass', 'Laminas\SomeClass'], ['', null], ]; } From cf51d98466e3ab423c78f2a45cc0e49ef098fd59 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 16 Mar 2020 14:16:30 -0500 Subject: [PATCH 406/734] MC-32086: [AR] Report account is unavailable after URL change --- app/code/Magento/Analytics/Cron/SignUp.php | 11 +- app/code/Magento/Analytics/Cron/Update.php | 28 +- .../Patch/Data/ActivateDataCollection.php | 95 +++++++ .../Setup/Patch/Data/PrepareInitialConfig.php | 58 ++--- .../Analytics/Test/Unit/Cron/UpdateTest.php | 15 +- .../Analytics/etc/adminhtml/system.xml | 9 +- app/code/Magento/Analytics/etc/di.xml | 2 +- .../Magento/Analytics/Cron/UpdateTest.php | 239 ++++++++++++++++++ .../Model/Config/Backend/EnabledTest.php | 163 ++++++++++++ ...nabled_subscription_with_invalid_token.php | 19 +- ...bscription_with_invalid_token_rollback.php | 23 +- 11 files changed, 594 insertions(+), 68 deletions(-) create mode 100644 app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php create mode 100644 dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php diff --git a/app/code/Magento/Analytics/Cron/SignUp.php b/app/code/Magento/Analytics/Cron/SignUp.php index 8f97b839ec8ee..2588b87e84c1c 100644 --- a/app/code/Magento/Analytics/Cron/SignUp.php +++ b/app/code/Magento/Analytics/Cron/SignUp.php @@ -7,6 +7,7 @@ use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; use Magento\Analytics\Model\Connector; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; @@ -57,22 +58,24 @@ public function __construct( } /** - * Execute scheduled subscription operation + * Execute scheduled subscription operation. + * * In case of failure writes message to notifications inbox * * @return bool + * @throws NotFoundException */ public function execute() { - $attemptsCount = $this->flagManager->getFlagData(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + $attemptsCount = (int)$this->flagManager->getFlagData(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); - if (($attemptsCount === null) || ($attemptsCount <= 0)) { + if ($attemptsCount <= 0) { $this->deleteAnalyticsCronExpr(); $this->flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); return false; } - $attemptsCount -= 1; + $attemptsCount--; $this->flagManager->saveFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); $signUpResult = $this->connector->execute('signUp'); if ($signUpResult === false) { diff --git a/app/code/Magento/Analytics/Cron/Update.php b/app/code/Magento/Analytics/Cron/Update.php index 9062a7bac7551..b5e4b82a0777e 100644 --- a/app/code/Magento/Analytics/Cron/Update.php +++ b/app/code/Magento/Analytics/Cron/Update.php @@ -8,6 +8,7 @@ use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Baseurl\SubscriptionUpdateHandler; use Magento\Analytics\Model\Connector; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; @@ -67,26 +68,37 @@ public function __construct( * Execute scheduled update operation * * @return bool + * @throws NotFoundException */ public function execute() { $result = false; - $attemptsCount = $this->flagManager + $attemptsCount = (int)$this->flagManager ->getFlagData(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); - if ($attemptsCount) { - $attemptsCount -= 1; + if (($attemptsCount > 0) && $this->analyticsToken->isTokenExist()) { + $attemptsCount--; + $this->flagManager + ->saveFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); $result = $this->connector->execute('update'); } if ($result || ($attemptsCount <= 0) || (!$this->analyticsToken->isTokenExist())) { - $this->flagManager - ->deleteFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); - $this->flagManager->deleteFlag(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); - $this->configWriter->delete(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH); - $this->reinitableConfig->reinit(); + $this->exitFromUpdateProcess(); } return $result; } + + /** + * Clean-up flags and refresh configuration + */ + private function exitFromUpdateProcess(): void + { + $this->flagManager + ->deleteFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); + $this->flagManager->deleteFlag(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); + $this->configWriter->delete(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH); + $this->reinitableConfig->reinit(); + } } diff --git a/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php b/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php new file mode 100644 index 0000000000000..dd60d74b53d09 --- /dev/null +++ b/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Analytics\Setup\Patch\Data; + +use Magento\Analytics\Model\Config\Backend\CollectionTime; +use Magento\Analytics\Model\SubscriptionStatusProvider; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Activate data collection mechanism + */ +class ActivateDataCollection implements DataPatchInterface +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SubscriptionStatusProvider + */ + private $subscriptionStatusProvider; + + /** + * @var string + */ + private $analyticsCollectionTimeConfigPath = 'analytics/general/collection_time'; + + /** + * @var CollectionTime + */ + private $collectionTimeBackendModel; + + /** + * @param ScopeConfigInterface $scopeConfig + * @param SubscriptionStatusProvider $subscriptionStatusProvider + * @param CollectionTime $collectionTimeBackendModel + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + SubscriptionStatusProvider $subscriptionStatusProvider, + CollectionTime $collectionTimeBackendModel + ) { + $this->scopeConfig = $scopeConfig; + $this->subscriptionStatusProvider = $subscriptionStatusProvider; + $this->collectionTimeBackendModel = $collectionTimeBackendModel; + } + + /** + * @inheritDoc + * + * @throws LocalizedException + */ + public function apply() + { + $subscriptionStatus = $this->subscriptionStatusProvider->getStatus(); + $isCollectionProcessActivated = $this->scopeConfig->getValue(CollectionTime::CRON_SCHEDULE_PATH); + if ($subscriptionStatus !== $this->subscriptionStatusProvider->getStatusForDisabledSubscription() + && !$isCollectionProcessActivated + ) { + $this->collectionTimeBackendModel + ->setValue($this->scopeConfig->getValue($this->analyticsCollectionTimeConfigPath)); + $this->collectionTimeBackendModel->setPath($this->analyticsCollectionTimeConfigPath); + $this->collectionTimeBackendModel->afterSave(); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return [ + PrepareInitialConfig::class, + ]; + } +} diff --git a/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php index a352854a8b77b..97ac340f9d491 100644 --- a/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php +++ b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php @@ -4,17 +4,18 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Setup\Patch\Data; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Config\Model\Config\Source\Enabledisable; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Initial patch. - * - * @package Magento\Analytics\Setup\Patch + * Active subscription process for Advanced Reporting */ class PrepareInitialConfig implements DataPatchInterface, PatchVersionInterface { @@ -24,50 +25,47 @@ class PrepareInitialConfig implements DataPatchInterface, PatchVersionInterface private $moduleDataSetup; /** - * PrepareInitialConfig constructor. + * @var SubscriptionHandler + */ + private $subscriptionHandler; + + /** + * @var string + */ + private $subscriptionEnabledConfigPath = 'analytics/subscription/enabled'; + + /** * @param ModuleDataSetupInterface $moduleDataSetup + * @param SubscriptionHandler $subscriptionHandler */ public function __construct( - ModuleDataSetupInterface $moduleDataSetup + ModuleDataSetupInterface $moduleDataSetup, + SubscriptionHandler $subscriptionHandler ) { $this->moduleDataSetup = $moduleDataSetup; + $this->subscriptionHandler = $subscriptionHandler; } /** - * {@inheritdoc} + * @inheritDoc */ public function apply() { - $this->moduleDataSetup->getConnection()->insertMultiple( + $this->moduleDataSetup->getConnection()->insert( $this->moduleDataSetup->getTable('core_config_data'), [ - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => 'analytics/subscription/enabled', - 'value' => 1 - ], - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => SubscriptionHandler::CRON_STRING_PATH, - 'value' => join(' ', SubscriptionHandler::CRON_EXPR_ARRAY) - ] + 'path' => $this->subscriptionEnabledConfigPath, + 'value' => Enabledisable::ENABLE_VALUE, ] ); - $this->moduleDataSetup->getConnection()->insert( - $this->moduleDataSetup->getTable('flag'), - [ - 'flag_code' => SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, - 'state' => 0, - 'flag_data' => 24, - ] - ); + $this->subscriptionHandler->processEnabled(); + + return $this; } /** - * {@inheritdoc} + * @inheritDoc */ public static function getDependencies() { @@ -75,7 +73,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritDoc */ public static function getVersion() { @@ -83,7 +81,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritDoc */ public function getAliases() { diff --git a/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php index aa3011ffc94f6..fa007268474c4 100644 --- a/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php @@ -11,6 +11,7 @@ use Magento\Analytics\Model\Connector; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; class UpdateTest extends \PHPUnit\Framework\TestCase @@ -45,6 +46,9 @@ class UpdateTest extends \PHPUnit\Framework\TestCase */ private $update; + /** + * @inheritDoc + */ protected function setUp() { $this->connectorMock = $this->getMockBuilder(Connector::class) @@ -74,6 +78,7 @@ protected function setUp() /** * @return void + * @throws NotFoundException */ public function testExecuteWithoutToken() { @@ -82,12 +87,12 @@ public function testExecuteWithoutToken() ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE) ->willReturn(10); $this->connectorMock - ->expects($this->once()) + ->expects($this->never()) ->method('execute') ->with('update') ->willReturn(false); $this->analyticsTokenMock - ->expects($this->once()) + ->expects($this->any()) ->method('isTokenExist') ->willReturn(false); $this->addFinalOutputAsserts(); @@ -120,6 +125,7 @@ private function addFinalOutputAsserts(bool $isExecuted = true) * @param $counterData * @return void * @dataProvider executeWithEmptyReverseCounterDataProvider + * @throws NotFoundException */ public function testExecuteWithEmptyReverseCounter($counterData) { @@ -159,6 +165,7 @@ public function executeWithEmptyReverseCounterDataProvider() * @param bool $functionResult * @return void * @dataProvider executeRegularScenarioDataProvider + * @throws NotFoundException */ public function testExecuteRegularScenario( int $reverseCount, @@ -170,6 +177,10 @@ public function testExecuteRegularScenario( ->method('getFlagData') ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE) ->willReturn($reverseCount); + $this->flagManagerMock + ->expects($this->once()) + ->method('saveFlag') + ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $reverseCount - 1); $this->connectorMock ->expects($this->once()) ->method('execute') diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 999d565353329..0aba6e4dd00ed 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -28,6 +28,9 @@ <label>Time of day to send data</label> <frontend_model>Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel</frontend_model> <backend_model>Magento\Analytics\Model\Config\Backend\CollectionTime</backend_model> + <depends> + <field id="analytics/general/enabled">1</field> + </depends> </field> <field id="vertical" translate="hint label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> <hint>Industry Data</hint> @@ -36,9 +39,9 @@ <source_model>Magento\Analytics\Model\Config\Source\Vertical</source_model> <backend_model>Magento\Analytics\Model\Config\Backend\Vertical</backend_model> <frontend_model>Magento\Analytics\Block\Adminhtml\System\Config\Vertical</frontend_model> - <depends> - <field id="analytics/general/enabled">1</field> - </depends> + <depends> + <field id="analytics/general/enabled">1</field> + </depends> </field> <field id="additional_comment" translate="label comment" type="label" sortOrder="40" showInDefault="1"> <label><![CDATA[<strong>Get more insights from Magento Business Intelligence</strong>]]></label> diff --git a/app/code/Magento/Analytics/etc/di.xml b/app/code/Magento/Analytics/etc/di.xml index b9bb9cc9ff00c..cdb42f18718b7 100644 --- a/app/code/Magento/Analytics/etc/di.xml +++ b/app/code/Magento/Analytics/etc/di.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Analytics\ReportXML\ConfigInterface" type="Magento\Analytics\ReportXML\Config" /> + <preference for="Magento\Analytics\ReportXml\ConfigInterface" type="Magento\Analytics\ReportXml\Config" /> <preference for="Magento\Analytics\Model\ConfigInterface" type="Magento\Analytics\Model\Config" /> <preference for="Magento\Analytics\Model\ReportWriterInterface" type="Magento\Analytics\Model\ReportWriter" /> <preference for="Magento\Analytics\Api\LinkProviderInterface" type="Magento\Analytics\Model\LinkProvider" /> diff --git a/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php b/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php new file mode 100644 index 0000000000000..c8ce98d34074b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php @@ -0,0 +1,239 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Analytics\Cron; + +use Magento\Analytics\Model\Config\Backend\Baseurl\SubscriptionUpdateHandler; +use Magento\Analytics\Model\Connector\Http\Client\Curl as CurlClient; +use Magento\Analytics\Model\Connector\Http\ClientInterface; +use Magento\Config\Model\PreparedValueFactory; +use Magento\Config\Model\ResourceModel\Config\Data as ConfigDataResource; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\FlagManager; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UpdateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PreparedValueFactory + */ + private $preparedValueFactory; + + /** + * @var ConfigDataResource + */ + private $configValueResourceModel; + + /** + * @var Update + */ + private $updateCron; + + /** + * @var ClientInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private $httpClient; + + /** + * @var FlagManager + */ + private $flagManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @return void + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->httpClient = $this->getMockBuilder(ClientInterface::class) + ->getMockForAbstractClass(); + $this->objectManager->addSharedInstance($this->httpClient, CurlClient::class); + $this->preparedValueFactory = $this->objectManager->get(PreparedValueFactory::class); + $this->configValueResourceModel = $this->objectManager->get(ConfigDataResource::class); + $this->updateCron = $this->objectManager->get(Update::class); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $this->flagManager = $this->objectManager->get(FlagManager::class); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testSuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $this->mockRequestCall(201, 'URL has been changed'); + + $this->updateCron->execute(); + $this->assertEmpty($this->getUpdateCounterFlag()); + $this->assertEmpty($this->getPreviousBaseUrlFlag()); + $this->assertEmpty($this->getConfigValue(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH)); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testUnsuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $reverseCounter = $this->getUpdateCounterFlag(); + $this->mockRequestCall(500, 'Unauthorized access'); + + $this->updateCron->execute(); + $this->assertEquals($reverseCounter - 1, $this->getUpdateCounterFlag()); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testLastUnsuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $this->setUpdateCounterValue(1); + $this->mockRequestCall(500, 'Unauthorized access'); + + $this->updateCron->execute(); + $this->assertEmpty($this->getUpdateCounterFlag()); + $this->assertEmpty($this->getPreviousBaseUrlFlag()); + $this->assertEmpty($this->getConfigValue(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH)); + } + + /** + * Save configuration value + * + * @param string $path The configuration path in format section/group/field_name + * @param string $value The configuration value + * @param string $scope The configuration scope (default, website, or store) + * @return void + * @throws \Magento\Framework\Exception\RuntimeException + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + private function saveConfigValue( + string $path, + string $value, + string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + $configValue = $this->preparedValueFactory->create( + $path, + $value, + $scope + ); + $this->configValueResourceModel->save($configValue); + } + + /** + * Get configuration value + * + * @param string $path + * @param string $scopeType + * @return mixed + */ + private function getConfigValue( + string $path, + string $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + return $this->scopeConfig->getValue( + $path, + $scopeType + ); + } + + /** + * Get update counter flag value + * + * @return int|null + */ + private function getUpdateCounterFlag(): ?int + { + return $this->flagManager + ->getFlagData(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); + } + + /** + * Get previous URL flag value + * + * @return string|null + */ + private function getPreviousBaseUrlFlag(): ?string + { + return $this->flagManager + ->getFlagData(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); + } + + /** + * Set response mock for the HTTP client + * + * @param int $responseCode + * @param string $responseMessage + */ + private function mockRequestCall(int $responseCode, string $responseMessage): void + { + $response = $this->objectManager->create( + \Zend_Http_Response::class, + [ + 'code' => $responseCode, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => json_encode(['message' => $responseMessage]) + ] + ); + + $this->httpClient + ->expects($this->once()) + ->method('request') + ->willReturn($response); + } + + /** + * Set value for update counter flag + * + * @param int $value + */ + private function setUpdateCounterValue(int $value): void + { + $this->flagManager + ->saveFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $value); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php b/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php new file mode 100644 index 0000000000000..61cc8b56af949 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Analytics\Model\Config\Backend; + +use Magento\Analytics\Model\SubscriptionStatusProvider; +use Magento\Config\Model\Config\Source\Enabledisable; +use Magento\Config\Model\PreparedValueFactory; +use Magento\Config\Model\ResourceModel\Config\Data as ConfigDataResource; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class EnabledTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SubscriptionStatusProvider + */ + private $subscriptionStatusProvider; + + /** + * @var PreparedValueFactory + */ + private $preparedValueFactory; + + /** + * @var ConfigDataResource + */ + private $configValueResourceModel; + + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfig; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $this->subscriptionStatusProvider = $this->objectManager->get(SubscriptionStatusProvider::class); + $this->preparedValueFactory = $this->objectManager->get(PreparedValueFactory::class); + $this->configValueResourceModel = $this->objectManager->get(ConfigDataResource::class); + $this->reinitableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + } + + /** + * @magentoDbIsolation enabled + */ + public function testDisable() + { + $this->checkInitialStatus(); + $this->saveConfigValue(Enabled::XML_ENABLED_CONFIG_STRUCTURE_PATH, (string)Enabledisable::DISABLE_VALUE); + $this->reinitableConfig->reinit(); + + $this->checkDisabledStatus(); + } + + /** + * @depends testDisable + * @magentoDbIsolation enabled + */ + public function testReEnable() + { + $this->checkDisabledStatus(); + $this->saveConfigValue(Enabled::XML_ENABLED_CONFIG_STRUCTURE_PATH, (string)Enabledisable::ENABLE_VALUE); + $this->checkReEnabledStatus(); + } + + /** + * Get configuration value + * + * @param string $path + * @param string $scopeType + * @return mixed + */ + private function getConfigValue( + string $path, + string $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + return $this->scopeConfig->getValue( + $path, + $scopeType + ); + } + + /** + * Save configuration value + * + * @param string $path The configuration path in format section/group/field_name + * @param string $value The configuration value + * @param string $scope The configuration scope (default, website, or store) + * @return void + * @throws \Magento\Framework\Exception\RuntimeException + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + private function saveConfigValue( + string $path, + string $value, + string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + $configValue = $this->preparedValueFactory->create( + $path, + $value, + $scope + ); + $this->configValueResourceModel->save($configValue); + } + + /** + * Check the instance status after installation + */ + private function checkInitialStatus() + { + $this->assertNotSame(SubscriptionStatusProvider::DISABLED, $this->subscriptionStatusProvider->getStatus()); + $this->assertNotEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } + + /** + * Check the instance status after disabling AR synchronisation + */ + private function checkDisabledStatus() + { + $this->assertSame(SubscriptionStatusProvider::DISABLED, $this->subscriptionStatusProvider->getStatus()); + $this->assertEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } + + /** + * Check the instance status after re-activation AR synchronisation + */ + private function checkReEnabledStatus() + { + $this->assertContains( + $this->subscriptionStatusProvider->getStatus(), + [ + SubscriptionStatusProvider::ENABLED, + SubscriptionStatusProvider::PENDING, + ] + ); + $this->assertNotEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php index 0106bf6f1bdac..c52de227deae8 100644 --- a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php +++ b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php @@ -3,27 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\FlagManager; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** - * @var $configWriter \Magento\Framework\App\Config\Storage\WriterInterface + * @var $configWriter WriterInterface */ -$configWriter = $objectManager->get(\Magento\Framework\App\Config\Storage\WriterInterface::class); - +$configWriter = $objectManager->get(WriterInterface::class); $configWriter->delete(SubscriptionHandler::CRON_STRING_PATH); -$configWriter->save('analytics/subscription/enabled', 1); /** - * @var $analyticsToken \Magento\Analytics\Model\AnalyticsToken + * @var $analyticsToken AnalyticsToken */ -$analyticsToken = $objectManager->get(\Magento\Analytics\Model\AnalyticsToken::class); +$analyticsToken = $objectManager->get(AnalyticsToken::class); $analyticsToken->storeToken('42'); /** - * @var $flagManager \Magento\Framework\FlagManager + * @var $flagManager FlagManager */ -$flagManager = $objectManager->get(\Magento\Framework\FlagManager::class); - +$flagManager = $objectManager->get(FlagManager::class); $flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); diff --git a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php index 3fd3e21e282e0..a47a4b3475c9d 100644 --- a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php @@ -3,27 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\FlagManager; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** - * @var $configWriter \Magento\Framework\App\Config\Storage\WriterInterface + * @var $configWriter WriterInterface */ -$configWriter = $objectManager->get(\Magento\Framework\App\Config\Storage\WriterInterface::class); - -$configWriter->delete(SubscriptionHandler::CRON_STRING_PATH); -$configWriter->save('analytics/subscription/enabled', 0); +$configWriter = $objectManager->get(WriterInterface::class); +$configWriter->save(SubscriptionHandler::CRON_STRING_PATH, join(' ', SubscriptionHandler::CRON_EXPR_ARRAY)); /** - * @var $analyticsToken \Magento\Analytics\Model\AnalyticsToken + * @var $analyticsToken AnalyticsToken */ -$analyticsToken = $objectManager->get(\Magento\Analytics\Model\AnalyticsToken::class); +$analyticsToken = $objectManager->get(AnalyticsToken::class); $analyticsToken->storeToken(null); /** - * @var $flagManager \Magento\Framework\FlagManager + * @var $flagManager FlagManager */ -$flagManager = $objectManager->get(\Magento\Framework\FlagManager::class); - -$flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); +$flagManager = $objectManager->get(FlagManager::class); +$flagManager->saveFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, 24); From 61b8d6bde7b0a0ac8c97f192babd69f30abe2917 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Mon, 16 Mar 2020 15:07:37 -0500 Subject: [PATCH 407/734] MC-32278: Position sort does not work in GraphQl. - fix static --- .../ProductCollectionSearchCriteriaBuilder.php | 10 +++++++--- .../Magento/GraphQl/Catalog/ProductSearchTest.php | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php index a5db92db3fc0e..27ac971fc5abf 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php @@ -14,11 +14,15 @@ use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; use Magento\Framework\Api\SearchCriteriaInterface; +/** + * Builds a search criteria intended for the product collection based on search criteria used on SearchAPI + */ class ProductCollectionSearchCriteriaBuilder { - /** - * @var SearchCriteriaInterfaceFactory - */ + /** @var CollectionFactory */ + private $collectionFactory; + + /** @var SearchCriteriaInterfaceFactory */ private $searchCriteriaFactory; /** @var FilterBuilder */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 1907630325e65..f20d0da66f01b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -1121,6 +1121,7 @@ public function testFilterWithinSpecificPriceRangeSortedByNameDesc() /** * @magentoApiDataFixture Magento/Catalog/_files/category_with_three_products.php + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function testSortByPosition() { From fc176360b69d57d77a1e1d6f215890f5004bf63b Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Mon, 16 Mar 2020 16:01:47 -0500 Subject: [PATCH 408/734] MC-32201: Reorder functionality - MC-32362: Reorder interface --- .../Magento/Quote/Api/ReorderInterface.php | 21 --- app/code/Magento/Quote/etc/di.xml | 1 - .../Controller/AbstractController/Reorder.php | 10 +- .../Model/Reorder/Data/Error.php} | 24 ++-- .../Model/Reorder/Data}/ReorderOutput.php | 18 +-- .../Quote => Sales/Model/Reorder}/Reorder.php | 133 +++++++++++++----- .../SalesGraphQl/Model/Resolver/Reorder.php | 23 +-- .../Magento/SalesGraphQl/etc/schema.graphqls | 21 ++- .../Magento/GraphQl/Sales/ReorderTest.php | 32 +++-- 9 files changed, 170 insertions(+), 113 deletions(-) delete mode 100644 app/code/Magento/Quote/Api/ReorderInterface.php rename app/code/Magento/{Quote/Api/Data/Reorder/LineItemError.php => Sales/Model/Reorder/Data/Error.php} (61%) rename app/code/Magento/{Quote/Api/Data/Reorder => Sales/Model/Reorder/Data}/ReorderOutput.php (56%) rename app/code/Magento/{Quote/Model/Quote => Sales/Model/Reorder}/Reorder.php (56%) diff --git a/app/code/Magento/Quote/Api/ReorderInterface.php b/app/code/Magento/Quote/Api/ReorderInterface.php deleted file mode 100644 index 0fa882fb5213f..0000000000000 --- a/app/code/Magento/Quote/Api/ReorderInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Quote\Api; - -use Magento\Quote\Api\Data\Reorder\ReorderOutput; - -/** - * Allows customer to quickly reorder previously added products and put them to the Cart - */ -interface ReorderInterface -{ - /** - * @param string $incrementOrderId - * @param string $storeId - * @return ReorderOutput - */ - public function execute(string $incrementOrderId, string $storeId): ReorderOutput; -} diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index 45856c754efbd..f66001e7789cf 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -44,7 +44,6 @@ <preference for="Magento\Quote\Api\Data\EstimateAddressInterface" type="Magento\Quote\Model\EstimateAddress" /> <preference for="Magento\Quote\Api\Data\ProductOptionInterface" type="Magento\Quote\Model\Quote\ProductOption" /> <preference for="Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface" type="Magento\Quote\Model\ValidationRules\QuoteValidationComposite\Proxy"/> - <preference for="Magento\Quote\Api\ReorderInterface" type="Magento\Quote\Model\Quote\Reorder" /> <type name="Magento\Webapi\Controller\Rest\ParamsOverrider"> <arguments> <argument name="paramOverriders" xsi:type="array"> diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index 46f6887fda878..7c670a3ec2ce0 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -13,7 +13,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Registry; -use Magento\Quote\Api\ReorderInterface; use Magento\Sales\Helper\Reorder as ReorderHelper; /** @@ -32,7 +31,7 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface protected $_coreRegistry; /** - * @var ReorderInterface + * @var \Magento\Sales\Model\Reorder\Reorder */ private $reorder; @@ -43,18 +42,19 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface * @param OrderLoaderInterface $orderLoader * @param Registry $registry * @param ReorderHelper|null $reorderHelper + * @param \Magento\Sales\Model\Reorder\Reorder|null $reorder */ public function __construct( Action\Context $context, OrderLoaderInterface $orderLoader, Registry $registry, ReorderHelper $reorderHelper = null, - ReorderInterface $reOrder = null + \Magento\Sales\Model\Reorder\Reorder $reorder = null ) { $this->orderLoader = $orderLoader; $this->_coreRegistry = $registry; parent::__construct($context); - $this->reorder = $reOrder ?: ObjectManager::getInstance()->get(ReorderInterface::class); + $this->reorder = $reorder ?: ObjectManager::getInstance()->get(\Magento\Sales\Model\Reorder\Reorder::class); } /** @@ -80,7 +80,7 @@ public function execute() return $resultRedirect->setPath('checkout/cart'); } - $errors = $reorderOutput->getLineItemErrors(); + $errors = $reorderOutput->getErrors(); if (!empty($errors)) { $useNotice = $this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true); foreach ($errors as $error) { diff --git a/app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php b/app/code/Magento/Sales/Model/Reorder/Data/Error.php similarity index 61% rename from app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php rename to app/code/Magento/Sales/Model/Reorder/Data/Error.php index e224e85d0b8d7..b1e74bbfa9c5f 100644 --- a/app/code/Magento/Quote/Api/Data/Reorder/LineItemError.php +++ b/app/code/Magento/Sales/Model/Reorder/Data/Error.php @@ -3,46 +3,46 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Quote\Api\Data\Reorder; +namespace Magento\Sales\Model\Reorder\Data; /** - * DTO represent Cart line item error + * DTO represent error item */ -class LineItemError +class Error { /** * @var string */ - private $sku; + private $message; /** * @var string */ - private $message; + private $code; /** - * @param string $sku * @param string $message + * @param string $code */ - public function __construct(string $sku, string $message) + public function __construct(string $message, string $code) { - $this->sku = $sku; $this->message = $message; + $this->code = $code; } /** * @return string */ - public function getSku(): string + public function getMessage(): string { - return $this->sku; + return $this->message; } /** * @return string */ - public function getMessage(): string + public function getCode(): string { - return $this->message; + return $this->code; } } diff --git a/app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php similarity index 56% rename from app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php rename to app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php index 4e9acdc06596d..846b6bdc93377 100644 --- a/app/code/Magento/Quote/Api/Data/Reorder/ReorderOutput.php +++ b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Quote\Api\Data\Reorder; +namespace Magento\Sales\Model\Reorder\Data; use Magento\Quote\Api\Data\CartInterface; /** - * DTO represent output for \Magento\Quote\Api\ReorderInterface + * DTO represent output for \Magento\Sales\Model\Reorder\Reorder */ class ReorderOutput { @@ -20,16 +20,16 @@ class ReorderOutput /** * @var array */ - private $lineItemErrors; + private $errors; /** * @param CartInterface $cart - * @param array $lineItemErrors + * @param Error[] $errors */ - public function __construct(CartInterface $cart, array $lineItemErrors) + public function __construct(CartInterface $cart, array $errors) { $this->cart = $cart; - $this->lineItemErrors = $lineItemErrors; + $this->errors = $errors; } /** @@ -41,10 +41,10 @@ public function getCart(): CartInterface } /** - * @return LineItemError[] + * @return Error[] */ - public function getLineItemErrors(): array + public function getErrors(): array { - return $this->lineItemErrors; + return $this->errors; } } diff --git a/app/code/Magento/Quote/Model/Quote/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php similarity index 56% rename from app/code/Magento/Quote/Model/Quote/Reorder.php rename to app/code/Magento/Sales/Model/Reorder/Reorder.php index b2918f441508c..f2f819ad237a8 100644 --- a/app/code/Magento/Quote/Model/Quote/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -3,24 +3,35 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Quote\Model\Quote; +namespace Magento\Sales\Model\Reorder; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\ReorderInterface; -use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; -use Magento\Sales\Model\OrderFactory; use Magento\Sales\Helper\Reorder as ReorderHelper; +use Magento\Sales\Model\Order\Item; +use Magento\Sales\Model\OrderFactory; /** - * @inheritdoc + * Allows customer to quickly reorder previously added products and put them to the Cart */ -class Reorder implements ReorderInterface +class Reorder { + + /**#@+ + * Error message codes + */ + private const ERROR_PRODUCT_NOT_FOUND = 'PRODUCT_NOT_FOUND'; + private const ERROR_INSUFFICIENT_STOCK = 'INSUFFICIENT_STOCK'; + private const ERROR_NOT_SALABLE = 'NOT_SALABLE'; + private const ERROR_REORDER_NOT_AVAILABLE = 'REORDER_NOT_AVAILABLE'; + private const ERROR_UNDEFINED = 'UNDEFINED'; + /**#@-*/ + /** * @var OrderFactory */ @@ -56,6 +67,11 @@ class Reorder implements ReorderInterface */ private $productRepository; + /** + * @var Data\Error[] + */ + private $errors = []; + /** * @param OrderFactory $orderFactory * @param CartManagementInterface $cartManagement @@ -84,24 +100,25 @@ public function __construct( } /** - * @inheritdoc - * @throws InputException - * @throws NoSuchEntityException + * Allows customer to quickly reorder previously added products and put them to the Cart + * + * @param string $orderNumber + * @param string $storeId + * @return Data\ReorderOutput + * @throws InputException Order is not found + * @throws NoSuchEntityException The specified customer does not exist. */ - public function execute(string $incrementOrderId, string $storeId): \Magento\Quote\Api\Data\Reorder\ReorderOutput + public function execute(string $orderNumber, string $storeId): Data\ReorderOutput { - $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($incrementOrderId, $storeId); + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); if (!$order->getId()) { - throw new NoSuchEntityException( - __('Cannot find order with number "%1" in store "%2"', $incrementOrderId, $storeId) + throw new InputException( + __('Cannot find order with number "%1" in store "%2"', $orderNumber, $storeId) ); } - if (!$this->reorderHelper->canReorder($order->getId())) { - throw new InputException(__('Reorder is not available.')); - } - $customerId = $order->getCustomerId(); + $this->errors = []; try { /** @var \Magento\Quote\Model\Quote $cart */ @@ -110,41 +127,46 @@ public function execute(string $incrementOrderId, string $storeId): \Magento\Quo $this->createEmptyCartForCustomer->execute($customerId); $cart = $this->cartManagement->getCartForCustomer($customerId); } + if (!$this->reorderHelper->canReorder($order->getId())) { + $this->addError(__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); + return $this->prepareOutput($cart); + } - $lineItemsErrors = []; $items = $order->getItemsCollection(); foreach ($items as $item) { try { $this->addOrderItem($cart, $item); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->addLineItemError($lineItemsErrors, $item, $e->getMessage()); + $this->addError($e->getMessage()); } catch (\Throwable $e) { $this->logger->critical($e); - $this->addLineItemError( - $lineItemsErrors, - $item, - __('We can\'t add this item to your shopping cart right now.') + $this->addError( + __('We can\'t add this item to your shopping cart right now.'), + self::ERROR_UNDEFINED ); } } - $this->cartRepository->save($cart); + try { + $this->cartRepository->save($cart); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->addError($e->getMessage()); + } - return new \Magento\Quote\Api\Data\Reorder\ReorderOutput($cart, $lineItemsErrors); + return $this->prepareOutput($cart); } - /** * Convert order item to quote item * * @param \Magento\Quote\Model\Quote $cart - * @param \Magento\Sales\Model\Order\Item $orderItem + * @param Item $orderItem * @return void * @throws \Magento\Framework\Exception\LocalizedException */ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): void { - /* @var $orderItem \Magento\Sales\Model\Order\Item */ + /* @var $orderItem Item */ if ($orderItem->getParentItem() === null) { $info = $orderItem->getProductOptionByCode('info_buyRequest'); $info = new \Magento\Framework\DataObject($info); @@ -153,7 +175,11 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi try { $product = $this->productRepository->getById($orderItem->getProductId(), false, null, true); } catch (NoSuchEntityException $e) { - throw new LocalizedException(__('Could not find a product with ID "%1"', $orderItem->getProductId())); + $this->addError( + __('Could not find a product with ID "%1"', $orderItem->getProductId()), + self::ERROR_PRODUCT_NOT_FOUND + ); + return; } $cart->addProduct($product, $info); } @@ -162,16 +188,51 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi /** * Add order line item error * - * @param array $errors - * @param \Magento\Sales\Model\Order\Item $item * @param string $message + * @param string|null $code * @return void */ - private function addLineItemError(&$errors, \Magento\Sales\Model\Order\Item $item, $message): void + private function addError($message, string $code = null): void { - $errors[] = new \Magento\Quote\Api\Data\Reorder\LineItemError( - $item->getProduct() ? $item->getProduct()->getSku() : $item->getSku() ?? '', - $message + $this->errors[] = new Data\Error( + $message, + $code ?? $this->getErrorCode((string)$message) ); } + + /** + * Get message error code. Ad-hoc solution based on message parsing. + * + * @param string $message + * @return string + */ + private function getErrorCode(string $message): string + { + $code = self::ERROR_UNDEFINED; + switch ($message) { + case false !== strpos($message, 'Product that you are trying to add is not available.'): + $code = self::ERROR_NOT_SALABLE; + break; + case false !== strpos($message, 'The fewest you may purchase is'): + case false !== strpos($message, 'The most you may purchase is'): + case false !== strpos($message, 'The requested qty is not available'): + $code = self::ERROR_INSUFFICIENT_STOCK; + break; + } + + return $code; + } + + /** + * Prepare output + * + * @param CartInterface $cart + * @return Data\ReorderOutput + */ + protected function prepareOutput(CartInterface $cart): Data\ReorderOutput + { + $output = new Data\ReorderOutput($cart, $this->errors); + $this->errors = []; + return $output; + } } diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index ca39ad1ae2a66..9d21f6d5c1b69 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -12,8 +12,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\GraphQl\Model\Query\ContextInterface; -use Magento\Quote\Api\Data\Reorder\LineItemError; -use Magento\Quote\Api\ReorderInterface; +use Magento\Sales\Model\Reorder\Data\Error; use Magento\Sales\Model\OrderFactory; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -22,22 +21,27 @@ */ class Reorder implements ResolverInterface { + /** + * Order number + */ + private const ARGUMENT_ORDER_NUMBER = 'orderNumber'; + /** * @var OrderFactory */ private $orderFactory; /** - * @var ReorderInterface + * @var \Magento\Sales\Model\Reorder\Reorder */ private $reorder; /** - * @param ReorderInterface $reorder + * @param \Magento\Sales\Model\Reorder\Reorder $reorder * @param OrderFactory $orderFactory */ public function __construct( - ReorderInterface $reorder, + \Magento\Sales\Model\Reorder\Reorder $reorder, OrderFactory $orderFactory ) { $this->orderFactory = $orderFactory; @@ -76,14 +80,15 @@ public function resolve( 'cart' => [ 'model' => $reorderOutput->getCart(), ], - 'errors' => \array_map( - function(LineItemError $error) { + 'userInputErrors' => \array_map( + function(Error $error) { return [ - 'sku' => $error->getSku(), + 'path' => [self::ARGUMENT_ORDER_NUMBER], + 'code' => $error->getCode(), 'message' => $error->getMessage(), ]; }, - $reorderOutput->getLineItemErrors() + $reorderOutput->getErrors() ) ]; } diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index f025085395efb..b7bf14e2b9d06 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -19,15 +19,24 @@ type CustomerOrders { } type Mutation { - addAllOrderItemsToCart(orderNumber: String!): ReOrderOutput @doc(description:"Add all the products from Customer order to the Cart") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") + reorderItems(orderNumber: String!): ReorderItemsOutput @doc(description:"Add all the products from Customer order to the Cart") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") } -type ReOrderOutput @doc(description: "ReOrder output") { +type ReorderItemsOutput { cart: Cart! - errors:[ReOrderError] + userInputErrors:[CheckoutUserInputError]! } -type ReOrderError @doc(description: "Cart line item error") { - sku: String! - message: String! +type CheckoutUserInputError { + message: String! @doc(description: "Localized error message") + path: [String]! @doc(description: "Path to the input field which caused an error. Similar to how GraphQL specification defines path for errors in query: http://spec.graphql.org/draft/#sec-Errors") + code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") } + +enum CheckoutUserInputErrorCodes { + REORDER_NOT_AVAILABLE + PRODUCT_NOT_FOUND + NOT_SALABLE + INSUFFICIENT_STOCK + UNDEFINED +} \ No newline at end of file diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 22e48f2a588af..e05b05a8e6b2e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -68,7 +68,7 @@ public function testReorderMutation() { $response = $this->makeReorderForDefaultCustomer(); $this->assertResponseFields( - $response['addAllOrderItemsToCart'] ?? [], + $response['reorderItems'] ?? [], [ 'cart' => [ 'email' => self::CUSTOMER_EMAIL, @@ -82,7 +82,7 @@ public function testReorderMutation() ] ], ], - 'errors' => [] + 'userInputErrors' => [] ] ); } @@ -127,9 +127,10 @@ private function assertProductNotAvailable() { $response = $this->makeReorderForDefaultCustomer(); $expectedResponse = [ - 'errors' => [ + 'userInputErrors' => [ [ - 'sku' => 'simple', + 'path' => ['orderNumber'], + 'code' => 'NOT_SALABLE', 'message' => 'Product that you are trying to add is not available.', ], ], @@ -139,7 +140,7 @@ private function assertProductNotAvailable() 'items' => [], ], ]; - $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); } /** @@ -196,9 +197,10 @@ private function assertWithDeletedProduct( $registry->register('isSecureArea', false); $expectedResponse = [ - 'errors' => [ + 'userInputErrors' => [ [ - 'sku' => 'simple', + 'path' => ['orderNumber'], + 'code' => 'PRODUCT_NOT_FOUND', 'message' => 'Could not find a product with ID "' . $productId . '"', ], ], @@ -209,7 +211,7 @@ private function assertWithDeletedProduct( ], ]; $response = $this->makeReorderForDefaultCustomer(); - $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); } /** @@ -220,9 +222,10 @@ public function notFinishedTestTwoProducts() $response = $this->makeReorderForDefaultCustomer(self::INCREMENTED_ORDER_NUMBER); $expectedResponse = [ - 'errors' => [ + 'userInputErrors' => [ [ - 'sku' => 'simple-1', + 'path' => ['orderNumber'], + 'code' => 'UNDEFINED', 'message' => 'We can\'t add this item to your shopping cart right now.', ], ], @@ -239,7 +242,7 @@ public function notFinishedTestTwoProducts() ], ], ]; - $this->assertResponseFields($response['addAllOrderItemsToCart'] ?? [], $expectedResponse); + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); } /** @@ -282,9 +285,10 @@ protected function getQuery($orderNumber): string $query = <<<MUTATION mutation { - addAllOrderItemsToCart(orderNumber: "{$orderNumber}") { - errors { - sku + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code message } cart { From 509629bb64676841873ebd72dc2ea7d0dcbee9ec Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Mon, 16 Mar 2020 14:28:37 -0500 Subject: [PATCH 409/734] MC-32378: Implement control over minimum_should_match for elasticsearch queries --- .../FieldMapper/AddDefaultSearchField.php | 2 +- .../CopySearchableFieldsToSearchField.php | 2 +- .../FieldMapper/AddDefaultSearchFieldTest.php | 4 +-- .../CopySearchableFieldsToSearchFieldTest.php | 4 +-- app/code/Magento/Elasticsearch/i18n/en_US.csv | 1 + .../Unit/Model/Client/ElasticsearchTest.php | 2 +- app/code/Magento/Elasticsearch6/etc/di.xml | 4 +-- .../Model/Client/Elasticsearch.php | 33 +++++++++++++++---- .../Unit/Model/Client/ElasticsearchTest.php | 4 ++- .../Elasticsearch7/etc/adminhtml/system.xml | 8 +++++ .../Magento/Elasticsearch7/etc/config.xml | 1 + app/code/Magento/Elasticsearch7/etc/di.xml | 8 +++++ 12 files changed, 56 insertions(+), 17 deletions(-) rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Model/Adapter/FieldMapper/AddDefaultSearchField.php (92%) rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php (95%) rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php (93%) rename app/code/Magento/{Elasticsearch6 => Elasticsearch}/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php (95%) diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php similarity index 92% rename from app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php rename to app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php index 27767f6567d96..b503dbbc91678 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php similarity index 95% rename from app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php rename to app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php index 6179eacba5ad7..248adc9ed4e1f 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php similarity index 93% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php index 1a1e7f4e0d643..a0e2837ba8496 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\TestCase; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php similarity index 95% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php index cfe8b71095d21..33a772950a111 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\TestCase; diff --git a/app/code/Magento/Elasticsearch/i18n/en_US.csv b/app/code/Magento/Elasticsearch/i18n/en_US.csv index 85c9aefdb9f25..1c49a7f97c5b5 100644 --- a/app/code/Magento/Elasticsearch/i18n/en_US.csv +++ b/app/code/Magento/Elasticsearch/i18n/en_US.csv @@ -10,3 +10,4 @@ "Elasticsearch HTTP Password","Elasticsearch HTTP Password" "Elasticsearch Server Timeout","Elasticsearch Server Timeout" "Test Connection","Test Connection" +"Minimum terms to match","Minimum terms to match" diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index e3bcc3d219538..446a3471976a0 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -7,7 +7,7 @@ namespace Magento\Elasticsearch6\Test\Unit\Model\Client; use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch6\Model\Client\Elasticsearch; diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 641fbd069b627..e0974dda1d998 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -97,8 +97,8 @@ <type name="Magento\Elasticsearch6\Model\Client\Elasticsearch"> <arguments> <argument name="fieldsMappingPreprocessors" xsi:type="array"> - <item name="elasticsearch6_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> - <item name="elasticsearch6_add_default_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField</item> + <item name="elasticsearch6_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> + <item name="elasticsearch6_add_default_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php index c6c85b0edc338..feacca8d62804 100644 --- a/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php @@ -7,6 +7,7 @@ namespace Magento\Elasticsearch7\Model\Client; +use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; use Magento\Framework\Exception\LocalizedException; use Magento\AdvancedSearch\Model\Client\ClientInterface; @@ -32,16 +33,23 @@ class Elasticsearch implements ClientInterface */ private $pingResult; + /** + * @var FieldsMappingPreprocessorInterface[] + */ + private $fieldsMappingPreprocessors; + /** * Initialize Elasticsearch 7 Client * * @param array $options * @param \Elasticsearch\Client|null $elasticsearchClient + * @param array $fieldsMappingPreprocessors * @throws LocalizedException */ public function __construct( $options = [], - $elasticsearchClient = null + $elasticsearchClient = null, + $fieldsMappingPreprocessors = [] ) { if (empty($options['hostname']) || ((!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { @@ -54,6 +62,7 @@ public function __construct( $this->client[getmypid()] = $elasticsearchClient; } $this->clientOptions = $options; + $this->fieldsMappingPreprocessors = $fieldsMappingPreprocessors; } /** @@ -273,11 +282,7 @@ public function addFieldsMapping(array $fields, string $index, string $entityTyp 'include_type_name' => true, 'body' => [ $entityType => [ - 'properties' => [ - '_search' => [ - 'type' => 'text' - ], - ], + 'properties' => [], 'dynamic_templates' => [ [ 'price_mapping' => [ @@ -315,7 +320,7 @@ public function addFieldsMapping(array $fields, string $index, string $entityTyp ], ]; - foreach ($fields as $field => $fieldInfo) { + foreach ($this->applyFieldsMappingPreprocessors($fields) as $field => $fieldInfo) { $params['body'][$entityType]['properties'][$field] = $fieldInfo; } @@ -349,4 +354,18 @@ public function deleteMapping(string $index, string $entityType) ] ); } + + /** + * Apply fields mapping preprocessors + * + * @param array $properties + * @return array + */ + private function applyFieldsMappingPreprocessors(array $properties): array + { + foreach ($this->fieldsMappingPreprocessors as $preprocessor) { + $properties = $preprocessor->process($properties); + } + return $properties; + } } diff --git a/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php index cb07cfb7bf83d..f4be22150bba7 100644 --- a/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php @@ -11,6 +11,7 @@ use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch7\Model\Client\Elasticsearch; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; /** * Class ElasticsearchTest to test Elasticsearch 7 @@ -90,7 +91,8 @@ protected function setUp() Elasticsearch::class, [ 'options' => $this->getOptions(), - 'elasticsearchClient' => $this->elasticsearchClientMock + 'elasticsearchClient' => $this->elasticsearchClientMock, + 'fieldsMappingPreprocessors' => [new AddDefaultSearchField()] ] ); } diff --git a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml index cb7cdc2a5b531..1703f1af47ee5 100644 --- a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml @@ -79,6 +79,14 @@ <field id="engine">elasticsearch7</field> </depends> </field> + <field id="elasticsearch7_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> + <label>Minimum Terms to Match</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> + </field> </group> </section> </system> diff --git a/app/code/Magento/Elasticsearch7/etc/config.xml b/app/code/Magento/Elasticsearch7/etc/config.xml index 63d832c8445f5..c13586bb9e357 100644 --- a/app/code/Magento/Elasticsearch7/etc/config.xml +++ b/app/code/Magento/Elasticsearch7/etc/config.xml @@ -14,6 +14,7 @@ <elasticsearch7_index_prefix>magento2</elasticsearch7_index_prefix> <elasticsearch7_enable_auth>0</elasticsearch7_enable_auth> <elasticsearch7_server_timeout>15</elasticsearch7_server_timeout> + <elasticsearch7_minimum_should_match></elasticsearch7_minimum_should_match> </search> </catalog> </default> diff --git a/app/code/Magento/Elasticsearch7/etc/di.xml b/app/code/Magento/Elasticsearch7/etc/di.xml index 4f8129f8209f4..a54b92a8b6f66 100644 --- a/app/code/Magento/Elasticsearch7/etc/di.xml +++ b/app/code/Magento/Elasticsearch7/etc/di.xml @@ -213,4 +213,12 @@ </argument> </arguments> </type> + <type name="Magento\Elasticsearch7\Model\Client\Elasticsearch"> + <arguments> + <argument name="fieldsMappingPreprocessors" xsi:type="array"> + <item name="elasticsearch7_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> + <item name="elasticsearch7_add_default_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField</item> + </argument> + </arguments> + </type> </config> From df396bdad54b7b34bb3ece1cca02ac2d50fb0d6b Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Mon, 16 Mar 2020 21:01:27 -0500 Subject: [PATCH 410/734] MC-32201: Reorder functionality - fix messages --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 +- app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php | 2 +- app/code/Magento/SalesGraphQl/etc/schema.graphqls | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index f2f819ad237a8..918f72908879d 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -114,7 +114,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu if (!$order->getId()) { throw new InputException( - __('Cannot find order with number "%1" in store "%2"', $orderNumber, $storeId) + __('Cannot find order number "%1" in store "%2"', $orderNumber, $storeId) ); } $customerId = $order->getCustomerId(); diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index 9d21f6d5c1b69..d6bdabea4998c 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -70,7 +70,7 @@ public function resolve( $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); if ((int)$order->getCustomerId() !== $currentUserId) { throw new GraphQlInputException( - __('Order with number "%1" doesn\'t belong current customer', $orderNumber) + __('Order number "%1" doesn\'t belong to the current customer', $orderNumber) ); } diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index b7bf14e2b9d06..e47c01f6948be 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -19,15 +19,15 @@ type CustomerOrders { } type Mutation { - reorderItems(orderNumber: String!): ReorderItemsOutput @doc(description:"Add all the products from Customer order to the Cart") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") + reorderItems(orderNumber: String!): ReorderItemsOutput @doc(description:"Adds all products from a customer's previous order to the cart.") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") } type ReorderItemsOutput { - cart: Cart! - userInputErrors:[CheckoutUserInputError]! + cart: Cart! @doc(description:"Contains detailed information about the customer's cart.") + userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of reordering errors.") } -type CheckoutUserInputError { +type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart."){ message: String! @doc(description: "Localized error message") path: [String]! @doc(description: "Path to the input field which caused an error. Similar to how GraphQL specification defines path for errors in query: http://spec.graphql.org/draft/#sec-Errors") code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") From 6428b8c0f56dda13313171f1b39b2c4dbf36d0cc Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Tue, 17 Mar 2020 12:49:27 +0200 Subject: [PATCH 411/734] Fix static, test coverage --- .../Setup/Patch/Data/AddDataForItaly.php | 4 +- .../Magento/Directory/Model/RegionTest.php | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php index 44288c9f2a276..a5d4bcdc0b8be 100644 --- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Directory\Setup\Patch\Data; @@ -51,12 +50,15 @@ public function apply() $this->moduleDataSetup->getConnection(), $this->getDataForItaly() ); + + return $this; } /** * Italy states data. * * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function getDataForItaly() { diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php new file mode 100644 index 0000000000000..0b47f7b0719c6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Model\Country\Postcode\Config; + +use Magento\Directory\Model\Country; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class RegionTest extends TestCase +{ + /** + * @var Country + */ + protected $country; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->country = Bootstrap::getObjectManager()->create(Country::class); + } + + /** + * Verify country has regions. + * + * @var string $countryId + * @dataProvider getCountryIdDataProvider + */ + public function testCountryHasRegions($countryId) + { + $country = $this->country->loadByCode($countryId); + $region = $country->getRegions()->getItems(); + + $this->assertTrue(!empty($region), 'Country ' . $countryId . ' not have regions'); + } + + /** + * Data provider for testCountryHasRegions + * + * @return array + */ + public function getCountryIdDataProvider():array + { + return [ + ['countryId' => 'US'], + ['countryId' => 'CA'], + ['countryId' => 'CN'], + ['countryId' => 'IN'], + ['countryId' => 'AU'], + ['countryId' => 'BE'], + ['countryId' => 'CO'], + ['countryId' => 'MX'], + ['countryId' => 'PL'], + ['countryId' => 'IT'] + ]; + } +} From e918d3b7cc37132b2f020fc7643605a831391eae Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Tue, 17 Mar 2020 19:53:02 +0700 Subject: [PATCH 412/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Fix static code --- app/code/Magento/Downloadable/Model/Link/Builder.php | 4 +++- app/code/Magento/Downloadable/Model/Sample/Builder.php | 10 ++++++++++ .../Downloadable/Test/Unit/Model/Link/BuilderTest.php | 2 ++ .../Test/Unit/Model/Sample/BuilderTest.php | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index ad247127e1701..8bfeefe071d96 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -13,6 +13,8 @@ /** * Class Builder + * + * @package Magento\Downloadable\Model\Link * @api * @since 100.1.0 */ @@ -134,7 +136,7 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) } $useDefaultTitle = $this->data['use_default_title'] ?? false; - + if ($useDefaultTitle) { $link->setTitle(null); } diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index 28ecf79b8a151..e186b29c7729f 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -15,6 +15,8 @@ /** * Class Builder + * + * @package Magento\Downloadable\Model\Sample * @api * @since 100.1.0 */ @@ -71,6 +73,8 @@ public function __construct( } /** + * Init data for builder + * * @param array $data * @return $this; * @since 100.1.0 @@ -82,6 +86,8 @@ public function setData(array $data) } /** + * Build sample link + * * @param SampleInterface $sample * @return SampleInterface * @throws \Magento\Framework\Exception\LocalizedException @@ -122,6 +128,8 @@ public function build(SampleInterface $sample) } /** + * Reset data + * * @return void */ private function resetData() @@ -130,6 +138,8 @@ private function resetData() } /** + * Get component + * * @return Sample */ private function getComponent() diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 60a2a048c549c..6ad46d69e13d7 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -12,6 +12,8 @@ /** * Class BuilderTest + * + * @package Magento\Downloadable\Test\Unit\Model\Link */ class BuilderTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 08f260282756b..0a4badb2f51c7 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -12,6 +12,8 @@ /** * Class BuilderTest + * + * @package Magento\Downloadable\Test\Unit\Model\Sample */ class BuilderTest extends \PHPUnit\Framework\TestCase { From f3a84a94e8c402df05217706e75d7d646287f1b7 Mon Sep 17 00:00:00 2001 From: Andrii Kalinich <51681435+engcom-Echo@users.noreply.github.com> Date: Tue, 17 Mar 2020 15:20:15 +0200 Subject: [PATCH 413/734] Update RegionTest.php --- .../testsuite/Magento/Directory/Model/RegionTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php index 0b47f7b0719c6..e0e790b265572 100644 --- a/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php +++ b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php @@ -5,9 +5,8 @@ */ declare(strict_types=1); -namespace Magento\Directory\Model\Country\Postcode\Config; +namespace Magento\Directory\Model; -use Magento\Directory\Model\Country; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; From 8a92d8025f801a4bc85925120253a5d970288bb1 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Tue, 17 Mar 2020 09:43:14 -0500 Subject: [PATCH 414/734] MC-31586: Customer address is duplicated after setBillingAddressOnCart GraphQL mutation. - Added fix and api functional test --- .../Model/Cart/SetBillingAddressOnCart.php | 4 +- .../Customer/SetBillingAddressOnCartTest.php | 167 ++++++++++++++++++ 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index b7cddab9273ea..77eb622b70c2c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -118,9 +118,9 @@ private function createBillingAddress( $customerId = $context->getUserId(); // need to save address only for registered user and if save_in_address_book = true - if ((0 !== $customerId + if (0 !== $customerId && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] && $sameAsShipping !== true) === true + && (bool)$addressInput['save_in_address_book'] && !$sameAsShipping ) { $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 05323a5a7ddf4..a36548fa222b0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -871,6 +871,89 @@ public function testSetNewBillingAddressWithSaveInAddressBook() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetBillingAddressAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + same_as_shipping: true + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: true + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(1, $addresses); + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -1111,4 +1194,88 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @return string + */ + private function getSetShippingMethodsQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode + ): string { + return <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [{ + carrier_code: "$shippingCarrierCode" + method_code: "$shippingMethodCode" + }] + }) { + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + carrier_title + method_title + amount { + value + currency + } + } + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function getSetPaymentMethodQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getPlaceOrderQuery(string $maskedQuoteId): string + { + return <<<QUERY +mutation { + placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { + order { + order_number + } + } +} +QUERY; + } } From 9692fbc5c98ce31af9eb859b971d7f1dbb472c8b Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Tue, 17 Mar 2020 10:11:51 -0500 Subject: [PATCH 415/734] MC-32120: Catalog price rules are not included in CartItemPrices - Replaced hard dependency with soft. Static fix --- app/code/Magento/CatalogRuleGraphQl/composer.json | 4 +++- app/code/Magento/CatalogRuleGraphQl/etc/module.xml | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogRuleGraphQl/composer.json b/app/code/Magento/CatalogRuleGraphQl/composer.json index 2d09ec010aec3..f2aae6c79d16b 100644 --- a/app/code/Magento/CatalogRuleGraphQl/composer.json +++ b/app/code/Magento/CatalogRuleGraphQl/composer.json @@ -4,7 +4,9 @@ "type": "magento2-module", "require": { "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", + "magento/framework": "*" + }, + "suggest": { "magento/module-catalog-rule": "*" }, "license": [ diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/module.xml b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml index 8b4cad2c9f30f..affc22f35a738 100644 --- a/app/code/Magento/CatalogRuleGraphQl/etc/module.xml +++ b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml @@ -6,9 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_CatalogRuleGraphQl"> - <sequence> - <module name="Magento_CatalogRule"/> - </sequence> - </module> + <module name="Magento_CatalogRuleGraphQl" /> </config> From d421504658fd4cbb4e1919a92c77e5741cae8a50 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Tue, 17 Mar 2020 23:00:30 +0700 Subject: [PATCH 416/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Fix static code --- app/code/Magento/Downloadable/Model/Link/Builder.php | 1 + app/code/Magento/Downloadable/Model/Sample/Builder.php | 1 + .../Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php | 1 + .../Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php | 1 + 4 files changed, 4 insertions(+) diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index 8bfeefe071d96..4fd323bad3708 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Model\Link; use Magento\Downloadable\Helper\File; diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index e186b29c7729f..85f215e7b9b94 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Model\Sample; use Magento\Downloadable\Api\Data\SampleInterface; diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 6ad46d69e13d7..9b78f11e01a9b 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Test\Unit\Model\Link; use Magento\Downloadable\Api\Data\LinkInterface; diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 0a4badb2f51c7..797c548183cde 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Test\Unit\Model\Sample; use Magento\Downloadable\Api\Data\SampleInterface; From 5d73d1d69b51e3c556892fa654b6609255b793c2 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 17 Mar 2020 11:47:43 -0500 Subject: [PATCH 417/734] MC-32278: Position sort does not work in GraphQl. - fix static --- .../Model/Resolver/Products/DataProvider/ProductSearch.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 55f81b8067c8b..4c83afb89cc46 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -18,7 +18,6 @@ use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchResultsInterface; -use Magento\Framework\App\ObjectManager; /** * Product field data provider for product search, used for GraphQL resolver processing. @@ -69,7 +68,7 @@ public function __construct( CollectionProcessorInterface $collectionPreProcessor, CollectionPostProcessor $collectionPostProcessor, SearchResultApplierFactory $searchResultsApplierFactory, - ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder = null + ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder ) { $this->collectionFactory = $collectionFactory; $this->searchResultsFactory = $searchResultsFactory; @@ -77,8 +76,6 @@ public function __construct( $this->collectionPostProcessor = $collectionPostProcessor; $this->searchResultApplierFactory = $searchResultsApplierFactory; $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->searchCriteriaBuilder = $searchCriteriaBuilder - ?: ObjectManager::getInstance()->get(ProductCollectionSearchCriteriaBuilder::class); } /** From 0f02cca3bfabc2cac805d71e144ce26c48b0e797 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Tue, 17 Mar 2020 12:02:02 -0500 Subject: [PATCH 418/734] MC-32201: Reorder functionality (test coverage) --- .../Magento/Sales/Model/Reorder/Reorder.php | 31 ++- .../Sales/ReorderOverlayProductTest.php | 209 ++++++++++++++++++ .../Magento/GraphQl/Sales/ReorderTest.php | 31 --- .../product_simple_without_custom_options.php | 66 ++++++ ...simple_without_custom_options_rollback.php | 25 +++ .../_files/order_with_simple_product.php | 95 ++++++++ .../order_with_simple_product_rollback.php | 95 ++++++++ .../_files/order_with_two_simple_products.php | 124 ++++------- ...rder_with_two_simple_products_rollback.php | 4 +- 9 files changed, 555 insertions(+), 125 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index f2f819ad237a8..5463a84a4b430 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -32,6 +32,17 @@ class Reorder private const ERROR_UNDEFINED = 'UNDEFINED'; /**#@-*/ + /** + * List of error messages and codes. + */ + private const MESSAGE_CODES = [ + 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, + ]; + + /** * @var OrderFactory */ @@ -209,15 +220,17 @@ private function addError($message, string $code = null): void private function getErrorCode(string $message): string { $code = self::ERROR_UNDEFINED; - switch ($message) { - case false !== strpos($message, 'Product that you are trying to add is not available.'): - $code = self::ERROR_NOT_SALABLE; - break; - case false !== strpos($message, 'The fewest you may purchase is'): - case false !== strpos($message, 'The most you may purchase is'): - case false !== strpos($message, 'The requested qty is not available'): - $code = self::ERROR_INSUFFICIENT_STOCK; - break; + + $matchedCodes = array_filter( + self::MESSAGE_CODES, + function ($key) use ($message) { + return false !== strpos($message, $key); + }, + ARRAY_FILTER_USE_KEY + ); + + if (!empty($matchedCodes)) { + $code = current($matchedCodes); } return $code; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php new file mode 100644 index 0000000000000..05b5e5b74b149 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php @@ -0,0 +1,209 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder with and without products overlay in shopping cart. + */ +class ReorderOverlayProductTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_without_custom_options.php + * @throws NoSuchEntityException + */ + public function testWithoutOverlay() + { + $response = $this->addProductToCartAndReorder(); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_simple_products.php + * @throws NoSuchEntityException + */ + public function testWithOverlay() + { + $response = $this->addProductToCartAndReorder(); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 3, + 'items' => [ + [ + 'quantity' => 2, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @return array|bool|float|int|string + * @throws NoSuchEntityException + * @throws \Exception + */ + private function addProductToCartAndReorder() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('simple-2'); + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + + /** @var \Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer $emptyCartForCustomer */ + $emptyCartForCustomer = Bootstrap::getObjectManager() + ->create(\Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer::class); + $emptyCartForCustomer->execute(self::CUSTOMER_ID); + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $quote->addProduct($product, 1); + $cartRepository->save($quote); + + return $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + message + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index e05b05a8e6b2e..9452c4281f758 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -214,37 +214,6 @@ private function assertWithDeletedProduct( $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); } - /** - * @magentoApiDataFixture Magento/Sales/_files/order_with_two_simple_products.php - */ - public function notFinishedTestTwoProducts() - { - $response = $this->makeReorderForDefaultCustomer(self::INCREMENTED_ORDER_NUMBER); - - $expectedResponse = [ - 'userInputErrors' => [ - [ - 'path' => ['orderNumber'], - 'code' => 'UNDEFINED', - 'message' => 'We can\'t add this item to your shopping cart right now.', - ], - ], - 'cart' => [ - 'email' => 'customer@example.com', - 'total_quantity' => 1, - 'items' => [ - [ - 'quantity' => 1, - 'product' => [ - 'sku' => 'configurable', - ], - ], - ], - ], - ]; - $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); - } - /** * @param string $email * @param string $password diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php new file mode 100644 index 0000000000000..7296910614e33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +// simple product without custom options +$secondProductSku = 'simple-2'; + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); + +$adminWebsite = $objectManager->get(\Magento\Store\Api\WebsiteRepositoryInterface::class)->get('admin'); +/** @var $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$tierPriceExtensionAttributes1 = $tpExtensionAttributesFactory->create() + ->setWebsiteId($adminWebsite->getId()); +$productExtensionAttributesWebsiteIds = $productExtensionAttributesFactory->create( + ['website_ids' => $adminWebsite->getId()] +); +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); +/** @var $secondProduct \Magento\Catalog\Model\Product */ +$secondProduct = $objectManager->create(\Magento\Catalog\Model\Product::class); +$secondProduct->isObjectNew(true); +$secondProduct->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(22) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku($secondProductSku) + ->setPrice(11) + ->setWeight(1) + ->setShortDescription("Short description 2") + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setExtensionAttributes($productExtensionAttributesWebsiteIds) + ->setMetaTitle('meta title 2') + ->setMetaKeyword('meta keyword 2') + ->setMetaDescription('meta description 2') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + )->setCanSaveCustomOptions(false) + ->setHasOptions(false); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productRepository->save($secondProduct); + +$categoryLinkManagement->assignProductToCategories( + $secondProduct->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php new file mode 100644 index 0000000000000..e51f78e1703dc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple-2', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php new file mode 100644 index 0000000000000..34224849f528c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php @@ -0,0 +1,95 @@ +<?php + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100001001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php new file mode 100644 index 0000000000000..34224849f528c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php @@ -0,0 +1,95 @@ +<?php + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100001001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php index 6edc0ce6ad3a2..e9b0f9b3a656d 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php @@ -4,6 +4,7 @@ require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; /** \Magento\Customer\Model\Customer $customer */ $addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; @@ -16,13 +17,30 @@ $payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); $payment->setMethod('checkmo'); $customerIdFromFixture = 1; -$requestInfo = [ - 'qty' => 1, + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', ]; +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + /** @var \Magento\Sales\Model\Order\Item $orderItem */ $orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); -$orderItemSimple = clone $orderItem; $orderItem->setProductId($product->getId()); $orderItem->setQtyOrdered(1); $orderItem->setBasePrice($product->getPrice()); @@ -33,17 +51,28 @@ $orderItem->setName($product->getName()); $orderItem->setSku($product->getSku()); $orderItem->setStoreId(0); -//$orderItemSimple->setProductId($simpleProduct->getId()); -//$orderItemSimple->setParentItem($orderItem); -//$orderItemSimple->setStoreId(0); -//$orderItemSimple->setProductType($simpleProduct->getTypeId()); -//$orderItemSimple->setProductOptions(['info_buyRequest' => $requestInfo]); -//$orderItemSimple->setSku($simpleProduct->getSku()); +// create second order item +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); /** @var \Magento\Sales\Model\Order $order */ $order = $objectManager->create(\Magento\Sales\Model\Order::class); -$order->setIncrementId('100001001'); +$order->setIncrementId('100000001'); $order->setState(\Magento\Sales\Model\Order::STATE_NEW); $order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); $order->setCustomerIsGuest(false); @@ -56,7 +85,7 @@ $order->setAddresses([$billingAddress, $shippingAddress]); $order->setPayment($payment); $order->addItem($orderItem); -//$order->addItem($orderItemSimple); +$order->addItem($orderItem2); $order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); $order->setSubtotal(100); $order->setBaseSubtotal(100); @@ -64,76 +93,3 @@ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); $orderRepository->save($order); - - - - - - - - - - - -/** @var \Magento\Catalog\Model\Product $product */ -/** @var \Magento\Sales\Model\Order $order */ -//$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - -//require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; - - - - - -/** @var $product \Magento\Catalog\Model\Product */ -//$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -//$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) -// ->setAttributeSetId(4) -// ->setWebsiteIds([1]) -// ->setName('Simple Product') -// ->setSku('simple-2') -// ->setPrice(10) -// ->setDescription('Description with <b>html tag</b>') -// ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) -// ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) -// ->setCategoryIds([2]) -// ->set -// ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) -// ->setUrlKey('simple-product-2') -// ->save(); - -// -//$orderItems[] = [ -// 'product_id' => $product->getId(), -// 'base_price' => 123, -// 'order_id' => $order->getId(), -// 'price' => 123, -// 'row_total' => 126, -// 'product_type' => 'simple' -//]; - -/** @var array $orderItemData */ -//foreach ($orderItems as $orderItemData) { -// -// $requestInfo = [ -// 'product' => $orderItemData['product_id'], -// 'qty' => 1, -// ]; -// /** @var $orderItem \Magento\Sales\Model\Order\Item */ -// $orderItem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( -// \Magento\Sales\Model\Order\Item::class -// ); -// $orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); -// $orderItem->setData($orderItemData)->save(); -// $order->addItem($orderItem); -//} - - -/** @var OrderRepositoryInterface $orderRepository */ -//$orderRepository = $objectManager->create(OrderRepositoryInterface::class); -//$orderRepository->save($order); - -//$customerIdFromFixture = 1; -///** @var $order \Magento\Sales\Model\Order */ -//$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); - diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php index 6a0de02b02be8..4a8e7f3936bfe 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php @@ -1,4 +1,6 @@ <?php -require __DIR__ . '/../../../Magento/Sales/_files/order_item_with_configurable_for_reorder_rollback.php'; + require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require 'default_rollback.php'; + From e35f496b27c42f1c1205fd200299fd78988d2954 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 17 Mar 2020 12:17:13 -0500 Subject: [PATCH 419/734] MC-32388: Add url_path as a possible filter in categoryList query --- .../Model/Category/CategoryFilter.php | 22 +++++++--------- .../CategoryFilterAttributesForAst.php | 2 +- .../Model/Resolver/CategoryList.php | 25 +++++++------------ .../GraphQl/Catalog/CategoryListTest.php | 7 +++--- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 314edf263aa36..ff9bd862267f2 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\ArgumentApplier\Filter; +use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match; use Magento\Search\Model\Query; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; @@ -58,18 +59,14 @@ public function __construct( * @param array $criteria * @param StoreInterface $store * @return int[] + * @throws InputException */ - public function getCategoryIds(array $criteria, StoreInterface $store): array + public function getResult(array $criteria, StoreInterface $store): array { $categoryIds = []; - try { - $criteria[Filter::ARGUMENT_NAME] = $this->formatMatchFilters($criteria['filters'], $store); - } catch (InputException $e) { - //Return empty set when match filter is too short. (matches search api behavior) - return $categoryIds; - } - $criteria[Filter::ARGUMENT_NAME][CategoryInterface::KEY_IS_ACTIVE] = ['eq' => 1]; + $criteria[Filter::ARGUMENT_NAME] = $this->formatMatchFilters($criteria['filters'], $store); + $criteria[Filter::ARGUMENT_NAME][CategoryInterface::KEY_IS_ACTIVE] = ['eq' => 1]; $searchCriteria = $this->searchCriteriaBuilder->build('categoryList', $criteria); $categories = $this->categoryList->getList($searchCriteria); foreach ($categories->getItems() as $category) { @@ -95,16 +92,15 @@ private function formatMatchFilters(array $filters, StoreInterface $store): arra ); foreach ($filters as $filter => $condition) { - $conditionType = array_keys($condition)[0]; + $conditionType = current(array_keys($condition)); if ($conditionType === 'match') { - $searchValue = $condition[$conditionType]; - $matchLength = strlen(trim($searchValue)); + $searchValue = trim(str_replace(Match::SPECIAL_CHARACTERS, '', $condition[$conditionType])); + $matchLength = strlen($searchValue); if ($matchLength < $minQueryLength) { - throw new InputException(__('Invalid match filter')); + throw new InputException(__('Invalid match filter. Minimum length is %1.', $minQueryLength)); } unset($filters[$filter]['match']); $filters[$filter]['like'] = '%' . $searchValue . '%'; - } } return $filters; diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php index a0c7c18474b8a..287f98e9f6848 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesInterface; /** - * Retrieves attributes for a field for the ast converter + * Retrieve filterable attributes for Category queries */ class CategoryFilterAttributesForAst implements FieldEntityAttributesInterface { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 7379b6957d699..ef2af022fd1bc 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -8,13 +8,13 @@ namespace Magento\CatalogGraphQl\Model\Resolver; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; +use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; use Magento\CatalogGraphQl\Model\Category\CategoryFilter; -use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; /** * Category List resolver, used for GraphQL category data request processing. @@ -26,11 +26,6 @@ class CategoryList implements ResolverInterface */ private $categoryTree; - /** - * @var CollectionFactory - */ - private $collectionFactory; - /** * @var CategoryFilter */ @@ -45,18 +40,15 @@ class CategoryList implements ResolverInterface * @param CategoryTree $categoryTree * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree * @param CategoryFilter $categoryFilter - * @param CollectionFactory $collectionFactory */ public function __construct( CategoryTree $categoryTree, ExtractDataFromCategoryTree $extractDataFromCategoryTree, - CategoryFilter $categoryFilter, - CollectionFactory $collectionFactory + CategoryFilter $categoryFilter ) { $this->categoryTree = $categoryTree; $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; $this->categoryFilter = $categoryFilter; - $this->collectionFactory = $collectionFactory; } /** @@ -69,11 +61,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $store = $context->getExtensionAttributes()->getStore(); - $rootCategoryIds = []; if (!isset($args['filters'])) { - $rootCategoryIds[] = (int)$store->getRootCategoryId(); - } else { - $rootCategoryIds = $this->categoryFilter->getCategoryIds($args, $store); + $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; + } + try { + $rootCategoryIds = $this->categoryFilter->getResult($args, $store); + } catch (InputException $e) { + throw new GraphQlInputException(__($e->getMessage())); } return $this->fetchCategories($rootCategoryIds, $info); @@ -85,7 +79,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value * @param array $categoryIds * @param ResolveInfo $info * @return array - * @throws GraphQlNoSuchEntityException */ private function fetchCategories(array $categoryIds, ResolveInfo $info) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 25d29ccf98e86..4831671dec450 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -360,6 +360,8 @@ public function testEmptyFiltersReturnRootCategory() * Filtering with match value less than minimum query should return empty result * * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage Invalid match filter. Minimum length is 3. */ public function testMinimumMatchQueryLength() { @@ -376,10 +378,7 @@ public function testMinimumMatchQueryLength() } } QUERY; - $result = $this->graphQlQuery($query); - $this->assertArrayNotHasKey('errors', $result); - $this->assertArrayHasKey('categoryList', $result); - $this->assertEquals([], $result['categoryList']); + $this->graphQlQuery($query); } /** From 5cd3e75a4ec878853a251f3276f148489a105a06 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Tue, 17 Mar 2020 12:14:00 -0500 Subject: [PATCH 420/734] MC-31986: Add support for ES 7 to 2.4-develop --- .../Model/Adapter/FieldType.php | 69 ------------- .../Model/Adapter/Container/Attribute.php | 99 ------------------- .../Model/Adapter/DataMapperInterface.php | 24 ----- .../Adapter/Index/IndexNameResolverTest.php | 2 +- app/code/Magento/Elasticsearch7/composer.json | 10 +- 5 files changed, 6 insertions(+), 198 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php delete mode 100644 app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php rename app/code/Magento/Elasticsearch/Test/Unit/{ => Elasticsearch5}/Model/Adapter/Index/IndexNameResolverTest.php (99%) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php deleted file mode 100644 index 6891b8c693624..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter; - -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface; - -/** - * Class FieldType - * - * @api - * @since 100.1.0 - * - * @deprecated This class provide not full data about field type. Only basic rules apply on this class. - * @see ResolverInterface - */ -class FieldType -{ - /**#@+ - * @deprecated - * @see \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface - * - * Text flags for Elasticsearch field types - */ - const ES_DATA_TYPE_TEXT = 'text'; - const ES_DATA_TYPE_KEYWORD = 'keyword'; - const ES_DATA_TYPE_FLOAT = 'float'; - const ES_DATA_TYPE_INT = 'integer'; - const ES_DATA_TYPE_DATE = 'date'; - - /** @deprecated */ - const ES_DATA_TYPE_ARRAY = 'array'; - /**#@-*/ - - /** - * Get field type. - * - * @deprecated - * @see ResolverInterface::getFieldType - * - * @param AbstractAttribute $attribute - * @return string - * @since 100.1.0 - */ - public function getFieldType($attribute) - { - trigger_error('Class is deprecated', E_USER_DEPRECATED); - $backendType = $attribute->getBackendType(); - $frontendInput = $attribute->getFrontendInput(); - - if ($backendType === 'timestamp') { - $fieldType = self::ES_DATA_TYPE_DATE; - } elseif ((in_array($backendType, ['int', 'smallint'], true) - || (in_array($frontendInput, ['select', 'boolean'], true) && $backendType !== 'varchar')) - && !$attribute->getIsUserDefined() - ) { - $fieldType = self::ES_DATA_TYPE_INT; - } elseif ($backendType === 'decimal') { - $fieldType = self::ES_DATA_TYPE_FLOAT; - } else { - $fieldType = self::ES_DATA_TYPE_TEXT; - } - - return $fieldType; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php b/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php deleted file mode 100644 index 12ca1e1a741ff..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\Container; - -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; -use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; - -/** - * @deprecated 100.2.0 - * This class is used only in deprecated \Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper - * and must not be used for new code - */ -class Attribute -{ - /** - * @var string[] - */ - private $idToCodeMap = []; - - /** - * @var Collection - */ - private $attributeCollection; - - /** - * @var EavAttribute[] - */ - private $attributes = []; - - /** - * @param Collection $attributeCollection - */ - public function __construct(Collection $attributeCollection) - { - $this->attributeCollection = $attributeCollection; - } - - /** - * @param int $attributeId - * @return string - */ - public function getAttributeCodeById($attributeId) - { - if (!array_key_exists($attributeId, $this->idToCodeMap)) { - $code = $attributeId === 'options' - ? 'options' - : $this->attributeCollection->getItemById($attributeId)->getAttributeCode(); - $this->idToCodeMap[$attributeId] = $code; - } - return $this->idToCodeMap[$attributeId]; - } - - /** - * @param string $attributeCode - * @return int - */ - public function getAttributeIdByCode($attributeCode) - { - if (!array_key_exists($attributeCode, array_flip($this->idToCodeMap))) { - $attributeId = $attributeCode === 'options' - ? 'options' - : $this->attributeCollection->getItemByColumnValue('attribute_code', $attributeCode)->getId(); - $this->idToCodeMap[$attributeId] = $attributeCode; - } - $codeToIdMap = array_flip($this->idToCodeMap); - return $codeToIdMap[$attributeCode]; - } - - /** - * @param string $attributeCode - * @return EavAttribute|null - */ - public function getAttribute($attributeCode) - { - $searchableAttributes = $this->getAttributes(); - return array_key_exists($attributeCode, $searchableAttributes) - ? $searchableAttributes[$attributeCode] - : null; - } - - /** - * @return EavAttribute[] - */ - public function getAttributes() - { - if (0 === count($this->attributes)) { - /** @var Collection $attributesCollection */ - $attributesCollection = $this->attributeCollection; - foreach ($attributesCollection as $attribute) { - /** @var EavAttribute $attribute */ - $this->attributes[$attribute->getAttributeCode()] = $attribute; - } - } - return $this->attributes; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php deleted file mode 100644 index e43a4da065314..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter; - -/** - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -interface DataMapperInterface -{ - /** - * Prepare index data for using in search engine metadata - * - * @param int $entityId - * @param array $entityIndexData - * @param int $storeId - * @param array $context - * @return array - */ - public function map($entityId, array $entityIndexData, $storeId, $context = []); -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php similarity index 99% rename from app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php index 0491517fbe979..e7e4faadd9815 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\Index; +namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Adapter\Index; use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver; use Psr\Log\LoggerInterface; diff --git a/app/code/Magento/Elasticsearch7/composer.json b/app/code/Magento/Elasticsearch7/composer.json index c6af833231be1..f5ca96990ee18 100644 --- a/app/code/Magento/Elasticsearch7/composer.json +++ b/app/code/Magento/Elasticsearch7/composer.json @@ -4,14 +4,14 @@ "require": { "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", - "magento/module-advanced-search": "*", - "magento/module-catalog-search": "*", - "magento/module-search": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~7.6" + "elasticsearch/elasticsearch": "~7.6", + "magento/module-advanced-search": "*", + "magento/module-catalog-search": "*" }, "suggest": { - "magento/module-config": "*" + "magento/module-config": "*", + "magento/module-search": "*" }, "type": "magento2-module", "license": [ From 52c56c0876b9a08b94a62c24acd93471eff94a60 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 17 Mar 2020 13:18:29 -0500 Subject: [PATCH 421/734] MC-32278: Position sort does not work in GraphQl. - fix static --- .../ProductCollectionSearchCriteriaBuilder.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php index 27ac971fc5abf..4a124d69bd20f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php @@ -8,7 +8,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch; use Magento\Catalog\Model\CategoryProductLink; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\Search\FilterGroupBuilder; use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; @@ -19,9 +18,6 @@ */ class ProductCollectionSearchCriteriaBuilder { - /** @var CollectionFactory */ - private $collectionFactory; - /** @var SearchCriteriaInterfaceFactory */ private $searchCriteriaFactory; @@ -32,18 +28,15 @@ class ProductCollectionSearchCriteriaBuilder private $filterGroupBuilder; /** - * @param CollectionFactory $collectionFactory * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory * @param FilterBuilder $filterBuilder * @param FilterGroupBuilder $filterGroupBuilder */ public function __construct( - CollectionFactory $collectionFactory, SearchCriteriaInterfaceFactory $searchCriteriaFactory, FilterBuilder $filterBuilder, FilterGroupBuilder $filterGroupBuilder ) { - $this->collectionFactory = $collectionFactory; $this->searchCriteriaFactory = $searchCriteriaFactory; $this->filterBuilder = $filterBuilder; $this->filterGroupBuilder = $filterGroupBuilder; @@ -53,6 +46,7 @@ public function __construct( * Build searchCriteria from search for product collection * * @param SearchCriteriaInterface $searchCriteria + * @return SearchCriteriaInterface */ public function build(SearchCriteriaInterface $searchCriteria): SearchCriteriaInterface { From c6f8e62646faec00a6701407fc886e082353c232 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 17 Mar 2020 13:20:28 -0500 Subject: [PATCH 422/734] MQE-2022: [3.0.0 RC1 - Release] Checklist --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 8f3b6381bfdb5..b788f4cfa4aa8 100644 --- a/composer.json +++ b/composer.json @@ -91,7 +91,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "dev-develop", + "magento/magento2-functional-testing-framework": "dev-MFTF3.0.0-RC1", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index adcff1bf0e73b..8277366ce2335 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3a25d06e6a86adbba9b2f2dfe202d69e", + "content-hash": "509c6d7015d1fdbe2f2807ba81ef94e4", "packages": [ { "name": "aws/aws-sdk-php", @@ -7494,16 +7494,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "dev-develop", + "version": "dev-MFTF3.0.0-RC1", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac" + "reference": "dc324c150312177db4ca2858a2998825462f6aea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac", - "reference": "24c7dfeb1387b9a46d14dbf169b23c3c0b2f9bac", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/dc324c150312177db4ca2858a2998825462f6aea", + "reference": "dc324c150312177db4ca2858a2998825462f6aea", "shasum": "" }, "require": { @@ -7562,7 +7562,7 @@ ], "psr-4": { "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" + "MFTF\\": "dev/tests/functional/tests/MFTF" } }, "notification-url": "https://packagist.org/downloads/", @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-13T16:06:57+00:00" + "time": "2020-03-17T17:03:26+00:00" }, { "name": "mikey179/vfsstream", From 2d63d4ff3a8e506361f69657887f4a5fef194bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Tue, 17 Mar 2020 22:17:59 +0100 Subject: [PATCH 423/734] Exclude chart.js from js bundling --- app/design/frontend/Magento/blank/etc/view.xml | 1 + app/design/frontend/Magento/luma/etc/view.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml index 5884699af15cd..39ca6c1c7f8e3 100644 --- a/app/design/frontend/Magento/blank/etc/view.xml +++ b/app/design/frontend/Magento/blank/etc/view.xml @@ -259,6 +259,7 @@ <var name="bundle_size">1MB</var> </vars> <exclude> + <item type="file">Lib::chartjs/Chart.min.js</item> <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> diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml index a2802b7e374f3..beb74c6f2cd20 100644 --- a/app/design/frontend/Magento/luma/etc/view.xml +++ b/app/design/frontend/Magento/luma/etc/view.xml @@ -270,6 +270,7 @@ <var name="bundle_size">1MB</var> </vars> <exclude> + <item type="file">Lib::chartjs/Chart.min.js</item> <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> From a242e3ad8939a3e095ef796c751e94422915cd52 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Wed, 18 Mar 2020 06:54:52 +0700 Subject: [PATCH 424/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Fix static code --- app/code/Magento/Downloadable/Model/Link/Builder.php | 3 +-- app/code/Magento/Downloadable/Model/Sample/Builder.php | 3 +-- .../Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php | 4 +--- .../Downloadable/Test/Unit/Model/Sample/BuilderTest.php | 4 +--- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index 4fd323bad3708..eefed4c5453ab 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -13,9 +13,8 @@ use Magento\Framework\DataObject\Copy; /** - * Class Builder + * Builder download link model for downloadable product * - * @package Magento\Downloadable\Model\Link * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index 85f215e7b9b94..c084e1317c723 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -15,9 +15,8 @@ use Magento\Framework\DataObject\Copy; /** - * Class Builder + * Builder download sample link model for downloadable product * - * @package Magento\Downloadable\Model\Sample * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 9b78f11e01a9b..46f4ca0408c73 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -12,9 +12,7 @@ use Magento\Downloadable\Helper\Download; /** - * Class BuilderTest - * - * @package Magento\Downloadable\Test\Unit\Model\Link + * Unit test for downloadable products' builder link class */ class BuilderTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 797c548183cde..817a0b61b927c 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -12,9 +12,7 @@ use Magento\Downloadable\Model\Sample\Builder; /** - * Class BuilderTest - * - * @package Magento\Downloadable\Test\Unit\Model\Sample + * Unit test for downloadable products' builder sample class */ class BuilderTest extends \PHPUnit\Framework\TestCase { From 40155f6594c0c08b1cd0c02a820ee4203e7412d9 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 18 Mar 2020 02:17:01 +0100 Subject: [PATCH 425/734] Add `<magentoCron` instruction --- .../AdminCreateApiDynamicBundleProductActionGroup.xml | 2 ++ ...inCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml | 1 + .../ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml | 1 + .../Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml | 1 + .../Test/AdminAddBundleProductToCartFromWishListPageTest.xml | 1 + .../Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml | 1 + .../Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml | 1 + .../Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml | 2 ++ .../Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml | 1 + .../Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml | 1 + .../Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml | 1 + .../Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml | 1 + .../Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml | 1 + .../Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml | 1 + .../Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml | 2 +- .../Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml | 1 + .../Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml | 1 + ...nShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml | 1 + .../Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml | 2 +- .../Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml | 1 + .../Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml | 1 + .../Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml | 1 + .../Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml | 1 + .../Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml | 1 + .../Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml | 1 + .../Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml | 1 + .../Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml | 1 + .../Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 2 +- .../Test/StorefrontBundleProductShownInCategoryListAndGrid.xml | 1 + .../Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml | 2 +- .../Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml | 1 + .../Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml | 1 + 32 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml index 7e17822710a18..952ae69d887d4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml @@ -61,5 +61,7 @@ <requiredEntity createDataKey="createBundleOption1_2"/> <requiredEntity createDataKey="simpleProduct4"/> </createData> + + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml index c9f5c52c05736..84b0dc1449878 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml @@ -70,5 +70,6 @@ <requiredEntity createDataKey="createBundleRadioButtonsOption"/> <requiredEntity createDataKey="simpleProduct2"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml index 13c31cf2e7127..bfeb5c6bcb4b9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml @@ -61,5 +61,6 @@ <requiredEntity createDataKey="createBundleOption1_2"/> <requiredEntity createDataKey="simpleProduct4"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index 2b6b139520f97..f336acc97a67c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -25,6 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml index 1498e52850fd5..bbddc9618e715 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml @@ -50,6 +50,7 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="createSimpleProduct2"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml index 3770e47079c98..64ad5d081690c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml index 2f2326b465062..3caa2c9681f55 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml @@ -24,6 +24,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml index b143bd63280ea..c85fb7f346607 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml @@ -164,6 +164,8 @@ <!-- Create second simple product for a bundle option --> <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml index f272f3f98a8c9..519626001fc7a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml index a3e9a8af40a34..c4035c3c2b75a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml @@ -26,6 +26,7 @@ <createData entity="ApiBundleProductPriceViewRange" stepKey="createDynamicBundleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml index 0c26fb1775bff..7ce30abf47c4c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml @@ -23,6 +23,7 @@ <createData entity="FixedBundleProduct" stepKey="createFixedBundleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml index fbb9dda50f0d9..9bac55d73b809 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -23,6 +23,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml index 5aa72fb651985..672d8e12ad720 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml index 28abd06253393..5890a3808afd3 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml @@ -24,6 +24,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!--Clear Filters--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 7d82c6e5b43ad..1e0e573ceab80 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!-- Admin Login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index 77be5b879b1c6..0f4f58f9cae4f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product we created in the test body --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml index 09297a4e1df80..06ddd43d0e2cc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml @@ -24,6 +24,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml index 173319affe53b..ec007d22c9d72 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml @@ -34,6 +34,7 @@ <requiredEntity createDataKey="createBundleOption"/> <requiredEntity createDataKey="createSimpleProduct"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete Simple Product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index 80920c2e6d851..b9673fac5f527 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml index 91a2d15287033..955fc8c4c6f50 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml @@ -20,6 +20,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> <actionGroup ref="LoginAsAdmin" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index 4efacbb267a0b..e2e1ae5b4e648 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -21,6 +21,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index f1124e5446b58..28d5a05cae88f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -23,6 +23,7 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 97e509db39fa7..04766b90b7941 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -24,6 +24,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!--Clear Filters--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml index 27369d38f0c35..cc2aeb0602d36 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml @@ -23,6 +23,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index 38926ccfbb7d6..84fe0d3274b11 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -30,6 +30,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct8"/> <createData entity="SimpleProduct2" stepKey="simpleProduct9"/> <createData entity="SimpleProduct2" stepKey="simpleProduct10"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index 05100284a3fe9..b8913c26d9886 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index d617ced82074e..aa51eded42dcd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index b174ee2ee3c70..eb298a78b7dd9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!-- Admin Login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml index 364d4fa68e590..452350e250611 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml @@ -25,6 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!--Logging out--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 613187a4c3856..e19c091ec3101 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index 3a40a1b7eeb71..641d04b5f6b14 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -22,6 +22,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index 7ced26bab2c96..56e2b4a97f135 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -23,6 +23,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="_defaultCategory" stepKey="createCategory"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> From 59987101ec2f06f71a319a65a5ea6cddc379cb44 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 18 Mar 2020 02:22:23 +0100 Subject: [PATCH 426/734] Add `<magentoCron` instruction --- .../Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml | 1 + .../Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml | 1 + .../Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml | 1 + ...retateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml | 2 ++ .../Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml | 1 + 5 files changed, 6 insertions(+) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index e82d54280d4e1..8765be57abada 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -28,6 +28,7 @@ <field key="firstname">John1</field> <field key="lastname">Doe1</field> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Reset admin order filter --> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index 9a1110bfda29a..0319acae9d982 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -30,6 +30,7 @@ <createData entity="Simple_US_Customer" stepKey="customer"/> <createData entity="BraintreeConfig" stepKey="BraintreeConfigurationData"/> <createData entity="CustomBraintreeConfigurationData" stepKey="CustomBraintreeConfigurationData"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml index c45a8aece5ffc..e194cd88f267a 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml @@ -33,6 +33,7 @@ <!--Create New Customer--> <createData stepKey="createCustomer" entity="Simple_US_Customer"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml index d2b0479f2bba6..f8f789c8f9209 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml @@ -43,6 +43,8 @@ <field key="group_id">3</field> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> + <!--Login as Admin User--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml index 977ee78c0d201..21ec46b655c60 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml @@ -82,6 +82,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/> From d634cac36720f4bc7d41d2f690bef7205e9452af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Wed, 18 Mar 2020 00:14:30 +0100 Subject: [PATCH 427/734] Cleanup ObjectManager usage - Magento_Catalog ViewModel,Plugin --- .../Plugin/Model/ResourceModel/Config.php | 43 +++--- .../Plugin/Model/ResourceModel/ConfigTest.php | 134 ++++++++++-------- .../ViewModel/Product/BreadcrumbsTest.php | 69 +++++---- .../Catalog/ViewModel/Product/Breadcrumbs.php | 45 +++--- 4 files changed, 155 insertions(+), 136 deletions(-) diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php index b942f5570f57d..a3e7a417e6e47 100644 --- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php @@ -3,9 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Plugin\Model\ResourceModel; -use Magento\Framework\App\ObjectManager; +use Magento\Eav\Model\Cache\Type; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; /** @@ -21,12 +26,12 @@ class Config /**#@-*/ /**#@-*/ - protected $cache; + private $cache; /** - * @var bool|null + * @var bool */ - protected $isCacheEnabled = null; + private $isCacheEnabled; /** * @var SerializerInterface @@ -34,30 +39,30 @@ class Config private $serializer; /** - * @param \Magento\Framework\App\CacheInterface $cache - * @param \Magento\Framework\App\Cache\StateInterface $cacheState + * @param CacheInterface $cache + * @param StateInterface $cacheState * @param SerializerInterface $serializer */ public function __construct( - \Magento\Framework\App\CacheInterface $cache, - \Magento\Framework\App\Cache\StateInterface $cacheState, - SerializerInterface $serializer = null + CacheInterface $cache, + StateInterface $cacheState, + SerializerInterface $serializer ) { $this->cache = $cache; - $this->isCacheEnabled = $cacheState->isEnabled(\Magento\Eav\Model\Cache\Type::TYPE_IDENTIFIER); - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); + $this->isCacheEnabled = $cacheState->isEnabled(Type::TYPE_IDENTIFIER); + $this->serializer = $serializer; } /** * Cache attribute used in listing. * * @param \Magento\Catalog\Model\ResourceModel\Config $config - * @param \Closure $proceed + * @param callable $proceed * @return array */ public function aroundGetAttributesUsedInListing( \Magento\Catalog\Model\ResourceModel\Config $config, - \Closure $proceed + callable $proceed ) { $cacheId = self::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); if ($this->isCacheEnabled && ($attributes = $this->cache->load($cacheId))) { @@ -69,8 +74,8 @@ public function aroundGetAttributesUsedInListing( $this->serializer->serialize($attributes), $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); } @@ -81,12 +86,12 @@ public function aroundGetAttributesUsedInListing( * Cache attributes used for sorting. * * @param \Magento\Catalog\Model\ResourceModel\Config $config - * @param \Closure $proceed + * @param callable $proceed * @return array */ public function aroundGetAttributesUsedForSortBy( \Magento\Catalog\Model\ResourceModel\Config $config, - \Closure $proceed + callable $proceed ) { $cacheId = self::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); @@ -99,8 +104,8 @@ public function aroundGetAttributesUsedForSortBy( $this->serializer->serialize($attributes), $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php index f36c934ca9acf..142de8fd1c5df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php @@ -3,43 +3,59 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Plugin\Model\ResourceModel; +use Magento\Catalog\Model\ResourceModel\Config as ConfigResourceModel; use Magento\Catalog\Plugin\Model\ResourceModel\Config; +use Magento\Eav\Model\Cache\Type; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ConfigTest extends \PHPUnit\Framework\TestCase +class ConfigTest extends TestCase { - /** @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $cache; + /** + * @var CacheInterface|MockObject + */ + private $cacheMock; - /** @var \Magento\Framework\App\Cache\StateInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $cacheState; + /** + * @var StateInterface|MockObject + */ + private $cacheStateMock; - /** @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $serializer; + /** + * @var SerializerInterface|MockObject + */ + private $serializerMock; - /** @var \Magento\Catalog\Model\ResourceModel\Config|\PHPUnit_Framework_MockObject_MockObject */ - private $subject; + /** + * @var ConfigResourceModel|MockObject + */ + private $configResourceModelMock; protected function setUp() { - $this->cache = $this->createMock(\Magento\Framework\App\CacheInterface::class); - $this->cacheState = $this->createMock(\Magento\Framework\App\Cache\StateInterface::class); - $this->serializer = $this->createMock(SerializerInterface::class); - $this->subject = $this->createMock(\Magento\Catalog\Model\ResourceModel\Config::class); + $this->cacheMock = $this->createMock(CacheInterface::class); + $this->cacheStateMock = $this->createMock(StateInterface::class); + $this->serializerMock = $this->createMock(SerializerInterface::class); + $this->configResourceModelMock = $this->createMock(ConfigResourceModel::class); } public function testGetAttributesUsedInListingOnCacheDisabled() { - $this->cache->expects($this->never())->method('load'); + $this->cacheMock->expects($this->never())->method('load'); $this->assertEquals( ['attributes'], $this->getConfig(false)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed(['attributes']) ) ); @@ -51,13 +67,13 @@ public function testGetAttributesUsedInListingFromCache() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); - $this->serializer->expects($this->once()) + $this->cacheMock->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with($serializedAttributes) ->willReturn($attributes); @@ -65,7 +81,7 @@ public function testGetAttributesUsedInListingFromCache() $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed() ) ); @@ -77,31 +93,31 @@ public function testGetAttributesUsedInListingWithCacheSave() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); - $this->serializer->expects($this->never()) + $this->cacheMock->method('load')->with($cacheId)->willReturn(false); + $this->serializerMock->expects($this->never()) ->method('unserialize'); - $this->serializer->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('serialize') ->with($attributes) ->willReturn($serializedAttributes); - $this->cache->expects($this->any())->method('save')->with( + $this->cacheMock->method('save')->with( $serializedAttributes, $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed($attributes) ) ); @@ -109,12 +125,12 @@ public function testGetAttributesUsedInListingWithCacheSave() public function testGetAttributesUsedForSortByOnCacheDisabled() { - $this->cache->expects($this->never())->method('load'); + $this->cacheMock->expects($this->never())->method('load'); $this->assertEquals( ['attributes'], $this->getConfig(false)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed(['attributes']) ) ); @@ -126,12 +142,12 @@ public function testGetAttributesUsedForSortByFromCache() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); - $this->serializer->expects($this->once()) + $this->cacheMock->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with($serializedAttributes) ->willReturn($attributes); @@ -139,7 +155,7 @@ public function testGetAttributesUsedForSortByFromCache() $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed() ) ); @@ -151,30 +167,30 @@ public function testGetAttributesUsedForSortByWithCacheSave() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); - $this->serializer->expects($this->never()) + $this->cacheMock->method('load')->with($cacheId)->willReturn(false); + $this->serializerMock->expects($this->never()) ->method('unserialize'); - $this->serializer->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('serialize') ->with($attributes) ->willReturn($serializedAttributes); - $this->cache->expects($this->any())->method('save')->with( + $this->cacheMock->method('save')->with( $serializedAttributes, $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed($attributes) ) ); @@ -182,29 +198,33 @@ public function testGetAttributesUsedForSortByWithCacheSave() /** * @param bool $cacheEnabledFlag - * @return \Magento\Catalog\Plugin\Model\ResourceModel\Config + * + * @return Config */ protected function getConfig($cacheEnabledFlag) { - $this->cacheState->expects($this->any())->method('isEnabled') - ->with(\Magento\Eav\Model\Cache\Type::TYPE_IDENTIFIER)->willReturn($cacheEnabledFlag); + $this->cacheStateMock->method('isEnabled') + ->with(Type::TYPE_IDENTIFIER) + ->willReturn($cacheEnabledFlag); + return (new ObjectManager($this))->getObject( - \Magento\Catalog\Plugin\Model\ResourceModel\Config::class, + Config::class, [ - 'cache' => $this->cache, - 'cacheState' => $this->cacheState, - 'serializer' => $this->serializer, + 'cache' => $this->cacheMock, + 'cacheState' => $this->cacheStateMock, + 'serializer' => $this->serializerMock, ] ); } /** * @param mixed $returnValue + * * @return callable */ protected function mockPluginProceed($returnValue = null) { - return function () use ($returnValue) { + return static function () use ($returnValue) { return $returnValue; }; } diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index a442041660893..91bb534fff627 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -11,65 +11,72 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\ViewModel\Product\Breadcrumbs; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Escaper; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Unit test for Magento\Catalog\ViewModel\Product\Breadcrumbs. */ -class BreadcrumbsTest extends \PHPUnit\Framework\TestCase +class BreadcrumbsTest extends TestCase { + private const XML_PATH_CATEGORY_URL_SUFFIX = 'catalog/seo/category_url_suffix'; + private const XML_PATH_PRODUCT_USE_CATEGORIES = 'catalog/seo/product_use_categories'; + /** * @var Breadcrumbs */ private $viewModel; /** - * @var CatalogHelper|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - private $catalogHelper; + private $objectManager; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CatalogHelper|MockObject */ - private $scopeConfig; + private $catalogHelperMock; /** - * @var ObjectManager + * @var ScopeConfigInterface|MockObject */ - private $objectManager; + private $scopeConfigMock; /** - * @var JsonHexTag|\PHPUnit_Framework_MockObject_MockObject + * @var JsonHexTag|MockObject */ - private $serializer; + private $serializerMock; /** * @inheritdoc */ protected function setUp() : void { - $this->catalogHelper = $this->getMockBuilder(CatalogHelper::class) + $this->catalogHelperMock = $this->getMockBuilder(CatalogHelper::class) ->setMethods(['getProduct']) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->setMethods(['getValue', 'isSetFlag']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $escaper = $this->getObjectManager()->getObject(\Magento\Framework\Escaper::class); + $escaper = $this->getObjectManager()->getObject(Escaper::class); - $this->serializer = $this->createMock(JsonHexTag::class); + $this->serializerMock = $this->createMock(JsonHexTag::class); $this->viewModel = $this->getObjectManager()->getObject( Breadcrumbs::class, [ - 'catalogData' => $this->catalogHelper, - 'scopeConfig' => $this->scopeConfig, + 'catalogData' => $this->catalogHelperMock, + 'scopeConfig' => $this->scopeConfigMock, 'escaper' => $escaper, - 'jsonSerializer' => $this->serializer + 'jsonSerializer' => $this->serializerMock ] ); } @@ -79,9 +86,9 @@ protected function setUp() : void */ public function testGetCategoryUrlSuffix() : void { - $this->scopeConfig->expects($this->once()) + $this->scopeConfigMock->expects($this->once()) ->method('getValue') - ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->with(static::XML_PATH_CATEGORY_URL_SUFFIX, ScopeInterface::SCOPE_STORE) ->willReturn('.html'); $this->assertEquals('.html', $this->viewModel->getCategoryUrlSuffix()); @@ -92,9 +99,9 @@ public function testGetCategoryUrlSuffix() : void */ public function testIsCategoryUsedInProductUrl() : void { - $this->scopeConfig->expects($this->once()) + $this->scopeConfigMock->expects($this->once()) ->method('isSetFlag') - ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->with(static::XML_PATH_PRODUCT_USE_CATEGORIES, ScopeInterface::SCOPE_STORE) ->willReturn(false); $this->assertFalse($this->viewModel->isCategoryUsedInProductUrl()); @@ -105,11 +112,12 @@ public function testIsCategoryUsedInProductUrl() : void * * @param Product|null $product * @param string $expectedName + * * @return void */ public function testGetProductName($product, string $expectedName) : void { - $this->catalogHelper->expects($this->atLeastOnce()) + $this->catalogHelperMock->expects($this->atLeastOnce()) ->method('getProduct') ->willReturn($product); @@ -132,27 +140,26 @@ public function productDataProvider() : array * * @param Product|null $product * @param string $expectedJson + * * @return void */ - public function testGetJsonConfiguration($product, string $expectedJson) : void + public function testGetJsonConfigurationHtmlEscaped($product, string $expectedJson) : void { - $this->catalogHelper->expects($this->atLeastOnce()) + $this->catalogHelperMock->expects($this->atLeastOnce()) ->method('getProduct') ->willReturn($product); - $this->scopeConfig->expects($this->any()) - ->method('isSetFlag') - ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_PRODUCT_USE_CATEGORIES, ScopeInterface::SCOPE_STORE) ->willReturn(false); - $this->scopeConfig->expects($this->any()) - ->method('getValue') - ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + $this->scopeConfigMock->method('getValue') + ->with(static::XML_PATH_CATEGORY_URL_SUFFIX, ScopeInterface::SCOPE_STORE) ->willReturn('."html'); - $this->serializer->expects($this->once())->method('serialize')->willReturn($expectedJson); + $this->serializerMock->expects($this->once())->method('serialize')->willReturn($expectedJson); - $this->assertEquals($expectedJson, $this->viewModel->getJsonConfiguration()); + $this->assertEquals($expectedJson, $this->viewModel->getJsonConfigurationHtmlEscaped()); } /** diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 1aad46fc1e2f5..d3c8c406ee34d 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -3,26 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\ViewModel\Product; use Magento\Catalog\Helper\Data; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; -use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Serialize\Serializer\JsonHexTag; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Framework\Escaper; +use Magento\Store\Model\ScopeInterface; /** * Product breadcrumbs view model. */ class Breadcrumbs extends DataObject implements ArgumentInterface { + private const XML_PATH_CATEGORY_URL_SUFFIX = 'catalog/seo/category_url_suffix'; + private const XML_PATH_PRODUCT_USE_CATEGORIES = 'catalog/seo/product_use_categories'; + /** - * Catalog data. - * * @var Data */ private $catalogData; @@ -45,24 +46,21 @@ class Breadcrumbs extends DataObject implements ArgumentInterface /** * @param Data $catalogData * @param ScopeConfigInterface $scopeConfig - * @param Json|null $json - * @param Escaper|null $escaper - * @param JsonHexTag|null $jsonSerializer - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param Escaper $escaper + * @param JsonHexTag $jsonSerializer */ public function __construct( Data $catalogData, ScopeConfigInterface $scopeConfig, - Json $json = null, - Escaper $escaper = null, - JsonHexTag $jsonSerializer = null + Escaper $escaper, + JsonHexTag $jsonSerializer ) { parent::__construct(); $this->catalogData = $catalogData; $this->scopeConfig = $scopeConfig; - $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); - $this->jsonSerializer = $jsonSerializer ?: ObjectManager::getInstance()->get(JsonHexTag::class); + $this->escaper = $escaper; + $this->jsonSerializer = $jsonSerializer; } /** @@ -73,8 +71,8 @@ public function __construct( public function getCategoryUrlSuffix() { return $this->scopeConfig->getValue( - 'catalog/seo/category_url_suffix', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + static::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE ); } @@ -86,8 +84,8 @@ public function getCategoryUrlSuffix() public function isCategoryUsedInProductUrl(): bool { return $this->scopeConfig->isSetFlag( - 'catalog/seo/product_use_categories', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + static::XML_PATH_PRODUCT_USE_CATEGORIES, + ScopeInterface::SCOPE_STORE ); } @@ -108,7 +106,7 @@ public function getProductName(): string * * @return string */ - public function getJsonConfigurationHtmlEscaped() : string + public function getJsonConfigurationHtmlEscaped(): string { return $this->jsonSerializer->serialize( [ @@ -120,15 +118,4 @@ public function getJsonConfigurationHtmlEscaped() : string ] ); } - - /** - * Returns breadcrumb json. - * - * @return string - * @deprecated in favor of new method with name {suffix}Html{postfix}() - */ - public function getJsonConfiguration() - { - return $this->getJsonConfigurationHtmlEscaped(); - } } From 24481e96c171a51ca326f107ca76662bdec8732d Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 18 Mar 2020 09:51:59 +0200 Subject: [PATCH 428/734] MC-32494: Order placed within PayPal Payflow Pro is not set to Suspected Fraud status when fraud filters are triggered --- .../Payflow/Service/Response/Transaction.php | 1 + .../Paypal/Model/Payflow/Transparent.php | 171 ++++++++++++++++-- .../Paypal/Plugin/TransparentOrderPayment.php | 60 ++++++ .../Unit/Model/Payflow/TransparentTest.php | 11 +- .../adminhtml/system/paypal_payflowpro.xml | 6 + app/code/Magento/Paypal/etc/config.xml | 1 + app/code/Magento/Paypal/etc/di.xml | 3 + app/code/Magento/Paypal/i18n/en_US.csv | 1 + .../Order/Payment/State/AuthorizeCommand.php | 26 ++- .../Order/Payment/State/CaptureCommand.php | 26 ++- .../Sales/etc/extension_attributes.xml | 3 + .../Paypal/Model/Payflow/TransparentTest.php | 167 +++++++++++++++++ 12 files changed, 458 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php index 1e97ac8b8c766..be650baa22d75 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php @@ -117,6 +117,7 @@ public function savePaymentInQuote($response, $cartId) $payment->setData(OrderPaymentInterface::CC_TYPE, $response->getData(OrderPaymentInterface::CC_TYPE)); $payment->setAdditionalInformation(Payflowpro::PNREF, $response->getData(Payflowpro::PNREF)); + $payment->setAdditionalInformation('result_code', $response->getData('result')); $expDate = $response->getData('expdate'); $expMonth = $this->getCcExpMonth($expDate); diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index 6569bdb20edfe..44a9b6f73c80e 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -7,23 +7,24 @@ namespace Magento\Paypal\Model\Payflow; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\State\InvalidTransitionException; use Magento\Payment\Helper\Formatter; use Magento\Payment\Model\InfoInterface; -use Magento\Paypal\Model\Payflowpro; -use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; -use Magento\Sales\Model\Order\Payment; -use Magento\Paypal\Model\Payflow\Service\Gateway; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Model\Method\TransparentInterface; use Magento\Payment\Model\Method\ConfigInterfaceFactory; -use Magento\Framework\Exception\State\InvalidTransitionException; +use Magento\Payment\Model\Method\TransparentInterface; +use Magento\Payment\Model\MethodInterface; +use Magento\Paypal\Model\Payflow\Service\Gateway; use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface; use Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator; +use Magento\Paypal\Model\Payflowpro; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; +use Magento\Sales\Model\Order\Payment; use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Api\Data\PaymentTokenInterfaceFactory; /** - * Payflow Pro payment gateway model + * Payflow Pro payment gateway model (transparent redirect). * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -35,6 +36,16 @@ class Transparent extends Payflowpro implements TransparentInterface const CC_VAULT_CODE = 'payflowpro_cc_vault'; + /** + * Result code of account verification transaction request. + */ + private const RESULT_CODE = 'result_code'; + + /** + * Fraud Management Filters config setting. + */ + private const CONFIG_FMF = 'fmf'; + /** * @var string */ @@ -45,6 +56,13 @@ class Transparent extends Payflowpro implements TransparentInterface */ protected $_infoBlockType = \Magento\Paypal\Block\Payment\Info::class; + /** + * Fetch transaction details availability option. + * + * @var bool + */ + protected $_canFetchTransactionInfo = false; + /** * @var ResponseValidator */ @@ -165,6 +183,14 @@ public function validate() */ public function authorize(InfoInterface $payment, $amount) { + if ($this->isFraudDetected($payment)) { + $this->markPaymentAsFraudulent($payment); + return $this; + } + + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + /** @var PaymentTokenInterface $vaultPaymentToken */ + $vaultPaymentToken = $payment->getExtensionAttributes()->getVaultPaymentToken(); /** @var Payment $payment */ $request = $this->buildBasicRequest(); @@ -177,9 +203,9 @@ public function authorize(InfoInterface $payment, $amount) $payPalCart = $this->payPalCartFactory->create(['salesModel' => $order]); $payPalCart->getAmounts(); - $token = $payment->getAdditionalInformation(self::PNREF); + $parentTransactionId = $vaultPaymentToken ? $vaultPaymentToken->getGatewayToken() : $zeroAmountAuthorizationId; $request->setData('trxtype', self::TRXTYPE_AUTH_ONLY); - $request->setData('origid', $token); + $request->setData('origid', $parentTransactionId); $request->setData('amt', $this->formatPrice($amount)); $request->setData('currency', $order->getBaseCurrencyCode()); $request->setData('itemamt', $this->formatPrice($payPalCart->getSubtotal())); @@ -200,10 +226,15 @@ public function authorize(InfoInterface $payment, $amount) $this->setTransStatus($payment, $response); - $this->createPaymentToken($payment, $token); + if ($vaultPaymentToken) { + $payment->setParentTransactionId($vaultPaymentToken->getGatewayToken()); + } else { + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); + } $payment->unsAdditionalInformation(self::CC_DETAILS); $payment->unsAdditionalInformation(self::PNREF); + $payment->unsAdditionalInformation(self::RESULT_CODE); return $this; } @@ -291,14 +322,126 @@ private function getPaymentExtensionAttributes(Payment $payment) */ public function capture(InfoInterface $payment, $amount) { + if ($this->isFraudDetected($payment)) { + $this->markPaymentAsFraudulent($payment); + return $this; + } + /** @var Payment $payment */ - $token = $payment->getAdditionalInformation(self::PNREF); + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + /** @var PaymentTokenInterface $vaultPaymentToken */ + $vaultPaymentToken = $payment->getExtensionAttributes()->getVaultPaymentToken(); + if ($vaultPaymentToken && empty($zeroAmountAuthorizationId)) { + $payment->setAdditionalInformation(self::PNREF, $vaultPaymentToken->getGatewayToken()); + if (!$payment->getParentTransactionId()) { + $payment->setParentTransactionId($vaultPaymentToken->getGatewayToken()); + } + } parent::capture($payment, $amount); - if ($token && !$payment->getAuthorizationTransaction()) { - $this->createPaymentToken($payment, $token); + if ($zeroAmountAuthorizationId && $vaultPaymentToken === null) { + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); } return $this; } + + /** + * Attempt to accept a pending payment. + * + * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount + * authorization). For accepting a payment should be created PayPal reference transaction with a real order amount. + * Fraud Protection Service filters do not screen reference transactions. + * + * @param InfoInterface $payment + * @return bool + * @throws InvalidTransitionException + * @throws LocalizedException + */ + public function acceptPayment(InfoInterface $payment) + { + if ($this->getConfigPaymentAction() === MethodInterface::ACTION_AUTHORIZE_CAPTURE) { + $invoices = iterator_to_array($payment->getOrder()->getInvoiceCollection()); + $invoice = count($invoices) ? reset($invoices) : null; + $payment->capture($invoice); + } else { + $amount = $payment->getOrder()->getBaseGrandTotal(); + $payment->authorize(true, $amount); + } + + return true; + } + + /** + * Deny a pending payment. + * + * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount + * authorization). This transaction type cannot be voided, so we do not send any request to payment gateway. + * + * @param InfoInterface $payment + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function denyPayment(InfoInterface $payment) + { + return true; + } + + /** + * Marks payment as fraudulent. + * + * @param InfoInterface $payment + * @throws \Exception + */ + private function markPaymentAsFraudulent(InfoInterface $payment): void + { + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + $payment->setTransactionId($zeroAmountAuthorizationId); + $payment->setIsTransactionClosed(0); + $payment->setIsTransactionPending(true); + $payment->setIsFraudDetected(true); + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); + $fraudulentMsg = 'Order is suspended as an account verification transaction is suspected to be fraudulent.'; + $extensionAttributes = $this->getPaymentExtensionAttributes($payment); + $extensionAttributes->setNotificationMessage($fraudulentMsg); + $payment->unsAdditionalInformation(self::CC_DETAILS); + $payment->unsAdditionalInformation(self::PNREF); + $payment->unsAdditionalInformation(self::RESULT_CODE); + } + + /** + * Checks if fraud filters were triggered for the payment. + * + * For current PayPal PayflowPro transparent redirect integration + * Fraud Protection Service filters screen only account verification + * transaction (also known as zero dollar authorization). + * Following reference transaction with real dollar amount will not be screened + * by Fraud Protection Service. + * + * @param InfoInterface $payment + * @return bool + */ + private function isFraudDetected(InfoInterface $payment): bool + { + $resultCode = $payment->getAdditionalInformation(self::RESULT_CODE); + $isFmfEnabled = (bool)$this->getConfig()->getValue(self::CONFIG_FMF); + return $isFmfEnabled && $this->getZeroAmountAuthorizationId($payment) && in_array( + $resultCode, + [self::RESPONSE_CODE_DECLINED_BY_FILTER, self::RESPONSE_CODE_FRAUDSERVICE_FILTER] + ); + } + + /** + * Returns zero dollar authorization transaction id. + * + * PNREF (transaction id) is available in payment additional information only right after + * PayPal account verification transaction (also known as zero dollar authorization). + * + * @param InfoInterface $payment + * @return string + */ + private function getZeroAmountAuthorizationId(InfoInterface $payment): string + { + return (string)$payment->getAdditionalInformation(self::PNREF); + } } diff --git a/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php b/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php new file mode 100644 index 0000000000000..ab1d9c210d7d7 --- /dev/null +++ b/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Plugin; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Model\Order\Payment; + +/** + * Updates invoice transaction id for PayPal PayflowPro payment. + */ +class TransparentOrderPayment +{ + /** + * @var InvoiceRepositoryInterface + */ + private $invoiceRepository; + + /** + * @param InvoiceRepositoryInterface $invoiceRepository + */ + public function __construct(InvoiceRepositoryInterface $invoiceRepository) + { + $this->invoiceRepository = $invoiceRepository; + } + + /** + * Updates invoice transaction id. + * + * Accepting PayPal PayflowPro payment actually means executing new reference transaction + * based on account verification. So for existing pending invoice, transaction id should be updated + * with the id of last reference transaction. + * + * @param Payment $subject + * @param Payment $result + * @return Payment + * @throws LocalizedException + */ + public function afterAccept(Payment $subject, Payment $result): Payment + { + $paymentMethod = $subject->getMethodInstance(); + if (!$paymentMethod instanceof \Magento\Paypal\Model\Payflow\Transparent) { + return $result; + } + + $invoices = iterator_to_array($subject->getOrder()->getInvoiceCollection()); + $invoice = reset($invoices); + if ($invoice) { + $invoice->setTransactionId($subject->getLastTransId()); + $this->invoiceRepository->save($invoice); + } + + return $result; + } +} diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index 9587600203561..acadbc80cd559 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -196,7 +196,9 @@ private function getPaymentExtensionInterfaceFactory() ->disableOriginalConstructor() ->getMock(); $orderPaymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods( + ['setVaultPaymentToken', 'getVaultPaymentToken', 'setNotificationMessage', 'getNotificationMessage'] + ) ->disableOriginalConstructor() ->getMock(); @@ -290,12 +292,17 @@ private function initPayment() $this->order = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - + $paymentExtensionAttributes = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->setMethods( + ['setVaultPaymentToken', 'getVaultPaymentToken', 'setNotificationMessage', 'getNotificationMessage'] + ) + ->getMockForAbstractClass(); $this->payment->method('getOrder')->willReturn($this->order); $this->payment->method('setTransactionId')->willReturnSelf(); $this->payment->method('setIsTransactionClosed')->willReturnSelf(); $this->payment->method('getCcExpYear')->willReturn('2019'); $this->payment->method('getCcExpMonth')->willReturn('05'); + $this->payment->method('getExtensionAttributes')->willReturn($paymentExtensionAttributes); return $this->payment; } diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml index c87a781f36c00..77ec9eb0d0069 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml @@ -170,6 +170,12 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <attribute type="shared">1</attribute> </field> + <field id="fmf" translate="label comment" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Fraud Management Filters</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Be sure to configure Fraud Management Filters in your PayPal account in "Service Settings/Fraud Protection" section. Attention! Please don't use Total Purchase Price Ceiling/Floor Filters. Current integration doesn't support them.</comment> + <config_path>payment/payflowpro/fmf</config_path> + </field> <group id="paypal_payflow_avs_check" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> <label>CVV and AVS Settings</label> <field id="heading_avs_settings" translate="label" sortOrder="0" showInDefault="1" showInWebsite="1"> diff --git a/app/code/Magento/Paypal/etc/config.xml b/app/code/Magento/Paypal/etc/config.xml index 6c0601f80137d..24bb54a86103d 100644 --- a/app/code/Magento/Paypal/etc/config.xml +++ b/app/code/Magento/Paypal/etc/config.xml @@ -109,6 +109,7 @@ <cgi_url>https://payflowlink.paypal.com</cgi_url> <transaction_url_test_mode>https://pilot-payflowpro.paypal.com</transaction_url_test_mode> <transaction_url>https://payflowpro.paypal.com</transaction_url> + <fmf>0</fmf> <avs_street>0</avs_street> <avs_zip>0</avs_zip> <avs_international>0</avs_international> diff --git a/app/code/Magento/Paypal/etc/di.xml b/app/code/Magento/Paypal/etc/di.xml index 973ed0f91924c..e148320fdaf17 100644 --- a/app/code/Magento/Paypal/etc/di.xml +++ b/app/code/Magento/Paypal/etc/di.xml @@ -255,4 +255,7 @@ <type name="Magento\Framework\Session\SessionStartChecker"> <plugin name="transparent_session_checker" type="Magento\Paypal\Plugin\TransparentSessionChecker"/> </type> + <type name="Magento\Sales\Model\Order\Payment"> + <plugin name="paypal_transparent" type="Magento\Paypal\Plugin\TransparentOrderPayment"/> + </type> </config> diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index 4e47c4c1f9e9f..54dd611d49073 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -738,3 +738,4 @@ User,User "PayPal Guest Checkout Credit Card Icons","PayPal Guest Checkout Credit Card Icons" "Elektronisches Lastschriftverfahren - German ELV","Elektronisches Lastschriftverfahren - German ELV" "Please enter at least 0 and at most 65535","Please enter at least 0 and at most 65535" +"Order is suspended as an account verification transaction is suspected to be fraudulent.","Order is suspended as an account verification transaction is suspected to be fraudulent." diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php index 98cf1babdc92d..89731b5130605 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php @@ -11,6 +11,9 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Process order state and status after authorize operation. + */ class AuthorizeCommand implements CommandInterface { /** @@ -28,6 +31,8 @@ public function __construct(StatusResolver $statusResolver = null) } /** + * Run command. + * * @param OrderPaymentInterface $payment * @param string|float $amount * @param OrderInterface $order @@ -50,6 +55,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $message .= ' Order is suspended as its authorizing amount %1 is suspected to be fraudulent.'; } + $message = $this->getNotificationMessage($payment) ?? $message; + if (!isset($status)) { $status = $this->statusResolver->getOrderStatusByState($order, $state); } @@ -61,12 +68,29 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** - * @deprecated 100.2.0 Replaced by a StatusResolver class call. + * Returns payment notification message. + * + * @param OrderPaymentInterface $payment + * @return string|null + */ + private function getNotificationMessage(OrderPaymentInterface $payment): ?string + { + $extensionAttributes = $payment->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getNotificationMessage()) { + return $extensionAttributes->getNotificationMessage(); + } + + return null; + } + + /** + * Sets order state and status. * * @param Order $order * @param string $status * @param string $state * @return void + * @deprecated 100.2.0 Replaced by a StatusResolver class call. */ protected function setOrderStateAndStatus(Order $order, $status, $state) { diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php index 0e4135a1bf814..f57e1933a7e5a 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php @@ -11,6 +11,9 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Process order state and status after capture operation. + */ class CaptureCommand implements CommandInterface { /** @@ -28,6 +31,8 @@ public function __construct(StatusResolver $statusResolver = null) } /** + * Run command. + * * @param OrderPaymentInterface $payment * @param string|float $amount * @param OrderInterface $order @@ -50,6 +55,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $message .= ' Order is suspended as its capturing amount %1 is suspected to be fraudulent.'; } + $message = $this->getNotificationMessage($payment) ?? $message; + if (!isset($status)) { $status = $this->statusResolver->getOrderStatusByState($order, $state); } @@ -61,12 +68,13 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** - * @deprecated 100.2.0 Replaced by a StatusResolver class call. + * Sets order state and status. * * @param Order $order * @param string $status * @param string $state * @return void + * @deprecated 100.2.0 Replaced by a StatusResolver class call. */ protected function setOrderStateAndStatus(Order $order, $status, $state) { @@ -76,4 +84,20 @@ protected function setOrderStateAndStatus(Order $order, $status, $state) $order->setState($state)->setStatus($status); } + + /** + * Returns payment notification message. + * + * @param OrderPaymentInterface $payment + * @return string|null + */ + private function getNotificationMessage(OrderPaymentInterface $payment): ?string + { + $extensionAttributes = $payment->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getNotificationMessage()) { + return $extensionAttributes->getNotificationMessage(); + } + + return null; + } } diff --git a/app/code/Magento/Sales/etc/extension_attributes.xml b/app/code/Magento/Sales/etc/extension_attributes.xml index 222f61cdc7324..08e295cb6721c 100644 --- a/app/code/Magento/Sales/etc/extension_attributes.xml +++ b/app/code/Magento/Sales/etc/extension_attributes.xml @@ -13,4 +13,7 @@ <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> <attribute code="payment_additional_info" type="Magento\Payment\Api\Data\PaymentAdditionalInfoInterface[]" /> </extension_attributes> + <extension_attributes for="Magento\Sales\Api\Data\OrderPaymentInterface"> + <attribute code="notification_message" type="string" /> + </extension_attributes> </config> diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php new file mode 100644 index 0000000000000..20720f491c18d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php @@ -0,0 +1,167 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Model\Payflow; + +use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Paypal\Model\Config; +use Magento\Paypal\Model\Payflowpro; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Sales\Api\Data\TransactionInterface; +use Magento\Sales\Api\OrderManagementInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\TransactionRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class TransparentTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PaymentInformationManagementInterface + */ + private $management; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->management = $this->objectManager->get(PaymentInformationManagementInterface::class); + } + + /** + * Checks a case when order should be placed in "Suspected Fraud" status based on after account verification. + * + * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php + * @magentoConfigFixture current_store payment/payflowpro/active 1 + * @magentoConfigFixture current_store payment/payflowpro/payment_action Authorization + * @magentoConfigFixture current_store payment/payflowpro/fmf 1 + */ + public function testPlaceOrderSuspectedFraud() + { + $quote = $this->getQuote('test_order_1'); + $this->addFraudPayment($quote); + $payment = $quote->getPayment(); + $pnref = $payment->getAdditionalInformation(Payflowpro::PNREF); + + $orderId = (int)$this->management->savePaymentInformationAndPlaceOrder($quote->getId(), $payment); + self::assertNotEmpty($orderId); + + /** @var OrderRepositoryInterface $orderManagement */ + $orderManagement = $this->objectManager->get(OrderRepositoryInterface::class); + $order = $orderManagement->get($orderId); + + self::assertEquals(Order::STATUS_FRAUD, $order->getStatus()); + self::assertEquals(Order::STATE_PAYMENT_REVIEW, $order->getState()); + + $transactions = $this->getPaymentTransactionList((int) $orderId); + self::assertEquals(1, sizeof($transactions), 'Only one transaction should be present.'); + + /** @var TransactionInterface $transaction */ + $transaction = array_pop($transactions); + self::assertEquals( + $pnref, + $transaction->getTxnId(), + 'Authorization transaction id should be equal to PNREF.' + ); + + self::assertContains( + 'Order is suspended as an account verification transaction is suspected to be fraudulent.', + $this->getOrderComment($orderId) + ); + } + + /** + * Retrieves quote by provided order ID. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId): CartInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Sets payment with fraud to quote. + * + * @return void + */ + private function addFraudPayment(CartInterface $quote) + { + $payment = $quote->getPayment(); + $payment->setMethod(Config::METHOD_PAYFLOWPRO); + $payment->setAdditionalInformation(Payflowpro::PNREF, 'A90A0D1B361D'); + $payment->setAdditionalInformation('result_code', Payflowpro::RESPONSE_CODE_FRAUDSERVICE_FILTER); + $payment->setCcType('VI'); + $payment->setCcLast4('1111'); + $payment->setCcExpMonth('3'); + $payment->setCcExpYear('2025'); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $quoteRepository->save($quote); + } + + /** + * Get list of order transactions. + * + * @param int $orderId + * @return TransactionInterface[] + */ + private function getPaymentTransactionList(int $orderId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('order_id', $orderId) + ->create(); + + /** @var TransactionRepositoryInterface $transactionRepository */ + $transactionRepository = $this->objectManager->get(TransactionRepositoryInterface::class); + return $transactionRepository->getList($searchCriteria) + ->getItems(); + } + + /** + * Returns order comment. + * + * @param int $orderId + * @return string + */ + private function getOrderComment(int $orderId): string + { + /** @var OrderManagementInterface $orderManagement */ + $orderManagement = $this->objectManager->get(OrderManagementInterface::class); + $comments = $orderManagement->getCommentsList($orderId)->getItems(); + $comment = reset($comments); + + return $comment ? $comment->getComment() : ''; + } +} From 851501e9435c95bc361794901a0f135a8974b6e7 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 18 Mar 2020 12:13:41 +0200 Subject: [PATCH 429/734] MC-31979: Product stock alert - unsubscribe for the product not working because cannot be accessed with GET method --- .../ProductAlert/Block/Email/Stock.php | 2 +- .../Controller/Unsubscribe/Email.php | 56 +++++++++++++++++++ app/code/Magento/ProductAlert/composer.json | 3 +- .../layout/productalert_unsubscribe_email.xml | 14 +++++ .../view/frontend/templates/email/email.phtml | 22 ++++++++ .../view/frontend/web/js/form-submitter.js | 15 +++++ 6 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php create mode 100644 app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml create mode 100644 app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml create mode 100644 app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js diff --git a/app/code/Magento/ProductAlert/Block/Email/Stock.php b/app/code/Magento/ProductAlert/Block/Email/Stock.php index d01960b8eb855..41f149eb5874e 100644 --- a/app/code/Magento/ProductAlert/Block/Email/Stock.php +++ b/app/code/Magento/ProductAlert/Block/Email/Stock.php @@ -27,7 +27,7 @@ public function getProductUnsubscribeUrl($productId) { $params = $this->_getUrlParams(); $params['product'] = $productId; - return $this->getUrl('productalert/unsubscribe/stock', $params); + return $this->getUrl('productalert/unsubscribe/email', $params); } /** diff --git a/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php new file mode 100644 index 0000000000000..d2f589374c225 --- /dev/null +++ b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ProductAlert\Controller\Unsubscribe; + +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\Action; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; + +/** + * Unsubscribing from 'Back in stock Alert'. + * + * Is used to transform a Get request that triggered in the email into the Post request endpoint + */ +class Email extends Action implements HttpGetActionInterface +{ + /** + * @var PageFactory + */ + private $resultPageFactory; + + /** + * @param Context $context + * @param PageFactory $resultPageFactory + */ + public function __construct( + Context $context, + PageFactory $resultPageFactory + ) { + $this->resultPageFactory = $resultPageFactory; + parent::__construct($context); + } + + /** + * Processes the the request triggered in Unsubscription email related to 'back in stock alert'. + * + * @return Page + */ + public function execute(): Page + { + $productId = (int)$this->getRequest()->getParam('product'); + /** @var Page $resultPage */ + $resultPage = $this->resultPageFactory->create(); + /** @var @va \Magento\Framework\View\Element\AbstractBlock $block */ + $block = $resultPage->getLayout()->getBlock('unsubscription_form'); + $block->setProductId($productId); + return $resultPage; + } +} diff --git a/app/code/Magento/ProductAlert/composer.json b/app/code/Magento/ProductAlert/composer.json index 25fe8edbede7b..5e3e486d4e4a8 100644 --- a/app/code/Magento/ProductAlert/composer.json +++ b/app/code/Magento/ProductAlert/composer.json @@ -10,7 +10,8 @@ "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-customer": "*", - "magento/module-store": "*" + "magento/module-store": "*", + "magento/module-theme": "*" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml new file mode 100644 index 0000000000000..8666fb83e01e3 --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="content"> + <block class="Magento\Framework\View\Element\Template" name="unsubscription_form" template="Magento_ProductAlert::email/email.phtml" /> + </referenceContainer> + </body> +</page> diff --git a/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml b/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml new file mode 100644 index 0000000000000..a99fe71d06dfd --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $block Magento\Framework\View\Element\Template */ +?> + +<form action="<?= $block->escapeUrl($block->getUrl('productalert/unsubscribe/stock')) ?>" + method="post" + data-form="unsubscription_form"> + <?= /* @noEscape */ $block->getBlockHtml('formkey') ?> + <input type="hidden" id="productId" name="product" value="<?= $block->escapeHtml($block->getProductId()) ?>" /> +</form> +<script type="text/x-magento-init"> + { + "[data-form=unsubscription_form]": { + "Magento_ProductAlert/js/form-submitter": {} + } + } +</script> diff --git a/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js b/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js new file mode 100644 index 0000000000000..7a0d5f663f3f9 --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery' +], function ($) { + 'use strict'; + + return function (data, element) { + + $(element).submit(); + }; +}); From 1d256047db2c54df910eb86bc9e3b7ee84c5ad5f Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 18 Mar 2020 12:42:05 +0200 Subject: [PATCH 430/734] MC-25010: [ElasticSearch] Products does not show in category page after cron:run --- .../Plugin/Category/Product/Action/Rows.php | 124 +++++++++++++ app/code/Magento/Elasticsearch/etc/di.xml | 3 + .../Model/ElasticsearchVersionChecker.php | 39 +++++ .../_files/category_tree_with_products.php | 114 ++++++++++++ .../category_tree_with_products_rollback.php | 44 +++++ .../Category/Product/Action/RowsTest.php | 164 ++++++++++++++++++ 6 files changed, 488 insertions(+) create mode 100644 app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php diff --git a/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php b/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php new file mode 100644 index 0000000000000..1d1d23017e68c --- /dev/null +++ b/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action; + +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer; +use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows as ActionRows; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +/** + * Catalog search indexer plugin for catalog category products assignment. + */ +class Rows +{ + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @param IndexerRegistry $indexerRegistry + * @param StoreManagerInterface $storeManager + * @param ResourceConnection $resource + * @param TableMaintainer $tableMaintainer + */ + public function __construct( + IndexerRegistry $indexerRegistry, + StoreManagerInterface $storeManager, + ResourceConnection $resource, + TableMaintainer $tableMaintainer + ) { + $this->indexerRegistry = $indexerRegistry; + $this->storeManager = $storeManager; + $this->connection = $resource->getConnection(); + $this->tableMaintainer = $tableMaintainer; + } + + /** + * Reindex after catalog category product reindex. + * + * @param ActionRows $subject + * @param ActionRows $result + * @param array $entityIds + * @param bool $useTempTable + * @return ActionRows + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute( + ActionRows $subject, + ActionRows $result, + array $entityIds, + bool $useTempTable = false + ): ActionRows { + $indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID); + if (!empty($entityIds) && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $indexTable = $this->getIndexTable((int) $store->getId(), $useTempTable); + $productIds = $this->getProductIdsFromIndex($indexTable, $entityIds); + if (!empty($productIds)) { + $indexer->reindexList($productIds); + } + } + } + + return $result; + } + + /** + * Return index table name. + * + * @param int $storeId + * @param bool $useTempTable + * + * @return string + */ + private function getIndexTable(int $storeId, bool $useTempTable) + { + return $useTempTable + ? $this->tableMaintainer->getMainReplicaTable($storeId) + : $this->tableMaintainer->getMainTable($storeId); + } + + /** + * Get all category products from index table. + * + * @param string $indexTable + * @param array $categoryIds + * + * @return array + */ + private function getProductIdsFromIndex(string $indexTable, array $categoryIds): array + { + return $this->connection->fetchCol( + $this->connection->select() + ->from($indexTable, ['product_id']) + ->where('category_id IN (?)', $categoryIds) + ->distinct() + ); + } +} diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index bb16bba127b56..68663ce649fcd 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -13,6 +13,9 @@ <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" /> + <type name="Magento\Catalog\Model\Indexer\Category\Product\Action\Rows"> + <plugin name="catalogsearchFulltextProductAssignment" type="Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action\Rows"/> + </type> <type name="Magento\Elasticsearch\Model\Config"> <arguments> <argument name="engineList" xsi:type="array"> diff --git a/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php new file mode 100644 index 0000000000000..5e006a0aa1197 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestModuleCatalogSearch\Model; + +use Magento\TestFramework\Helper\Curl; + +/** + * Retrieve elasticsearch version by curl request + */ +class ElasticsearchVersionChecker +{ + /** + * @var int + */ + private $version; + + /** + * @return int + */ + public function getVersion() : int + { + if (!$this->version) { + $curl = new Curl(); + $url = 'http://localhost:9200'; + $curl->get($url); + $curl->addHeader('content-type', 'application/json'); + $data = $curl->getBody(); + $versionData = explode('.', json_decode($data, true)['version']['number']); + $this->version = (int)array_shift($versionData); + } + + return $this->version; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php new file mode 100644 index 0000000000000..d98b1b738cf0d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterfaceFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Model\Config; + +$objectManager = Bootstrap::getObjectManager(); +$categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + +$categoryA = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category A', + 'parent_id' => 2, + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryA->isObjectNew(true); +$categoryA = $categoryRepository->save($categoryA); + +$categoryB = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category B', + 'parent_id' => 2, + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryB->isObjectNew(true); +$categoryB = $categoryRepository->save($categoryB); + +$categoryC = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category C', + 'parent_id' => $categoryB->getId(), + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryC->isObjectNew(true); +$categoryC = $categoryRepository->save($categoryC); + +$defaultAttributeSet = $objectManager->get(Config::class) + ->getEntityType('catalog_product') + ->getDefaultAttributeSetId(); +$product = $productFactory->create( + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => $defaultAttributeSet, + 'store_id' => Store::DEFAULT_STORE_ID, + 'website_ids' => [1], + 'name' => 'Simple Product B', + 'sku' => 'simpleB', + 'price' => 10, + 'weight' => 1, + 'stock_data' => ['use_config_manage_stock' => 0], + 'category_ids' => [$categoryB->getId()], + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + ], + ] +); +$productRepository->save($product); + +$product = $productFactory->create( + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => $defaultAttributeSet, + 'store_id' => Store::DEFAULT_STORE_ID, + 'website_ids' => [1], + 'name' => 'Simple Product C', + 'sku' => 'simpleC', + 'price' => 20, + 'weight' => 1, + 'stock_data' => ['use_config_manage_stock' => 0], + 'category_ids' => [$categoryC->getId()], + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + ], + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php new file mode 100644 index 0000000000000..448bc60f50b90 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + +$productSkus = ['simpleB', 'simpleC']; + +foreach ($productSkus as $productSku) { + try { + $productRepository->deleteById($productSku); + } catch (NoSuchEntityException $e) { + //Already deleted. + } +} + +$categoriesNames = ['Category A', 'Category B', 'Category C']; + +foreach ($categoriesNames as $categoryName) { + try { + $category = $categoryRepository->get($categoryName); + $categoryRepository->delete($category); + } catch (NoSuchEntityException $e) { + //Already deleted. + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php new file mode 100644 index 0000000000000..00a4c5b230055 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Catalog\Model\Indexer\Category\Product\Action; + +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows; +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\SearchCollectionFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\Framework\Search\EngineResolverInterface; + +/** + * Test for Magento\Catalog\Model\Indexer\Category\Product\Action\Rows class. + * This test executable with any configuration of ES and should not be deleted with removal of ES2. + * + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class RowsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var string + */ + private $searchEngine; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Rows + */ + private $rowsIndexer; + + /** + * @var DefaultCategory + */ + private $defaultCategoryHelper; + + /** + * @var SearchCollectionFactory + */ + private $fulltextSearchCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->rowsIndexer = $this->objectManager->get(Rows::class); + $this->defaultCategoryHelper = $this->objectManager->get(DefaultCategory::class); + $this->fulltextSearchCollectionFactory = $this->objectManager->get(SearchCollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function assertPreConditions() + { + $currentEngine = $this->objectManager->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + + /** + * Returns installed on server search service. + * + * @return string + */ + private function getInstalledSearchEngine(): string + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = $this->objectManager->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + + return $this->searchEngine; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree_with_products.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest + * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php + * @return void + */ + public function testLoadWithFilterCatalogView() + { + $categoryA = $this->getCategory('Category A'); + $categoryB = $this->getCategory('Category B'); + $categoryC = $this->getCategory('Category C'); + + /** Move $categoryB to $categoryA */ + $categoryB->move($categoryA->getId(), null); + $this->rowsIndexer->execute( + [ + $this->defaultCategoryHelper->getId(), + $categoryA->getId(), + $categoryB->getId(), + $categoryC->getId(), + ], + true + ); + + $fulltextCollection = $this->fulltextSearchCollectionFactory->create() + ->addCategoryFilter($categoryA); + + $this->assertProductsArePresentInCollection($fulltextCollection->getAllIds()); + } + + /** + * Assert that expected products are present in collection. + * + * @param array $productIds + * + * @return void + */ + private function assertProductsArePresentInCollection(array $productIds): void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + $firstProductId = $productRepository->get('simpleB')->getId(); + $secondProductId = $productRepository->get('simpleC')->getId(); + + $this->assertCount(2, $productIds); + $this->assertContains($secondProductId, $productIds); + $this->assertContains($firstProductId, $productIds); + } + + /** + * Gets category by name. + * + * @param string $name + * @return CategoryInterface + */ + private function getCategory(string $name): CategoryInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + /** @var CategoryListInterface $repository */ + $repository = $this->objectManager->get(CategoryListInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } +} From 3102341b0c5f113e8785711bb5b35c3700b58658 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 18 Mar 2020 13:04:20 +0200 Subject: [PATCH 431/734] MC-32306: Errors while trying to update downloadable product after MC-29952 --- .../Import/Product/Type/Downloadable.php | 6 +- .../Import/Product/Type/DownloadableTest.php | 60 ++++++++++++++----- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index f148550dd96bb..b408a77e0c95e 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -332,7 +332,7 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) { $this->rowNum = $rowNum; $error = false; - if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData)) { + if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData) && $isNewProduct) { $this->_entityModel->addRowError(self::ERROR_OPTIONS_NOT_FOUND, $this->rowNum); $error = true; } @@ -898,8 +898,8 @@ protected function uploadDownloadableFiles($fileName, $type = 'links', $renameFi try { $uploader = $this->uploaderHelper->getUploader($type, $this->parameters); if (!$this->uploaderHelper->isFileExist($fileName)) { - $uploader->move($fileName, $renameFileOff); - $fileName = $uploader['file']; + $res = $uploader->move($fileName, $renameFileOff); + $fileName = $res['file']; } } catch (\Exception $e) { $this->_entityModel->addRowError(self::ERROR_MOVE_FILE, $this->rowNum); diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php index 482bfa4f7c569..5c084f4588e07 100644 --- a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php @@ -6,6 +6,7 @@ namespace Magento\DownloadableImportExport\Test\Unit\Model\Import\Product\Type; +use Magento\Downloadable\Model\Url\DomainValidator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManager; /** @@ -39,6 +40,11 @@ class DownloadableTest extends \Magento\ImportExport\Test\Unit\Model\Import\Abst */ protected $prodAttrColFacMock; + /** + * @var DomainValidator + */ + private $domainValidator; + /** * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection|\PHPUnit_Framework_MockObject_MockObject */ @@ -498,7 +504,7 @@ public function dataForSave() /** * @dataProvider isRowValidData */ - public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) + public function testIsRowValid(array $rowData, $rowNum, $isNewProduct, $isDomainValid, $expectedResult) { $this->connectionMock->expects($this->any())->method('fetchAll')->with( $this->select @@ -514,6 +520,13 @@ public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) ], ] ); + + $this->domainValidator = $this->createMock(DomainValidator::class); + $this->domainValidator + ->expects($this->any())->method('isValid') + ->withAnyParameters() + ->willReturn($isDomainValid); + $this->downloadableModelMock = $this->objectManagerHelper->getObject( \Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable::class, [ @@ -522,11 +535,12 @@ public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) 'resource' => $this->resourceMock, 'params' => $this->paramsArray, 'uploaderHelper' => $this->uploaderHelper, - 'downloadableHelper' => $this->downloadableHelper + 'downloadableHelper' => $this->downloadableHelper, + 'domainValidator' => $this->domainValidator ] ); $result = $this->downloadableModelMock->isRowValid($rowData, $rowNum, $isNewProduct); - $this->assertNotNull($result); + $this->assertEquals($expectedResult, $result); } /** @@ -550,6 +564,8 @@ public function isRowValidData() . 'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], 0, + true, + true, true ], [ @@ -564,15 +580,8 @@ public function isRowValidData() . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], 1, - true - ], - [ - [ - 'sku' => 'downloadablesku12', - 'product_type' => 'downloadable', - 'name' => 'Downloadable Product 2', - ], - 2, + true, + true, true ], [ @@ -587,6 +596,8 @@ public function isRowValidData() . ' url=media/file2.mp4,sortorder=0', ], 3, + true, + true, true ], [ @@ -594,13 +605,15 @@ public function isRowValidData() 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'file=media/file.mp4,sortorder=1|group_title=Group Title, ' - . 'url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'title=Title 1, file=media/file.mp4,sortorder=1|title=Title 2,' . + ' group_title=Group Title, url=media/file2.mp4,sortorder=0', 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,' . 'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,' . ' url=media/file2.mp4,sortorder=0', ], 4, + true, + true, true ], [ //empty group title samples @@ -615,6 +628,8 @@ public function isRowValidData() . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], 5, + true, + true, true ], [ //empty group title links @@ -629,6 +644,19 @@ public function isRowValidData() . 'downloads=unlimited, url=media/file2.mp4,sortorder=0', ], 6, + true, + true, + true + ], + [ + [ + 'sku' => 'downloadablesku12', + 'product_type' => 'downloadable', + 'name' => 'Downloadable Product 2', + ], + 2, + false, + true, true ], [ @@ -640,7 +668,9 @@ public function isRowValidData() 'downloadable_links' => '', ], 7, - true + true, + true, + false ], ]; } From 6ad372ae3768d10a77810bce9bf2eeb8a5ea01d2 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <engcom-vendorworker-foxtrot@adobe.com> Date: Wed, 18 Mar 2020 13:40:49 +0200 Subject: [PATCH 432/734] magento/magento2#25540: Static tests fix. --- .../BundleImportExport/Model/Import/Product/Type/Bundle.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 33a7d2efaa273..dcc6e52460793 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -20,9 +20,8 @@ use Magento\Store\Model\StoreManagerInterface; /** - * Class Bundle + * Import entity Bundle product type. * - * @package Magento\BundleImportExport\Model\Import\Product\Type * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType @@ -723,6 +722,8 @@ protected function _initAttributes() } } } + + return $this; } /** From 1e1ebd3a9ee69038841f41ebe8771b6a1fa700d6 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Thu, 12 Mar 2020 13:17:37 +0200 Subject: [PATCH 433/734] Fix: ORDER BY has two similar conditions in the SQL query --- app/code/Magento/Catalog/Block/Product/ListProduct.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index 144cb682e2d22..f1adc1d924836 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -475,8 +475,6 @@ private function initializeProductCollection() $layer->setCurrentCategory($origCategory); } - $this->addToolbarBlock($collection); - $this->_eventManager->dispatch( 'catalog_block_product_list_collection', ['collection' => $collection] From a070cf368ed4b19c5e63b4453d9af2d72451901e Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <engcom-vendorworker-foxtrot@adobe.com> Date: Wed, 18 Mar 2020 15:00:43 +0200 Subject: [PATCH 434/734] magento/magento2#25540: MFTF test added. --- .../Test/UpdateBundleProductViaImportTest.xml | 70 +++++++++++++++++++ .../_data/catalog_product_import_bundle.csv | 3 + 2 files changed, 73 insertions(+) create mode 100644 app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml create mode 100644 dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv diff --git a/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml new file mode 100644 index 0000000000000..45b4c4f5ededd --- /dev/null +++ b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml @@ -0,0 +1,70 @@ +<?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="UpdateBundleProductViaImportTest"> + <annotations> + <features value="Import/Export"/> + <title value="Update Bundle product via import"/> + <description + value="Check that Bundle products are displaying on the storefront after updating product via importing CSV"/> + <severity value="MAJOR"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete products created via import --> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> + <argument name="sku" value="Bundle"/> + </actionGroup> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSimpleProduct"> + <argument name="sku" value="Simple"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!-- Create Bundle product via import --> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsCreate"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="catalog_product_import_bundle.csv"/> + <argument name="importNoticeMessage" value="Created: 2, Updated: 0, Deleted: 0"/> + </actionGroup> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCacheAfterCreate"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindexAfterCreate"/> + + <!-- Check Bundle product is visible on the storefront--> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryPageAfterCreation"> + <argument name="categoryName" value="New"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" + stepKey="assertBundleProductInStockAfterCreation"> + <argument name="productName" value="Bundle"/> + </actionGroup> + + <!-- Update Bundle product via import --> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsUpdate"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="catalog_product_import_bundle.csv"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 2, Deleted: 0"/> + </actionGroup> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCacheAfterUpdate"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindexAfterUpdate"/> + + <!-- Check Bundle product is still visible on the storefront--> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryPageAfterUpdate"> + <argument name="categoryName" value="New"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" + stepKey="assertBundleProductInStockAfterUpdate"> + <argument name="productName" value="Bundle"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv b/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv new file mode 100644 index 0000000000000..6804675940a02 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv @@ -0,0 +1,3 @@ +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,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels +Simple,,Default,simple,"Default Category/New",base,Simple,,,1.000000,1,"Taxable Goods","Catalog, Search",100.000000,,,,simple,Simple,Simple,"Simple ",,,,,,,,,"3/18/20, 6:56 AM","3/18/20, 6:56 AM",,,"Block after Info Column",,,,"Use config",,,,,,,"Use config",,,1000.0000,0.0000,1,0,0,1,1.0000,1,10000.0000,1,1,1.0000,1,1,1,1,1.0000,1,0,0,0,,,,,,,,,,,,,,,,,,,,, +Bundle,,Default,bundle,"Default Category/New",base,Bundle,,,,1,"Taxable Goods","Catalog, Search",,,,,bundle,Bundle,Bundle,"Bundle ",,,,,,,,,"3/18/20, 6:57 AM","3/18/20, 6:57 AM",,,"Block after Info Column",,,,"Use config",,,,,,,"Use config",,,0.0000,0.0000,1,0,0,1,1.0000,1,10000.0000,1,1,1.0000,1,1,1,1,1.0000,1,0,0,0,,,,,,,,,,,dynamic,dynamic,"Price range",dynamic,"name=Test Option,type=select,required=1,sku=Simple,price=0.0000,default=1,default_qty=1.0000,price_type=fixed,can_change_qty=0",together,,,,, From 947eaedf2397701d43f6366dacc2bcb8b65df3b1 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Wed, 18 Mar 2020 20:31:37 +0700 Subject: [PATCH 435/734] Fix the error that is wrong link title of a downloadable product when enabling "Use Default Value" - Update sample title incorrectly --- app/code/Magento/Downloadable/Model/Sample/Builder.php | 6 ++++++ .../Downloadable/Test/Unit/Model/Sample/BuilderTest.php | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index c084e1317c723..368d190319766 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -122,6 +122,12 @@ public function build(SampleInterface $sample) if (!$sample->getSortOrder()) { $sample->setSortOrder(1); } + + $useDefaultTitle = $this->data['use_default_title'] ?? false; + + if ($useDefaultTitle) { + $sample->setTitle(null); + } $this->resetData(); return $sample; diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 817a0b61b927c..938082fa0aef3 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -85,6 +85,7 @@ public function testBuild() { $data = [ 'file' => 'cXVlIHRhbA==', + 'use_default_title' => '1', 'type' => 'file' ]; $downloadableData = ['sort_order' => 1]; @@ -123,6 +124,10 @@ public function testBuild() )->willReturn($fileName); $this->sampleMock->expects($this->once())->method('setSampleFile')->with($fileName); $this->sampleMock->expects($this->once())->method('setSortOrder')->with(1); + $useDefaultTitle = $data['use_default_title'] ?? false; + if ($useDefaultTitle) { + $this->sampleMock->expects($this->once())->method('setTitle')->with(null); + } $this->service->setData($data); $this->service->build($this->sampleMock); From 18c8f31b6467e410bb94ab6b3fa2733dd024024e Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Wed, 18 Mar 2020 15:45:16 +0200 Subject: [PATCH 436/734] Customer Subscribes To Newsletter Subscription --- ...tStorefrontCustomerMessagesActionGroup.xml | 19 ++++++++++ .../StorefrontCustomerLoginActionGroup.xml | 21 +++++++++++ ...merNavigateToNewsletterPageActionGroup.xml | 15 ++++++++ ...erUpdateGeneralSubscriptionActionGroup.xml | 15 ++++++++ ...StorefrontCustomerNewsletterManagePage.xml | 14 ++++++++ .../StorefrontCustomerLoginFormSection.xml | 16 +++++++++ .../StorefrontCustomerNewsletterSection.xml | 15 ++++++++ ...frontCustomerSubscribeToNewsletterTest.xml | 35 +++++++++++++++++++ 8 files changed, 150 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml new file mode 100644 index 0000000000000..50e052207bd9e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCustomerMessagesActionGroup"> + <arguments> + <argument name="message" type="string"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCustomerMessagesSection.successMessage}}" stepKey="waitForElement"/> + <see userInput="{{message}}" selector="{{StorefrontCustomerMessagesSection.successMessage}}" stepKey="seeMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml new file mode 100644 index 0000000000000..4105034e33988 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerLoginActionGroup"> + <arguments> + <argument name="customer" type="entity"/> + </arguments> + + <click selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" stepKey="clickSignInLnk"/> + <fillField selector="{{StorefrontCustomerLoginFormSection.emailField}}" userInput="{{customer.email}}" stepKey="fillEmailField"/> + <fillField selector="{{StorefrontCustomerLoginFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="fillPasswordField"/> + <click selector="{{StorefrontCustomerLoginFormSection.signInAccountButton}}" stepKey="clickSignInButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml new file mode 100644 index 0000000000000..559dee27b551d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.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="StorefrontCustomerNavigateToNewsletterPageActionGroup"> + <amOnPage url="{{StorefrontCustomerNewsletterManagePage.url}}" stepKey="goToNewsletterPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml new file mode 100644 index 0000000000000..16f8b5d17d7e1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.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="StorefrontCustomerUpdateGeneralSubscriptionActionGroup"> + <click selector="{{StorefrontCustomerNewsletterSection.newsletterCheckbox}}" stepKey="checkNewsLetterSubscriptionCheckbox"/> + <click selector="{{StorefrontCustomerNewsletterSection.submit}}" stepKey="clickSubmitButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml new file mode 100644 index 0000000000000..62fa49f7b4b38 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerNewsletterManagePage" url="/newsletter/manage/" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerNewsletterSection"/> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml new file mode 100644 index 0000000000000..55f2835d584af --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.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="StorefrontCustomerLoginFormSection"> + <element name="emailField" type="input" selector=".fieldset.login #email.input-text"/> + <element name="passwordField" type="input" selector=".field.password.required #pass.input-text"/> + <element name="signInAccountButton" type="button" selector=".actions-toolbar #send2.action.login.primary" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml new file mode 100644 index 0000000000000..0275603b26227 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml @@ -0,0 +1,15 @@ +<?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="StorefrontCustomerNewsletterSection"> + <element name="newsletterCheckbox" type="checkbox" selector="#subscription.checkbox"/> + <element name="submit" type="button" selector=".action.save.primary"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml new file mode 100644 index 0000000000000..be937ebe4d970 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml @@ -0,0 +1,35 @@ +<?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="StorefrontCustomerSubscribeToNewsletterTest"> + <annotations> + <features value="Newsletter Subscription"/> + <stories value="Subscribe To Newsletter Subscription on StoreFront"/> + <title value="StoreFront Customer Newsletter Subscription"/> + <description value="Customer can be subscribed to Newsletter Subscription on StoreFront"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> + </after> + + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openHomePage"/> + <actionGroup ref="StorefrontCustomerLoginActionGroup" stepKey="loginAsCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerNavigateToNewsletterPageActionGroup" stepKey="navigateToNewsletterPage"/> + <actionGroup ref="StorefrontCustomerUpdateGeneralSubscriptionActionGroup" stepKey="subscribeToNewsletter"/> + <actionGroup ref="AssertStorefrontCustomerMessagesActionGroup" stepKey="assertMessage"> + <argument name="message" value="We have saved your subscription."/> + </actionGroup> + </test> +</tests> From df35ddac0dacf30be6547a7cbad113c3ed6ed3fc Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Wed, 18 Mar 2020 16:04:54 +0200 Subject: [PATCH 437/734] Fix failed unit test --- .../Catalog/Test/Unit/Block/Product/ListProductTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php index fe07f69e8046f..4653934311938 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php @@ -187,10 +187,6 @@ public function testGetIdentities() ->method('getProductCollection') ->will($this->returnValue($this->prodCollectionMock)); - $this->layoutMock->expects($this->once()) - ->method('getBlock') - ->will($this->returnValue($this->toolbarMock)); - $this->assertEquals( [$categoryTag, $productTag], $this->block->getIdentities() From 97bd66e984895f12c8f17b7f401ebf352a28805c Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi <a.beziazychnyi@atwix.com> Date: Tue, 17 Mar 2020 10:22:44 +0200 Subject: [PATCH 438/734] magento/magento2: fixes for the cache configuration schema --- .../Magento/Framework/Cache/ConfigTest.php | 89 ++++++++++++++ .../_files/invalidCacheConfigXmlArray.php | 52 ++++++++ .../Cache/_files/valid_cache_config.xml | 17 +++ .../Magento/Framework/App/Cache/TypeList.php | 9 +- .../App/Test/Unit/Cache/TypeListTest.php | 116 ++++++++++-------- .../Magento/Framework/Cache/Config/Reader.php | 31 +++-- .../Framework/Cache/Config/SchemaLocator.php | 12 +- .../Cache/Test/Unit/Config/ConverterTest.php | 30 ----- .../Test/Unit/Config/_files/cache_config.php | 23 ---- .../Test/Unit/Config/_files/cache_config.xml | 17 --- .../Magento/Framework/Cache/etc/cache.xsd | 40 +++--- 11 files changed, 286 insertions(+), 150 deletions(-) create mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml delete mode 100644 lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php delete mode 100644 lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php delete mode 100644 lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php new file mode 100644 index 0000000000000..2d4a1dc0c2ce3 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\Test\Integrity\Magento\Framework\Cache; + +use Magento\Framework\Config\Dom\UrnResolver; +use Magento\Framework\TestFramework\Unit\Utility\XsdValidator; +use PHPUnit\Framework\TestCase; + +/** + * Unit test of the cache configuration + */ +class ConfigTest extends TestCase +{ + /** + * Path to xsd schema file + * @var string + */ + private $xsdSchema; + + /** + * @var UrnResolver + */ + private $urnResolver; + + /** + * @var XsdValidator + */ + private $xsdValidator; + + /** + * Setup environment for test + */ + protected function setUp(): void + { + if (!function_exists('libxml_set_external_entity_loader')) { + $this->markTestSkipped('Skipped on HHVM. Will be fixed in MAGETWO-45033'); + } + $this->urnResolver = new UrnResolver(); + $this->xsdSchema = $this->urnResolver->getRealPath( + 'urn:magento:framework:Cache/etc/cache.xsd' + ); + $this->xsdValidator = new XsdValidator(); + } + + /** + * Tests invalid configurations + * + * @param string $xmlString + * @param array $expectedError + * @dataProvider schemaCorrectlyIdentifiesInvalidXmlDataProvider + */ + public function testSchemaCorrectlyIdentifiesInvalidXml( + string $xmlString, + array $expectedError + ): void { + $actualError = $this->xsdValidator->validate( + $this->xsdSchema, + $xmlString + ); + $this->assertEquals($expectedError, $actualError); + } + + /** + * Tests valid configurations + */ + public function testSchemaCorrectlyIdentifiesValidXml(): void + { + $xmlString = file_get_contents(__DIR__ . '/_files/valid_cache_config.xml'); + $actualResult = $this->xsdValidator->validate( + $this->xsdSchema, + $xmlString + ); + + $this->assertEmpty($actualResult); + } + + /** + * Data provider with invalid xml array according to cache.xsd + */ + public function schemaCorrectlyIdentifiesInvalidXmlDataProvider(): array + { + return include __DIR__ . '/_files/invalidCacheConfigXmlArray.php'; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php new file mode 100644 index 0000000000000..8d2d631334a9b --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'without_type_handle' => [ + '<?xml version="1.0"?><config></config>', + ["Element 'config': Missing child element(s). Expected is ( type ).\nLine: 1\n"], + ], + 'cache_config_with_notallowed_attribute' => [ + '<?xml version="1.0"?><config>' . + '<type name="test" translate="label,description" instance="Class\Name" notallowed="some value">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type', attribute 'notallowed': The attribute 'notallowed' is not allowed.\nLine: 1\n"], + ], + 'cache_config_without_name_attribute' => [ + '<?xml version="1.0"?><config><type translate="label,description" instance="Class\Name">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type': The attribute 'name' is required but missing.\nLine: 1\n"], + ], + 'cache_config_without_instance_attribute' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type': The attribute 'instance' is required but missing.\nLine: 1\n"], + ], + 'cache_config_without_label_element' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '<description>Test</description></type></config>', + ["Element 'type': Missing child element(s). Expected is ( label ).\nLine: 1\n"], + ], + 'cache_config_without_description_element' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '<label>Test</label></type></config>', + ["Element 'type': Missing child element(s). Expected is ( description ).\nLine: 1\n"], + ], + 'cache_config_without_child_elements' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '</type></config>', + ["Element 'type': Missing child element(s). Expected is one of ( label, description ).\nLine: 1\n"], + ], + 'cache_config_cache_name_not_unique' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name1">' . + '<label>Test1</label><description>Test1</description></type>' . + '<type name="test" translate="label,description" instance="Class\Name2">' . + '<label>Test2</label><description>Test2</description></type></config>', + [ + "Element 'type': Duplicate key-sequence ['test'] in unique identity-constraint" + . " 'uniqueCacheName'.\nLine: 1\n" + ], + ], +]; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml new file mode 100644 index 0000000000000..ef45c083daf0d --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Cache/etc/cache.xsd"> + <type name="type_name_1" translate="label,description" instance="Instance1\Class\Name"> + <label>Type1</label> + <description>Type1</description> + </type> + <type name="type_name_2" translate="label,description" instance="Instance2\Class\Name"> + <label>Type2</label> + <description>Type2</description> + </type> +</config> diff --git a/lib/internal/Magento/Framework/App/Cache/TypeList.php b/lib/internal/Magento/Framework/App/Cache/TypeList.php index b695ee3a37fa8..c0790c4d40ad4 100644 --- a/lib/internal/Magento/Framework/App/Cache/TypeList.php +++ b/lib/internal/Magento/Framework/App/Cache/TypeList.php @@ -8,6 +8,9 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\SerializerInterface; +/** + * Application cache type list + */ class TypeList implements TypeListInterface { const INVALIDATED_TYPES = 'core_cache_invalidate'; @@ -68,9 +71,7 @@ public function __construct( protected function _getTypeInstance($type) { $config = $this->_config->getType($type); - if (!isset($config['instance'])) { - return null; - } + return $this->_factory->get($config['instance']); } @@ -132,7 +133,7 @@ public function getTypes() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTypeLabels() { diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php index 8d9b297d7dded..02c9a872fe97f 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php @@ -6,21 +6,30 @@ namespace Magento\Framework\App\Test\Unit\Cache; -use \Magento\Framework\App\Cache\TypeList; +use Magento\Framework\App\Cache\InstanceFactory; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\Cache\TypeList; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Cache\Frontend\Decorator\TagScope; +use Magento\Framework\Cache\ConfigInterface; +use Magento\Framework\DataObject; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * Test class for \Magento\Framework\App\Cache\TypeList */ -class TypeListTest extends \PHPUnit\Framework\TestCase +class TypeListTest extends TestCase { /** - * @var \Magento\Framework\App\Cache\TypeList + * @var TypeList */ protected $_typeList; /** - * @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ protected $_cache; @@ -30,7 +39,7 @@ class TypeListTest extends \PHPUnit\Framework\TestCase protected $_typesArray; /** - * @var \Magento\Framework\Cache\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ConfigInterface|MockObject */ protected $_config; @@ -47,10 +56,10 @@ class TypeListTest extends \PHPUnit\Framework\TestCase /** * Expected cache type */ - const CACHE_TYPE = \Magento\Framework\Cache\FrontendInterface::class; + const CACHE_TYPE = TagScope::class; /** - * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SerializerInterface|MockObject */ private $serializerMock; @@ -58,33 +67,44 @@ protected function setUp() { $this->_typesArray = [ self::TYPE_KEY => [ + 'name' => self::TYPE_KEY, + 'instance' => self::CACHE_TYPE, 'label' => 'Type Label', 'description' => 'Type Description', ], ]; - $this->_config = - $this->createPartialMock(\Magento\Framework\Cache\ConfigInterface::class, ['getTypes', 'getType']); - $this->_config->expects($this->any())->method('getTypes')->will($this->returnValue($this->_typesArray)); + $this->_config = $this->createPartialMock( + ConfigInterface::class, + ['getTypes', 'getType'] + ); + $this->_config->expects($this->any())->method('getTypes') + ->will($this->returnValue($this->_typesArray)); + $this->_config->expects($this->any())->method('getType') + ->with(self::TYPE_KEY) + ->will($this->returnValue($this->_typesArray[self::TYPE_KEY])); $cacheState = $this->createPartialMock( - \Magento\Framework\App\Cache\StateInterface::class, + StateInterface::class, ['isEnabled', 'setEnabled', 'persist'] ); - $cacheState->expects($this->any())->method('isEnabled')->will($this->returnValue(self::IS_CACHE_ENABLED)); - $cacheBlockMock = $this->createMock(self::CACHE_TYPE); - $factory = $this->createPartialMock(\Magento\Framework\App\Cache\InstanceFactory::class, ['get']); - $factory->expects($this->any())->method('get')->with(self::CACHE_TYPE)->will( - $this->returnValue($cacheBlockMock) - ); + $cacheState->expects($this->any())->method('isEnabled') + ->will($this->returnValue(self::IS_CACHE_ENABLED)); + $cacheTypeMock = $this->createMock(self::CACHE_TYPE); + $cacheTypeMock->expects($this->any())->method('getTag') + ->will($this->returnValue('TEST')); + $factory = $this->createPartialMock(InstanceFactory::class, ['get']); + $factory->expects($this->any())->method('get') + ->with(self::CACHE_TYPE) + ->will($this->returnValue($cacheTypeMock)); $this->_cache = $this->createPartialMock( - \Magento\Framework\App\CacheInterface::class, + CacheInterface::class, ['load', 'getFrontend', 'save', 'remove', 'clean'] ); $this->serializerMock = $this->createMock(SerializerInterface::class); - $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectHelper = new ObjectManager($this); $this->_typeList = $objectHelper->getObject( - \Magento\Framework\App\Cache\TypeList::class, + TypeList::class, [ 'config' => $this->_config, 'cacheState' => $cacheState, @@ -114,9 +134,9 @@ public function testGetTypeLabels() public function testGetInvalidated() { $expectation = [self::TYPE_KEY => $this->_getPreparedType()]; - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue('serializedData') - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue('serializedData')); $this->serializerMock->expects($this->once()) ->method('unserialize') ->with('serializedData') @@ -127,9 +147,9 @@ public function testGetInvalidated() public function testInvalidate() { // there are no invalidated types - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue([]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue([])); $expectedInvalidated = [ self::TYPE_KEY => 1, ]; @@ -137,18 +157,16 @@ public function testInvalidate() ->method('serialize') ->with($expectedInvalidated) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->invalidate(self::TYPE_KEY); } public function testInvalidateList() { - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue([]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue([])); $expectedInvalidated = [ self::TYPE_KEY => 1, ]; @@ -156,10 +174,8 @@ public function testInvalidateList() ->method('serialize') ->with($expectedInvalidated) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->invalidate([self::TYPE_KEY]); } @@ -169,38 +185,36 @@ public function testCleanType() ->method('unserialize') ->with('serializedData') ->willReturn($this->_typesArray); - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue('serializedData') - ); - $this->_config->expects($this->once())->method('getType')->with(self::TYPE_KEY)->will( - $this->returnValue(['instance' => self::CACHE_TYPE]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue('serializedData')); + $this->_config->expects($this->once())->method('getType') + ->with(self::TYPE_KEY) + ->will($this->returnValue(['instance' => self::CACHE_TYPE])); unset($this->_typesArray[self::TYPE_KEY]); $this->serializerMock->expects($this->once()) ->method('serialize') ->with($this->_typesArray) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->cleanType(self::TYPE_KEY); } /** * Returns prepared type * - * @return \Magento\Framework\DataObject + * @return DataObject */ private function _getPreparedType() { - return new \Magento\Framework\DataObject( + return new DataObject( [ 'id' => self::TYPE_KEY, 'cache_type' => $this->_typesArray[self::TYPE_KEY]['label'], 'description' => $this->_typesArray[self::TYPE_KEY]['description'], - 'tags' => '', - 'status' => self::IS_CACHE_ENABLED, + 'tags' => 'TEST', + 'status' => (int)self::IS_CACHE_ENABLED, ] ); } diff --git a/lib/internal/Magento/Framework/Cache/Config/Reader.php b/lib/internal/Magento/Framework/Cache/Config/Reader.php index 445e91240e7e5..942a3931e0173 100644 --- a/lib/internal/Magento/Framework/Cache/Config/Reader.php +++ b/lib/internal/Magento/Framework/Cache/Config/Reader.php @@ -3,9 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Cache\Config; -class Reader extends \Magento\Framework\Config\Reader\Filesystem +use Magento\Framework\App\Area; +use Magento\Framework\Config\Dom; +use Magento\Framework\Config\FileResolverInterface; +use Magento\Framework\Config\Reader\Filesystem; +use Magento\Framework\Config\ValidationStateInterface; + +/** + * Cache configuration reader + */ +class Reader extends Filesystem { /** * List of id attributes for merge @@ -15,24 +25,27 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem protected $_idAttributes = ['/config/type' => 'name']; /** - * @param \Magento\Framework\Config\FileResolverInterface $fileResolver + * Initialize dependencies. + * + * @param FileResolverInterface $fileResolver * @param Converter $converter * @param SchemaLocator $schemaLocator - * @param \Magento\Framework\Config\ValidationStateInterface $validationState + * @param ValidationStateInterface $validationState * @param string $fileName * @param array $idAttributes * @param string $domDocumentClass * @param string $defaultScope + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function __construct( - \Magento\Framework\Config\FileResolverInterface $fileResolver, - \Magento\Framework\Cache\Config\Converter $converter, - \Magento\Framework\Cache\Config\SchemaLocator $schemaLocator, - \Magento\Framework\Config\ValidationStateInterface $validationState, + FileResolverInterface $fileResolver, + Converter $converter, + SchemaLocator $schemaLocator, + ValidationStateInterface $validationState, $fileName = 'cache.xml', $idAttributes = [], - $domDocumentClass = \Magento\Framework\Config\Dom::class, - $defaultScope = 'global' + $domDocumentClass = Dom::class, + $defaultScope = Area::AREA_GLOBAL ) { parent::__construct( $fileResolver, diff --git a/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php index 5471dbcfb6c62..2d3be1b1a4067 100644 --- a/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php @@ -7,6 +7,9 @@ */ namespace Magento\Framework\Cache\Config; +/** + * Cache configuration schema locator + */ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface { /** @@ -15,6 +18,9 @@ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface protected $urnResolver; /** + * Initialize dependencies. + * + * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver */ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolver) { @@ -25,6 +31,7 @@ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolv * Get path to merged config schema * * @return string|null + * @throws \Magento\Framework\Exception\NotFoundException */ public function getSchema() { @@ -34,10 +41,11 @@ public function getSchema() /** * Get path to pre file validation schema * - * @return null + * @return string|null + * @throws \Magento\Framework\Exception\NotFoundException */ public function getPerFileSchema() { - return null; + return $this->urnResolver->getRealPath('urn:magento:framework:Cache/etc/cache.xsd'); } } diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php deleted file mode 100644 index 7f86e162311c8..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Cache\Test\Unit\Config; - -class ConverterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Cache\Config\Converter - */ - protected $_model; - - protected function setUp() - { - $this->_model = new \Magento\Framework\Cache\Config\Converter(); - } - - public function testConvert() - { - $dom = new \DOMDocument(); - $xmlFile = __DIR__ . '/_files/cache_config.xml'; - $dom->loadXML(file_get_contents($xmlFile)); - - $convertedFile = __DIR__ . '/_files/cache_config.php'; - $expectedResult = include $convertedFile; - $this->assertEquals($expectedResult, $this->_model->convert($dom)); - } -} diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php deleted file mode 100644 index 0a45e50bbe198..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -return [ - 'types' => [ - 'config' => [ - 'name' => 'config', - 'translate' => 'label,description', - 'instance' => \Magento\Framework\App\Cache\Type\Config::class, - 'label' => 'Configuration', - 'description' => 'Cache Description', - ], - 'layout' => [ - 'name' => 'layout', - 'translate' => 'label,description', - 'instance' => \Magento\Framework\App\Cache\Type\Layout::class, - 'label' => 'Layouts', - 'description' => 'Layout building instructions', - ], - ] -]; diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml deleted file mode 100644 index 315ddd9cec67c..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Cache/etc/cache.xsd"> - <type name="config" translate="label,description" instance="Magento\Framework\App\Cache\Type\Config"> - <label>Configuration</label> - <description>Cache Description</description> - </type> - <type name="layout" translate="label,description" instance="Magento\Framework\App\Cache\Type\Layout"> - <label>Layouts</label> - <description>Layout building instructions</description> - </type> -</config> diff --git a/lib/internal/Magento/Framework/Cache/etc/cache.xsd b/lib/internal/Magento/Framework/Cache/etc/cache.xsd index 74b831bb6ac03..d997e295140f5 100644 --- a/lib/internal/Magento/Framework/Cache/etc/cache.xsd +++ b/lib/internal/Magento/Framework/Cache/etc/cache.xsd @@ -6,24 +6,36 @@ */ --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config" type="configType" /> - - <xs:complexType name="configType"> - <xs:sequence> - <xs:element type="cacheType" name="type" maxOccurs="unbounded" minOccurs="0"/> - </xs:sequence> - </xs:complexType> - <xs:complexType name="cacheType"> <xs:annotation> - <xs:documentation>Cache type declaration</xs:documentation> + <xs:documentation> + Cache type declaration + </xs:documentation> </xs:annotation> - <xs:choice maxOccurs="unbounded" minOccurs="0"> - <xs:element name="label" type="xs:string" /> - <xs:element name="description" type="xs:string" /> - </xs:choice> + <xs:all> + <xs:element name="label" type="xs:string" minOccurs="1" maxOccurs="1"/> + <xs:element name="description" type="xs:string" minOccurs="1" maxOccurs="1"/> + </xs:all> <xs:attribute type="xs:string" name="name" use="required"/> <xs:attribute type="xs:string" name="translate" use="optional"/> - <xs:attribute type="xs:string" name="instance" use="optional"/> + <xs:attribute type="xs:string" name="instance" use="required"/> + </xs:complexType> + + <xs:element name="config" type="configType"> + <xs:unique name="uniqueCacheName"> + <xs:annotation> + <xs:documentation> + Cache name must be unique. + </xs:documentation> + </xs:annotation> + <xs:selector xpath="type"/> + <xs:field xpath="@name"/> + </xs:unique> + </xs:element> + + <xs:complexType name="configType"> + <xs:sequence> + <xs:element type="cacheType" name="type" maxOccurs="unbounded" minOccurs="1"/> + </xs:sequence> </xs:complexType> </xs:schema> From 5abd3665e5f93588670a81503e6823d11d2e0d55 Mon Sep 17 00:00:00 2001 From: Abrar pathan <abrarkhan@krishtechnolabs.com> Date: Wed, 18 Mar 2020 20:08:48 +0530 Subject: [PATCH 439/734] fixed issue#27335 --- .../luma/Magento_Customer/web/css/source/_module.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 34a2dbfeca472..a0a36f55574fe 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -348,6 +348,12 @@ position: relative; } } + + .additional-addresses { + table > thead > tr > th { + white-space: nowrap; + } + } } // From 17ea2a55eb7abdb0889f2a208e70f8358a306d04 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 18 Mar 2020 09:42:02 -0500 Subject: [PATCH 440/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 8277366ce2335..7e3c487a9068d 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,33 +7498,33 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "dc324c150312177db4ca2858a2998825462f6aea" + "reference": "18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/dc324c150312177db4ca2858a2998825462f6aea", - "reference": "dc324c150312177db4ca2858a2998825462f6aea", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49", + "reference": "18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49", "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.3.0", "aws/aws-sdk-php": "^3.132", "codeception/codeception": "~2.4.5", - "composer/composer": "^1.4", - "consolidation/robo": "^1.0.0", + "composer/composer": "^1.6", "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "ext-curl": "*", "ext-dom": "*", "ext-json": "*", "ext-openssl": "*", - "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", "php": "~7.2.0||~7.3.0", "php-webdriver/webdriver": "^1.8.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0", + "symfony/console": "^4.4", + "symfony/finder": "^4.4", + "symfony/mime": "^5.0", + "symfony/process": "^4.4", "vlucas/phpdotenv": "^2.4" }, "replace": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-17T17:03:26+00:00" + "time": "2020-03-17T23:01:25+00:00" }, { "name": "mikey179/vfsstream", From 77c7055b871fa6abbd93730490aed68d53823976 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 18 Mar 2020 09:55:49 -0500 Subject: [PATCH 441/734] MQE-1960: MFTF 3.0.0 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 7e3c487a9068d..cc3996ad3208c 100644 --- a/composer.lock +++ b/composer.lock @@ -7498,12 +7498,12 @@ "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49" + "reference": "d82e6cf1e798a3266ae23582ae53fc6b77fa452d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49", - "reference": "18435a3d1547ad3fa2cce97d6c257fbf9b4f9c49", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/d82e6cf1e798a3266ae23582ae53fc6b77fa452d", + "reference": "d82e6cf1e798a3266ae23582ae53fc6b77fa452d", "shasum": "" }, "require": { @@ -7576,7 +7576,7 @@ "magento", "testing" ], - "time": "2020-03-17T23:01:25+00:00" + "time": "2020-03-18T14:48:53+00:00" }, { "name": "mikey179/vfsstream", From e396c01ad4a728c075d5abef614ba9fb9bb2b61c Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Wed, 18 Mar 2020 11:15:17 -0500 Subject: [PATCH 442/734] MC-31586: Customer address is duplicated after setBillingAddressOnCart GraphQL mutation. - review fixes --- .../Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 1 + .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 77eb622b70c2c..23fc35790ac0e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -118,6 +118,7 @@ private function createBillingAddress( $customerId = $context->getUserId(); // need to save address only for registered user and if save_in_address_book = true + // and address is not same as shipping if (0 !== $customerId && isset($addressInput['save_in_address_book']) && (bool)$addressInput['save_in_address_book'] && !$sameAsShipping diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index a36548fa222b0..4725a9012fc98 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -894,7 +894,7 @@ public function testSetBillingAddressAndPlaceOrder() street: ["test street 1", "test street 2"] city: "test city" region: "AZ" - postcode: "887766" + postcode: "88776" country_code: "US" telephone: "88776655" save_in_address_book: true From 81efe96e24e43f356559e5ee34edd2a2755aa572 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 18 Mar 2020 12:07:01 -0500 Subject: [PATCH 443/734] MC-32201: Reorder functionality --- .../Controller/AbstractController/Reorder.php | 1 + .../Model/Reorder/CustomerCartProvider.php | 106 +++++++++++++ .../Sales/Model/Reorder/Data/Error.php | 8 +- .../Model/Reorder/Data/ReorderOutput.php | 7 +- .../Magento/Sales/Model/Reorder/Reorder.php | 52 +++---- .../Unit/Controller/Guest/ReorderTest.php | 147 ------------------ .../SalesGraphQl/Model/Resolver/Reorder.php | 2 +- 7 files changed, 140 insertions(+), 183 deletions(-) create mode 100644 app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php delete mode 100644 app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index 7c670a3ec2ce0..062ad78e5001d 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -43,6 +43,7 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface * @param Registry $registry * @param ReorderHelper|null $reorderHelper * @param \Magento\Sales\Model\Reorder\Reorder|null $reorder + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Action\Context $context, diff --git a/app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php b/app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php new file mode 100644 index 0000000000000..698c20e980134 --- /dev/null +++ b/app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Reorder; + +use Magento\Framework\Exception\AlreadyExistsException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; + +/** + * Get customer cart or create empty cart. Ensure mask_id is created + */ +class CustomerCartProvider +{ + /** + * @var CartManagementInterface + */ + private $cartManagement; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var QuoteIdMaskResourceModel + */ + private $quoteIdMaskResourceModel; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedQuoteId; + + /** + * @param CartManagementInterface $cartManagement + * @param QuoteIdMaskFactory $quoteIdMaskFactory + * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + */ + public function __construct( + CartManagementInterface $cartManagement, + QuoteIdMaskFactory $quoteIdMaskFactory, + QuoteIdMaskResourceModel $quoteIdMaskResourceModel, + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + ) { + $this->cartManagement = $cartManagement; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel; + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + + /** + * Get customer cart + * + * @param int $customerId + * @return \Magento\Quote\Model\Quote + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function provide(int $customerId): \Magento\Quote\Model\Quote + { + try { + /** @var \Magento\Quote\Model\Quote $cart */ + $cart = $this->cartManagement->getCartForCustomer($customerId); + } catch (NoSuchEntityException $e) { + $this->cartManagement->createEmptyCartForCustomer($customerId); + $cart = $this->cartManagement->getCartForCustomer($customerId); + } + try { + $this->ensureQuoteMaskIdExist((int)$cart->getId()); + } catch (AlreadyExistsException $e) { + // do nothing, we already have masked id + } + + return $cart; + } + + /** + * Create masked id for customer's active quote if it's not exists + * + * @param int $quoteId + * @return void + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + private function ensureQuoteMaskIdExist(int $quoteId): void + { + try { + $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); + } catch (NoSuchEntityException $e) { + $maskedId = ''; + } + if ($maskedId === '') { + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->setQuoteId($quoteId); + $this->quoteIdMaskResourceModel->save($quoteIdMask); + } + } +} diff --git a/app/code/Magento/Sales/Model/Reorder/Data/Error.php b/app/code/Magento/Sales/Model/Reorder/Data/Error.php index b1e74bbfa9c5f..10332b7ccaadd 100644 --- a/app/code/Magento/Sales/Model/Reorder/Data/Error.php +++ b/app/code/Magento/Sales/Model/Reorder/Data/Error.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Reorder\Data; /** - * DTO represent error item + * DTO represents error item */ class Error { @@ -31,6 +33,8 @@ public function __construct(string $message, string $code) } /** + * Get error message + * * @return string */ public function getMessage(): string @@ -39,6 +43,8 @@ public function getMessage(): string } /** + * Get error code + * * @return string */ public function getCode(): string diff --git a/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php index 846b6bdc93377..70b7ddbe0d1a7 100644 --- a/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php +++ b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Model\Reorder\Data; use Magento\Quote\Api\Data\CartInterface; @@ -18,7 +19,7 @@ class ReorderOutput private $cart; /** - * @var array + * @var Error[] */ private $errors; @@ -33,6 +34,8 @@ public function __construct(CartInterface $cart, array $errors) } /** + * Get Shopping Cart + * * @return CartInterface */ public function getCart(): CartInterface @@ -41,6 +44,8 @@ public function getCart(): CartInterface } /** + * Get errors happened during reorder + * * @return Error[] */ public function getErrors(): array diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index e652a9777abd8..ff5b7f9b3d16b 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -3,25 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Model\Reorder; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; use Magento\Sales\Helper\Reorder as ReorderHelper; use Magento\Sales\Model\Order\Item; use Magento\Sales\Model\OrderFactory; /** - * Allows customer to quickly reorder previously added products and put them to the Cart + * Allows customer quickly to reorder previously added products and put them to the Cart + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Reorder { - /**#@+ * Error message codes */ @@ -42,17 +41,11 @@ class Reorder 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, ]; - /** * @var OrderFactory */ private $orderFactory; - /** - * @var CartManagementInterface - */ - private $cartManagement; - /** * @var ReorderHelper */ @@ -63,11 +56,6 @@ class Reorder */ private $logger; - /** - * @var CreateEmptyCartForCustomer - */ - private $createEmptyCartForCustomer; - /** * @var CartRepositoryInterface */ @@ -83,41 +71,44 @@ class Reorder */ private $errors = []; + /** + * @var CustomerCartProvider + */ + private $customerCartProvider; + /** * @param OrderFactory $orderFactory - * @param CartManagementInterface $cartManagement - * @param ReorderHelper $reorderHelper - * @param \Psr\Log\LoggerInterface $logger - * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer + * @param CustomerCartProvider $customerCartProvider * @param CartRepositoryInterface $cartRepository * @param ProductRepositoryInterface $productRepository + * @param ReorderHelper $reorderHelper + * @param \Psr\Log\LoggerInterface $logger */ public function __construct( OrderFactory $orderFactory, - CartManagementInterface $cartManagement, - CreateEmptyCartForCustomer $createEmptyCartForCustomer, + CustomerCartProvider $customerCartProvider, CartRepositoryInterface $cartRepository, ProductRepositoryInterface $productRepository, ReorderHelper $reorderHelper, \Psr\Log\LoggerInterface $logger ) { $this->orderFactory = $orderFactory; - $this->cartManagement = $cartManagement; - $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; $this->cartRepository = $cartRepository; $this->productRepository = $productRepository; $this->reorderHelper = $reorderHelper; $this->logger = $logger; + $this->customerCartProvider = $customerCartProvider; } /** - * Allows customer to quickly reorder previously added products and put them to the Cart + * Allows customer quickly to reorder previously added products and put them to the Cart * * @param string $orderNumber * @param string $storeId * @return Data\ReorderOutput * @throws InputException Order is not found * @throws NoSuchEntityException The specified customer does not exist. + * @throws \Magento\Framework\Exception\CouldNotSaveException Could not create customer Cart */ public function execute(string $orderNumber, string $storeId): Data\ReorderOutput { @@ -128,16 +119,10 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu __('Cannot find order number "%1" in store "%2"', $orderNumber, $storeId) ); } - $customerId = $order->getCustomerId(); + $customerId = (int)$order->getCustomerId(); $this->errors = []; - try { - /** @var \Magento\Quote\Model\Quote $cart */ - $cart = $this->cartManagement->getCartForCustomer($customerId); - } catch (NoSuchEntityException $e) { - $this->createEmptyCartForCustomer->execute($customerId); - $cart = $this->cartManagement->getCartForCustomer($customerId); - } + $cart = $this->customerCartProvider->provide($customerId); if (!$this->reorderHelper->canReorder($order->getId())) { $this->addError(__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); return $this->prepareOutput($cart); @@ -161,6 +146,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu try { $this->cartRepository->save($cart); } catch (\Magento\Framework\Exception\LocalizedException $e) { + // handle exception from \Magento\Quote\Model\QuoteRepository\SaveHandler::save $this->addError($e->getMessage()); } @@ -242,7 +228,7 @@ function ($key) use ($message) { * @param CartInterface $cart * @return Data\ReorderOutput */ - protected function prepareOutput(CartInterface $cart): Data\ReorderOutput + private function prepareOutput(CartInterface $cart): Data\ReorderOutput { $output = new Data\ReorderOutput($cart, $this->errors); $this->errors = []; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php deleted file mode 100644 index 964a10f232daf..0000000000000 --- a/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Sales\Test\Unit\Controller\Guest; - -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\Controller\Result\Redirect; -use Magento\Framework\Controller\Result\RedirectFactory; -use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\Registry; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Sales\Controller\Guest\OrderLoader; -use Magento\Sales\Controller\Guest\Reorder; -use Magento\Sales\Helper\Reorder as ReorderHelper; -use Magento\Sales\Model\Order; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; - -/** - * Test class for Reorder - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ReorderTest extends TestCase -{ - /** - * Stub Order Id - */ - private const STUB_ORDER_ID = 1; - - /** - * @var Reorder - */ - private $reorder; - - /** - * @var Registry|MockObject - */ - private $registryMock; - - /** - * @var OrderLoader|MockObject - */ - private $orderLoaderMock; - - /** - * @var RequestInterface|MockObject - */ - private $requestMock; - - /** - * @var RedirectFactory|MockObject - */ - private $resultRedirectFactoryMock; - - /** - * @var ReorderHelper|MockObject - */ - private $reorderHelperMock; - - /** - * @var MessageManagerInterface|MockObject - */ - private $messageManagerMock; - - /** - * Setup environment for test - */ - protected function setUp() - { - $contextMock = $this->createMock(Context::class); - $this->registryMock = $this->createMock(Registry::class); - $this->orderLoaderMock = $this->createMock(OrderLoader::class); - $this->requestMock = $this->createMock(RequestInterface::class); - $this->resultRedirectFactoryMock = $this->createMock(RedirectFactory::class); - $this->reorderHelperMock = $this->createMock(ReorderHelper::class); - $this->messageManagerMock = $this->createMock(MessageManagerInterface::class); - - $contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); - $contextMock->expects($this->once())->method('getResultRedirectFactory') - ->willReturn($this->resultRedirectFactoryMock); - $contextMock->expects($this->once())->method('getMessageManager') - ->willReturn($this->messageManagerMock); - - $objectManagerMock = $this->createMock(ObjectManagerInterface::class); - $objectManagerMock->expects($this->once())->method('get') - ->with(ReorderHelper::class) - ->willReturn($this->reorderHelperMock); - - ObjectManager::setInstance($objectManagerMock); - - $objectManager = new ObjectManagerHelper($this); - $this->reorder = $objectManager->getObject( - Reorder::class, - [ - 'context' => $contextMock, - 'orderLoader' => $this->orderLoaderMock, - 'registry' => $this->registryMock - ] - ); - } - - /** - * Test execute() with the reorder is not allowed - */ - public function testExecuteWithReorderIsNotAllowed() - { - $orderMock = $this->createMock(Order::class); - $orderMock->method('getId')->willReturn(self::STUB_ORDER_ID); - - $this->orderLoaderMock->method('load') - ->with($this->requestMock) - ->willReturn($this->resultRedirectFactoryMock); - - $this->registryMock->expects($this->once())->method('registry') - ->with('current_order') - ->willReturn($orderMock); - - $this->reorderHelperMock->method('canReorder')->with(self::STUB_ORDER_ID) - ->willReturn(false); - - $resultRedirectMock = $this->createMock(Redirect::class); - $this->resultRedirectFactoryMock->expects($this->once())->method('create')->willReturn($resultRedirectMock); - - $this->reorderHelperMock->method('canReorder')->with(self::STUB_ORDER_ID) - ->willReturn(false); - - /** Expected Error Message */ - $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') - ->with('Reorder is not available.')->willReturnSelf(); - $resultRedirectMock->expects($this->once()) - ->method('setPath') - ->with('checkout/cart')->willReturnSelf(); - - /** Assert result */ - $this->assertEquals($resultRedirectMock, $this->reorder->execute()); - } -} diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php index d6bdabea4998c..8bf4220d1ec3d 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -81,7 +81,7 @@ public function resolve( 'model' => $reorderOutput->getCart(), ], 'userInputErrors' => \array_map( - function(Error $error) { + function (Error $error) { return [ 'path' => [self::ARGUMENT_ORDER_NUMBER], 'code' => $error->getCode(), From 8c31040aeca2071aacaa11c04b466bb608e58310 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 18 Mar 2020 12:16:21 -0500 Subject: [PATCH 444/734] MC-32201: Reorder functionality --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index ff5b7f9b3d16b..456a600ee4241 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -192,7 +192,7 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi private function addError($message, string $code = null): void { $this->errors[] = new Data\Error( - $message, + (string)$message, $code ?? $this->getErrorCode((string)$message) ); } From f6d047a51fd91a02c4ab9347dc2f41532d827ebb Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 18 Mar 2020 19:47:45 +0200 Subject: [PATCH 445/734] fix expected count items --- .../testsuite/Magento/Backend/Model/Dashboard/ChartTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php index fbca1b8af7e0e..c7d2e3ef17ec0 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -7,7 +7,6 @@ namespace Magento\Backend\Model\Dashboard; -use Magento\Backend\Model\Dashboard\Chart; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -62,7 +61,7 @@ public function getChartDataProvider(): array 'quantity' ], [ - 6, + 19, '1m', 'quantity' ], From b3520743fe24806a0ddba2ccc1f59eab6eb33990 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Wed, 18 Mar 2020 15:35:46 -0500 Subject: [PATCH 446/734] MC-32201: Reorder functionality --- .../Model/Cart}/CustomerCartProvider.php | 9 +- .../Model/Cart/CreateEmptyCartForCustomer.php | 86 ++++--------------- .../Magento/Sales/Model/Reorder/Reorder.php | 1 + .../Magento/SalesGraphQl/etc/schema.graphqls | 2 +- .../Magento/GraphQl/Sales/ReorderTest.php | 5 -- 5 files changed, 24 insertions(+), 79 deletions(-) rename app/code/Magento/{Sales/Model/Reorder => Quote/Model/Cart}/CustomerCartProvider.php (93%) diff --git a/app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php b/app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php similarity index 93% rename from app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php rename to app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php index 698c20e980134..e37658127dcc1 100644 --- a/app/code/Magento/Sales/Model/Reorder/CustomerCartProvider.php +++ b/app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php @@ -5,11 +5,12 @@ */ declare(strict_types=1); -namespace Magento\Sales\Model\Reorder; +namespace Magento\Quote\Model\Cart; use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; @@ -61,14 +62,14 @@ public function __construct( * Get customer cart * * @param int $customerId - * @return \Magento\Quote\Model\Quote + * @return Quote * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function provide(int $customerId): \Magento\Quote\Model\Quote + public function provide(int $customerId): Quote { try { - /** @var \Magento\Quote\Model\Quote $cart */ + /** @var Quote $cart */ $cart = $this->cartManagement->getCartForCustomer($customerId); } catch (NoSuchEntityException $e) { $this->cartManagement->createEmptyCartForCustomer($customerId); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php index 52fb19df34a8a..c84ab1ee148ea 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php @@ -8,10 +8,11 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Quote\Api\CartManagementInterface; -use Magento\Quote\Model\QuoteIdMask; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; +use Magento\Quote\Model\Cart\CustomerCartProvider; +use Magento\Framework\App\ObjectManager; /** * Create empty cart for customer @@ -19,41 +20,32 @@ class CreateEmptyCartForCustomer { /** - * @var CartManagementInterface - */ - private $cartManagement; - - /** - * @var QuoteIdMaskFactory - */ - private $quoteIdMaskFactory; - - /** - * @var QuoteIdMaskResourceModel + * @var QuoteIdToMaskedQuoteIdInterface */ - private $quoteIdMaskResourceModel; + private $quoteIdToMaskedQuoteId; /** - * @var QuoteIdToMaskedQuoteIdInterface + * @var CustomerCartProvider */ - private $quoteIdToMaskedQuoteId; + private $cartProvider; /** * @param CartManagementInterface $cartManagement * @param QuoteIdMaskFactory $quoteIdMaskFactory * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + * @param CustomerCartProvider $cartProvider + * @SuppressWarnings(PHPMD.UnusedFormalParameter) Parameters can't be removed according to backward compatibility */ public function __construct( CartManagementInterface $cartManagement, QuoteIdMaskFactory $quoteIdMaskFactory, QuoteIdMaskResourceModel $quoteIdMaskResourceModel, - QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId, + CustomerCartProvider $cartProvider ) { - $this->cartManagement = $cartManagement; - $this->quoteIdMaskFactory = $quoteIdMaskFactory; - $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel; $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + $this->cartProvider = $cartProvider ?? ObjectManager::getInstance()->get(CustomerCartProvider::class); } /** @@ -62,59 +54,15 @@ public function __construct( * @param int $customerId * @param string|null $predefinedMaskedQuoteId * @return string - */ - public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string - { - $quoteId = (int) $this->cartManagement->createEmptyCartForCustomer($customerId); - - if ($predefinedMaskedQuoteId !== null) { - $maskedId = $this->createPredefinedMaskId($quoteId, $predefinedMaskedQuoteId); - } else { - $maskedId = $this->getQuoteMaskId($quoteId); - } - - return $maskedId; - } - - /** - * Create quote masked id from predefined value - * - * @param int $quoteId - * @param string $maskId - * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException - */ - private function createPredefinedMaskId(int $quoteId, string $maskId): string - { - /** @var QuoteIdMask $quoteIdMask */ - $quoteIdMask = $this->quoteIdMaskFactory->create(); - $quoteIdMask->setQuoteId($quoteId); - $quoteIdMask->setMaskedId($maskId); - - $this->quoteIdMaskResourceModel->save($quoteIdMask); - - return $quoteIdMask->getMaskedId(); - } - - /** - * Fetch or create masked id for customer's active quote - * - * @param int $quoteId - * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException + * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\NoSuchEntityException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) Parameter can't be removed according to backward compatibility */ - private function getQuoteMaskId(int $quoteId): string + public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string { - $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); - if ($maskedId === '') { - $quoteIdMask = $this->quoteIdMaskFactory->create(); - $quoteIdMask->setQuoteId($quoteId); - - $this->quoteIdMaskResourceModel->save($quoteIdMask); - $maskedId = $quoteIdMask->getMaskedId(); - } + $cart = $this->cartProvider->provide($customerId); + $quoteId = (int) $cart->getId(); - return $maskedId; + return $this->quoteIdToMaskedQuoteId->execute($quoteId); } } diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 456a600ee4241..e494c7dd54237 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -11,6 +11,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Cart\CustomerCartProvider; use Magento\Sales\Helper\Reorder as ReorderHelper; use Magento\Sales\Model\Order\Item; use Magento\Sales\Model\OrderFactory; diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index e47c01f6948be..1ed0a6be58665 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -39,4 +39,4 @@ enum CheckoutUserInputErrorCodes { NOT_SALABLE INSUFFICIENT_STOCK UNDEFINED -} \ No newline at end of file +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 9452c4281f758..f43d2f2384529 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -28,11 +28,6 @@ class ReorderTest extends GraphQlAbstract */ private const ORDER_NUMBER = '100000001'; - /** - * Incremented order number - */ - private const INCREMENTED_ORDER_NUMBER = '100001001'; - /** * Customer email */ From 216f71b30c52daaac674015e6e6428c6220c16b1 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Wed, 18 Mar 2020 16:21:12 -0500 Subject: [PATCH 447/734] MC-32387: Add pagination support to categoryList query - added new categories query to support pagination - added api-functional tests to cover new query --- .../CatalogGraphQl/Model/AttributesJoiner.php | 37 +- .../Model/Category/CategoryFilter.php | 36 +- .../Model/Resolver/CategoriesQuery.php | 107 +++ .../Model/Resolver/CategoryList.php | 3 +- .../CatalogGraphQl/etc/schema.graphqls | 13 +- .../CategoriesQuery/CategoriesFilterTest.php | 735 +++++++++++++++++ .../CategoriesPaginationTest.php | 244 ++++++ .../CategoriesQuery/CategoryTreeTest.php | 743 ++++++++++++++++++ 8 files changed, 1905 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index b3a9672a47010..69592657241a0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -45,7 +45,7 @@ public function __construct(array $fieldToAttributeMap = []) * @param AbstractCollection $collection * @return void */ - public function join(FieldNode $fieldNode, AbstractCollection $collection) : void + public function join(FieldNode $fieldNode, AbstractCollection $collection): void { foreach ($this->getQueryFields($fieldNode) as $field) { $this->addFieldToCollection($collection, $field); @@ -60,19 +60,20 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection) : voi */ public function getQueryFields(FieldNode $fieldNode): array { - if (!isset($this->queryFields[$fieldNode->name->value])) { - $this->queryFields[$fieldNode->name->value] = []; + if (null === $this->getFieldNodeSelections($fieldNode)) { $query = $fieldNode->selectionSet->selections; + $selectedFields = []; /** @var FieldNode $field */ foreach ($query as $field) { if ($field->kind === 'InlineFragment') { continue; } - $this->queryFields[$fieldNode->name->value][] = $field->name->value; + $selectedFields[] = $field->name->value; } + $this->setSelectionsForFieldNode($fieldNode, $selectedFields); } - return $this->queryFields[$fieldNode->name->value]; + return $this->getFieldNodeSelections($fieldNode); } /** @@ -83,7 +84,7 @@ public function getQueryFields(FieldNode $fieldNode): array * @param AbstractCollection $collection * @param string $field */ - private function addFieldToCollection(AbstractCollection $collection, string $field) + private function addFieldToCollection(AbstractCollection $collection, string $field): void { $attribute = isset($this->fieldToAttributeMap[$field]) ? $this->fieldToAttributeMap[$field] : $field; @@ -99,4 +100,28 @@ private function addFieldToCollection(AbstractCollection $collection, string $fi } } } + + /** + * Get the fields selections for a query node + * + * @param FieldNode $fieldNode + * @return array|null + */ + private function getFieldNodeSelections(FieldNode $fieldNode): ?array + { + return $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] ?? null; + } + + /** + * Set the field selections for a query node + * + * Index nodes by name and position so nodes with same name don't collide + * + * @param FieldNode $fieldNode + * @param array $selectedFields + */ + private function setSelectionsForFieldNode(FieldNode $fieldNode, array $selectedFields): void + { + $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] = $selectedFields; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index ff9bd862267f2..09694b7cf1a0c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\ArgumentApplier\Filter; use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match; use Magento\Search\Model\Query; @@ -19,7 +20,7 @@ use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; /** - * Category filter allows to filter collection using 'id, url_key, name' from search criteria. + * Category filter allows filtering category results by attributes. */ class CategoryFilter { @@ -54,25 +55,50 @@ public function __construct( } /** - * Search for categories, return list of ids + * Search for categories * * @param array $criteria * @param StoreInterface $store * @return int[] * @throws InputException */ - public function getResult(array $criteria, StoreInterface $store): array + public function getResult(array $criteria, StoreInterface $store) { $categoryIds = []; - $criteria[Filter::ARGUMENT_NAME] = $this->formatMatchFilters($criteria['filters'], $store); $criteria[Filter::ARGUMENT_NAME][CategoryInterface::KEY_IS_ACTIVE] = ['eq' => 1]; $searchCriteria = $this->searchCriteriaBuilder->build('categoryList', $criteria); + $pageSize = $criteria['pageSize'] ?? 20; + $currentPage = $criteria['currentPage'] ?? 1; + $searchCriteria->setPageSize($pageSize)->setCurrentPage($currentPage); + $categories = $this->categoryList->getList($searchCriteria); foreach ($categories->getItems() as $category) { $categoryIds[] = (int)$category->getId(); } - return $categoryIds; + + $totalPages = 0; + if ($categories->getTotalCount() > 0 && $searchCriteria->getPageSize() > 0) { + $totalPages = ceil($categories->getTotalCount() / $searchCriteria->getPageSize()); + } + if ($searchCriteria->getCurrentPage() > $totalPages && $categories->getTotalCount() > 0) { + throw new GraphQlInputException( + __( + 'currentPage value %1 specified is greater than the %2 page(s) available.', + [$searchCriteria->getCurrentPage(), $totalPages] + ) + ); + } + + return [ + 'category_ids' => $categoryIds, + 'total_count' => $categories->getTotalCount(), + 'page_info' => [ + 'total_pages' => $totalPages, + 'page_size' => $searchCriteria->getPageSize(), + 'current_page' => $searchCriteria->getCurrentPage(), + ] + ]; } /** diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php new file mode 100644 index 0000000000000..d06dace3cd76b --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver; + +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; +use Magento\CatalogGraphQl\Model\Category\CategoryFilter; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; + +/** + * Categories resolver, used for GraphQL category data request processing. + */ +class CategoriesQuery implements ResolverInterface +{ + /** + * @var CategoryTree + */ + private $categoryTree; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @var CategoryFilter + */ + private $categoryFilter; + + /** + * @var ExtractDataFromCategoryTree + */ + private $extractDataFromCategoryTree; + + /** + * @param CategoryTree $categoryTree + * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree + * @param CategoryFilter $categoryFilter + * @param CollectionFactory $collectionFactory + */ + public function __construct( + CategoryTree $categoryTree, + ExtractDataFromCategoryTree $extractDataFromCategoryTree, + CategoryFilter $categoryFilter, + CollectionFactory $collectionFactory + ) { + $this->categoryTree = $categoryTree; + $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; + $this->categoryFilter = $categoryFilter; + $this->collectionFactory = $collectionFactory; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $store = $context->getExtensionAttributes()->getStore(); + + if (isset($args['currentPage']) && $args['currentPage'] < 1) { + throw new GraphQlInputException(__('currentPage value must be greater than 0.')); + } + if (isset($args['pageSize']) && $args['pageSize'] < 1) { + throw new GraphQlInputException(__('pageSize value must be greater than 0.')); + } + if (!isset($args['filters'])) { + //When no filters are specified, get the root category + $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; + } + + $filterResult = $this->categoryFilter->getResult($args, $store); + + $rootCategoryIds = $filterResult['category_ids']; + $filterResult['items'] = $this->fetchCategories($rootCategoryIds, $info); + return $filterResult; + } + + /** + * Fetch category tree data + * + * @param array $categoryIds + * @param ResolveInfo $info + * @return array + */ + private function fetchCategories(array $categoryIds, ResolveInfo $info) + { + $fetchedCategories = []; + foreach ($categoryIds as $categoryId) { + $categoryTree = $this->categoryTree->getTree($info, $categoryId); + if (empty($categoryTree)) { + continue; + } + $fetchedCategories[] = current($this->extractDataFromCategoryTree->execute($categoryTree)); + } + + return $fetchedCategories; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index ef2af022fd1bc..f32c5a1f38425 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -65,7 +65,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; } try { - $rootCategoryIds = $this->categoryFilter->getResult($args, $store); + $filterResults = $this->categoryFilter->getResult($args, $store); + $rootCategoryIds = $filterResults['category_ids']; } catch (InputException $e) { throw new GraphQlInputException(__($e->getMessage())); } diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index c0cc2f33c0cb7..8783e9394c7e4 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -15,8 +15,13 @@ type Query { ): CategoryTree @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @deprecated(reason: "Use 'categoryList' query instead of 'category' query") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") categoryList( - filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") + filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") ): [CategoryTree] @doc(description: "Returns an array of categories based on the specified filters.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") + categories ( + filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional.") + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1.") + ): CategoryResult @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoriesQuery") } type Price @doc(description: "Price is deprecated, replaced by ProductPrice. The Price object defines the price of a product as well as any tax-related adjustments.") { @@ -133,6 +138,12 @@ type CategoryTree implements CategoryInterface @doc(description: "Category Tree children: [CategoryTree] @doc(description: "Child categories tree.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } +type CategoryResult @doc(description: "Collection of CategoryTree object and pagination information.") { + items: [CategoryTree] @doc(description: "List of categories that match filter criteria.") + page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.") + total_count: Int @doc(description: "The total number of categories that match the criteria.") +} + type CustomizableDateOption implements CustomizableOptionInterface @doc(description: "CustomizableDateOption contains information about a date picker that is defined as part of a customizable option.") { value: CustomizableDateValue @doc(description: "An object that defines a date field in a customizable option.") product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php new file mode 100644 index 0000000000000..883b58474ae79 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php @@ -0,0 +1,735 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test categories query filtering works as expected + */ +class CategoriesFilterTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterSingleCategoryDataProvider + * @param string $field + * @param string $condition + * @param string $value + * @param array $expectedResult + */ + public function testFilterSingleCategoryByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categories(filters: { $field : { $condition : "$value" } }){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $this->assertResponseFields($result['categories']['items'][0], $expectedResult); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterMultipleCategoriesDataProvider + * @param $field + * @param $condition + * @param $value + * @param $expectedResult + */ + public function testFilterMultipleCategoriesByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categories(filters: { $field : { $condition : $value } }){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(count($expectedResult), $result['categories']['items']); + foreach ($expectedResult as $i => $expected) { + $this->assertResponseFields($result['categories']['items'][$i], $expected); + } + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterCategoryByMultipleFields() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["6","7","8","9","10"]}, name: {match: "Movable"}}){ + total_count + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(3, $result['categories']['items']); + $this->assertEquals(3, $result['categories']['total_count']); + + $expectedCategories = [7 => 'Movable', 9 => 'Movable Position 1', 10 => 'Movable Position 2']; + $actualCategories = array_column($result['categories']['items'], 'name', 'id'); + $this->assertEquals($expectedCategories, $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterWithInactiveCategory() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {in: ["inactive", "category-2"]}}){ + items{ + id + name + url_key + url_path + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $actualCategories = array_column($result['categories']['items'], 'url_key', 'id'); + $this->assertContains('category-2', $actualCategories); + $this->assertNotContains('inactive', $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testQueryChildCategoriesWithProducts() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3"]}}){ + items{ + id + name + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + } + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $baseCategory = $result['categories']['items'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.1', 'description' => 'Category 1.1 description.'], + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.1', $firstChildCategory['name']); + $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + //Check second child category + $secondChildCategory = $baseCategory['children'][1]; + $this->assertEquals('Category 1.2', $secondChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($secondChildCategory, $firstChildCategoryChildren); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories_disabled.php + */ + public function testQueryCategoryWithDisabledChildren() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3"]}}){ + items{ + id + name + image + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + children{ + name + image + } + } + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $baseCategory = $result['categories']['items'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.2', $firstChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); + + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testNoResultsFound() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {in: ["inactive", "does-not-exist"]}}){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('categories', $result); + $this->assertEquals([], $result['categories']['items']); + } + + /** + * When no filters are supplied, the root category is returned + * + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testEmptyFiltersReturnRootCategory() + { + $query = <<<QUERY +{ + categories{ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $storeRootCategoryId = $storeManager->getStore()->getRootCategoryId(); + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('categories', $result); + $this->assertEquals('Default Category', $result['categories']['items'][0]['name']); + $this->assertEquals($storeRootCategoryId, $result['categories']['items'][0]['id']); + } + + /** + * Filtering with match value less than minimum query should return empty result + * + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage Invalid match filter. Minimum query length is 3. + */ + public function testMinimumMatchQueryLength() + { + $query = <<<QUERY +{ + categories(filters: {name: {match: "mo"}}){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * Test category image full name is returned + * + * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_long_image_name.php + */ + public function testCategoryImageName() + { + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = Bootstrap::getObjectManager()->get(CategoryCollection::class); + $categoryModel = $categoryCollection + ->addAttributeToSelect('image') + ->addAttributeToFilter('name', ['eq' => 'Parent Image Category']) + ->getFirstItem(); + $categoryId = $categoryModel->getId(); + + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["$categoryId"]}}) { + items{ + id + name + image + } + } +} +QUERY; + $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $storeBaseUrl = $storeManager->getStore()->getBaseUrl('media'); + + $expected = "catalog/category/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg"; + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . $expected; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']; + $this->assertArrayNotHasKey('errors', $response); + $this->assertNotEmpty($response['categories']['items']); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); + $categories['items'][0]['image'] = str_replace('index.php/', '', $categories['items'][0]['image']); + $this->assertEquals('Parent Image Category', $categories['items'][0]['name']); + $this->assertEquals($expectedImageUrl, $categories['items'][0]['image']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathTopLevelCategory() + { + $urlPath = 'category-1'; + $query = <<<QUERY +{ + categories(filters: {url_path: {eq: "$urlPath"}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(1, $categories); + $this->assertEquals($urlPath, $categories['items'][0]['url_path']); + $this->assertEquals('Category 1', $categories['items'][0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNestedCategory() + { + $urlPath = 'category-1/category-1-1/category-1-1-1'; + $query = <<<QUERY +{ + categories(filters: {url_path: {eq: "$urlPath"}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(1, $categories); + $this->assertEquals($urlPath, $categories['items'][0]['url_path']); + $this->assertEquals('Category 1.1.1', $categories['items'][0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathMultipleCategories() + { + $urlPaths = ['category-1/category-1-1', 'inactive', 'movable-position-2']; + $urlPathsString = '"' . implode('", "', $urlPaths) . '"'; + $query = <<<QUERY +{ + categories(filters: {url_path: {in: [$urlPathsString]}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(2, $categories['items']); + $this->assertEquals($urlPaths[0], $categories['items'][0]['url_path']); + $this->assertEquals('Category 1.1', $categories['items'][0]['name']); + $this->assertEquals($urlPaths[2], $categories['items'][1]['url_path']); + $this->assertEquals('Movable Position 2', $categories['items'][1]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNoResults() + { + $query = <<<QUERY +{ + categories(filters: {url_path: {in: ["not-a-category url path"]}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(0, $categories['items']); + } + + /** + * @return array + */ + public function filterSingleCategoryDataProvider(): array + { + return [ + [ + 'ids', + 'eq', + '4', + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ] + ], + [ + 'name', + 'match', + 'Movable Position 2', + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ], + [ + 'url_key', + 'eq', + 'category-1-1-1', + [ + 'id' => '5', + 'name' => 'Category 1.1.1', + 'url_key' => 'category-1-1-1', + 'url_path' => 'category-1/category-1-1/category-1-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4/5', + 'position' => '1' + ] + ], + ]; + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function filterMultipleCategoriesDataProvider(): array + { + return[ + //Filter by multiple IDs + [ + 'ids', + 'in', + '["4", "9", "10"]', + [ + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ], + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ] + ], + //Filter by multiple url keys + [ + 'url_key', + 'in', + '["category-1-2", "movable"]', + [ + [ + 'id' => '7', + 'name' => 'Movable', + 'url_key' => 'movable', + 'url_path' => 'movable', + 'children_count' => '0', + 'path' => '1/2/7', + 'position' => '3' + ], + [ + 'id' => '13', + 'name' => 'Category 1.2', + 'url_key' => 'category-1-2', + 'url_path' => 'category-1/category-1-2', + 'children_count' => '0', + 'path' => '1/2/3/13', + 'position' => '2' + ] + ] + ], + //Filter by matching multiple names + [ + 'name', + 'match', + '"Position"', + [ + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ], + [ + 'id' => '11', + 'name' => 'Movable Position 3', + 'url_key' => 'movable-position-3', + 'url_path' => 'movable-position-3', + 'children_count' => '0', + 'path' => '1/2/11', + 'position' => '7' + ] + ] + ] + ]; + } + + /** + * Check category products + * + * @param array $category + * @param array $expectedProducts + */ + private function assertCategoryProducts(array $category, array $expectedProducts) + { + $this->assertEquals(count($expectedProducts), $category['products']['total_count']); + $this->assertCount(count($expectedProducts), $category['products']['items']); + $this->assertResponseFields($category['products']['items'], $expectedProducts); + } + + /** + * Check category child categories + * + * @param array $category + * @param array $expectedChildren + */ + private function assertCategoryChildren(array $category, array $expectedChildren) + { + $this->assertArrayHasKey('children', $category); + $this->assertCount(count($expectedChildren), $category['children']); + foreach ($expectedChildren as $i => $expectedChild) { + $this->assertResponseFields($category['children'][$i], $expectedChild); + } + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php new file mode 100644 index 0000000000000..28b864da119ff --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php @@ -0,0 +1,244 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\TestFramework\TestCase\GraphQlAbstract; +use function sprintf; + +/** + * Test pagination for the categories query + */ +class CategoriesPaginationTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testDefaultPagination() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3", "4", "5", "6", "7", "8", "9"]}}) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(6, $categories); + $this->assertEquals(count($categories), $response['categories']['total_count']); + $this->assertArrayHasKey('page_info', $response['categories']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(1, $pageInfo['current_page']); + $this->assertEquals(20, $pageInfo['page_size']); + $this->assertEquals(1, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testPageSize() + { + $query = <<<QUERY +{ + categories( + filters: {ids: {in: ["3", "4", "5", "6", "7", "8", "9"]}} + pageSize: 2 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(2, $categories); + $this->assertNotEquals(count($categories), $response['categories']['total_count']); + $this->assertEquals(6, $response['categories']['total_count']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(1, $pageInfo['current_page']); + $this->assertEquals(2, $pageInfo['page_size']); + $this->assertEquals(3, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testCurrentPage() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 3 + currentPage: 3 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(1, $categories); + $this->assertEquals(7, $response['categories']['total_count']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(3, $pageInfo['current_page']); + $this->assertEquals(3, $pageInfo['page_size']); + $this->assertEquals(3, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testPaging() + { + $baseQuery = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 2 + currentPage: %s + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $page1Query = sprintf($baseQuery, 1); + $page1Result = $this->graphQlQuery($page1Query); + $this->assertEquals('Default Category', $page1Result['categories']['items'][0]['name']); + $this->assertEquals('Category 1', $page1Result['categories']['items'][1]['name']); + $this->assertEquals(7, $page1Result['categories']['total_count']); + + $page2Query = sprintf($baseQuery, 2); + $page2Result = $this->graphQlQuery($page2Query); + $this->assertEquals('Category 1.1', $page2Result['categories']['items'][0]['name']); + $this->assertEquals('Category 1.1.1', $page2Result['categories']['items'][1]['name']); + + $lastPageQuery = sprintf($baseQuery, $page1Result['categories']['page_info']['total_pages']); + $lastPageResult = $this->graphQlQuery($lastPageQuery); + $this->assertCount(1, $lastPageResult['categories']['items']); + $this->assertEquals('Category 1.2', $lastPageResult['categories']['items'][0]['name']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage currentPage value must be greater than 0. + */ + public function testCurrentPageZero() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + currentPage: 0 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage pageSize value must be greater than 0. + */ + public function testPageSizeZero() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 0 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage currentPage value 6 specified is greater than the 2 page(s) available. + */ + public function testCurrentPageTooLarge() + { + $query = <<<QUERY +{ + categories( + filters: {url_key: {in: ["category-1", "category-1-1", "category-1-1-1"]}} + pageSize: 2 + currentPage: 6 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php new file mode 100644 index 0000000000000..fe545f4f7ebf7 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php @@ -0,0 +1,743 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\CategoryRepository; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use function assertCount; + +/** + * Test category tree data is returned correctly from "categories" query + */ +class CategoryTreeTest extends GraphQlAbstract +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CategoryRepository + */ + private $categoryRepository; + + /** + * @var Store + */ + private $store; + + /** + * @var MetadataPool + */ + private $metadataPool; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->categoryRepository = $this->objectManager->get(CategoryRepository::class); + $this->store = $this->objectManager->get(Store::class); + $this->metadataPool = $this->objectManager->get(MetadataPool::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCategoriesTree() + { + $rootCategoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$rootCategoryId}"}}) { + items{ + id + level + description + path + path_in_store + product_count + url_key + url_path + children { + id + description + available_sort_by + default_sort_by + image + level + children { + id + filter_price_range + description + image + meta_keywords + level + is_anchor + children { + level + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertArrayHasKey('categories', $response); + $baseCategory = $response['categories']['items'][0]; + $this->assertEquals( + 'Its a description of Test Category 1.2', + $baseCategory['children'][0]['children'][1]['description'] + ); + $this->assertEquals('default-category', $baseCategory['url_key']); + $this->assertEquals([], $baseCategory['children'][0]['available_sort_by']); + $this->assertEquals('name', $baseCategory['children'][0]['default_sort_by']); + $this->assertCount(7, $baseCategory['children']); + $this->assertCount(2, $baseCategory['children'][0]['children']); + $this->assertEquals(13, $baseCategory['children'][0]['children'][1]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testRootCategoryTree() + { + $query = <<<QUERY +{ + categories { + items{ + id + level + description + path + path_in_store + product_count + url_key + url_path + children { + id + description + available_sort_by + default_sort_by + image + level + children { + id + filter_price_range + description + image + meta_keywords + level + is_anchor + children { + level + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertArrayHasKey('categories', $response); + $baseCategory = $response['categories']['items'][0]; + $this->assertEquals( + 'Its a description of Test Category 1.2', + $baseCategory['children'][0]['children'][1]['description'] + ); + $this->assertEquals('default-category', $baseCategory['url_key']); + $this->assertEquals([], $baseCategory['children'][0]['available_sort_by']); + $this->assertEquals('name', $baseCategory['children'][0]['default_sort_by']); + $this->assertCount(7, $baseCategory['children']); + $this->assertCount(2, $baseCategory['children'][0]['children']); + $this->assertEquals(13, $baseCategory['children'][0]['children'][1]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testCategoriesTreeWithDisabledCategory() + { + $category = $this->categoryRepository->get(3); + $category->setIsActive(false); + $this->categoryRepository->save($category); + + $rootCategoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$rootCategoryId}"}}) { + items{ + id + name + level + description + children { + id + name + productImagePreview: products(pageSize: 1) { + items { + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('categories', $response); + $this->assertArrayHasKey('children', $response['categories']['items'][0]); + $this->assertSame(6, count($response['categories']['items'][0]['children'])); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetCategoryById() + { + $categoryId = 13; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertEquals('Category 1.2', $response['categories']['items'][0]['name']); + $this->assertEquals(13, $response['categories']['items'][0]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetDisabledCategory() + { + $categoryId = 8; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetCategoryIdZero() + { + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "0"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + } + + public function testNonExistentCategoryWithProductCount() + { + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "99"}}) { + total_count + items{ + product_count + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + $this->assertEquals(0, $response['categories']['total_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCategoryProducts() + { + $categoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + products { + total_count + page_info { + current_page + page_size + } + items { + attribute_set_id + country_of_manufacture + created_at + description { + html + } + gift_message_available + id + categories { + name + url_path + available_sort_by + level + } + image { url, label } + meta_description + meta_keyword + meta_title + media_gallery_entries { + disabled + file + id + label + media_type + position + types + content { + base64_encoded_data + type + name + } + video_content { + media_type + video_description + video_metadata + video_provider + video_title + video_url + } + } + name + new_from_date + new_to_date + options_container + price { + minimalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + maximalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + regularPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + } + product_links { + link_type + linked_product_sku + linked_product_type + position + sku + } + short_description { + html + } + sku + small_image { url, label } + thumbnail { url, label } + special_from_date + special_price + special_to_date + swatch_image + tier_price + tier_prices { + customer_group_id + percentage_value + qty + value + website_id + } + type_id + updated_at + url_key + url_path + websites { + id + name + code + sort_order + default_group_id + is_default + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('products', $response['categories']['items'][0]); + $baseCategory = $response['categories']['items'][0]; + $this->assertArrayHasKey('total_count', $baseCategory['products']); + $this->assertGreaterThanOrEqual(1, $baseCategory['products']['total_count']); + $this->assertEquals(1, $baseCategory['products']['page_info']['current_page']); + $this->assertEquals(20, $baseCategory['products']['page_info']['page_size']); + $this->assertArrayHasKey('sku', $baseCategory['products']['items'][0]); + $firstProduct = $baseCategory['products']['items'][0]; + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); + $firstProductModel = $productRepository->get($firstProduct['sku'], false, null, true); + $this->assertBaseFields($firstProductModel, $firstProduct); + $this->assertAttributes($firstProduct); + $this->assertWebsites($firstProductModel, $firstProduct['websites']); + $this->assertEquals('Category 1', $firstProduct['categories'][0]['name']); + $this->assertEquals('category-1/category-1-1', $firstProduct['categories'][1]['url_path']); + $this->assertCount(3, $firstProduct['categories']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testAnchorCategory() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {eq: "category-1"}}) { + items { + name + products(sort: {name: DESC}) { + total_count + items { + sku + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $expectedResponse = [ + 'categories' => [ + 'items' => [ + 0 => [ + 'name' => 'Category 1', + 'products' => [ + 'total_count' => 3, + 'items' => [ + ['sku' => '12345'], + ['sku' => 'simple-4'], + ['sku' => 'simple'] + ] + ] + ] + ] + ] + ]; + $this->assertEquals($expectedResponse, $response); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testBreadCrumbs() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {eq: "category-1-1-1"}}) { + items{ + name + breadcrumbs { + category_id + category_name + category_level + category_url_key + category_url_path + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $expectedResponse = [ + 'categories' => [ + 'items' => [ + 0 => [ + 'name' => 'Category 1.1.1', + 'breadcrumbs' => [ + [ + 'category_id' => 3, + 'category_name' => "Category 1", + 'category_level' => 2, + 'category_url_key' => "category-1", + 'category_url_path' => "category-1" + ], + [ + 'category_id' => 4, + 'category_name' => "Category 1.1", + 'category_level' => 3, + 'category_url_key' => "category-1-1", + 'category_url_path' => "category-1/category-1-1" + ], + ] + ] + ] + ] + ]; + $this->assertEquals($expectedResponse, $response); + } + + /** + * Test category image is returned as full url (not relative path) + * + * @param string $imagePrefix + * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_image.php + * @dataProvider categoryImageDataProvider + */ + public function testCategoryImage(?string $imagePrefix) + { + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->get(CategoryCollection::class); + $categoryModel = $categoryCollection + ->addAttributeToSelect('image') + ->addAttributeToFilter('name', ['eq' => 'Parent Image Category']) + ->getFirstItem(); + $categoryId = $categoryModel->getId(); + + if ($imagePrefix !== null) { + // update image to account for different stored image formats + $connection = $categoryCollection->getConnection(); + $productLinkField = $this->metadataPool + ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + + $defaultStoreId = $this->store->getId(); + + $imageAttributeValue = $imagePrefix . basename($categoryModel->getImage()); + + if (!empty($imageAttributeValue)) { + $query = sprintf( + 'UPDATE %s SET `value` = "%s" ' . + 'WHERE `%s` = %d ' . + 'AND `store_id`= %d ' . + 'AND `attribute_id` = ' . + '(SELECT `ea`.`attribute_id` FROM %s ea WHERE `ea`.`attribute_code` = "image" LIMIT 1)', + $connection->getTableName('catalog_category_entity_varchar'), + $imageAttributeValue, + $productLinkField, + $categoryModel->getData($productLinkField), + $defaultStoreId, + $connection->getTableName('eav_attribute') + ); + $connection->query($query); + } + } + + $query = <<<QUERY +{ +categories(filters: {ids: {in: ["$categoryId"]}}) { + items { + id + name + url_key + image + children { + id + name + url_key + image + } + } +} +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertNotEmpty($response['categories']); + $categories = $response['categories']['items']; + $storeBaseUrl = $this->objectManager->get(StoreManagerInterface::class)->getStore()->getBaseUrl('media'); + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . ltrim($categoryModel->getImage(), '/'); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); + + $this->assertEquals($categoryId, $categories[0]['id']); + $this->assertEquals('Parent Image Category', $categories[0]['name']); + $categories[0]['image'] = str_replace('index.php/', '', $categories[0]['image']); + $this->assertEquals($expectedImageUrl, $categories[0]['image']); + + $childCategory = $categories[0]['children'][0]; + $this->assertEquals('Child Image Category', $childCategory['name']); + $childCategory['image'] = str_replace('index.php/', '', $childCategory['image']); + $this->assertEquals($expectedImageUrl, $childCategory['image']); + } + + /** + * @return array + */ + public function categoryImageDataProvider(): array + { + return [ + 'default_filename_strategy' => [ + 'image_prefix' => null + ], + 'just_filename_strategy' => [ + 'image_prefix' => '' + ], + 'with_pub_media_strategy' => [ + 'image_prefix' => '/pub/media/catalog/category/' + ], + 'catalog_category_strategy' => [ + 'image_prefix' => 'catalog/category/' + ], + ]; + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertBaseFields($product, $actualResponse) + { + $assertionMap = [ + ['response_field' => 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()], + ['response_field' => 'created_at', 'expected_value' => $product->getCreatedAt()], + ['response_field' => 'name', 'expected_value' => $product->getName()], + ['response_field' => 'price', 'expected_value' => [ + 'minimalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'regularPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'maximalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + ] + ], + ['response_field' => 'sku', 'expected_value' => $product->getSku()], + ['response_field' => 'type_id', 'expected_value' => $product->getTypeId()], + ['response_field' => 'updated_at', 'expected_value' => $product->getUpdatedAt()], + ]; + $this->assertResponseFields($actualResponse, $assertionMap); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertWebsites($product, $actualResponse) + { + $assertionMap = [ + [ + 'id' => current($product->getExtensionAttributes()->getWebsiteIds()), + 'name' => 'Main Website', + 'code' => 'base', + 'sort_order' => 0, + 'default_group_id' => '1', + 'is_default' => true, + ] + ]; + $this->assertEquals($actualResponse, $assertionMap); + } + + /** + * @param array $actualResponse + */ + private function assertAttributes($actualResponse) + { + $eavAttributes = [ + 'url_key', + 'description', + 'meta_description', + 'meta_keyword', + 'meta_title', + 'short_description', + 'country_of_manufacture', + 'gift_message_available', + 'new_from_date', + 'new_to_date', + 'options_container', + 'special_price', + 'special_from_date', + 'special_to_date', + ]; + foreach ($eavAttributes as $eavAttribute) { + $this->assertArrayHasKey($eavAttribute, $actualResponse); + } + } +} From 1b65630b5e6dacc501d85424fbbe97875214fb66 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Wed, 18 Mar 2020 17:36:12 -0500 Subject: [PATCH 448/734] MC-32201: Reorder functionality --- ...tProvider.php => CustomerCartResolver.php} | 15 +++++++---- .../Model/Cart/CreateEmptyCartForCustomer.php | 26 +++++-------------- .../Magento/Sales/Model/Reorder/Reorder.php | 8 +++--- 3 files changed, 21 insertions(+), 28 deletions(-) rename app/code/Magento/Quote/Model/Cart/{CustomerCartProvider.php => CustomerCartResolver.php} (83%) diff --git a/app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php similarity index 83% rename from app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php rename to app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php index e37658127dcc1..3bdd23bdd0b34 100644 --- a/app/code/Magento/Quote/Model/Cart/CustomerCartProvider.php +++ b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php @@ -18,7 +18,7 @@ /** * Get customer cart or create empty cart. Ensure mask_id is created */ -class CustomerCartProvider +class CustomerCartResolver { /** * @var CartManagementInterface @@ -62,11 +62,12 @@ public function __construct( * Get customer cart * * @param int $customerId + * @param string|null $predefinedMaskedQuoteId * @return Quote * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function provide(int $customerId): Quote + public function provide(int $customerId, string $predefinedMaskedQuoteId = null): Quote { try { /** @var Quote $cart */ @@ -76,7 +77,7 @@ public function provide(int $customerId): Quote $cart = $this->cartManagement->getCartForCustomer($customerId); } try { - $this->ensureQuoteMaskIdExist((int)$cart->getId()); + $this->ensureQuoteMaskIdExist((int)$cart->getId(), $predefinedMaskedQuoteId); } catch (AlreadyExistsException $e) { // do nothing, we already have masked id } @@ -88,10 +89,11 @@ public function provide(int $customerId): Quote * Create masked id for customer's active quote if it's not exists * * @param int $quoteId + * @param string|null $predefinedMaskedQuoteId * @return void - * @throws \Magento\Framework\Exception\AlreadyExistsException + * @throws AlreadyExistsException */ - private function ensureQuoteMaskIdExist(int $quoteId): void + private function ensureQuoteMaskIdExist(int $quoteId, string $predefinedMaskedQuoteId = null): void { try { $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); @@ -101,6 +103,9 @@ private function ensureQuoteMaskIdExist(int $quoteId): void if ($maskedId === '') { $quoteIdMask = $this->quoteIdMaskFactory->create(); $quoteIdMask->setQuoteId($quoteId); + if (null !== $predefinedMaskedQuoteId) { + $quoteIdMask->setMaskedId($predefinedMaskedQuoteId); + } $this->quoteIdMaskResourceModel->save($quoteIdMask); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php index c84ab1ee148ea..7cff9e3293d55 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php @@ -7,12 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Quote\Api\CartManagementInterface; -use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; -use Magento\Quote\Model\Cart\CustomerCartProvider; -use Magento\Framework\App\ObjectManager; +use Magento\Quote\Model\Cart\CustomerCartResolver; /** * Create empty cart for customer @@ -25,27 +21,20 @@ class CreateEmptyCartForCustomer private $quoteIdToMaskedQuoteId; /** - * @var CustomerCartProvider + * @var CustomerCartResolver */ - private $cartProvider; + private $cartResolver; /** - * @param CartManagementInterface $cartManagement - * @param QuoteIdMaskFactory $quoteIdMaskFactory - * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId - * @param CustomerCartProvider $cartProvider - * @SuppressWarnings(PHPMD.UnusedFormalParameter) Parameters can't be removed according to backward compatibility + * @param CustomerCartResolver $cartResolver */ public function __construct( - CartManagementInterface $cartManagement, - QuoteIdMaskFactory $quoteIdMaskFactory, - QuoteIdMaskResourceModel $quoteIdMaskResourceModel, QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId, - CustomerCartProvider $cartProvider + CustomerCartResolver $cartResolver ) { $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; - $this->cartProvider = $cartProvider ?? ObjectManager::getInstance()->get(CustomerCartProvider::class); + $this->cartResolver = $cartResolver; } /** @@ -56,11 +45,10 @@ public function __construct( * @return string * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\NoSuchEntityException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) Parameter can't be removed according to backward compatibility */ public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string { - $cart = $this->cartProvider->provide($customerId); + $cart = $this->cartResolver->provide($customerId, $predefinedMaskedQuoteId); $quoteId = (int) $cart->getId(); return $this->quoteIdToMaskedQuoteId->execute($quoteId); diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index e494c7dd54237..c6157add759d9 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -11,7 +11,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Cart\CustomerCartProvider; +use Magento\Quote\Model\Cart\CustomerCartResolver; use Magento\Sales\Helper\Reorder as ReorderHelper; use Magento\Sales\Model\Order\Item; use Magento\Sales\Model\OrderFactory; @@ -73,13 +73,13 @@ class Reorder private $errors = []; /** - * @var CustomerCartProvider + * @var CustomerCartResolver */ private $customerCartProvider; /** * @param OrderFactory $orderFactory - * @param CustomerCartProvider $customerCartProvider + * @param CustomerCartResolver $customerCartProvider * @param CartRepositoryInterface $cartRepository * @param ProductRepositoryInterface $productRepository * @param ReorderHelper $reorderHelper @@ -87,7 +87,7 @@ class Reorder */ public function __construct( OrderFactory $orderFactory, - CustomerCartProvider $customerCartProvider, + CustomerCartResolver $customerCartProvider, CartRepositoryInterface $cartRepository, ProductRepositoryInterface $productRepository, ReorderHelper $reorderHelper, From 061dc48932c9f13483588712a5498f3119dbf48e Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Wed, 18 Mar 2020 17:48:36 -0500 Subject: [PATCH 449/734] MC-32201: Reorder functionality --- app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php | 4 ++-- .../QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php | 2 +- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php index 3bdd23bdd0b34..fa33d056b3e18 100644 --- a/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php +++ b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php @@ -59,7 +59,7 @@ public function __construct( } /** - * Get customer cart + * Get customer cart by customer id with predefined masked quote id * * @param int $customerId * @param string|null $predefinedMaskedQuoteId @@ -67,7 +67,7 @@ public function __construct( * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function provide(int $customerId, string $predefinedMaskedQuoteId = null): Quote + public function resolve(int $customerId, string $predefinedMaskedQuoteId = null): Quote { try { /** @var Quote $cart */ diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php index 7cff9e3293d55..b769be6c313be 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php @@ -48,7 +48,7 @@ public function __construct( */ public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string { - $cart = $this->cartResolver->provide($customerId, $predefinedMaskedQuoteId); + $cart = $this->cartResolver->resolve($customerId, $predefinedMaskedQuoteId); $quoteId = (int) $cart->getId(); return $this->quoteIdToMaskedQuoteId->execute($quoteId); diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index c6157add759d9..403ec0c484f7d 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -123,7 +123,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $customerId = (int)$order->getCustomerId(); $this->errors = []; - $cart = $this->customerCartProvider->provide($customerId); + $cart = $this->customerCartProvider->resolve($customerId); if (!$this->reorderHelper->canReorder($order->getId())) { $this->addError(__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); return $this->prepareOutput($cart); From 2cb136731d732349715e85a75715eea6535a7c62 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Wed, 18 Mar 2020 20:15:44 -0500 Subject: [PATCH 450/734] MC-32201: Reorder functionality --- .../Magento/Sales/Model/Reorder/Reorder.php | 32 +++++++++++++------ .../Magento/GraphQl/Sales/ReorderTest.php | 3 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 403ec0c484f7d..ae802edb29254 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -125,7 +125,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $cart = $this->customerCartProvider->resolve($customerId); if (!$this->reorderHelper->canReorder($order->getId())) { - $this->addError(__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); + $this->addError((string)__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); return $this->prepareOutput($cart); } @@ -134,13 +134,10 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu try { $this->addOrderItem($cart, $item); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->addError($e->getMessage()); + $this->addError($this->addCartItemError($item, $e->getMessage())); } catch (\Throwable $e) { $this->logger->critical($e); - $this->addError( - __('We can\'t add this item to your shopping cart right now.'), - self::ERROR_UNDEFINED - ); + $this->addError($this->addCartItemError($item, $e->getMessage()), self::ERROR_UNDEFINED); } } @@ -174,7 +171,7 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi $product = $this->productRepository->getById($orderItem->getProductId(), false, null, true); } catch (NoSuchEntityException $e) { $this->addError( - __('Could not find a product with ID "%1"', $orderItem->getProductId()), + (string)__('Could not find a product with ID "%1"', $orderItem->getProductId()), self::ERROR_PRODUCT_NOT_FOUND ); return; @@ -190,11 +187,11 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi * @param string|null $code * @return void */ - private function addError($message, string $code = null): void + private function addError(string $message, string $code = null): void { $this->errors[] = new Data\Error( - (string)$message, - $code ?? $this->getErrorCode((string)$message) + $message, + $code ?? $this->getErrorCode($message) ); } @@ -235,4 +232,19 @@ private function prepareOutput(CartInterface $cart): Data\ReorderOutput $this->errors = []; return $output; } + + /** + * Add error message for a cart item + * + * @param Item $item + * @param string $message + * @return string + */ + private function addCartItemError(Item $item, string $message): string + { + return (string) __( + 'Could not add the product with SKU "%sku" to the shopping cart: %message', + ['sku' => $item->getSku() ?? '-', 'message' => $message] + ); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index f43d2f2384529..12f286e613fd2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -126,7 +126,8 @@ private function assertProductNotAvailable() [ 'path' => ['orderNumber'], 'code' => 'NOT_SALABLE', - 'message' => 'Product that you are trying to add is not available.', + 'message' => 'Could not add the product with SKU "simple" to the shopping cart: ' + . 'Product that you are trying to add is not available.', ], ], 'cart' => [ From b8163347447bc8ee6636440c1d4ab522cc950af2 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Thu, 19 Mar 2020 08:52:34 +0200 Subject: [PATCH 451/734] Fixing static test --- app/code/Magento/Catalog/Block/Product/ListProduct.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index f1adc1d924836..59f5cc1b53b26 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -355,6 +355,7 @@ public function getIdentities() } foreach ($this->_getProductCollection() as $item) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $identities = array_merge($identities, $item->getIdentities()); } From 26d4b9299d8aefeb37c6cd9f3eb933b8ae23b1c9 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 19 Mar 2020 08:59:01 +0200 Subject: [PATCH 452/734] MC-25206: Error during image import if "no_selection" is set --- .../Model/Import/Product.php | 22 +++++++++++++++++++ .../Model/Import/ProductTest.php | 17 ++++++++++++++ .../_files/import_media_with_no_selection.csv | 2 ++ 3 files changed, 41 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index d1bce7aaaf951..56baf1c395de0 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1723,6 +1723,7 @@ protected function _saveProducts() } $rowData[self::COL_MEDIA_IMAGE] = []; + list($rowImages, $rowData) = $this->clearNoSelectionImages($rowImages, $rowData); /* * Note: to avoid problems with undefined sorting, the value of media gallery items positions @@ -1929,6 +1930,27 @@ protected function _saveProducts() } //phpcs:enable Generic.Metrics.NestingLevel + /** + * Clears entries from Image Set and Row Data marked as no_selection + * + * @param array $rowImages + * @param array $rowData + * @return array + */ + private function clearNoSelectionImages($rowImages, $rowData) + { + foreach ($rowImages as $column => $columnImages) { + foreach ($columnImages as $key => $image) { + if ($image == 'no_selection') { + unset($rowImages[$column][$key]); + unset($rowData[$column]); + } + } + } + + return [$rowImages, $rowData]; + } + /** * Prepare array with image states (visible or hidden from product page) * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 93684b1e7a070..a9b1fe6d936ba 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -925,6 +925,23 @@ static function (DataObject $image) { $this->assertEquals('/m/a/magento_additional_image_two.jpg', $imageItem->getFile()); } + /** + * Tests importing product images with "no_selection" attribute. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoAppIsolation enabled + */ + public function testSaveImagesNoSelection() + { + $this->importDataForMediaTest('import_media_with_no_selection.csv'); + $product = $this->getProductBySku('simple_new'); + + $this->assertEquals('/m/a/magento_image.jpg', $product->getData('image')); + $this->assertEquals(null, $product->getData('small_image')); + $this->assertEquals(null, $product->getData('thumbnail')); + $this->assertEquals(null, $product->getData('swatch_image')); + } + /** * Test that new images should be added after the existing ones. * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv new file mode 100644 index 0000000000000..d603d512eeb7a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.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,deferred_stock_update,use_config_deferred_stock_update,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,giftcard_type,giftcard_allow_open_amount,giftcard_open_amount_min,giftcard_open_amount_max,giftcard_amount,use_config_is_redeemable,giftcard_is_redeemable,use_config_lifetime,giftcard_lifetime,use_config_allow_message,giftcard_allow_message,use_config_email_template,giftcard_email_template,associated_skus,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels +simple_new,,Default,virtual,Default Category/cat1,base,simple_new,,,,1,Taxable Goods,"Catalog, Search",10,,,,simple_new,simple_new,simple_new,simple_new,magento_image.jpg,,no_selection,,no_selection,,no_selection,,"3/12/20, 3:02 PM","3/12/20, 3:02 PM",,,Block after Info Column,,,,Use config,,,,,,,Use config,,,123,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, From 75af054d697377f5e144bd35fe09aeb709371108 Mon Sep 17 00:00:00 2001 From: Dmitry Tsymbal <d.tsymbal@atwix.com> Date: Thu, 19 Mar 2020 10:24:22 +0200 Subject: [PATCH 453/734] Re-using Exisitng Action Groups --- .../StorefrontCustomerLoginActionGroup.xml | 21 ------------------- .../StorefrontCustomerLoginFormSection.xml | 16 -------------- ...frontCustomerSubscribeToNewsletterTest.xml | 5 ++--- 3 files changed, 2 insertions(+), 40 deletions(-) delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml deleted file mode 100644 index 4105034e33988..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLoginActionGroup.xml +++ /dev/null @@ -1,21 +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="StorefrontCustomerLoginActionGroup"> - <arguments> - <argument name="customer" type="entity"/> - </arguments> - - <click selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" stepKey="clickSignInLnk"/> - <fillField selector="{{StorefrontCustomerLoginFormSection.emailField}}" userInput="{{customer.email}}" stepKey="fillEmailField"/> - <fillField selector="{{StorefrontCustomerLoginFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="fillPasswordField"/> - <click selector="{{StorefrontCustomerLoginFormSection.signInAccountButton}}" stepKey="clickSignInButton"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml deleted file mode 100644 index 55f2835d584af..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginFormSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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="StorefrontCustomerLoginFormSection"> - <element name="emailField" type="input" selector=".fieldset.login #email.input-text"/> - <element name="passwordField" type="input" selector=".field.password.required #pass.input-text"/> - <element name="signInAccountButton" type="button" selector=".actions-toolbar #send2.action.login.primary" timeout="30"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml index be937ebe4d970..4658b162dcc55 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml @@ -22,9 +22,8 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> </after> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openHomePage"/> - <actionGroup ref="StorefrontCustomerLoginActionGroup" stepKey="loginAsCustomer"> - <argument name="customer" value="$$createCustomer$$"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <actionGroup ref="StorefrontCustomerNavigateToNewsletterPageActionGroup" stepKey="navigateToNewsletterPage"/> <actionGroup ref="StorefrontCustomerUpdateGeneralSubscriptionActionGroup" stepKey="subscribeToNewsletter"/> From b7f6d644399456fda2c12148cc79945aee2ee894 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 19 Mar 2020 10:35:35 +0200 Subject: [PATCH 454/734] MC-32402: [Magento Cloud] - tax calculation with b2b extension --- .../Product/Price/Plugin/CustomerGroup.php | 5 +- .../Price/Plugin/CustomerGroupTest.php | 132 ++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php index 9b99ee8c8dc8c..e1f4cd28d0090 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php @@ -15,6 +15,9 @@ use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +/** + * Update catalog_product_index_price table after delete or save customer group + */ class CustomerGroup { /** @@ -80,7 +83,7 @@ public function aroundSave( \Closure $proceed, GroupInterface $group ) { - $isGroupNew = !$group->getId(); + $isGroupNew = $group->getId() === null; $group = $proceed($group); if ($isGroupNew) { foreach ($this->getAffectedDimensions((string)$group->getId()) as $dimensions) { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php new file mode 100644 index 0000000000000..8e12ba7670638 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Price\Plugin; + +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Magento\Catalog\Model\Indexer\Product\Price\Plugin\CustomerGroup; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\Customer\Model\Data\Group; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Test for CustomerGroup plugin + */ +class CustomerGroupTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CustomerGroup + */ + private $model; + + /** + * @var DimensionFactory|MockObject + */ + private $dimensionFactory; + + /** + * @var TableMaintainer|MockObject + */ + private $tableMaintainerMock; + + /** + * @var DimensionModeConfiguration|MockObject + */ + private $dimensionModeConfiguration; + + /** + * @var \Callable + */ + private $proceedMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->dimensionFactory = $this->createPartialMock( + DimensionFactory::class, + ['create'] + ); + + $this->dimensionModeConfiguration = $this->createPartialMock( + DimensionModeConfiguration::class, + ['getDimensionConfiguration'] + ); + + $this->tableMaintainerMock = $this->createPartialMock( + TableMaintainer::class, + ['createTablesForDimensions'] + ); + + $this->model = $this->objectManager->getObject( + CustomerGroup::class, + [ + 'dimensionFactory' => $this->dimensionFactory, + 'dimensionModeConfiguration' => $this->dimensionModeConfiguration, + 'tableMaintainer' => $this->tableMaintainerMock, + ] + ); + } + + /** + * Check of call count createTablesForDimensions() method + * + * @param $customerGroupId + * @param $callTimes + * + * @dataProvider aroundSaveDataProvider + */ + public function testAroundSave($customerGroupId, $callTimes) + { + $subjectMock = $this->createMock(GroupRepositoryInterface::class); + $customerGroupMock = $this->createPartialMock( + Group::class, + ['getId'] + ); + $customerGroupMock->method('getId')->willReturn($customerGroupId); + $this->tableMaintainerMock->expects( + $this->exactly($callTimes) + )->method('createTablesForDimensions'); + $this->proceedMock = function ($customerGroupMock) { + return $customerGroupMock; + }; + $this->dimensionModeConfiguration->method('getDimensionConfiguration')->willReturn( + [CustomerGroupDimensionProvider::DIMENSION_NAME] + ); + $this->model->aroundSave($subjectMock, $this->proceedMock, $customerGroupMock); + } + + /** + * Data provider for testAroundSave + * + * @return array + */ + public function aroundSaveDataProvider() + { + return [ + 'customer_group_id = 0' => [ + 'customer_group_id' => '0', + 'create_tables_call_times' => 0 + ], + 'customer_group_id = 1' => [ + 'customer_group_id' => '1', + 'create_tables_call_times' => 0 + ], + 'customer_group_id = null' => [ + 'customer_group_id' => null, + 'create_tables_call_times' => 1 + ], + ]; + } +} From cad48befd6567944dbe6f2737c0d72ad756b3cc8 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 19 Mar 2020 10:45:23 +0200 Subject: [PATCH 455/734] MC-29052: Magento\FunctionalTestingFramework.functional.AdminTaxReportGridTest fails randomly --- .../Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml index b8e0881c4431f..3e0998b79c3f5 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -15,8 +15,8 @@ <title value="Checking Tax Report grid"/> <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> <severity value="MAJOR"/> - <testCaseId value="MC-6230"/> - <useCaseId value="MC-25815"/> + <testCaseId value="MC-25815"/> + <useCaseId value="MAGETWO-91521"/> <group value="Tax"/> </annotations> <before> From 5c5d32fa6b47183561d136535a7ccf0e15bcdf7a Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Thu, 19 Mar 2020 09:52:23 +0100 Subject: [PATCH 456/734] Move JS module initialization to separate tasks --- lib/web/mage/apply/main.js | 12 +++++++++--- lib/web/mage/bootstrap.js | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/web/mage/apply/main.js b/lib/web/mage/apply/main.js index 21255fbeb34d1..6b5423836e0df 100644 --- a/lib/web/mage/apply/main.js +++ b/lib/web/mage/apply/main.js @@ -22,16 +22,22 @@ define([ */ function init(el, config, component) { require([component], function (fn) { + var $el; if (typeof fn === 'object') { fn = fn[component].bind(fn); } if (_.isFunction(fn)) { - fn(config, el); - } else if ($(el)[component]) { - $(el)[component](config); + fn = fn.bind(null, config, el); + } else { + $el = $(el); + if ($el[component]) { + fn = $el[component].bind($el, config); + } } + // Init module in separate task to prevent blocking main thread. + setTimeout(fn); }, function (error) { if ('console' in window && typeof window.console.error === 'function') { console.error(error); diff --git a/lib/web/mage/bootstrap.js b/lib/web/mage/bootstrap.js index f7b2cfaa314cb..924b1628f3eb9 100644 --- a/lib/web/mage/bootstrap.js +++ b/lib/web/mage/bootstrap.js @@ -16,6 +16,7 @@ define([ /** * Init all components defined via data-mage-init attribute. + * Execute in a separate task to prevent main thread blocking. */ - $(mage.apply); + setTimeout(mage.apply); }); From 254a46c8a11683c3958b09b80e59343befa1e2b5 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 19 Mar 2020 11:32:17 +0200 Subject: [PATCH 457/734] MC-25206: Error during image import if "no_selection" is set --- .../Model/Import/_files/import_media_with_no_selection.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv index d603d512eeb7a..e194637867c1a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv @@ -1,2 +1,2 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_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,deferred_stock_update,use_config_deferred_stock_update,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,giftcard_type,giftcard_allow_open_amount,giftcard_open_amount_min,giftcard_open_amount_max,giftcard_amount,use_config_is_redeemable,giftcard_is_redeemable,use_config_lifetime,giftcard_lifetime,use_config_allow_message,giftcard_allow_message,use_config_email_template,giftcard_email_template,associated_skus,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels -simple_new,,Default,virtual,Default Category/cat1,base,simple_new,,,,1,Taxable Goods,"Catalog, Search",10,,,,simple_new,simple_new,simple_new,simple_new,magento_image.jpg,,no_selection,,no_selection,,no_selection,,"3/12/20, 3:02 PM","3/12/20, 3:02 PM",,,Block after Info Column,,,,Use config,,,,,,,Use config,,,123,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +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,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,deferred_stock_update,use_config_deferred_stock_update,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,use_config_lifetime,use_config_email_template,associated_skus,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels +simple_new,,Default,virtual,Default Category/cat1,base,simple_new,,,,1,Taxable Goods,"Catalog, Search",10,,,,simple_new,simple_new,simple_new,simple_new,magento_image.jpg,,no_selection,,no_selection,,no_selection,,"3/12/20, 3:02 PM","3/12/20, 3:02 PM",,,Block after Info Column,,,,,,,,,,Use config,,,123,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,, From b6f71cc4c3fb2a0e7f3370665add24e5fbc2720e Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 19 Mar 2020 11:39:48 +0200 Subject: [PATCH 458/734] Add xml declaration for layout file --- .../view/frontend/layout/catalog_widget_product_list.xml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml index db44d8b62dc1a..20d1891967318 100644 --- a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml +++ b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml @@ -1,8 +1,4 @@ -<!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - +<?xml version="1.0"?> <!-- ~ Copyright © Magento, Inc. All rights reserved. ~ See COPYING.txt for license details. @@ -14,4 +10,4 @@ <block class="Magento\Framework\View\Element\Template" name="category.product.type.details.renderers.default" as="default"/> </block> </body> -</page> \ No newline at end of file +</page> From c325160dc331e0358ab0a4d91220d12b015226f4 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 19 Mar 2020 11:45:23 +0200 Subject: [PATCH 459/734] Refactored copyright block --- .../view/frontend/layout/catalog_widget_product_list.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml index 20d1891967318..4fe7af7f34683 100644 --- a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml +++ b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml @@ -1,9 +1,10 @@ <?xml version="1.0"?> <!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.widget.details.renderers"> From 143bd5b5bce5284d53a5e13f6a3b0863f6a2c66b Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 19 Mar 2020 14:58:10 +0200 Subject: [PATCH 460/734] magento/magento2#25540: Refactoring. --- .../Model/Import/Product/Type/Bundle.php | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index dcc6e52460793..e6522054d9f94 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -26,18 +26,11 @@ */ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { - /** - * phpcs:disable Magento2.Commenting.ConstantsPHPDocFormatting - */ - /** * Delimiter before product option value. */ const BEFORE_OPTION_VALUE_DELIMITER = ';'; - /** - * Pair value separator. - */ const PAIR_VALUE_SEPARATOR = '='; /** @@ -50,25 +43,12 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst */ const VALUE_FIXED = 'fixed'; - /** - * Not fixed dynamic attribute. - */ const NOT_FIXED_DYNAMIC_ATTRIBUTE = 'price_view'; - /** - * Selection price type fixed. - */ const SELECTION_PRICE_TYPE_FIXED = 0; - /** - * Selection price type percent. - */ const SELECTION_PRICE_TYPE_PERCENT = 1; - /** - * phpcs:enable Magento2.Commenting.ConstantsPHPDocFormatting - */ - /** * Array of cached options. * From 9aa1cfcf5d2f2e6a68cb8f83e8ad511d9a51bb23 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Thu, 19 Mar 2020 18:37:29 +0530 Subject: [PATCH 461/734] fixed static test --- .../Magento_Ui/web/css/source/module/_masonry-grid.less | 2 +- .../tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less index 7e2eeb59e116b..237395db8cb43 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -117,8 +117,8 @@ &-sortby { select { - border: none; background-image: url(../images/arrows-bg.svg); + border: none; padding-right: 3.2rem; &:active { background-image+: url('../images/arrows-bg.svg'); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js index 7797180ce65b5..e5983f1d42e06 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -1,3 +1,9 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ define([ 'Magento_Ui/js/grid/sortBy' ], function (SortBy) { From a38c352327732530a0652acd37e1548bac3d6336 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 19 Mar 2020 15:10:15 +0200 Subject: [PATCH 462/734] MC-24261: [MFTF TEST] Storefront Gallery - Configurable Product with several attributes: prepend variation media --- ...tPageCloseFullscreenGalleryActionGroup.xml | 18 + ...ductPageOpenImageFullscreenActionGroup.xml | 22 + .../Test/Mftf/Data/ProductAttributeData.xml | 4 - .../Section/AdminProductImagesSection.xml | 1 - .../Section/StorefrontProductMediaSection.xml | 5 +- ...rontRecentlyViewedAtStoreViewLevelTest.xml | 3 +- ...urableProductWithSeveralAttributesTest.xml | 383 ++++++++++++++++++ ...nAddProductVideoWithPreviewActionGroup.xml | 20 + .../Section/AdminProductNewVideoSection.xml | 3 +- .../Test/Mftf/Data/SwatchAttributeData.xml | 28 ++ .../Mftf/Data/SwatchAttributeOptionData.xml | 27 ++ ...watchProductAttributeFrontendLabelData.xml | 15 + .../swatch_product_attribute-meta.xml | 46 +++ .../swatch_product_attribute_option-meta.xml | 28 ++ .../StorefrontProductInfoMainSection.xml | 1 + 15 files changed, 596 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml create mode 100644 app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml new file mode 100644 index 0000000000000..d7dde159f4bdd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml @@ -0,0 +1,18 @@ +<?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="StorefrontProductPageCloseFullscreenGalleryActionGroup"> + <annotations> + <description>Closes a product image gallery full-screen page.</description> + </annotations> + + <click selector="{{StorefrontProductMediaSection.closeFullscreenImage}}" stepKey="closeFullScreenPage"/> + <waitForPageLoad stepKey="waitsForCloseFullScreenPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml new file mode 100644 index 0000000000000..14bd47a234fd2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageOpenImageFullscreenActionGroup"> + <annotations> + <description>Finds image of the product in thumbnails and open a full-screen review.</description> + </annotations> + <arguments> + <argument name="imageNumber" type="string" defaultValue="2"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnail(imageNumber)}}" stepKey="waitThumbnailAppears"/> + <conditionalClick selector="{{StorefrontProductMediaSection.fotoramaImageThumbnail(imageNumber)}}" dependentSelector="{{StorefrontProductMediaSection.fotoramaImageThumbnailActive(imageNumber)}}" visible="false" stepKey="clickOnThumbnailImage"/> + <click selector="{{StorefrontProductMediaSection.gallery}}" stepKey="openFullScreenPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index e4a91902cb79b..abb4301fd8f9f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -394,10 +394,6 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> - <entity name="VisualSwatchProductAttribute" type="ProductAttribute"> - <data key="frontend_input">swatch_visual</data> - <data key="attribute_code" unique="suffix">visual_swatch</data> - </entity> <entity name="ProductColorAttribute" type="ProductAttribute"> <data key="frontend_label">Color</data> <data key="attribute_code" unique="suffix">color_attr</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml index f20e9b3a11e5e..c2de91aadbc0c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml @@ -18,7 +18,6 @@ <element name="modalOkBtn" type="button" selector="button.action-primary.action-accept"/> <element name="uploadProgressBar" type="text" selector=".uploader .file-row"/> <element name="productImagesToggleState" type="button" selector="[data-index='gallery'] > [data-state-collapsible='{{status}}']" parameterized="true"/> - <element name="nthProductImage" type="button" selector="#media_gallery_content > div:nth-child({{var}}) img.product-image" parameterized="true"/> <element name="nthRemoveImageBtn" type="button" selector="#media_gallery_content > div:nth-child({{var}}) button.action-remove" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index d3e43d9ea2dfa..447113ea65bb2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductMediaSection"> <element name="gallerySpinner" type="block" selector="#maincontent .fotorama__spinner--show" /> - <element name="gallery" type="block" selector="[data-gallery-role='gallery']" /> + <element name="gallery" type="block" selector="[data-gallery-role='gallery']" timeout="30"/> <element name="productImage" type="text" selector="//*[@data-gallery-role='gallery' and not(contains(@class, 'fullscreen'))]//img[contains(@src, '{{filename}}') and not(contains(@class, 'full'))]" parameterized="true" /> <element name="productImageFullscreen" type="text" selector="//*[@data-gallery-role='gallery' and contains(@class, 'fullscreen')]//img[contains(@src, '{{filename}}') and contains(@class, 'full')]" parameterized="true" /> <element name="closeFullscreenImage" type="button" selector="//*[@data-gallery-role='gallery' and contains(@class, 'fullscreen')]//*[@data-gallery-role='fotorama__fullscreen-icon']" /> @@ -19,5 +19,8 @@ <element name="productImageInFotorama" type="file" selector=".fotorama__nav__shaft img[src*='{{imageName}}']" parameterized="true"/> <element name="fotoramaPrevButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--left')]"/> <element name="fotoramaNextButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--right')]"/> + <element name="fotoramaAnyMedia" type="text" selector=".fotorama__nav__shaft img"/> + <element name="fotoramaImageThumbnail" type="block" selector="//div[contains(@class, 'fotorama__nav__shaft')]//div[contains(@class, 'fotorama__nav__frame--thumb')][{{imageNumber}}]" parameterized="true" timeout="30"/> + <element name="fotoramaImageThumbnailActive" type="block" selector="//div[contains(@class, 'fotorama__nav__shaft')]//div[contains(@class, 'fotorama__nav__frame--thumb') and contains(@class, 'fotorama__active')][{{imageNumber}}]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index f8ee9e562a6a9..0e09635489d9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -12,9 +12,10 @@ <stories value="Recently Viewed Product"/> <title value="Recently Viewed Product at store view level"/> <description value="Recently Viewed Product should not be displayed on second store view, if configured as, Per Store View "/> - <testCaseId value="MC-31877"/> + <testCaseId value="MC-32112"/> <severity value="CRITICAL"/> <group value="catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml new file mode 100644 index 0000000000000..a39352ce16189 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml @@ -0,0 +1,383 @@ +<?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="StorefrontGalleryConfigurableProductWithSeveralAttributesTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Verify functionality of updating Gallery items on 'view Product' Storefront page for Configurable Product with several attributes of different types"/> + <title value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> + <description value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-26396"/> + <group value="catalog"/> + <group value="configurableProduct"/> + <group value="swatch"/> + </annotations> + <before> + <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> + <!--Create 1 configurable product with 2 variations--> + <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> + <!--Create product drop down attribute--> + <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> + <createData entity="productAttributeOption1" stepKey="dropdownAttributeFirstOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="dropdownAttributeSecondOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getDropdownAttributeFirsOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getDropdownAttributeSecondOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </getData> + + <!-- Create product swatch attribute with 2 variations --> + <createData entity="VisualSwatchProductAttributeForm" stepKey="createVisualSwatchAttribute"/> + <createData entity="SwatchProductAttributeOption1" stepKey="swatchAttributeFirstOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption2" stepKey="swatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSwatchAttributeFirsOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSwatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </getData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Open configurable product edit page --> + <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> + + <!-- Add attributes to configurable product--> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> + + <!-- Find Dropdown attribute in grid and select it --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindDropdownAttribute"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForDropdownAttribute"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createDropdownAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForDropdownAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForDropdownAttribute"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectDropdownAttribute"/> + <!-- Find Swatch attribute in grid and select it --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForSwatchAttribute"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createVisualSwatchAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForSwatchAttribute"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectSwatchAttribute"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToSelectOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createDropdownAttribute.default_frontend_label$)}}" stepKey="selectAllDropdownAttributeOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createVisualSwatchAttribute.frontend_label[0]$)}}" stepKey="selectAllSwatchAttributeOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToApplyQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextToProceedToSummary"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickGenerateProductsButton"/> + + <!-- Load media for configurable product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProduct"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProduct"> + <argument name="image" value="Magento3"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="image" value="{{TestImageNew.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"/> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> + + <!-- Load media for configurable product variation option1-option1--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1Option1"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeFirstOption.option[store_labels][0][label]$-$swatchAttributeFirstOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1Option1"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption1Option1"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1Option1"> + <argument name="image" value="{{placeholderSmallImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1Option1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option1"/> + + <!-- Load media for configurable product variation option1-option2--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1Option2"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeFirstOption.option[store_labels][0][label]$-$swatchAttributeSecondOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addFirstVideoToConfigurableProductVariationOption1Option2"> + <argument name="image" value="{{Magento3.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertFirstVideoAddedToConfigurableProductVariationOption1Option2"/> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1Option2"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption1Option2"> + <argument name="image" value="{{placeholderThumbnailImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption1Option2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option2"/> + + <!-- Load media for configurable product variation option2-option2--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption2Option2"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeSecondOption.option[store_labels][0][label]$-$swatchAttributeSecondOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption2Option2"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption2Option2"/> + + <!-- Reindex invalidated indices after product attribute has been created --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + </before> + + <after> + <createData entity="DefaultProductVideoConfig" stepKey="resetStoreDefaultVideoConfig"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> + <argument name="product" value="$createConfigurableProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForDeleteSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 5 record(s) have been deleted." stepKey="seeDeleteSuccessMessage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductAttributeB"> + <argument name="ProductAttribute" value="$createDropdownAttribute$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductAttributeF"> + <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + </after> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="productUrl" value="$$createConfigurableProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case0"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case0"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case0"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase0"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase0"> + <expectedResult type="variable">getListOfThumbnailsCase0</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase0</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase0"/> + + <!--CASE 1: Selected options = F2; Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case1"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case1"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase1[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case1"/> + <assertRegExp expected="|{{placeholderThumbnailImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase1[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case1"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case1"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case1"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case1"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase1"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase1"> + <expectedResult type="variable">getListOfThumbnailsCase1</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase1</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase1"/> + + <!--CASE 2: Selected options = F1; Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case2"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase2[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case2"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case2"/> + <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase2[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case2"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case2"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case2"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case2"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase2"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase2"> + <expectedResult type="variable">getListOfThumbnailsCase2</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase2</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase2"/> + + <!--CASE 3: Selected options = B2,F1; Expected media : C1, C2, C3--> + <selectOption userInput="$dropdownAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB2Case3"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case3"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case3"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case3"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase3"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase3"> + <expectedResult type="variable">getListOfThumbnailsCase3</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase3</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase3"/> + + <!--CASE 4: Selected options = B2,F2, Expected media : G1, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case4"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> + <assertRegExp expected="|{{ProductImage.filename}}.*.png|" actual="$getListOfThumbnailsCase4[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case4"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case4"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case4"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case4"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase4"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase4"> + <expectedResult type="variable">getListOfThumbnailsCase4</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase4</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase4"/> + + <!--CASE 5: Selected options = B2, Expected media : C1, C2, C3--> + <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF2Case5"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase5"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase5"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case5"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case5"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case5"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase5"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase5"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase5"> + <expectedResult type="variable">getListOfThumbnailsCase5</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase5</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase5"/> + + <!--CASE 6: Selected options = B1, Expected media : D1, D2, D3, C1, C2, C3--> + <selectOption userInput="$dropdownAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB1Case6"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase6"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase6"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase6[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case6"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case6"/> + <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase6[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case6"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case6"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case6"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case6"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase6"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase6"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase6"> + <expectedResult type="variable">getListOfThumbnailsCase6</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase6</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase6"/> + + <!--CASE 7: Selected options = B1,F2, Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case7"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase7"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase7"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case7"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase7[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case7"/> + <assertRegExp expected="|{{placeholderThumbnailImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase7[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case7"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case7"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case7"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case7"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase7"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase7"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase7"> + <expectedResult type="variable">getListOfThumbnailsCase7</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase7</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase7"/> + + <!--CASE 8: Selected options = B1,F1, Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case8"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase8"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase8"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase8[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case8"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case8"/> + <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase8[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case8"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case8"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case8"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case8"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase8"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase8"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase8"> + <expectedResult type="variable">getListOfThumbnailsCase8</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase8</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase8"/> + + <!--CASE 9: Selected options = none, Expected media : C1, C2, C3--> + <selectOption userInput="Choose an Option..." selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="unselectB1Case9"/> + <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF1Case9"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase9"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase9"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case9"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case9"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case9"/> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase9"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase9"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase9"> + <expectedResult type="variable">getListOfThumbnailsCase9</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase9</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase9"/> + </test> +</tests> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml new file mode 100644 index 0000000000000..e491a1676a402 --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddProductVideoWithPreviewActionGroup" extends="AddProductVideoActionGroup"> + <annotations> + <description>Adds product video with a preview image on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="image" type="string" defaultValue="{{ImageUpload_1.file}}"/> + </arguments> + <attachFile selector="{{AdminProductNewVideoSection.previewImageUploader}}" userInput="{{image}}" after="waitForSaveButtonVisible" stepKey="addPreviewImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml index 58a1c40a4e470..d1890f9490d98 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml @@ -22,5 +22,6 @@ <element name="thumbnailCheckbox" type="checkbox" selector="#video_thumbnail"/> <element name="hideFromProductPageCheckbox" type="checkbox" selector="#new_video_disabled"/> <element name="errorElement" type="text" selector="#{{inputName}}-error" parameterized="true" /> + <element name="previewImageUploader" type="file" selector=".field-new_video_screenshot #new_video_screenshot"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml index 6070ae25f570f..97702b9deb9b6 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -21,4 +21,32 @@ <entity name="textSwatchProductAttribute" type="ProductAttribute" extends="productDropDownAttribute"> <data key="frontend_input">swatch_text</data> </entity> + <entity name="VisualSwatchProductAttribute" type="ProductAttribute"> + <data key="frontend_input">swatch_visual</data> + <data key="attribute_code" unique="suffix">visual_swatch</data> + </entity> + + <entity name="VisualSwatchProductAttributeForm" type="SwatchProductAttributeForm"> + <data key="frontend_label[0]" unique="suffix">VisualSwatchAttribute-</data> + <data key="frontend_input">swatch_visual</data> + <data key="is_required">0</data> + <data key="update_product_preview_image">1</data> + <data key="use_product_image_for_swatch">0</data> + <data key="attribute_code" unique="suffix">visual_swatch_attribute_</data> + <data key="is_global">1</data> + <data key="default_value_yesno">0</data> + <data key="is_unique">0</data> + <data key="is_used_in_grid">1</data> + <data key="is_visible_in_grid">1</data> + <data key="is_filterable_in_grid">1</data> + <data key="is_searchable">1</data> + <data key="is_comparable">1</data> + <data key="is_filterable">1</data> + <data key="is_filterable_in_search">1</data> + <data key="is_used_for_promo_rules">0</data> + <data key="is_html_allowed_on_front">1</data> + <data key="is_visible_on_front">1</data> + <data key="used_in_product_listing">1</data> + <data key="used_for_sort_by">0</data> + </entity> </entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml new file mode 100644 index 0000000000000..a46bc0544b642 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml @@ -0,0 +1,27 @@ +<?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="SwatchProductAttributeOption1" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-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="SwatchProductAttributeOption2" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-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/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml new file mode 100644 index 0000000000000..095fd65f3a99c --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml @@ -0,0 +1,15 @@ +<?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="SwatchProductAttributeFrontendLabel" type="FrontendLabel"> + <data key="store_id">0</data> + <data key="label" unique="suffix">Swatch-Attribute-</data> + </entity> +</entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml new file mode 100644 index 0000000000000..795892dbb4d47 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateSwatchProductAttributeForm" dataType="SwatchProductAttributeForm" type="create" + auth="adminFormKey" url="catalog/product_attribute/save" method="POST" successRegex="/messages-message-success/" returnRegex=""> + <contentType>application/x-www-form-urlencoded</contentType> + <field key="frontend_label[0]">string</field> + <field key="frontend_label[1]">string</field> + <field key="frontend_input">string</field> + <field key="is_required">string</field> + <field key="update_product_preview_image">string</field> + <field key="use_product_image_for_swatch">string</field> + <field key="visual_swatch_validation">string</field> + <field key="visual_swatch_validation_unique">string</field> + <field key="text_swatch_validation">string</field> + <field key="text_swatch_validation_unique">string</field> + <field key="dropdown_attribute_validation">string</field> + <field key="dropdown_attribute_validation_unique">string</field> + <field key="attribute_code">string</field> + <field key="is_global">string</field> + <field key="default_value_text">string</field> + <field key="default_value_yesno">string</field> + <field key="default_value_date">string</field> + <field key="default_value_textarea">string</field> + <field key="is_unique">string</field> + <field key="is_used_in_grid">string</field> + <field key="is_visible_in_grid">string</field> + <field key="is_filterable_in_grid">string</field> + <field key="is_searchable">string</field> + <field key="is_comparable">string</field> + <field key="is_filterable">string</field> + <field key="is_filterable_in_search">string</field> + <field key="is_used_for_promo_rules">string</field> + <field key="is_html_allowed_on_front">string</field> + <field key="is_visible_on_front">string</field> + <field key="used_in_product_listing">string</field> + <field key="used_for_sort_by">string</field> + </operation> +</operations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml new file mode 100644 index 0000000000000..b70550f71400b --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="create" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options" method="POST"> + <contentType>application/json</contentType> + <object dataType="ProductSwatchAttributeOption" key="option"> + <field key="label">string</field> + <field key="sort_order">integer</field> + <field key="is_default">boolean</field> + <array key="store_labels"> + <value>StoreLabel</value> + </array> + </object> + </operation> + <operation name="DeleteProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="delete" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options/{option_id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="get" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options/" method="GET"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 5b714f01fd46f..18ea4152939ac 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -17,5 +17,6 @@ <element name="productSwatch" type="button" selector="//div[@class='swatch-option'][@aria-label='{{var1}}']" parameterized="true"/> <element name="visualSwatchOption" type="button" selector=".swatch-option[option-tooltip-value='#{{visualSwatchOption}}']" parameterized="true"/> <element name="swatchOptionTooltip" type="block" selector="div.swatch-option-tooltip"/> + <element name="swatchAttributeSelectedOption" type="text" selector="#product-options-wrapper .swatch-option.selected"/> </section> </sections> From 42576498389a27f9b1b9f6f9ae800f6e2aa80405 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 19 Mar 2020 15:18:30 +0200 Subject: [PATCH 463/734] MC-32306: Errors while trying to update downloadable product after MC-29952 --- .../Model/Import/Product/Type/Downloadable.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index b408a77e0c95e..1d8e4954cbe54 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -17,6 +17,7 @@ * * phpcs:disable Magento2.Commenting.ConstantsPHPDocFormatting * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { From ef3342444ed85f2f041e0b53c00e8a1058bdde5c Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Thu, 19 Mar 2020 09:19:37 -0500 Subject: [PATCH 464/734] MC-32201: Reorder functionality --- .../QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php | 1 + .../customer_order_item_with_product_and_custom_options.php | 4 ++++ .../Sales/_files/order_with_product_out_of_stock.php | 4 ++++ .../_files/order_with_product_out_of_stock_rollback.php | 4 ++++ .../Magento/Sales/_files/order_with_simple_product.php | 4 ++++ .../Sales/_files/order_with_simple_product_rollback.php | 4 ++++ .../Magento/Sales/_files/order_with_two_simple_products.php | 4 ++++ .../_files/order_with_two_simple_products_rollback.php | 6 ++++-- 8 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php index b769be6c313be..442e26ba53b66 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php @@ -12,6 +12,7 @@ /** * Create empty cart for customer + * Masked quote ID will be returned as a result */ class CreateEmptyCartForCustomer { diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php index f7ceda14df13c..e56bba76fe196 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; require __DIR__ . '/../../../Magento/Catalog/_files/order_item_with_product_and_custom_options.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php index 2db288ff1da71..65ef24f5a03d2 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ use Magento\TestFramework\Helper\Bootstrap; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php index b146386192be7..9e9e808c853d1 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php @@ -1,3 +1,7 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ require __DIR__ . '/../../../Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php index 34224849f528c..1e8e9beccc87f 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ use Magento\Sales\Api\OrderRepositoryInterface; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php index 34224849f528c..1e8e9beccc87f 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ use Magento\Sales\Api\OrderRepositoryInterface; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php index e9b0f9b3a656d..077fa6c5f541e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ use Magento\Sales\Api\OrderRepositoryInterface; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php index 4a8e7f3936bfe..b6bc7c7113a85 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php @@ -1,6 +1,8 @@ <?php - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; require 'default_rollback.php'; - From 270e3dc001716b180c1cbdf91288a6debdeac7fb Mon Sep 17 00:00:00 2001 From: root <greenspa@adobe.com> Date: Wed, 18 Mar 2020 14:54:24 +0000 Subject: [PATCH 465/734] COMOPS-910: Split MFTF tests by BLOCKER severity --- .../Test/Mftf/Test/AdminAddBundleItemsTest.xml | 2 +- .../AdminAssociateBundleProductToWebsitesTest.xml | 2 +- .../Mftf/Test/AdminProductBundleCreationTest.xml | 2 +- .../Test/Mftf/Test/BundleProductFixedPricingTest.xml | 2 +- .../Test/CurrencyChangingBundleProductInCartTest.xml | 2 +- .../Mftf/Test/StorefrontBundleProductDetailsTest.xml | 2 +- ...ntBundleProductShownInCategoryListAndGridTest.xml | 2 +- .../Mftf/Test/StorefrontEditBundleProductTest.xml | 2 +- ...StorefrontSpecialPriceBundleProductInCartTest.xml | 2 +- .../Test/Mftf/Test/AddToCartCrossSellTest.xml | 2 +- .../Test/AdminAddDefaultImageSimpleProductTest.xml | 2 +- .../Test/AdminAddDefaultVideoSimpleProductTest.xml | 2 +- ...AdminAssignProductAttributeToAttributeSetTest.xml | 2 +- .../AdminCreateAndEditSimpleProductSettingsTest.xml | 2 +- .../Mftf/Test/AdminCreateAttributeSetEntityTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateCategoryTest.xml | 6 +++--- ...teCustomProductAttributeWithDropdownFieldTest.xml | 2 +- .../Test/AdminCreateDropdownProductAttributeTest.xml | 2 +- ...buteVisibleInStorefrontAdvancedSearchFormTest.xml | 2 +- ...buteVisibleInStorefrontAdvancedSearchFormTest.xml | 2 +- .../Test/AdminCreateNewGroupForAttributeSetTest.xml | 2 +- ...dminCreateProductAttributeFromProductPageTest.xml | 2 +- ...inCreateProductAttributeRequiredTextFieldTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateSimpleProductTest.xml | 8 ++++---- ...eDropdownProductAttributeFromAttributeSetTest.xml | 2 +- .../Mftf/Test/AdminDeleteProductAttributeTest.xml | 2 +- .../Test/Mftf/Test/AdminDeleteRootCategoryTest.xml | 2 +- .../Test/Mftf/Test/AdminDeleteSimpleProductTest.xml | 2 +- .../Test/AdminDeleteSystemProductAttributeTest.xml | 2 +- ...TextFieldProductAttributeFromAttributeSetTest.xml | 2 +- .../Test/AdminEditTextEditorProductAttributeTest.xml | 2 +- .../Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml | 2 +- .../AdminMoveCategoryAndCheckUrlRewritesTest.xml | 2 +- ...veCategoryToAnotherPositionInCategoryTreeTest.xml | 2 +- .../AdminMultipleWebsitesUseDefaultValuesTest.xml | 2 +- .../Test/AdminNavigateMultipleUpSellProductsTest.xml | 2 +- ...ductCategoryIndexerInUpdateOnScheduleModeTest.xml | 2 +- .../Test/Mftf/Test/AdminSimpleProductImagesTest.xml | 4 ++-- .../Test/AdminSimpleSetEditRelatedProductsTest.xml | 2 +- ...nUnassignProductAttributeFromAttributeSetTest.xml | 2 +- ...eCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml | 2 +- .../Test/AdminUpdateCategoryAndMakeInactiveTest.xml | 2 +- .../Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml | 2 +- .../AdminUpdateCategoryUrlKeyWithStoreViewTest.xml | 2 +- .../Test/AdminUpdateCategoryWithProductsTest.xml | 2 +- ...AdminUpdateFlatCategoryNameAndDescriptionTest.xml | 2 +- ...uctWithRegularPriceInStockDisabledProductTest.xml | 2 +- ...ularPriceInStockVisibleInCatalogAndSearchTest.xml | 2 +- ...thRegularPriceInStockVisibleInCatalogOnlyTest.xml | 2 +- ...tWithRegularPriceInStockWithCustomOptionsTest.xml | 2 +- .../AdminUpdateTopCategoryUrlWithNoRedirectTest.xml | 2 +- .../AdminUpdateTopCategoryUrlWithRedirectTest.xml | 2 +- ...nfigurableOptionTextInputLengthValidationHint.xml | 2 +- .../Mftf/Test/CreateProductAttributeEntityTest.xml | 12 ++++++------ ...roductAvailableAfterEnablingSubCategoriesTest.xml | 2 +- ...SaveProductWithCustomOptionsSecondWebsiteTest.xml | 2 +- .../Mftf/Test/SimpleProductTwoCustomOptionsTest.xml | 2 +- .../Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml | 2 +- .../StorefrontProductNameWithDoubleQuoteTest.xml | 2 +- .../Test/StorefrontProductWithEmptyAttributeTest.xml | 2 +- ...cialPriceForDifferentTimezonesForWebsitesTest.xml | 2 +- ...ryProductAndProductCategoryPartialReindexTest.xml | 2 +- .../Test/AdminApplyCatalogRuleByCategoryTest.xml | 2 +- .../RewriteStoreLevelUrlKeyOfChildCategoryTest.xml | 2 +- .../OnePageCheckoutAsCustomerUsingNewAddressTest.xml | 2 +- ...eCheckoutAsCustomerUsingNonDefaultAddressTest.xml | 2 +- .../Test/OnePageCheckoutWithAllProductTypesTest.xml | 2 +- ...ToTheShoppingCartWithoutAnySelectedOptionTest.xml | 2 +- ...ShippingRecalculationAfterCouponCodeAddedTest.xml | 2 +- ...thAllTypesOfCustomOptionToTheShoppingCartTest.xml | 2 +- .../Mftf/Test/StorefrontCustomerCheckoutTest.xml | 2 +- ...stomerRegistrationAndDisableGuestCheckoutTest.xml | 2 +- .../Test/Mftf/Test/StorefrontGuestCheckoutTest.xml | 2 +- ...tentDataForGuestCustomerWithPhysicalQuoteTest.xml | 2 +- ...UpdatePriceInShoppingCartAfterProductSaveTest.xml | 2 +- ...refrontUpdateShoppingCartSimpleProductQtyTest.xml | 2 +- ...pingCartSimpleWithCustomOptionsProductQtyTest.xml | 2 +- .../Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml | 2 +- .../Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml | 2 +- .../Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml | 2 +- .../Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml | 2 +- ...dminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml | 2 +- ...nAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml | 2 +- ...idgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml | 2 +- ...WidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml | 2 +- ...WidgetToWYSIWYGWithCatalogProductListTypeTest.xml | 2 +- ...ToWYSIWYGWithRecentlyComparedProductsTypeTest.xml | 2 +- ...etToWYSIWYGWithRecentlyViewedProductsTypeTest.xml | 2 +- .../Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml | 2 +- .../Mftf/Test/AdminConfigurableProductCreateTest.xml | 4 ++-- .../Mftf/Test/AdminConfigurableProductDeleteTest.xml | 4 ++-- .../Mftf/Test/AdminConfigurableProductUpdateTest.xml | 10 +++++----- ...erifyConfigurableProductLayeredNavigationTest.xml | 2 +- ...dNewDefaultBillingShippingCustomerAddressTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateCustomerTest.xml | 2 +- .../AdminDeleteCustomerAddressesFromTheGridTest.xml | 2 +- .../Test/Mftf/Test/AdminDeleteCustomerTest.xml | 2 +- .../AdminDeleteDefaultBillingCustomerAddressTest.xml | 2 +- ...EditDefaultBillingShippingCustomerAddressTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateCustomerTest.xml | 4 ++-- .../Mftf/Test/StorefrontAddCustomerAddressTest.xml | 6 +++--- .../Test/Mftf/Test/StorefrontCreateCustomerTest.xml | 2 +- .../StorefrontResetCustomerPasswordSuccessTest.xml | 2 +- .../StorefrontUpdateCustomerAddressFranceTest.xml | 2 +- .../Test/StorefrontUpdateCustomerAddressUKTest.xml | 2 +- .../Test/StorefrontUpdateCustomerPasswordTest.xml | 4 ++-- .../ProductQuickSearchUsingElasticSearchTest.xml | 2 +- .../StorefrontElasticSearchForChineseLocaleTest.xml | 2 +- ...torefrontElasticsearch6SearchInvalidValueTest.xml | 2 +- .../AdminImportProductsWithAddUpdateBehaviorTest.xml | 2 +- .../AdminImportProductsWithDeleteBehaviorTest.xml | 2 +- ...ProductImportCSVFileCorrectDifferentFilesTest.xml | 2 +- ...tVisibilityDifferentStoreViewsAfterImportTest.xml | 2 +- ...rksWhenUpdatingProductThroughImportingCSVTest.xml | 2 +- .../Test/StoreFrontCheckingWithMultishipmentTest.xml | 2 +- ...gCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml | 2 +- .../Test/YoutubeVideoWindowOnProductPageTest.xml | 2 +- ...onfigurableProductToOrderFromShoppingCartTest.xml | 2 +- .../AddSimpleProductToOrderFromShoppingCartTest.xml | 2 +- ...edOrderWithProductQtyWithoutStockDecreaseTest.xml | 2 +- .../Test/AdminCreateCreditMemoPartialRefundTest.xml | 2 +- .../AdminCreateCreditMemoWithPurchaseOrderTest.xml | 2 +- ...tomerWithTwoAddressesTaxableAndNonTaxableTest.xml | 2 +- ...nCreateOrderWithSelectedShoppingCartItemsTest.xml | 2 +- .../AdminSubmitsOrderWithAndWithoutEmailTest.xml | 2 +- .../Test/CreateInvoiceAndCheckInvoiceOrderTest.xml | 2 +- .../Test/CreateOrderFromEditCustomerPageTest.xml | 2 +- ...ConfigurableProductsInComparedOnOrderPageTest.xml | 2 +- ...LastOrderedConfigurableProductOnOrderPageTest.xml | 2 +- .../MoveLastOrderedSimpleProductOnOrderPageTest.xml | 2 +- ...entlyViewedConfigurableProductOnOrderPageTest.xml | 2 +- .../MoveSimpleProductsInComparedOnOrderPageTest.xml | 2 +- .../Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml | 2 +- .../Test/StorefrontAutoGeneratedCouponCodeTest.xml | 2 +- .../Test/StorefrontCartPriceRuleSubtotalTest.xml | 2 +- .../Mftf/Test/StorefrontFilterByImageSwatchTest.xml | 2 +- .../Mftf/Test/StorefrontFilterByTextSwatchTest.xml | 2 +- .../Mftf/Test/StorefrontFilterByVisualSwatchTest.xml | 2 +- ...CategoryUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...eProductUrLRewriteAndAddPermanentRedirectTest.xml | 2 +- ...eProductUrLRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...CategoryUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...CategoryUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...inUrlRewritesForProductInAnchorCategoriesTest.xml | 2 +- .../Test/Mftf/Test/NewProductsListWidgetTest.xml | 2 +- .../Widget/Test/Mftf/Test/ProductsListWidgetTest.xml | 2 +- 146 files changed, 167 insertions(+), 167 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index 2b6b139520f97..c67a207ebf0b1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -14,7 +14,7 @@ <stories value="Create/Edit bundle product in Admin"/> <title value="Admin should be able to add/edit bundle items when creating/editing a bundle product"/> <description value="Admin should be able to add/edit bundle items when creating/editing a bundle product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-223"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml index 823ad303c5455..baf9652522909 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml @@ -15,7 +15,7 @@ <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"/> + <severity value="BLOCKER"/> <group value="bundle"/> <group value="catalog"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 7d82c6e5b43ad..0af44f64f8df1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -14,7 +14,7 @@ <stories value="Create/Edit bundle product in Admin"/> <title value="Admin should be able to save and publish a bundle product"/> <description value="Admin should be able to save and publish a bundle product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-225"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index 80920c2e6d851..171ed1323a6b2 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle Product Pricing"/> <title value="Admin should be able to apply fixed pricing for Bundled Product"/> <description value="Admin should be able to apply fixed pricing for Bundled Product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-186"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index 4efacbb267a0b..14753da609967 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -13,7 +13,7 @@ <stories value="Check that after changing currency price of cart is correct when the bundle product added to the cart"/> <title value="User should be able change the currency and get right price in cart when the bundle product added to the cart"/> <description value="User should be able change the currency and add one more product in cart and get right price in previous currency"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94467"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index b174ee2ee3c70..15d5ff7d3560b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle product details page"/> <title value="Customer should be able to see basic bundle product details"/> <description value="Customer should be able to see basic bundle product details"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-230"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml index 62a66b7d092ef..a7e34b4744b90 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle products list on Storefront"/> <title value="Customer should be able to see bundle products in the category products list and grid views"/> <description value="Customer should be able to see bundle products in the category products list and grid views"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-226"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index 3a40a1b7eeb71..c0ea00167d329 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle products list on Storefront"/> <title value="Customer should be able to change chosen options for Bundle Product when clicking Edit button in Shopping Cart page"/> <description value="Customer should be able to change chosen options for Bundle Product when clicking Edit button in Shopping Cart page"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-290"/> <group value="Bundle"/> </annotations> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml index 44ac68a2759f3..32662321a611e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml @@ -13,7 +13,7 @@ <stories value="Add bundle product to cart on storefront"/> <title value="Customer should not be able to add a Bundle Product to the cart when added a special price for associated products"/> <description value="Customer should not be able to add a Bundle Product to the cart when added a special price for associated products"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-19134"/> <useCaseId value="MC-18963"/> <group value="bundle"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml index 9abad132d32db..faba11f2234bc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml @@ -14,7 +14,7 @@ <stories value="Promote Products as Cross-Sells"/> <title value="Admin should be able to add cross-sell to products."/> <description value="Create products, add products to cross sells, and check that they appear in the Shopping Cart page."/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-9143"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index ac1f967b66e41..ce8ce5ead1153 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -14,7 +14,7 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add default image for a Simple Product"/> <description value="Admin should be able to add default images for a Simple Product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-113"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index ba91817a8dc6e..adf13e4e31435 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -14,7 +14,7 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add default product video for a Simple Product"/> <description value="Admin should be able to add default product video for a Simple Product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-111"/> <group value="Catalog"/> <skip> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index 83916d9d96027..980760699dc71 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -14,7 +14,7 @@ <stories value="Add/Update attribute set"/> <title value="Admin should be able to assign attributes to an attribute set"/> <description value="Admin should be able to assign attributes to an attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-168"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml index 2fddc667b60fd..9cb709a8c7e62 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml @@ -14,7 +14,7 @@ <stories value="Create/Edit simple product in Admin"/> <title value="Admin should be able to set/edit other product information when creating/editing a simple product"/> <description value="Admin should be able to set/edit product information when creating/editing a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3241"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml index f2783a9fcf2cb..aba7bf6073117 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml @@ -15,7 +15,7 @@ <title value="Create attribute set with new product attribute"/> <description value="Admin should be able to create attribute set with new product attribute"/> <testCaseId value="MC-10884"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml index 44a83f2dbadd4..47069004e5009 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml @@ -14,7 +14,7 @@ <stories value="Create a Category via the Admin"/> <title value="Admin should be able to create a Category"/> <description value="Admin should be able to create a Category"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-72102"/> <group value="category"/> </annotations> @@ -43,7 +43,7 @@ <stories value="Default layout configuration MAGETWO-88793"/> <title value="Admin should be able to configure the default layout for Category Page from System Configuration"/> <description value="Admin should be able to configure the default layout for Category Page from System Configuration"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-89024"/> <group value="category"/> </annotations> @@ -75,7 +75,7 @@ <stories value="Default layout configuration MAGETWO-88793"/> <title value="Category should not be saved once layered navigation price step field is left empty"/> <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-95797"/> <group value="category"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index 2b33f4a6bb1c0..06d7d1081d058 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -13,7 +13,7 @@ <stories value="Create product Attribute"/> <title value="Create Custom Product Attribute Dropdown Field (Not Required) from Product Page"/> <description value="login as admin and create configurable product attribute with Dropdown field"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10905"/> <group value="mtf_migrated"/> <skip> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml index 4b69123635852..34d7fc1c24c61 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml @@ -12,7 +12,7 @@ <stories value="Create/configure Dropdown product attribute"/> <title value="Admin should be able to create dropdown product attribute"/> <description value="Admin should be able to create dropdown product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-4982"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 7ae42948175b1..4d2250d650da2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -15,7 +15,7 @@ <title value="AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest"/> <description value="Admin should able to create product Dropdown attribute and check its visibility on frontend in Advanced Search form"/> <testCaseId value="MC-10827"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 62eea9d48ccc0..b5b5fa3b12dc7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -15,7 +15,7 @@ <title value="Create product attribute of type Multiple Select and check its visibility on frontend in Advanced Search form"/> <description value="Admin should be able to create product attribute of type Multiple Select and check its visibility on frontend in Advanced Search form"/> <testCaseId value="MC-10828"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml index 13a7974575640..6cd4ce6ac47c4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml @@ -13,7 +13,7 @@ <stories value="Edit attribute set"/> <title value="Admin should be able to create new group in an Attribute Set"/> <description value="The test verifies creating a new group in an attribute set and a validation message in case of empty group name"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-170"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index 18d1ec5b30f72..ae4720e19118d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -13,7 +13,7 @@ <stories value="Create product Attribute"/> <title value="Create Product Attribute from Product Page"/> <description value="Login as admin and create new product attribute from product page with Text Field"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10899"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml index a6632fa1ddabb..a75cb093ec8ee 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml @@ -13,7 +13,7 @@ <stories value="Manage products"/> <title value="Create Custom Product Attribute Text Field (Required) from Product Page"/> <description value="Login as admin and create product attribute with Text Field and Required option"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10906"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml index 1c6ed551d3f72..9f479edb91ce6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml @@ -14,7 +14,7 @@ <stories value="Create a Simple Product via Admin"/> <title value="Admin should be able to create a Simple Product"/> <description value="Admin should be able to create a Simple Product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-23414"/> <group value="product"/> </annotations> @@ -46,7 +46,7 @@ <stories value="Default layout configuration MAGETWO-88793"/> <title value="Admin should be able to configure a default layout for Product Page from System Configuration"/> <description value="Admin should be able to configure a default layout for Product Page from System Configuration"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-89023"/> <group value="product"/> </annotations> @@ -76,7 +76,7 @@ <stories value="Create a Simple Product via Admin"/> <title value="Admin should be able to create a product with zero price"/> <description value="Admin should be able to create a product with zero price"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-89910"/> <group value="product"/> </annotations> @@ -97,7 +97,7 @@ <stories value="Create a Simple Product via Admin"/> <title value="Admin should not be able to create a product with a negative price"/> <description value="Admin should not be able to create a product with a negative price"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-89912"/> <group value="product"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml index db57b5e7d1181..9d52399f15a96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml @@ -13,7 +13,7 @@ <stories value="Delete product attributes"/> <title value="Delete Product Attribute, Dropdown Type, from Attribute Set"/> <description value="Login as admin and delete dropdown type product attribute from attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10885"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml index d90bb162acca9..20d2a924954cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml @@ -14,7 +14,7 @@ <title value="Delete Product Attribute"/> <description value="Admin should able to delete a product attribute"/> <testCaseId value="MC-10887"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml index 5e9e536203f1e..4974b983beeb9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml @@ -13,7 +13,7 @@ <title value="Can delete a root category not assigned to any store"/> <description value="Login as admin and delete a root category not assigned to any store"/> <testCaseId value="MC-6048"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml index 5c8b90a26594b..b5d7f413730aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml @@ -14,7 +14,7 @@ <stories value="Delete products"/> <title value="Delete Simple Product"/> <description value="Admin should be able to delete a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11013"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml index c3a550165de89..be54262987b05 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml @@ -14,7 +14,7 @@ <title value="Delete System Product Attribute"/> <description value="Admin should not be able to see Delete Attribute button"/> <testCaseId value="MC-10893"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml index 44996d167feaa..3d6eca36ec06d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml @@ -13,7 +13,7 @@ <stories value="Delete product attributes"/> <title value="Delete Product Attribute, Text Field, from Attribute Set"/> <description value="Login as admin and delete Text Field type product attribute from attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10886"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml index 30b06ac8e0b43..ebbfdc4d72f40 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml @@ -14,7 +14,7 @@ <group value="Catalog"/> <title value="Admin are able to change Input Type of Text Editor product attribute"/> <description value="Admin are able to change Input Type of Text Editor product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6215"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 72ef78accb7fc..820a578e0252f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -13,7 +13,7 @@ <stories value="Edit categories"/> <title value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <description value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10022"/> <useCaseId value="MAGETWO-89248"/> <group value="category"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml index 77ae5dbf64840..ac766feef1742 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml @@ -14,7 +14,7 @@ <description value="Login as admin, move category from one to another and check category url rewrites"/> <testCaseId value="MC-6494"/> <features value="Catalog"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml index 801d925c0fd84..f49b9b5b5d3e3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml @@ -14,7 +14,7 @@ <title value="Move Category to Another Position in Category Tree"/> <description value="Test log in to Move Category and Move Category to Another Position in Category Tree"/> <testCaseId value="MC-13612"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index d56f64d72a1c1..c1cfcf7ebe10f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -14,7 +14,7 @@ <stories value="Create websites"/> <title value="Use Default Value checkboxes should be checked for new website scope"/> <description value="Use Default Value checkboxes for product attribute should be checked for new website scope"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92454"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml index e88645fdc3782..d0e466114a1d4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml @@ -14,7 +14,7 @@ <title value="Promote Multiple Products (Simple, Configurable) as Up-Sell Products"/> <description value="Login as admin and add simple and configurable Products as Up-Sell products"/> <testCaseId value="MC-8902"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index b547fbe69fbf7..4cdbd75648d56 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -14,7 +14,7 @@ <title value="Product Categories Indexer in Update on Schedule mode"/> <description value="The test verifies that in Update on Schedule mode if displaying of category products on Storefront changes due to product properties change, the changes are NOT applied immediately, but applied only after cron runs (twice)."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11146"/> <group value="catalog"/> <group value="indexer"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index 3d505b9f893eb..cd80467c66d06 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -14,7 +14,7 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add images of different types and sizes to Simple Product"/> <description value="Admin should be able to add images of different types and sizes to Simple Product"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-189"/> <group value="Catalog"/> </annotations> @@ -172,7 +172,7 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> <description value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-191"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml index 84eb3a843aa1f..76e0bdd5d46ad 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -14,7 +14,7 @@ <stories value="Create/edit simple product"/> <title value="Admin should be able to set/edit Related Products information when editing a simple product"/> <description value="Admin should be able to set/edit Related Products information when editing a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3411"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 831444c924ec1..307eceddae3ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -14,7 +14,7 @@ <stories value="Add/Update attribute set"/> <title value="Admin should be able to unassign attributes from an attribute set"/> <description value="Admin should be able to unassign attributes from an attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-194"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml index 0f4e6e2854948..4b1cd1f674425 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml @@ -13,7 +13,7 @@ <title value="Update category, check default URL key on the custom store view"/> <description value="Login as admin and update category and check default URL Key on custom store view"/> <testCaseId value="MC-6063"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml index b121ba46410e4..4eab0ca8f0694 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml @@ -13,7 +13,7 @@ <title value="Update category, make inactive"/> <description value="Login as admin and update category and make it Inactive"/> <testCaseId value="MC-6060"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml index 299298266d061..3c99e86dba2c9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml @@ -14,7 +14,7 @@ <stories value="Update SEO-friendly URL via the Admin"/> <title value="SEO-friendly URL should update regardless of scope or redirect change."/> <description value="SEO-friendly URL should update regardless of scope or redirect change."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92338"/> <group value="category"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml index c0c8f53307b20..6ecb7e09d5a2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml @@ -13,7 +13,7 @@ <title value="Update category, URL key with custom store view"/> <description value="Login as admin and update category URL Key with store view"/> <testCaseId value="MC-6062"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml index 065ebb74785d4..6dde1567b68f8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml @@ -13,7 +13,7 @@ <title value="Update category, sort products by default sorting"/> <description value="Login as admin, update category and sort products"/> <testCaseId value="MC-6059"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml index 2ae3c67cb222d..2c356636df56c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml @@ -13,7 +13,7 @@ <title value="Flat Catalog - Update Category Name and Description"/> <description value="Login as admin and update flat category name and description"/> <testCaseId value="MC-11010"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> <group value="WYSIWYGDisabled"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml index 270b95b7e52c5..d0935948e88bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml @@ -14,7 +14,7 @@ <title value="Update Simple Product with Regular Price (In Stock), Disabled Product"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock), Disabled Product"/> <testCaseId value="MC-10816"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 2490782d86b8b..423a7d23d7d4a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -14,7 +14,7 @@ <title value="Update Simple Product with Regular Price (In Stock) Visible in Catalog and Search"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Visible in Catalog and Search"/> <testCaseId value="MC-10802"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 2f0ef84d4be0d..29a4c5e009d07 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -14,7 +14,7 @@ <title value="Update Simple Product with Regular Price (In Stock) Visible in Catalog Only"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Visible in Catalog Only"/> <testCaseId value="MC-10804"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index 4b13323afdc44..00b6a5def6169 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -14,7 +14,7 @@ <title value="Update Simple Product with Regular Price (In Stock) with Custom Options"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) with Custom Options"/> <testCaseId value="MC-10819"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml index df3f0529b1bd4..f499d87c84682 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Update top category url and do not create redirect"/> <description value="Login as admin and update top category url and do not create redirect"/> <testCaseId value="MC-6056"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml index fddaced13fa4d..eabedebaeab83 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Update top category url and create redirect"/> <description value="Login as admin and update top category url and create redirect"/> <testCaseId value="MC-6057"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml index a4785e25d39bb..850c0e98c45f3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml @@ -14,7 +14,7 @@ <stories value="Customizable text option input-length validation hint changes dynamically"/> <title value="You should have a dynamic length validation hint when using text option has max char limit"/> <description value="You should have a dynamic length validation hint when using text option has max char limit"/> - <severity value="MINOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92229"/> <group value="product"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index d5dee9e462560..ac6c34a8cc1f1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -14,7 +14,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a TextField product attribute"/> <description value="Admin should be able to create a TextField product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10894"/> <group value="Catalog"/> <group value="mtf_migrated"/> @@ -70,7 +70,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a Date product attribute"/> <description value="Admin should be able to create a Date product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10895"/> <group value="Catalog"/> <skip> @@ -133,7 +133,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a Price product attribute"/> <description value="Admin should be able to create a Price product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10897"/> <group value="Catalog"/> <group value="mtf_migrated"/> @@ -187,7 +187,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a Dropdown product attribute"/> <description value="Admin should be able to create a Dropdown product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10896"/> <group value="Catalog"/> <group value="mtf_migrated"/> @@ -274,7 +274,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a Dropdown product attribute containing a single quote"/> <description value="Admin should be able to create a Dropdown product attribute containing a single quote"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10898"/> <group value="Catalog"/> <group value="mtf_migrated"/> @@ -343,7 +343,7 @@ <stories value="Create Product Attributes"/> <title value="Admin should be able to create a MultiSelect product attribute"/> <description value="Admin should be able to create a MultiSelect product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10888"/> <group value="Catalog"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index 9e0dea7e06ded..da7165600d64d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -14,7 +14,7 @@ <stories value="Categories"/> <title value="Check that parent categories are showing products after enabling subcategories after fully reindex"/> <description value="Check that parent categories are showing products after enabling subcategories after fully reindex"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97370"/> <useCaseId value="MAGETWO-96846"/> <group value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index f2c5750a2b18e..783054c3ffb5a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -14,7 +14,7 @@ <stories value="Purchase a product with Custom Options of different types"/> <title value="You should be able to save a product with custom options assigned to a different website"/> <description value="Custom Options should not be split when saving the product after assigning to a different website"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-91436"/> <group value="product"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml index 41bacc69baca4..f5ac8b243979f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml @@ -14,7 +14,7 @@ <stories value="Create simple product with two custom options" /> <title value="Admin should be able to create simple product with two custom options"/> <description value="Admin should be able to create simple product with two custom options"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-248"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml index 120fa30832075..68d847907a448 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml @@ -13,7 +13,7 @@ <features value="Catalog"/> <title value="Storefront Fotorama arrows test"/> <description value="Check arrows next to the thumbs are not visible than there is room for all pictures."/> - <severity value="MINOR"/> + <severity value="BLOCKER"/> <group value="Catalog"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml index 1615f75395fed..64b8d10b75419 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml @@ -14,7 +14,7 @@ <stories value="Create products"/> <title value="Product with double quote in name"/> <description value="Product with a double quote in the name should appear correctly on the storefront"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="product"/> <testCaseId value="MAGETWO-92384"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index cd97d64227e83..40733b120f1e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -14,7 +14,7 @@ <stories value="Create products"/> <title value="Product attribute is not visible on storefront if it is empty"/> <description value="Product attribute should not be visible on storefront if it is empty"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-91893"/> <group value="product"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 59f0b2f5dd76e..f28029d44b9e0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -14,7 +14,7 @@ <stories value="Special price"/> <title value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> <description value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97508"/> <useCaseId value="MAGETWO-96847"/> <group value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index 7ef1619319289..e4446abf6624e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -14,7 +14,7 @@ <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"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11386"/> <group value="catalog"/> <group value="indexer"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index d7d7da58c27fc..b2ce64fe651c0 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -13,7 +13,7 @@ <stories value="Apply catalog price rule"/> <title value="Admin should be able to apply the catalog rule by category"/> <description value="Admin should be able to apply the catalog rule by category"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-74"/> <group value="CatalogRule"/> </annotations> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index e176fba9fd189..d67ff6fe0bace 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -12,7 +12,7 @@ <stories value="MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope"/> <description value="Rewriting Store-level URL key of child category"/> <features value="CatalogUrlRewrite"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94934"/> <group value="CatalogUrlRewrite"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml index 600163501b556..4065b3691b250 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using new address test"/> <description value="Checkout as customer using new address"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14740"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index 661957a1de980..571fb8c4cf3a5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using non default address test"/> <description value="Checkout as customer using non default address"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14739"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 2b2316b20396e..ce686fc5974dd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout with all product types test"/> <description value="Checkout with all product types"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14742"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml index 450bfff27125a..9e5542838745a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml @@ -13,7 +13,7 @@ <title value="Add simple product with all types of custom options to cart without selecting any options"/> <description value="Add simple product with all types of custom options to cart without selecting any options"/> <testCaseId value="MC-14725"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index d7b462d7f649b..ba26b44c11ba3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -13,7 +13,7 @@ <stories value="Checkout Free Shipping Recalculation after Coupon Code Added"/> <description value="User should be able to do checkout free shipping recalculation after adding coupon code"/> <features value="Checkout"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-96537"/> <useCaseId value="MAGETWO-96431"/> <group value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml index 319183d4641e6..8081abcff307b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml @@ -13,7 +13,7 @@ <title value="Create a simple products with all types of oprtions and add it to the cart"/> <description value="Create a simple products with all types of oprtions and add it to the cart"/> <testCaseId value="MC-14726"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 3a686c8efdd5f..08de5a9e6daa7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via Storefront"/> <title value="Customer Checkout via Storefront"/> <description value="Should be able to place an order as a customer."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-30274"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml index 2b96d385487bc..c616faf716f03 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml @@ -12,7 +12,7 @@ <stories value="Checkout"/> <title value="Verify customer checkout by following new customer registration when guest checkout is disabled"/> <description value="Customer is redirected to checkout on login, follow the new Customer registration when guest checkout is disabled"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14704"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index 53d7904ffdc38..28bdd1d536d43 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via Guest Checkout"/> <title value="Guest Checkout - guest should be able to place an order"/> <description value="Should be able to place an order as a Guest"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-12825"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml index c106ec9c552ff..391cfd254101a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via Guest Checkout"/> <title value="Persistent Data for Guest Customer with physical quote"/> <description value="One can use Persistent Data for Guest Customer with physical quote"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-13479"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index 46c4abf4eab1a..6208b43bcb39b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via the Storefront"/> <title value="Update price in shopping cart after product save"/> <description value="Price in shopping cart should be updated after product save with changed price"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-58179"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml index d0d75317531b7..d166bfdac0ba4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml @@ -15,7 +15,7 @@ <title value="Check updating shopping cart while updating items qty"/> <description value="Check updating shopping cart while updating items qty"/> <testCaseId value="MC-14731" /> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml index 0b52b08980ded..91a601dc6ef52 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml @@ -15,7 +15,7 @@ <title value="Check updating shopping cart while updating qty of items with custom options"/> <description value="Check updating shopping cart while updating qty of items with custom options"/> <testCaseId value="MC-14732" /> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml index f54547015eb9c..a577f9853ea1d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Verify that admin is able to upload image to a CMS Page with TinyMCE3 enabled"/> <description value="Verify that admin is able to upload image to CMS Page with TinyMCE3 enabled"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-95725"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index e22f6e085a32b..162c9a60fd6b1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to add image to WYSIWYG content of Block"/> <description value="Admin should be able to add image to WYSIWYG content of Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-84376"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index 51afa7b59d366..0476ecf99ad36 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to add image to WYSIWYG content of CMS Page"/> <description value="Admin should be able to add image to WYSIWYG content of CMS Page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-85825"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml index e5aecd0f3da81..887fe88533f74 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to add widget to WYSIWYG content of Block"/> <description value="Admin should be able to add widget to WYSIWYG content Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-84654"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml index 5f9bfaf47a157..450003db465a8 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: CMS page link"/> <description value="Admin should be able to create a CMS page with widget type: CMS page link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83781"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml index 81826eeab5e10..633dd4dbc3388 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: CMS Static Block"/> <description value="Admin should be able to create a CMS page with widget type: CMS Static Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83787"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 5d745c625ac10..6b634282eaf37 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog category link"/> <description value="Admin should be able to create a CMS page with widget type: Catalog category link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83611"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index 940c1979710e1..e8c2fc7f3cbf8 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog product link"/> <description value="Admin should be able to create a CMS page with widget type: Catalog product link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83788"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml index f849c31948c3c..2124206466c2d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog product list"/> <description value="Admin should be able to create a CMS page with widget type: Catalog product list"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-67091"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml index f4f1da8763fbb..85ae0380d4b43 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Recently Compared Products"/> <description value="Admin should be able to create a CMS page with widget type: Recently Compared Products"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83792"/> </annotations> <!--Main test--> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml index d9c080e034dd2..14182a4c33549 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Recently Viewed Products"/> <description value="Admin should be able to create a CMS page with widget type: Recently Viewed Products"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83789"/> </annotations> <before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index 385616dcca9b9..8d4326040c919 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -14,7 +14,7 @@ <stories value="MAGETWO-91559 - Static blocks with same ID appear in place of correct block"/> <title value="Check static blocks: ID should be unique per Store View"/> <description value="Check static blocks: ID should be unique per Store View"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94229"/> <group value="Cms"/> </annotations> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml index 692ba32c6db28..99cd0cd4242a7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml @@ -14,7 +14,7 @@ <stories value="Create, Read, Update, Delete"/> <title value="admin should be able to create a configurable product with attributes"/> <description value="admin should be able to create a configurable product with attributes"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-84"/> <group value="ConfigurableProduct"/> </annotations> @@ -76,7 +76,7 @@ <stories value="Create, Read, Update, Delete"/> <title value="admin should be able to create a configurable product after incorrect sku"/> <description value="admin should be able to create a configurable product after incorrect sku"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-96365"/> <useCaseId value="MAGETWO-94556"/> <group value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml index 0d945ebecf73a..1016a46ebc213 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml @@ -16,7 +16,7 @@ <description value="admin should be able to delete a configurable product"/> <testCaseId value="MC-87"/> <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> </annotations> <before> @@ -110,7 +110,7 @@ <description value="admin should be able to mass delete configurable products"/> <testCaseId value="MC-99"/> <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index fdc467728451a..e50a851589239 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -16,7 +16,7 @@ <description value="admin should be able to bulk update attributes of configurable products"/> <testCaseId value="MC-88"/> <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> </annotations> <before> @@ -91,7 +91,7 @@ <description value="Admin should be able to remove a product configuration"/> <testCaseId value="MC-63"/> <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> </annotations> <before> @@ -187,7 +187,7 @@ <description value="Admin should be able to disable a product configuration"/> <testCaseId value="MC-119"/> <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> </annotations> <before> @@ -274,7 +274,7 @@ <stories value="Edit a configurable product in admin"/> <title value="Admin should be able to remove a configuration from a Configurable Product"/> <description value="Admin should be able to remove a configuration from a Configurable Product"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-86"/> <group value="ConfigurableProduct"/> </annotations> @@ -323,7 +323,7 @@ <stories value="Edit a configurable product in admin"/> <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-95"/> <group value="ConfigurableProduct"/> </annotations> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index 65db458e07a04..618881906c47d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -14,7 +14,7 @@ <title value="Out of stock configurable attribute option doesn't show in Layered Navigation"/> <description value=" Login as admin and verify out of stock configurable attribute option doesn't show in Layered Navigation"/> <testCaseId value="MC-13734"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="ConfigurableProduct"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml index 5600b6088cfe5..c8072dcc0a299 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml @@ -13,7 +13,7 @@ <stories value="Add new default billing/shipping customer address"/> <title value="Add new default billing/shipping customer address"/> <description value="Add new default billing/shipping customer address on customer addresses tab"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94814"/> <group value="customer"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index 2021b589790cf..1cf73e97f91df 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -14,7 +14,7 @@ <stories value="Create a Customer via the Admin"/> <title value="Admin should be able to create a customer"/> <description value="Admin should be able to create a customer"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-72095"/> <group value="customer"/> <group value="create"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml index d4af9ab58a299..b21f2a795bde7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml @@ -12,7 +12,7 @@ <title value="Admin delete customer addresses from the grid"/> <description value="Admin delete customer addresses from the grid"/> <features value="Module/ Customer"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94850"/> <stories value="MAGETWO-94346: Implement handling of large number of addresses on admin edit customer page"/> <group value="customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml index b5ade97dbb968..14a89df5eb0a5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml @@ -13,7 +13,7 @@ <stories value="Delete customer"/> <title value="DeleteCustomerBackendEntityTestVariation1"/> <description value="Login as admin and delete the customer"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14587"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml index 54ea673f7249f..98a9414f29885 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml @@ -12,7 +12,7 @@ <title value="Admin delete default billing customer address"/> <description value="Admin delete default billing customer address"/> <features value="Module/ Customer"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94816"/> <stories value="MAGETWO-94346: Implement handling of large number of addresses on admin edit customer page"/> <group value="customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml index 4f69a9bbfb695..cf1ff53225603 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml @@ -13,7 +13,7 @@ <stories value="Edit default billing/shipping customer address"/> <title value="Edit default billing/shipping customer address"/> <description value="Edit default billing/shipping customer address on customer addresses tab"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94815"/> <group value="customer"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml index dc357976887d9..426015b75fdb9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml @@ -14,7 +14,7 @@ <stories value="Update Customer Information in Admin"/> <title value="Update Customer Info from Default to Non-Default in Admin"/> <description value="Update Customer Info from Default to Non-Default in Admin"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-13619"/> <group value="Customer"/> <group value="mtf_migrated"/> @@ -209,7 +209,7 @@ <stories value="Delete Customer Address in Admin"/> <title value="Delete Customer Address in Admin"/> <description value="Delete Customer Address in Admin"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-13623"/> <group value="Customer"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml index ac6612184e32c..e9b52fd64135b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml @@ -14,7 +14,7 @@ <stories value="Implement handling of large number of addresses on storefront Address book"/> <title value="Storefront - My account - Address Book - add new address"/> <description value="Storefront user should be able to create a new address via the storefront"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97364"/> <group value="customer"/> <group value="create"/> @@ -53,7 +53,7 @@ <stories value="Implement handling of large number of addresses on storefront Address book"/> <title value="Storefront - My account - Address Book - add new default billing/shipping address"/> <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97364"/> <group value="customer"/> <group value="create"/> @@ -91,7 +91,7 @@ <stories value="Implement handling of large number of addresses on storefront Address book"/> <title value="Storefront - My account - Address Book - add new non default billing/shipping address"/> <description value="Storefront user should be able to create a new non default address via the storefront"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97500"/> <group value="customer"/> <group value="create"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml index 0bc46e8717f33..0d64ceb545831 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml @@ -14,7 +14,7 @@ <stories value="Create a Customer via the Storefront"/> <title value="As a Customer I should be able to register an account on Storefront"/> <description value="As a Customer I should be able to register an account on Storefront"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-23546"/> <group value="customer"/> <group value="create"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml index 7ea49f3684afc..63f372a080acc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml @@ -14,7 +14,7 @@ <stories value="Customer Login"/> <title value="Forgot Password on Storefront validates customer email input"/> <description value="Forgot Password on Storefront validates customer email input"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-13679"/> <group value="Customer"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml index 2f6f4fb5e2dca..6a4ed633fd413 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml @@ -14,7 +14,7 @@ <title value="Update Customer Address (France) in Storefront"/> <description value="Test log in to Storefront and Update Customer Address (France) in Storefront"/> <testCaseId value="MC-10912"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="customer"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml index d51bc1dcc9b18..b3ad6cc96aae1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml @@ -14,7 +14,7 @@ <title value="Update Customer Address (UK) in Storefront"/> <description value="Test log in to Storefront and Update Customer Address (UK) in Storefront"/> <testCaseId value="MC-10911"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="customer"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml index 9bc253c91af92..58c13898de3ee 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml @@ -14,7 +14,7 @@ <stories value="Customer Update Password"/> <title value="Update Customer Password on Storefront, Valid Current Password"/> <description value="Update Customer Password on Storefront, Valid Current Password"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10916"/> <group value="Customer"/> <group value="mtf_migrated"/> @@ -78,4 +78,4 @@ <remove keyForRemoval="loginWithNewPassword"/> <remove keyForRemoval="seeMyEmail"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml index 9fcc1909ab42c..e4430f6850660 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml @@ -14,7 +14,7 @@ <stories value="Quick Search of products on Storefront when ES 5.x is enabled"/> <title value="Product quick search doesn't throw exception after ES is chosen as search engine"/> <description value="Verify no elastic search exception is thrown when searching for product before catalogsearch reindexing"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94995"/> <group value="Catalog"/> </annotations> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml index 71e0401a1c30a..4f438237d073e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -14,7 +14,7 @@ <stories value="Elasticsearch6 for Chinese"/> <title value="Elastic search for Chinese locale"/> <description value="Elastic search for Chinese locale"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6310"/> <useCaseId value="MAGETWO-91625"/> <group value="elasticsearch"/> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 622b78fce01b9..49aef41d7f31c 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -14,7 +14,7 @@ <stories value="Search Product on Storefront"/> <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17906"/> <useCaseId value="MC-15759"/> <group value="elasticsearch"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 3eebb9def9c7a..0bd447905fb47 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -14,7 +14,7 @@ <stories value="Import Products"/> <features value="Import/Export"/> <title value="Verify Magento native import products with add/update behavior."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14077"/> <group value="importExport"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml index 9934ac2e0c8c2..800e8203d19ce 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml @@ -14,7 +14,7 @@ <stories value="Verify Magento native import products with delete behavior."/> <features value="Import/Export"/> <title value="Verify Magento native import products with delete behavior."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-30587"/> <group value="importExport"/> </annotations> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml index 593282b9bb867..c5926428daaa7 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml @@ -14,7 +14,7 @@ <features value="Import/Export"/> <stories value="Product Import"/> <title value="Product import from CSV file correct from different files."/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17104"/> <useCaseId value="MAGETWO-70803"/> <group value="importExport"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml index de3b52c3c3a98..e176052d7a280 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -14,7 +14,7 @@ <stories value="Import Products"/> <title value="Checking product visibility in different store views after product importing"/> <description value="Checking product visibility in different store views after product importing"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6406"/> <useCaseId value="MAGETWO-59265"/> <group value="importExport"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml index 4d4e87f9387cc..b23e3703b5cfd 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -14,7 +14,7 @@ <stories value="Import Products"/> <title value="Check that new URL Key works after updating a product through importing CSV file"/> <description value="Check that new URL Key works after updating a product through importing CSV file"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6317"/> <useCaseId value="MAGETWO-91544"/> <group value="importExport"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml index a054649c5365c..811af2baef56f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml @@ -14,7 +14,7 @@ <stories value="Checking multi shipment with multiple shipment adresses on front end order page"/> <title value="Checking multi shipment with multiple shipment adresses on front end order page"/> <description value="Shipping price shows 0 when you return from multiple checkout to cart"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-18519"/> <group value="Multishipment"/> </annotations> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml index 81b536746616e..f37b45c639263 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml @@ -14,7 +14,7 @@ <stories value="Multishipping"/> <title value="Process multishipping checkout when Cart page is opened in another tab"/> <description value="Process multishipping checkout when Cart page is opened in another tab"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17871"/> <useCaseId value="MC-17469"/> <group value="multishipping"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml index 862831c09d1d7..dc9b74a8be36b 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml @@ -15,7 +15,7 @@ <testCaseId value="MAGETWO-95254"/> <title value="Youtube video window on the product page"/> <description value="Check Youtube video window on the product page"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="ProductVideo"/> <skip> <issueId value="MC-32197"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml index 49ed69d76196a..1c6c2d1494e2c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Shopping Cart"/> <title value="Add configurable product to order from shopping cart test"/> <description value="Add configurable product to order from shopping cart"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16008"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml index 1485613f4e4c2..e6bcbc3b08028 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Shopping Cart"/> <title value="Add simple product to order from shopping cart test"/> <description value="Add simple product to order from shopping cart"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16007"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml index d64af533b04e0..ee69d9fd1ca46 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml @@ -14,7 +14,7 @@ <stories value="Cancel Created Order"/> <title value="Cancel the created order with product quantity without stock decrease"/> <description value="Create an order with product quantity without stock decrease, cancel the order and verify product quantity in backend"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16071"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 2cacfe934427c..8114c04a2926e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -13,7 +13,7 @@ <stories value="Credit memo entity"/> <title value="Create Credit Memo for Offline Payment Methods"/> <description value="Assert items return to stock (partial refund)"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15861"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 2d5b2d3c66906..0d19d10bd84d2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -13,7 +13,7 @@ <stories value="Credit memo entity"/> <title value="Create Credit Memo with purchase order payment method"/> <description value="Create Credit Memo with purchase order payment payment and assert 0 shipping refund"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15864"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml index b687e63fbc328..72080c3170c48 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml @@ -14,7 +14,7 @@ <description value="Tax should not be displayed for non taxable address when switching from taxable address"/> <testCaseId value="MC-21721"/> <features value="Sales"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="Sales"/> </annotations> <before> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml index 60ade9ebe01e7..841db82adefb7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml @@ -13,7 +13,7 @@ <stories value="MC-17838: Shopping cart items added to the order created in the admin"/> <description value="Shopping cart items must not be added to the order unless they were moved manually"/> <features value="Sales"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="Sales"/> </annotations> <before> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index b9e2d475f9ff6..d28b636770856 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -13,7 +13,7 @@ <stories value="Create orders"/> <title value="Email is required to create an order from Admin Panel"/> <description value="Admin should not be able to submit orders without an email address"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92980"/> <group value="sales"/> </annotations> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml index 6cfb2fa5ee911..73f3b5310731e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml @@ -13,7 +13,7 @@ <stories value="Create Invoice for Offline Payment Methods"/> <title value="Create invoice and check invoice order test"/> <description value="Create invoice for offline payment methods and check invoice order on admin dashboard"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15868"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml index 3bd6e9656ebc0..e8eea2019c941 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -14,7 +14,7 @@ <stories value="Create Order"/> <title value="Create order from edit customer page and add products to wish list and shopping cart"/> <description value="Create an order from edit customer page and add products to the wish list and shopping cart "/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16161"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml index e126e7eab0abc..1f655f319076b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Products in Comparison List Section"/> <title value="Move configurable products in compared on order page test"/> <description value="Move configurable products in compared on order page test"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16104"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml index 90b18266b22c4..f9215ff5019ff 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Last Ordered Products Section"/> <title value="Move last ordered configurable product on order page test"/> <description value="Move last ordered configurable product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16155"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml index 5355dba260060..4596faedbbe6c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Last Ordered Products Section"/> <title value="Move last ordered simple product on order page test"/> <description value="Move last ordered simple product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16154"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml index 16e44fbb8842f..4fafd3d163930 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Recently Viewed Products Section"/> <title value="Move recently viewed configurable product on order page test"/> <description value="Move recently viewed configurable product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16163"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml index 9790f03abed5a..9ee96e2b64678 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Products in Comparison List Section"/> <title value="Move simple products in compared on order page test"/> <description value="Move simple products in compared on order page test"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16103"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 04dadd95f9f43..0a2f6f4aba4e2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -13,7 +13,7 @@ <stories value="Print Order"/> <title value="Print Order from Guest on Frontend"/> <description value="Print Order from Guest on Frontend"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16225"/> <group value="sales"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 60ece859dde96..96ba6208a7518 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -15,7 +15,7 @@ <title value="[Cart Price Rule] Auto generated coupon code considers 'Uses per Coupon' and 'Uses per Customer' options"/> <description value="[Cart Price Rule] Auto generated coupon code considers 'Uses per Coupon' and 'Uses per Customer' options"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-59323"/> <group value="salesRule"/> </annotations> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml index a32d42e26d15f..7d343cd6dafd8 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml @@ -14,7 +14,7 @@ <stories value="Create cart price rule"/> <title value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> <description value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-235"/> <group value="SalesRule"/> </annotations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index 39b3ca51327ba..1fe46c20a542d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using image swatches"/> <description value="Customers can filter products using image swatches"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3461"/> <group value="Swatches"/> </annotations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml index 1d1c5c9c4e683..820150b10d06a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using text swatches"/> <description value="Customers can filter products using text swatches"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3462"/> <group value="Swatches"/> </annotations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index 0b6238d7d46be..76ec3658243bf 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using visual swatches"/> <description value="Customers can filter products using visual swatches "/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3082"/> <group value="Swatches"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index c70112c0953b3..676c7bd95bbdb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Create custom URL rewrite, permanent"/> <description value="Login as Admin and create custom UrlRewrite and add redirect type permenent"/> <testCaseId value="MC-5343"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index b03912728a3d9..b7e0d3bb6b4dc 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Create product URL rewrite, with permanent redirect"/> <description value="Login as admin, create product UrlRewrite and add Permanent redirect"/> <testCaseId value="MC-5341"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 4b03d28d44867..ce17e0de10d56 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Create product URL rewrite, with temporary redirect"/> <description value="Login as admin, create product UrlRewrite and add Temporary redirect"/> <testCaseId value="MC-5340"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index a91da90581dda..b226925748f78 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Update Category URL Rewrites, permanent"/> <description value="Login as Admin and update category UrlRewrite and add Permanent redirect type"/> <testCaseId value="MC-5357"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index e49318af53639..3147fcc848b0b 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Update Category URL Rewrites, Temporary redirect type"/> <description value="Login as Admin and update category UrlRewrite and add Temporary redirect type"/> <testCaseId value="MC-5356"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index 98c85114631aa..32795649c9375 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -13,7 +13,7 @@ <stories value="Url-rewrites for product in anchor categories"/> <title value="Url-rewrites for product in anchor categories"/> <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16568"/> <group value="urlRewrite"/> </annotations> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index e3e0b957cf550..0550c1cc2ca42 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -17,7 +17,7 @@ <stories value="New products list widget"/> <title value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> <description value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="Widget"/> </annotations> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml index df9b724783372..d7be487382c56 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml @@ -13,7 +13,7 @@ <stories value="Products list widget"/> <title value="Admin should be able to set Products List Widget"/> <description value="Admin should be able to set Products List Widget"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97041"/> <group value="Widget"/> <group value="WYSIWYGDisabled"/> From 63ce966810e5d168a5cf90550cc3a235de75fb83 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Thu, 19 Mar 2020 15:49:52 +0100 Subject: [PATCH 466/734] Change implementation for use DTO in API. And provide with integration test --- .../Model/BulkManagement.php | 1 - .../Model/MassSchedule.php | 4 +- .../Model/SaveMultipleOperations.php | 7 +- .../Model/SaveMultipleOperationsTest.php | 116 ++++++++++++++++++ 4 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkManagement.php b/app/code/Magento/AsynchronousOperations/Model/BulkManagement.php index 983bac21f3c1b..b47bb26985df0 100644 --- a/app/code/Magento/AsynchronousOperations/Model/BulkManagement.php +++ b/app/code/Magento/AsynchronousOperations/Model/BulkManagement.php @@ -117,7 +117,6 @@ public function scheduleBulk($bulkUuid, array $operations, $description, $userId $bulkSummary->setUserId($userId); $bulkSummary->setUserType($userType); $bulkSummary->setOperationCount((int)$bulkSummary->getOperationCount() + count($operations)); - $this->entityManager->save($bulkSummary); $connection->commit(); diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index c20ffeb415384..e91499768a18a 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -140,7 +140,6 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $operations = []; $requestItems = []; - $singleOperationsData = []; $bulkException = new BulkException(); foreach ($entitiesArray as $key => $entityParams) { /** @var \Magento\AsynchronousOperations\Api\Data\ItemStatusInterface $requestItem */ @@ -149,7 +148,6 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ try { $operation = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); $operations[] = $operation; - $singleOperationsData[] = $operation->getData(); $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); $requestItem->setDataHash( @@ -170,7 +168,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ } } - $this->saveMultipleOperations->execute($singleOperationsData); + $this->saveMultipleOperations->execute($operations); if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) { throw new LocalizedException( __('Something went wrong while processing the request.') diff --git a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php index a2599b35a8723..88f44c64117f2 100644 --- a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php +++ b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php @@ -8,7 +8,6 @@ namespace Magento\AsynchronousOperations\Model; -use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; use Magento\AsynchronousOperations\Model\ResourceModel\Operation as OperationResource; use Magento\Framework\Exception\CouldNotSaveException; @@ -41,10 +40,14 @@ public function __construct( public function execute(array $operations): void { try { + $operationsToInsert = array_map(function ($operation) { + return $operation->getData(); + }, $operations); + $connection = $this->operationResource->getConnection(); $connection->insertMultiple( $this->operationResource->getTable(OperationResource::TABLE_NAME), - $operations + $operationsToInsert ); } catch (\Exception $exception) { throw new CouldNotSaveException(__($exception->getMessage())); diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php new file mode 100644 index 0000000000000..6601ad09993c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AsynchronousOperations\Model; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; +use Magento\AsynchronousOperations\Model\BulkStatus; +use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterface; +use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterfaceFactory; +use Magento\Framework\EntityManager\EntityManager; + +class SaveMultipleOperationsTest extends \PHPUnit\Framework\TestCase +{ + + private const BULK_UUID = "bulk-uuid-multiple-0"; + + /** + * @var BulkStatus + */ + private $bulkStatusManagement; + + /** + * @var OperationInterfaceFactory + */ + private $operationFactory; + + /** + * @var SaveMultipleOperationsInterface + */ + private $saveMultipleOperationsInterface; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @var BulkSummaryInterfaceFactory + */ + private $bulkSummaryFactory; + + /** + * Set Up the test + */ + protected function setUp() + { + $this->saveMultipleOperationsInterface = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + SaveMultipleOperationsInterface::class + ); + $this->operationFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + OperationInterfaceFactory::class + ); + $this->bulkStatusManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + BulkStatus::class + ); + $this->bulkSummaryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + BulkSummaryInterfaceFactory::class + ); + $this->entityManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + EntityManager::class + ); + } + + /** + * Test execute() of SaveMultipleOperations + */ + public function testExecute() + { + $operation = $this->createOperation(); + $operations = [$operation, $operation, $operation]; + + $bulkSummary = $this->bulkSummaryFactory->create(); + $this->entityManager->load($bulkSummary, self::BULK_UUID); + $bulkSummary->setBulkId(self::BULK_UUID); + $bulkSummary->setDescription("Test Bulk"); + $bulkSummary->setUserId(1); + $bulkSummary->setUserType(1); + $bulkSummary->setOperationCount(count($operations)); + $this->entityManager->save($bulkSummary); + + $this->saveMultipleOperationsInterface->execute($operations); + $operationsCount = $this->bulkStatusManagement->getOperationsCountByBulkIdAndStatus(self::BULK_UUID, OperationInterface::STATUS_TYPE_OPEN); + $this->assertEquals($operationsCount, 3); + } + + /** + * Create Operation object and pre-fill with test data + * @return OperationInterface + */ + public function createOperation() + { + $serializedData = [ + 'entity_id' => null, + 'entity_link' => '', + 'meta_information' => json_encode([ + 'entity_id' => 5, + 'meta_information' => 'Test' + ]) + ]; + + $data = [ + 'data' => [ + OperationInterface::BULK_ID => self::BULK_UUID, + OperationInterface::TOPIC_NAME => "topic-4", + OperationInterface::SERIALIZED_DATA => json_encode($serializedData), + OperationInterface::STATUS => OperationInterface::STATUS_TYPE_OPEN, + ], + ]; + return $this->operationFactory->create($data); + } +} From 69848dbb3b93bc6e75b2f132a48c049256d214ea Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@users.noreply.github.com> Date: Thu, 19 Mar 2020 10:19:09 -0500 Subject: [PATCH 467/734] MC-32201: Reorder functionality Co-Authored-By: Kevin Harper <keharper@users.noreply.github.com> --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 6 +++--- app/code/Magento/SalesGraphQl/etc/schema.graphqls | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index ae802edb29254..9ab3a29611ade 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -36,10 +36,10 @@ class Reorder * List of error messages and codes. */ private const MESSAGE_CODES = [ - 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'The product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, - 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested quantity is not available' => self::ERROR_INSUFFICIENT_STOCK, ]; /** @@ -125,7 +125,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $cart = $this->customerCartProvider->resolve($customerId); if (!$this->reorderHelper->canReorder($order->getId())) { - $this->addError((string)__('Reorder is not available.'), self::ERROR_REORDER_NOT_AVAILABLE); + $this->addError((string)__('Reorders are not allowed.'), self::ERROR_REORDER_NOT_AVAILABLE); return $this->prepareOutput($cart); } diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index 1ed0a6be58665..f823c25cf2d9f 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -29,7 +29,7 @@ type ReorderItemsOutput { type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart."){ message: String! @doc(description: "Localized error message") - path: [String]! @doc(description: "Path to the input field which caused an error. Similar to how GraphQL specification defines path for errors in query: http://spec.graphql.org/draft/#sec-Errors") + path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") } From 5b71e66ceabc1f74aed0b3919167117942b19a16 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 19 Mar 2020 17:44:27 +0200 Subject: [PATCH 468/734] MC-32306: Errors while trying to update downloadable product after MC-29952 --- .../Import/Product/Type/DownloadableTest.php | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php index 5c084f4588e07..2c5b91486d347 100644 --- a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php @@ -553,7 +553,7 @@ public function isRowValidData() { return [ [ - [ + 'row_data' => [ 'sku' => 'downloadablesku1', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 1', @@ -563,13 +563,13 @@ public function isRowValidData() . 'downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title, ' . 'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 0, - true, - true, - true + 'row_num' => 0, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', @@ -579,29 +579,29 @@ public function isRowValidData() . ' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 1, - true, - true, - true + 'row_num' => 1, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'title=Title 1, file=media/file.mp4,sortorder=1|title=Title 2,' - . ' url=media/file2.mp4,sortorder=0', - 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,' - . 'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,' - . ' url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 3, - true, - true, - true + 'row_num' => 3, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', @@ -611,66 +611,66 @@ public function isRowValidData() . 'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,' . ' url=media/file2.mp4,sortorder=0', ], - 4, - true, - true, - true + 'row_num' => 4, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ //empty group title samples - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'group_title=, title=Title 1, file=media/file.mp4,sortorder=1' - . '|group_title=, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' - . ' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,' - . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 5, - true, - true, - true + 'row_num' => 5, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ //empty group title links - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,' - . 'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', - 'downloadable_links' => 'group_title=, title=Title 1, price=10, downloads=unlimited, ' - . 'file=media/file_link.mp4,sortorder=1|group_title=, title=Title 2, price=10, ' - . 'downloads=unlimited, url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 6, - true, - true, - true + 'row_num' => 6, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', ], - 2, - false, - true, - true + 'row_num' => 2, + 'is_new_product' => false, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', 'downloadable_samples' => '', 'downloadable_links' => '', ], - 7, - true, - true, - false + 'row_num' => 7, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => false ], ]; } From 0be47d5d7c5a96ce3504a749f7d5d21a18d1a052 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Thu, 19 Mar 2020 10:46:49 -0500 Subject: [PATCH 469/734] MC-32201: Reorder functionality --- app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php index fa33d056b3e18..0461b9b84b6d1 100644 --- a/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php +++ b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php @@ -78,6 +78,7 @@ public function resolve(int $customerId, string $predefinedMaskedQuoteId = null) } try { $this->ensureQuoteMaskIdExist((int)$cart->getId(), $predefinedMaskedQuoteId); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (AlreadyExistsException $e) { // do nothing, we already have masked id } From ecd5c9b942a19485b965122b6c40b8c78b9df551 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 19 Mar 2020 11:04:03 -0500 Subject: [PATCH 470/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas -- fix merge conflict --- .../HTTP/PhpEnvironment/Response.php | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index d1168d746ba68..2816d1f2c4848 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -19,11 +19,6 @@ class Response extends \Laminas\Http\PhpEnvironment\Response implements \Magento */ protected $isRedirect = false; - /** - * @var bool - */ - private $headersSent; - /** * @inheritdoc */ @@ -192,27 +187,4 @@ public function __sleep() { return ['content', 'isRedirect', 'statusCode']; } - - /** - * Sending provided headers. - * - * Had to be overridden because the original did not work correctly with multi-headers. - */ - public function sendHeaders() - { - if ($this->headersSent()) { - return $this; - } - - $status = $this->renderStatusLine(); - header($status); - - /** @var \Laminas\Http\Header\HeaderInterface $header */ - foreach ($this->getHeaders() as $header) { - header($header->toString(), false); - } - - $this->headersSent = true; - return $this; - } } From 2c8d8878c7509ef1faca6b4aa84b2d5a06a56e86 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 19 Mar 2020 18:16:45 +0200 Subject: [PATCH 471/734] added xml declaration for one more file --- .../frontend/layout/catalog_widget_product_list.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml index ce31f588c6c8c..571155185693a 100644 --- a/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml +++ b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml @@ -1,8 +1,10 @@ +<?xml version="1.0"?> <!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> From 7e7700d5e056f6b7ab43fe822f9e283b90f623ca Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Thu, 19 Mar 2020 11:21:44 -0500 Subject: [PATCH 472/734] MC-32201: Reorder functionality --- app/code/Magento/Quote/Model/Quote.php | 1 + .../Magento/Sales/Model/Reorder/Reorder.php | 36 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 82b7913446fd0..8cb136636d262 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1679,6 +1679,7 @@ public function addProduct( // collect errors instead of throwing first one if ($item->getHasError()) { + $this->deleteItem($item); foreach ($item->getMessage(false) as $message) { if (!in_array($message, $errors)) { // filter duplicate messages diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 9ab3a29611ade..ced17cbd9b055 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -36,10 +36,10 @@ class Reorder * List of error messages and codes. */ private const MESSAGE_CODES = [ - 'The product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, - 'The requested quantity is not available' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, ]; /** @@ -131,14 +131,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $items = $order->getItemsCollection(); foreach ($items as $item) { - try { - $this->addOrderItem($cart, $item); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->addError($this->addCartItemError($item, $e->getMessage())); - } catch (\Throwable $e) { - $this->logger->critical($e); - $this->addError($this->addCartItemError($item, $e->getMessage()), self::ERROR_UNDEFINED); - } + $this->addOrderItem($cart, $item); } try { @@ -157,7 +150,6 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu * @param \Magento\Quote\Model\Quote $cart * @param Item $orderItem * @return void - * @throws \Magento\Framework\Exception\LocalizedException */ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): void { @@ -176,7 +168,14 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi ); return; } - $cart->addProduct($product, $info); + try { + $cart->addProduct($product, $info); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->addError($this->addCartItemError($product->getSku(), $e->getMessage())); + } catch (\Throwable $e) { + $this->logger->critical($e); + $this->addError($this->addCartItemError($product->getSku()), self::ERROR_UNDEFINED); + } } } @@ -236,15 +235,14 @@ private function prepareOutput(CartInterface $cart): Data\ReorderOutput /** * Add error message for a cart item * - * @param Item $item - * @param string $message + * @param string $sku + * @param string|null $message * @return string */ - private function addCartItemError(Item $item, string $message): string + private function addCartItemError(string $sku, string $message = null): string { - return (string) __( - 'Could not add the product with SKU "%sku" to the shopping cart: %message', - ['sku' => $item->getSku() ?? '-', 'message' => $message] - ); + return (string)($message + ? __('Could not add the product with SKU "%1" to the shopping cart: %2', $sku, $message) + : __('Could not add the product with SKU "%1" to the shopping cart', $sku)); } } From fe026486e1c7d64c31ddc6f017c4f7df20b48e56 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Thu, 19 Mar 2020 23:44:16 +0700 Subject: [PATCH 473/734] Fix issue 16315: Product save with onthefly index ignores website assignments --- .../Model/Indexer/Product/Flat/Action/Row.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index 64a7c4be4e03c..c67d77168bf4b 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -85,10 +85,24 @@ public function execute($id = null) $ids = [$id]; $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $select = $this->_connection->select(); + $select->from(['e' => $this->_productIndexerHelper->getTable('store')], ['e.store_id']) + ->where('c.product_id = ' . $id) + ->joinLeft( + ['c' => $this->_productIndexerHelper->getTable('catalog_product_website')], + 'e.website_id = c.website_id', + [] + ); + $storeIds = $this->_connection->fetchCol($select); + $stores = $this->_storeManager->getStores(); foreach ($stores as $store) { $tableExists = $this->_isFlatTableExists($store->getId()); if ($tableExists) { + if (!in_array($store->getId(), $storeIds)) { + $this->flatItemEraser->deleteProductsFromStore($id, $store->getId()); + continue; + } $this->flatItemEraser->removeDeletedProducts($ids, $store->getId()); $this->flatItemEraser->removeDisabledProducts($ids, $store->getId()); } From bcd7893d6cd05df03f448ae9fc0437213c4bebef Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Thu, 19 Mar 2020 13:28:01 -0500 Subject: [PATCH 474/734] MC-32201: Reorder functionality --- app/code/Magento/Quote/Model/Quote.php | 7 +++++-- app/code/Magento/Sales/Model/Reorder/Reorder.php | 1 + .../testsuite/Magento/GraphQl/Sales/ReorderTest.php | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 8cb136636d262..6db568c8bffb0 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -865,6 +865,8 @@ public function beforeSave() } parent::beforeSave(); + + return $this; } /** @@ -946,7 +948,7 @@ public function assignCustomerWithAddressChange( try { $defaultBillingAddress = $this->addressRepository->getById($customer->getDefaultBilling()); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // + // phpcs:ignore Magento2.CodeAnalysis.EmptyStatement } if (isset($defaultBillingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $billingAddress */ @@ -960,7 +962,7 @@ public function assignCustomerWithAddressChange( try { $defaultShippingAddress = $this->addressRepository->getById($customer->getDefaultShipping()); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // + // phpcs:ignore Magento2.CodeAnalysis.EmptyStatement } if (isset($defaultShippingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $shippingAddress */ @@ -1686,6 +1688,7 @@ public function addProduct( $errors[] = $message; } } + break; } } if (!empty($errors)) { diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index ced17cbd9b055..d362085149de4 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -37,6 +37,7 @@ class Reorder */ private const MESSAGE_CODES = [ 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'This product is out of stock' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 12f286e613fd2..94c89fc960b45 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -15,6 +15,7 @@ /** * Test Reorder + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ReorderTest extends GraphQlAbstract { From faced716b53f4d152113d0a0171cfc171ecbefa0 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Thu, 19 Mar 2020 14:42:32 -0500 Subject: [PATCH 475/734] MC-32201: Reorder functionality --- app/code/Magento/Quote/Model/Quote.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 6db568c8bffb0..c7d40c82bbcc2 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -947,8 +947,8 @@ public function assignCustomerWithAddressChange( } else { try { $defaultBillingAddress = $this->addressRepository->getById($customer->getDefaultBilling()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // phpcs:ignore Magento2.CodeAnalysis.EmptyStatement } if (isset($defaultBillingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $billingAddress */ @@ -961,8 +961,8 @@ public function assignCustomerWithAddressChange( if (null === $shippingAddress) { try { $defaultShippingAddress = $this->addressRepository->getById($customer->getDefaultShipping()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // phpcs:ignore Magento2.CodeAnalysis.EmptyStatement } if (isset($defaultShippingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $shippingAddress */ From d6d666bde98af324f1f8d4a087b8f960f1177420 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 19 Mar 2020 15:11:14 -0500 Subject: [PATCH 476/734] MC-32378: Set up ElasticSearch so that an "AND" query is performed when searching --- .../FieldMapper/AddDefaultSearchField.php | 2 +- .../CopySearchableFieldsToSearchField.php | 2 +- app/code/Magento/Elasticsearch/i18n/en_US.csv | 2 +- .../Elasticsearch7/etc/adminhtml/system.xml | 2 +- .../Controller/QuickSearchTest.php | 31 +++++ .../_files/products_for_search.php | 108 ++++++++++++++++++ .../_files/products_for_search_rollback.php | 36 ++++++ 7 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php create mode 100644 dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php index b503dbbc91678..472a0e490d024 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php @@ -21,7 +21,7 @@ class AddDefaultSearchField implements FieldsMappingPreprocessorInterface /** * Add default search field (catch all field) to the fields. * - * Emulates catch all field (_all) for elasticsearch version 6.0+ + * Emulates catch all field (_all) for elasticsearch * * @param array $mapping * @return array diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php index 248adc9ed4e1f..fc653ec953dc7 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php @@ -21,7 +21,7 @@ class CopySearchableFieldsToSearchField implements FieldsMappingPreprocessorInte /** * Add "copy_to" parameter for default search field to index fields. * - * Emulates catch all field (_all) for elasticsearch version 6.0+ + * Emulates catch all field (_all) for elasticsearch * * @param array $mapping * @return array diff --git a/app/code/Magento/Elasticsearch/i18n/en_US.csv b/app/code/Magento/Elasticsearch/i18n/en_US.csv index 1c49a7f97c5b5..3a0ec556dbf8d 100644 --- a/app/code/Magento/Elasticsearch/i18n/en_US.csv +++ b/app/code/Magento/Elasticsearch/i18n/en_US.csv @@ -10,4 +10,4 @@ "Elasticsearch HTTP Password","Elasticsearch HTTP Password" "Elasticsearch Server Timeout","Elasticsearch Server Timeout" "Test Connection","Test Connection" -"Minimum terms to match","Minimum terms to match" +"Minimum Terms to Match","Minimum Terms to Match" diff --git a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml index 1703f1af47ee5..8a6f991eaa5a0 100644 --- a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml @@ -79,7 +79,7 @@ <field id="engine">elasticsearch7</field> </depends> </field> - <field id="elasticsearch7_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> + <field id="elasticsearch7_minimum_should_match" translate="label" type="text" sortOrder="89" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch7</field> diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php new file mode 100644 index 0000000000000..4bbbf396d0e4d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Controller; + +use Magento\TestFramework\TestCase\AbstractController; + +class QuickSearchTest extends AbstractController +{ + /** + * Tests quick search with "Minimum Terms to Match" sets to "100%". + * + * @magentoAppArea frontend + * @magentoDbIsolation disabled + * @magentoConfigFixture current_store catalog/search/elasticsearch7_minimum_should_match 100% + * @magentoConfigFixture current_store catalog/search/elasticsearch6_minimum_should_match 100% + * @magentoDataFixture Magento/Elasticsearch/_files/products_for_search.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + */ + public function testQuickSearchWithImprovedPriceRangeCalculation() + { + $this->dispatch('/catalogsearch/result/?q=24+MB04'); + $responseBody = $this->getResponse()->getBody(); + $this->assertContains('search product 2', $responseBody); + $this->assertNotContains('search product 1', $responseBody); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php new file mode 100644 index 0000000000000..8792a1060ebf2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +include __DIR__ . '/../../Catalog/_files/category.php'; + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; + +$products = [ + [ + 'type' => 'simple', + 'id' => 201, + 'name' => 'search product 1', + 'sku' => '24 MB06', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => 4, + 'website_ids' => [1], + 'price' => 10, + 'category_id' => 333, + 'meta_title' => 'Key Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'id' => 202, + 'name' => 'search product 2', + 'sku' => '24 MB04', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => 4, + 'website_ids' => [1], + 'price' => 10, + 'category_id' => 333, + 'meta_title' => 'Last Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'id' => 203, + 'name' => 'search product 3', + 'sku' => '24 MB02', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => 4, + 'website_ids' => [1], + 'price' => 20, + 'category_id' => 333, + 'meta_title' => 'First Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'id' => 204, + 'name' => 'search product 4', + 'sku' => '24 MB01', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => 4, + 'website_ids' => [1], + 'price' => 30, + 'category_id' => 333, + 'meta_title' => 'A title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], +]; + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(CategoryLinkManagementInterface::class); + +$categoriesToAssign = []; + +foreach ($products as $data) { + /** @var $product Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(Product::class); + $product + ->setTypeId($data['type']) + ->setId($data['id']) + ->setAttributeSetId($data['attribute_set']) + ->setWebsiteIds($data['website_ids']) + ->setName($data['name']) + ->setSku($data['sku']) + ->setPrice($data['price']) + ->setMetaTitle($data['meta_title']) + ->setMetaKeyword($data['meta_keyword']) + ->setMetaDescription($data['meta_keyword']) + ->setVisibility($data['visibility']) + ->setStatus($data['status']) + ->setStockData(['use_config_manage_stock' => 0]) + ->save(); + + $categoriesToAssign[$data['sku']][] = $data['category_id']; +} + +foreach ($categoriesToAssign as $sku => $categoryIds) { + $categoryLinkManagement->assignProductToCategories($sku, $categoryIds); +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php new file mode 100644 index 0000000000000..b0cfcd7a3e5e3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + +$productSkus = ['24 MB06', '24 MB04', '24 MB02', '24 MB01']; +foreach ($productSkus as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + } +} + +include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From fc28ad00de0b4af8e44fba2a5c62610c0bde179e Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Thu, 19 Mar 2020 15:58:56 -0500 Subject: [PATCH 477/734] MC-32387: Add pagination support to categoryList query - add performance tests --- setup/performance-toolkit/benchmark.jmx | 244 ++++++++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 7db2b26e3c621..fef19b05deafd 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -40947,6 +40947,250 @@ if(response.data.categoryList.length !== 4){ </hashTree> + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="GraphQL Categories Query: Get Multiple Categories By Id" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${graphqlGetCategoryListByCategoryIdPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "GraphQL Categories Query: Get Multiple Categories By Id"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/api/header_manager_before_token.jmx</stringProp></HeaderManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); + +var numbers = []; + +var sanity = 0; +for(var i = 0; i < 4; i++){ + sanity++; + if(sanity > 100){ + break; + } + var number = random.nextInt(categories.length) + if(numbers.indexOf(number) >= 0){ + i--; + continue; + } + numbers.push(number); +} + +vars.put("category_id_1", categories[numbers[0]].id); +vars.put("category_id_2", categories[numbers[1]].id); +vars.put("category_id_3", categories[numbers[2]].id); +vars.put("category_id_4", categories[numbers[3]].id); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_multiple_categories_setup.jmx</stringProp> + </JSR223Sampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="[categories query] Get multiple categories by ID" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query" : "{\n categories(filters:{ids: {in: [\"${category_id_1}\", \"${category_id_2}\", \"${category_id_3}\", \"${category_id_4}\"]}}) {\n total_count\n page_info {\n total_pages\n current_page\n page_size\n }\n items{\n id\n children {\n id\n name\n url_key\n url_path\n children_count\n path\n image\n productImagePreview: products(pageSize: 1, sort: {name: ASC}) {\n items {\n small_image {\n label\n url\n }\n }\n }\n }\n }\n }\n}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/categories_query_get_multiple_categories_by_id.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <JSR223Assertion guiclass="TestBeanGUI" testclass="JSR223Assertion" testname="Assert category count" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var response = JSON.parse(prev.getResponseDataAsString()); + +if(response.data == undefined || response.data.categories == undefined){ + AssertionResult.setFailureMessage("Categories result is empty."); + AssertionResult.setFailure(true); +} + +if(response.data.categories.items.length !== 4){ + AssertionResult.setFailureMessage("Categories query expected to find 4 categories. " + response.data.categories.items.length + " returned."); + AssertionResult.setFailure(true); +} +</stringProp> + </JSR223Assertion> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="GraphQL Categories Query: Get Many Categories with Pagination" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${graphqlGetCategoryListByCategoryIdPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "GraphQL Categories Query: Get Many Categories with Pagination"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/api/header_manager_before_token.jmx</stringProp></HeaderManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="[categories query] Get many categories by name" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query" : "{\n categories(filters:{name: {match: \"Category\"}}) {\n total_count\n page_info {\n total_pages\n current_page\n page_size\n }\n items{\n id\n children {\n id\n name\n url_key\n url_path\n children_count\n path\n image\n productImagePreview: products(pageSize: 1, sort: {name: ASC}) {\n items {\n small_image {\n label\n url\n }\n }\n }\n }\n }\n }\n}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/categories_query_get_many_categories_by_name_match.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <JSR223Assertion guiclass="TestBeanGUI" testclass="JSR223Assertion" testname="Assert category count" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var response = JSON.parse(prev.getResponseDataAsString()); + +if(response.data == undefined || response.data.categories == undefined){ + AssertionResult.setFailureMessage("Categories result is empty."); + AssertionResult.setFailure(true); +} + +if(response.data.categories.items.length != 20){ + AssertionResult.setFailureMessage("Categories query expected to find 20 categories. " + response.data.categories.items.length + " returned."); + AssertionResult.setFailure(true); +} +</stringProp> + </JSR223Assertion> + <hashTree/> + </hashTree> + </hashTree> + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="GraphQL Get Url Info by url_key" enabled="true"> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> From a7001ce3a4612d9090d07dd705bff098def2d66c Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 20 Mar 2020 09:49:18 +0200 Subject: [PATCH 478/734] MC-32593: var/resource_config.json is regenerated each time an image is requested by get.php --- app/code/Magento/MediaStorage/App/Media.php | 40 ++++++--- .../MediaStorage/Test/Unit/App/MediaTest.php | 84 +++++++++++-------- 2 files changed, 81 insertions(+), 43 deletions(-) diff --git a/app/code/Magento/MediaStorage/App/Media.php b/app/code/Magento/MediaStorage/App/Media.php index 15bf7bb62e970..f4a2125149c1d 100644 --- a/app/code/Magento/MediaStorage/App/Media.php +++ b/app/code/Magento/MediaStorage/App/Media.php @@ -20,6 +20,7 @@ use Magento\Framework\AppInterface; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\Driver\File; use Magento\MediaStorage\Model\File\Storage\Config; use Magento\MediaStorage\Model\File\Storage\ConfigFactory; use Magento\MediaStorage\Model\File\Storage\Response; @@ -27,8 +28,9 @@ use Magento\MediaStorage\Model\File\Storage\SynchronizationFactory; use Magento\MediaStorage\Service\ImageResize; + /** - * Media Storage + * The class resize original images * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -70,7 +72,12 @@ class Media implements AppInterface /** * @var WriteInterface */ - private $directory; + private $directoryPub; + + /** + * @var \Magento\Framework\Filesystem\Directory\WriteInterface + */ + private $directoryMedia; /** * @var ConfigFactory @@ -109,6 +116,7 @@ class Media implements AppInterface * @param PlaceholderFactory $placeholderFactory * @param State $state * @param ImageResize $imageResize + * @param File $file * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -122,15 +130,17 @@ public function __construct( Filesystem $filesystem, PlaceholderFactory $placeholderFactory, State $state, - ImageResize $imageResize + ImageResize $imageResize, + File $file ) { $this->response = $response; $this->isAllowed = $isAllowed; - $this->directory = $filesystem->getDirectoryWrite(DirectoryList::PUB); + $this->directoryPub = $filesystem->getDirectoryWrite(DirectoryList::PUB); + $this->directoryMedia = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $mediaDirectory = trim($mediaDirectory); if (!empty($mediaDirectory)) { // phpcs:ignore Magento2.Functions.DiscouragedFunction - $this->mediaDirectoryPath = str_replace('\\', '/', realpath($mediaDirectory)); + $this->mediaDirectoryPath = str_replace('\\', '/', $file->getRealPath($mediaDirectory)); } $this->configCacheFile = $configCacheFile; $this->relativeFileName = $relativeFileName; @@ -151,7 +161,7 @@ public function launch(): ResponseInterface { $this->appState->setAreaCode(Area::AREA_GLOBAL); - if ($this->mediaDirectoryPath !== $this->directory->getAbsolutePath()) { + if ($this->checkMediaDirectoryChanged()) { // Path to media directory changed or absent - update the config /** @var Config $config */ $config = $this->configFactory->create(['cacheFile' => $this->configCacheFile]); @@ -166,11 +176,11 @@ public function launch(): ResponseInterface try { /** @var Synchronization $sync */ - $sync = $this->syncFactory->create(['directory' => $this->directory]); + $sync = $this->syncFactory->create(['directory' => $this->directoryPub]); $sync->synchronize($this->relativeFileName); $this->imageResize->resizeFromImageName($this->getOriginalImage($this->relativeFileName)); - if ($this->directory->isReadable($this->relativeFileName)) { - $this->response->setFilePath($this->directory->getAbsolutePath($this->relativeFileName)); + if ($this->directoryPub->isReadable($this->relativeFileName)) { + $this->response->setFilePath($this->directoryPub->getAbsolutePath($this->relativeFileName)); } else { $this->setPlaceholderImage(); } @@ -182,7 +192,17 @@ public function launch(): ResponseInterface } /** - * Set Placeholder as a response + * Check if media directory changed + * + * @return bool + */ + private function checkMediaDirectoryChanged(): bool + { + return rtrim($this->mediaDirectoryPath, '/') !== rtrim($this->directoryMedia->getAbsolutePath(), '/'); + } + + /** + * Set placeholder image into response * * @return void */ diff --git a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php index 8d3211684d377..13ff664bf8064 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php @@ -15,6 +15,7 @@ use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Read; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverPool; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaStorage\App\Media; use Magento\MediaStorage\Model\File\Storage\Config; @@ -74,7 +75,12 @@ class MediaTest extends TestCase /** * @var Read|MockObject */ - private $directoryMock; + private $directoryMediaMock; + + /** + * @var \Magento\Framework\Filesystem\Directory\Read|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryPubMock; protected function setUp() { @@ -96,12 +102,30 @@ protected function setUp() ->will($this->returnValue($this->sync)); $this->filesystemMock = $this->createMock(Filesystem::class); - $this->directoryMock = $this->getMockForAbstractClass(WriteInterface::class); - + $this->directoryPubMock = $this->getMockForAbstractClass( + WriteInterface::class, + [], + '', + false, + true, + true, + ['isReadable', 'getAbsolutePath'] + ); + $this->directoryMediaMock = $this->getMockForAbstractClass( + WriteInterface::class, + [], + '', + false, + true, + true, + ['getAbsolutePath'] + ); $this->filesystemMock->expects($this->any()) ->method('getDirectoryWrite') - ->with(DirectoryList::PUB) - ->will($this->returnValue($this->directoryMock)); + ->willReturnMap([ + [DirectoryList::PUB, DriverPool::FILE, $this->directoryPubMock], + [DirectoryList::MEDIA, DriverPool::FILE, $this->directoryMediaMock], + ]); $this->responseMock = $this->createMock(Response::class); } @@ -116,17 +140,17 @@ public function testProcessRequestCreatesConfigFileMediaDirectoryIsNotProvided() $this->mediaModel = $this->getMediaModel(); $filePath = '/absolute/path/to/test/file.png'; - $this->directoryMock->expects($this->any()) + $this->directoryMediaMock->expects($this->once()) + ->method('getAbsolutePath') + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); + $this->directoryPubMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(self::RELATIVE_FILE_PATH) + ->will($this->returnValue($filePath)); $this->configMock->expects($this->once())->method('save'); $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(true)); @@ -140,18 +164,18 @@ public function testProcessRequestReturnsFileIfItsProperlySynchronized() $filePath = '/absolute/path/to/test/file.png'; $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryMediaMock->expects($this->once()) + ->method('getAbsolutePath') + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(true)); - $this->directoryMock->expects($this->any()) + $this->directoryPubMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(self::RELATIVE_FILE_PATH) + ->will($this->returnValue($filePath)); $this->responseMock->expects($this->once())->method('setFilePath')->with($filePath); $this->assertSame($this->responseMock, $this->mediaModel->launch()); } @@ -161,11 +185,11 @@ public function testProcessRequestReturnsNotFoundIfFileIsNotSynchronized() $this->mediaModel = $this->getMediaModel(); $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryMediaMock->expects($this->once()) ->method('getAbsolutePath') - ->with() + ->with(null) ->will($this->returnValue(self::MEDIA_DIRECTORY)); - $this->directoryMock->expects($this->once()) + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(false)); @@ -205,16 +229,10 @@ public function testCatchException($isDeveloper, $setBodyCalls) public function testExceptionWhenIsAllowedReturnsFalse() { $this->mediaModel = $this->getMediaModel(false); - - $filePath = '/absolute/path/to/test/file.png'; - $this->directoryMock->expects($this->any()) + $this->directoryMediaMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); $this->configMock->expects($this->once())->method('save'); $this->expectException(LogicException::class); From fc034eb473f654b066d8d416de387d8207ea65b8 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 20 Mar 2020 09:51:35 +0200 Subject: [PATCH 479/734] MC-32593: var/resource_config.json is regenerated each time an image is requested by get.php --- app/code/Magento/MediaStorage/App/Media.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/MediaStorage/App/Media.php b/app/code/Magento/MediaStorage/App/Media.php index f4a2125149c1d..ca5ff458c52e9 100644 --- a/app/code/Magento/MediaStorage/App/Media.php +++ b/app/code/Magento/MediaStorage/App/Media.php @@ -28,7 +28,6 @@ use Magento\MediaStorage\Model\File\Storage\SynchronizationFactory; use Magento\MediaStorage\Service\ImageResize; - /** * The class resize original images * From 7600c7cf07907695e1600c5e664c8a8d1cb0816c Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Fri, 20 Mar 2020 09:54:10 +0200 Subject: [PATCH 480/734] product attribute - deny HTML tags for labels --- .../adminhtml/templates/catalog/product/attribute/labels.phtml | 2 +- .../view/adminhtml/ui_component/product_attribute_add_form.xml | 1 + .../Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index 1d5d251f00de9..e5c943d24b519 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -29,7 +29,7 @@ <?php $_labels = $block->getLabelValues() ?> <?php foreach ($block->getStores() as $_store) :?> <td class="col-store-view"> - <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option<?php endif; ?>" + <input class="input-text validate-no-html-tags<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option<?php endif; ?>" type="text" name="frontend_label[<?= $block->escapeHtmlAttr($_store->getId()) ?>]" value="<?= $block->escapeHtmlAttr($_labels[$_store->getId()]) ?>" diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 3a6621137ed5a..7f87eb0cdd391 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -63,6 +63,7 @@ <required>true</required> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="validate-no-html-tags" xsi:type="boolean">true</rule> </validation> <dataType>string</dataType> <label translate="true">Attribute Label</label> diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index be9d2700664c7..adad61417a4f6 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -130,7 +130,8 @@ protected function _prepareForm() 'label' => __('Default Label'), 'title' => __('Default label'), 'required' => true, - 'value' => is_array($labels) ? $labels[0] : $labels + 'value' => is_array($labels) ? $labels[0] : $labels, + 'class' => 'validate-no-html-tags', ] ); From 334a84ef12f40030106048269c50b9773b646573 Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Fri, 20 Mar 2020 09:00:46 +0100 Subject: [PATCH 481/734] Fix static tests --- lib/web/mage/apply/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/apply/main.js b/lib/web/mage/apply/main.js index 6b5423836e0df..827283da3dbd9 100644 --- a/lib/web/mage/apply/main.js +++ b/lib/web/mage/apply/main.js @@ -32,6 +32,7 @@ define([ fn = fn.bind(null, config, el); } else { $el = $(el); + if ($el[component]) { fn = $el[component].bind($el, config); } From f0146faa05fcf3eae59fabf266ff3fe55527d60f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 20 Mar 2020 11:09:44 +0200 Subject: [PATCH 482/734] MC-32578: [MFTF] StoreFrontRecentlyViewedAtStoreViewLevelTest fails because of bad design --- .../Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index f8ee9e562a6a9..0e09635489d9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -12,9 +12,10 @@ <stories value="Recently Viewed Product"/> <title value="Recently Viewed Product at store view level"/> <description value="Recently Viewed Product should not be displayed on second store view, if configured as, Per Store View "/> - <testCaseId value="MC-31877"/> + <testCaseId value="MC-32112"/> <severity value="CRITICAL"/> <group value="catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From 5da33136df3be1b805b925e2d92d13a8e0524519 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Fri, 20 Mar 2020 10:14:54 +0100 Subject: [PATCH 483/734] Fix static test --- .../Model/SaveMultipleOperationsTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php index 6601ad09993c4..cbf20d9e8ab59 100644 --- a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php @@ -84,7 +84,8 @@ public function testExecute() $this->entityManager->save($bulkSummary); $this->saveMultipleOperationsInterface->execute($operations); - $operationsCount = $this->bulkStatusManagement->getOperationsCountByBulkIdAndStatus(self::BULK_UUID, OperationInterface::STATUS_TYPE_OPEN); + $operationsCount = $this->bulkStatusManagement + ->getOperationsCountByBulkIdAndStatus(self::BULK_UUID, OperationInterface::STATUS_TYPE_OPEN); $this->assertEquals($operationsCount, 3); } From f6f414b7feb964192742795e2a82e2db042d76ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Fri, 20 Mar 2020 11:32:03 +0100 Subject: [PATCH 484/734] Fix #6310 - set default value for weight switcher to 0 --- .../Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php index 8b855e095dec7..4b1b684ea2f8c 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php @@ -60,7 +60,7 @@ public function __construct( $this->localeFormat = $localeFormat; $this->weightSwitcher = $factoryElement->create('radios'); $this->weightSwitcher->setValue( - WeightResolver::HAS_WEIGHT + WeightResolver::HAS_NO_WEIGHT )->setValues( [ ['value' => WeightResolver::HAS_WEIGHT, 'label' => __('Yes')], @@ -84,10 +84,6 @@ public function __construct( */ public function getElementHtml() { - if (!$this->getForm()->getDataObject()->getTypeInstance()->hasWeight()) { - $this->weightSwitcher->setValue(WeightResolver::HAS_NO_WEIGHT); - } - if ($this->getDisabled()) { $this->weightSwitcher->setDisabled($this->getDisabled()); } From a102e6f81fd397afbf9f6f1ca53e39a4024a343b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 20 Mar 2020 12:50:44 +0200 Subject: [PATCH 485/734] MC-24238: [MFTF Test] Using Instant Purchase --- .../Test/Mftf/Data/BraintreeData.xml | 16 +- ...antPurchaseConfirmationDataActionGroup.xml | 25 +++ .../StorefrontInstantPurchasePopupSection.xml | 3 + ...efrontInstantPurchaseFunctionalityTest.xml | 173 ++++++++++++++++++ .../Mftf/Section/ModalConfirmationSection.xml | 2 +- .../Page/StorefrontOnePageCheckoutPage.xml | 14 ++ .../StorefrontOnePageCheckoutSection.xml | 14 ++ 7 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml create mode 100644 app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml create mode 100644 app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml create mode 100644 app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index f95ba2eb590dc..cba402f8f43fc 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -53,13 +53,13 @@ <data key="value">sandbox</data> </entity> <entity name="MerchantId" type="merchant_id"> - <data key="value">MERCH_ID</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_merchant_id}}</data> </entity> <entity name="PublicKey" type="public_key"> - <data key="value">PUBLIC_KEY</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_public_key}}</data> </entity> <entity name="PrivateKey" type="private_key"> - <data key="value">PRIVATE_KEY</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_private_key}}</data> </entity> <!-- default configuration used to restore Magento config --> @@ -141,6 +141,16 @@ <data key="cardNumberEnding">5100</data> <data key="cardExpire">12/2020</data> </entity> + <entity name="VisaDefaultCard" type="data"> + <data key="cardNumber">4111111111111111</data> + <data key="month">01</data> + <data key="year">30</data> + <data key="cvv">123</data> + </entity> + <entity name="VisaDefaultCardInfo"> + <data key="cardNumberEnding">1111</data> + <data key="cardExpire">01/2030</data> + </entity> <entity name="BraintreeConfigurationData" type="data"> <data key="title">Credit Card (Braintree)</data> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml b/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml new file mode 100644 index 0000000000000..0245ba309e3b2 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontInstantPurchaseConfirmationDataActionGroup"> + <annotations> + <description>Click on "Instant Purchase" button and assert shipping and billing information</description> + </annotations> + <arguments> + <argument name="shippingStreet" type="string" defaultValue="{{US_Address_TX.street[0]}}"/> + <argument name="billingStreet" type="string" defaultValue="{{US_Address_TX_Default_Billing.street[0]}}"/> + <argument name="cardEnding" type="string" defaultValue="{{StoredPaymentMethods.cardNumberEnding}}"/> + </arguments> + <click selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="clickInstantPurchaseButton"/> + <waitForElementVisible selector="{{ModalConfirmationSection.OkButton}}" stepKey="waitForButtonAppears"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.shippingAddress(shippingStreet)}}" stepKey="assertShippingAddress"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.billingAddress(billingStreet)}}" stepKey="assertBillingAddress"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.paymentMethod(cardEnding)}}" stepKey="assertCardEnding"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml index 8e93d049ea141..2336868086a69 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml @@ -11,5 +11,8 @@ <section name="StorefrontInstantPurchasePopupSection"> <element name="modalTitle" type="text" selector=".modal-popup .modal-title"/> <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="shippingAddress" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Shipping Address:')]/following-sibling::p[contains(text(),'{{Data}}')][1]" parameterized="true"/> + <element name="billingAddress" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Billing Address:')]/following-sibling::p[contains(text(),'{{Data}}')]" parameterized="true"/> + <element name="paymentMethod" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Payment Method:')]/following-sibling::p[contains(text(),'{{Data}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml new file mode 100644 index 0000000000000..b0235fe32ca27 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml @@ -0,0 +1,173 @@ +<?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="StorefrontInstantPurchaseFunctionalityTest"> + <annotations> + <features value="InstantPurchase"/> + <stories value="Using Instant Purchase"/> + <title value="Checks that Instant Purchase functionality works fine"/> + <description value="Checks that customer with different billing and shipping addresses work with Instant Purchase functionality fine"/> + <useCaseId value="MAGETWO-90898"/> + <testCaseId value="MC-25924"/> + <severity value="CRITICAL"/> + <group value="instant_purchase"/> + <group value="vault"/> + <group value="braintree"/> + </annotations> + <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> + <!-- Configure Braintree payment method --> + <createData entity="BraintreeConfig" stepKey="configureBraintreePayment"/> + <!-- Enable Braintree with Vault --> + <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintreeAndVault"/> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> + <!-- Create all product variations --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> + <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> + <!-- Create Bundle Product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Create Downloadable Product --> + <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + <!-- Create Grouped Product --> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="createLinkForGroupedProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Log in as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + <!-- Customer placed order from storefront with payment method --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStep"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethod"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoad"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSection"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardData"> + <argument name="cartData" value="PaymentAndShippingInfo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardData"/> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUse"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </before> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Set configs to default --> + <createData entity="DefaultBraintreeConfig" stepKey="defaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollBackCustomBraintreeConfigurationData"/> + <!-- Remove created products/attributes --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <!-- Remove Downloadable Product --> + <magentoCLI command="downloadable:domains:remove static.magento.com" stepKey="removeDownloadableDomain"/> + <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <!-- Remove Configurable Product --> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + </after> + <!-- 1. Browse all product page and verify that the "Instant Purchase" button appears --> + <!-- Virtual product --> + <amOnPage url="{{StorefrontProductPage.url($createVirtualProduct.custom_attributes[url_key]$)}}" stepKey="openVirtualProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnVirtualProductPage"/> + <!-- Downloadable Product --> + <amOnPage url="{{StorefrontProductPage.url($createDownloadableProduct.custom_attributes[url_key]$)}}" stepKey="openDownloadableProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnDownloadableProductPage"/> + <!-- Bundle Product --> + <amOnPage url="{{StorefrontProductPage.url($createBundleProduct.custom_attributes[url_key]$)}}" stepKey="openBundleProductPage"/> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnBundleProductPage"/> + <!-- Grouped product --> + <amOnPage url="{{StorefrontProductPage.url($createGroupedProduct.custom_attributes[url_key]$)}}" stepKey="openGroupedProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnGroupedProductPage"/> + <!-- Configurable Product --> + <amOnPage url="{{StorefrontProductPage.url($createConfigProductCreateConfigurableProduct.custom_attributes[url_key]$)}}" stepKey="openConfigurableProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnConfigurableProductPage"/> + <!-- 2. Click on "Instant Purchase" and assert information --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="openSimpleProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButton"/> + <actionGroup ref="AssertStorefrontInstantPurchaseConfirmationDataActionGroup" stepKey="assertInstantPurchasePopupData"> + <argument name="shippingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="billingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="cardEnding" value="{{StoredPaymentMethods.cardNumberEnding}}"/> + </actionGroup> + <!-- 3. Confirm Instant Purchase --> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="placeOrderAgain"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see userInput="Your order number is:" selector="{{StorefrontMessagesSection.success}}" stepKey="seePlaceOrderSuccessMessage"/> + <!-- 4. Customer changes his default address --> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToAddressPage"/> + <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="clickOnEditAdditionalAddressButton"/> + <checkOption selector="{{StorefrontCustomerAddressFormSection.useAsDefaultBillingAddressCheckBox}}" stepKey="checkUseAsDefaultBillingAddressCheckbox"/> + <actionGroup ref="AdminSaveCustomerAddressActionGroup" stepKey="saveAddress"/> + <!-- 5.1 Customer places a new order from the storefront with new payment credentials --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCartAgain"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicartAgain"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRateAgain"/> + <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStepAgain"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethodAgain"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoadAgain"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSectionAgain"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardDataAgain"> + <argument name="cartData" value="VisaDefaultCard"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardDataAgain"/> + <!-- 5.2 Customer save this payment method --> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUseAgain"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrderAgain"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <!-- 6. Customer opens simple product page --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="openSimpleProductPageAgain"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonAgain"/> + <!-- 7. Click on "Instant Purchase" and verify that information are different from previous --> + <actionGroup ref="AssertStorefrontInstantPurchaseConfirmationDataActionGroup" stepKey="assertInstantPurchasePopupDataAgain"> + <argument name="shippingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="billingStreet" value="{{UK_Not_Default_Address.street[0]}}"/> + <argument name="cardEnding" value="{{VisaDefaultCardInfo.cardNumberEnding}}"/> + </actionGroup> + <!-- 8. Confirm Instant Purchase --> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="placeOrderFinalTime"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessageAgain"/> + <see userInput="Your order number is:" selector="{{StorefrontMessagesSection.success}}" stepKey="seePlaceOrderSuccessMessageAgain"/> + </test> +</tests> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml index 4bf84d9ee63da..07c4498efdafc 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml @@ -11,6 +11,6 @@ <section name="ModalConfirmationSection"> <element name="modalContent" type="text" selector="aside.confirm div.modal-content"/> <element name="CancelButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-dismiss')]"/> - <element name="OkButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-accept')]"/> + <element name="OkButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-accept')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml b/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml new file mode 100644 index 0000000000000..f767dcedc13b6 --- /dev/null +++ b/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> + <section name="StorefrontOnePageCheckoutPaymentSection"/> + </page> +</pages> diff --git a/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml b/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml new file mode 100644 index 0000000000000..53ad03bd01ef8 --- /dev/null +++ b/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontOnePageCheckoutPaymentSection"> + <element name="saveForLaterUse" type="checkbox" selector="fieldset.payment > div.choice > input[name='vault[is_enabled]']"/> + </section> +</sections> From e3ec2787006576b655502662613e41348efcce76 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 20 Mar 2020 09:20:16 -0500 Subject: [PATCH 486/734] MC-32387: Add pagination support to categoryList query --- .../CatalogGraphQl/Model/Resolver/CategoriesQuery.php | 11 +---------- .../Catalog/CategoriesQuery/CategoriesFilterTest.php | 2 +- .../CategoriesQuery/CategoriesPaginationTest.php | 1 - .../Catalog/CategoriesQuery/CategoryTreeTest.php | 2 -- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php index d06dace3cd76b..bb04ea621d816 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php @@ -14,7 +14,6 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; use Magento\CatalogGraphQl\Model\Category\CategoryFilter; -use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; /** * Categories resolver, used for GraphQL category data request processing. @@ -26,11 +25,6 @@ class CategoriesQuery implements ResolverInterface */ private $categoryTree; - /** - * @var CollectionFactory - */ - private $collectionFactory; - /** * @var CategoryFilter */ @@ -45,18 +39,15 @@ class CategoriesQuery implements ResolverInterface * @param CategoryTree $categoryTree * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree * @param CategoryFilter $categoryFilter - * @param CollectionFactory $collectionFactory */ public function __construct( CategoryTree $categoryTree, ExtractDataFromCategoryTree $extractDataFromCategoryTree, - CategoryFilter $categoryFilter, - CollectionFactory $collectionFactory + CategoryFilter $categoryFilter ) { $this->categoryTree = $categoryTree; $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; $this->categoryFilter = $categoryFilter; - $this->collectionFactory = $collectionFactory; } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php index 883b58474ae79..b30eb24da6a2d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php @@ -368,7 +368,7 @@ public function testEmptyFiltersReturnRootCategory() * * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @expectedException \Exception - * @expectedExceptionMessage Invalid match filter. Minimum query length is 3. + * @expectedExceptionMessage Invalid match filter. Minimum length is 3. */ public function testMinimumMatchQueryLength() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php index 28b864da119ff..3d6982757b932 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php @@ -8,7 +8,6 @@ namespace Magento\GraphQl\Catalog\CategoriesQuery; use Magento\TestFramework\TestCase\GraphQlAbstract; -use function sprintf; /** * Test pagination for the categories query diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php index fe545f4f7ebf7..96b303c689683 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php @@ -15,9 +15,7 @@ use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\ObjectManager; -use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; use Magento\TestFramework\TestCase\GraphQlAbstract; -use function assertCount; /** * Test category tree data is returned correctly from "categories" query From 22e21276540d4b4f0aed6985aa7ce1eef72519c2 Mon Sep 17 00:00:00 2001 From: Fil Maj <maj.fil@gmail.com> Date: Fri, 20 Mar 2020 11:23:13 -0400 Subject: [PATCH 487/734] Updating link to Adobe CLA in contributing.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 89aea7299855c..37b7bc2ca8c3a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -31,7 +31,7 @@ If you are a new GitHub user, we recommend that you create your own [free github This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. 1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. -2. Review the [Contributor License Agreement](https://magento.com/legaldocuments/mca) if this is your first time contributing. +2. Review the [Contributor License Agreement](https://opensource.adobe.com/cla.html) if this is your first time contributing. 3. Create and test your work. 4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#pull_request). 5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. From f2a562c9516e5f20662c9bf7ff7b6f7ec72f9cb2 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Fri, 20 Mar 2020 22:41:20 +0700 Subject: [PATCH 488/734] Fix issue 16315: Fix code standard & write unit test --- .../Model/Indexer/Product/Flat/Action/Row.php | 29 +++++++++++++------ .../Indexer/Product/Flat/Action/RowTest.php | 16 ++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index c67d77168bf4b..4bc985c58d768 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -85,15 +85,7 @@ public function execute($id = null) $ids = [$id]; $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); - $select = $this->_connection->select(); - $select->from(['e' => $this->_productIndexerHelper->getTable('store')], ['e.store_id']) - ->where('c.product_id = ' . $id) - ->joinLeft( - ['c' => $this->_productIndexerHelper->getTable('catalog_product_website')], - 'e.website_id = c.website_id', - [] - ); - $storeIds = $this->_connection->fetchCol($select); + $storeIds = $this->getProductAvailableStores($id); $stores = $this->_storeManager->getStores(); foreach ($stores as $store) { @@ -143,4 +135,23 @@ public function execute($id = null) return $this; } + + /** + * Get list store id where product is enable + * + * @param int $productId + * @return array + */ + private function getProductAvailableStores($productId) + { + $select = $this->_connection->select(); + $select->from(['e' => $this->_productIndexerHelper->getTable('store')], ['e.store_id']) + ->where('c.product_id = ' . $productId) + ->joinLeft( + ['c' => $this->_productIndexerHelper->getTable('catalog_product_website')], + 'e.website_id = c.website_id', + [] + ); + return $this->_connection->fetchCol($select); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php index 11d07872fef91..61fad897c6418 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php @@ -160,8 +160,24 @@ public function testExecuteWithExistingFlatTablesCreatesTables() ->willReturn('store_flat_table'); $this->connection->expects($this->any())->method('isTableExists')->with('store_flat_table') ->willReturn(true); + $this->connection->expects($this->any())->method('fetchCol') + ->willReturn(['store_id_1']); $this->flatItemEraser->expects($this->once())->method('removeDeletedProducts'); $this->flatTableBuilder->expects($this->never())->method('build')->with('store_id_1', ['product_id_1']); $this->model->execute('product_id_1'); } + + public function testExecuteWithExistingFlatTablesRemoveProductFromStore() + { + $this->productIndexerHelper->expects($this->any())->method('getFlatTableName') + ->willReturn('store_flat_table'); + $this->connection->expects($this->any())->method('isTableExists')->with('store_flat_table') + ->willReturn(true); + $this->connection->expects($this->any())->method('fetchCol') + ->willReturn([1]); + $this->flatItemEraser->expects($this->once())->method('deleteProductsFromStore'); + $this->flatItemEraser->expects($this->never())->method('removeDeletedProducts'); + $this->flatTableBuilder->expects($this->never())->method('build')->with('store_id_1', ['product_id_1']); + $this->model->execute('product_id_1'); + } } From b38b4ecd52dcdd9d288a923ed0841ebec51823ef Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 20 Mar 2020 17:44:43 +0200 Subject: [PATCH 489/734] MC-32578: [MFTF] StoreFrontRecentlyViewedAtStoreViewLevelTest fails because of bad design --- .../Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml index 8243f21cb6510..c0c142fe2cba1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -12,9 +12,10 @@ <stories value="Recently Viewed Product"/> <title value="Recently Viewed Product at store level"/> <description value="Recently Viewed Product should not be displayed on second store , if configured as, Per Store "/> - <testCaseId value="MC-32018"/> + <testCaseId value="MC-32226"/> <severity value="CRITICAL"/> <group value="catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -163,4 +164,4 @@ <assertNotContains expected="$$createSimpleProduct1.name$$" actual="$grabDontSeeHomeProduct1" stepKey="assertNotSeeProduct1"/> </test> -</tests> \ No newline at end of file +</tests> From 96f8dedfc17e73c98447b1c518b14bf9e5678339 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 20 Mar 2020 11:08:20 -0500 Subject: [PATCH 490/734] MC-32387: Add pagination support to categoryList query --- .../CatalogGraphQl/Model/Resolver/CategoriesQuery.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php index bb04ea621d816..eb6708dc48f01 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; +use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -68,7 +69,11 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; } - $filterResult = $this->categoryFilter->getResult($args, $store); + try { + $filterResult = $this->categoryFilter->getResult($args, $store); + } catch (InputException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } $rootCategoryIds = $filterResult['category_ids']; $filterResult['items'] = $this->fetchCategories($rootCategoryIds, $info); From 6cbfa7754beb020200ba008947b0c2d36595e7f6 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Fri, 20 Mar 2020 19:03:46 +0200 Subject: [PATCH 491/734] fix code style failed tests --- .../templates/catalog/product/attribute/labels.phtml | 10 ++++++---- .../Adminhtml/Attribute/Edit/Main/AbstractMain.php | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index e5c943d24b519..85a6b347f2feb 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -19,7 +19,7 @@ <table class="admin__control-table" id="attribute-labels-table"> <thead> <tr> - <?php foreach ($block->getStores() as $_store) :?> + <?php foreach ($block->getStores() as $_store): ?> <th class="col-store-view"><?= $block->escapeHtml($_store->getName()) ?></th> <?php endforeach; ?> </tr> @@ -27,13 +27,15 @@ <tbody> <tr> <?php $_labels = $block->getLabelValues() ?> - <?php foreach ($block->getStores() as $_store) :?> + <?php foreach ($block->getStores() as $_store): ?> <td class="col-store-view"> - <input class="input-text validate-no-html-tags<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option<?php endif; ?>" + <?php $isRequired = $_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID; ?> + <?php $isRequiredClass = $isRequired ? 'required-option' : ''; ?> + <input class="input-text validate-no-html-tags <?= /* @noEscape */ $isRequiredClass ?>" type="text" name="frontend_label[<?= $block->escapeHtmlAttr($_store->getId()) ?>]" value="<?= $block->escapeHtmlAttr($_labels[$_store->getId()]) ?>" - <?php if ($block->getReadOnly()) :?> + <?php if ($block->getReadOnly()): ?> disabled="disabled" <?php endif;?>/> </td> diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index adad61417a4f6..6f2a5b2163fda 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -283,6 +283,7 @@ protected function _initFormValues() * Adding js block to the end of this block * * @param string $html + * * @return string */ protected function _afterToHtml($html) From b595774692f9b0ee77ce56e4b2583e1b53682e12 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Fri, 20 Mar 2020 12:27:11 -0500 Subject: [PATCH 492/734] MC-32201: Reorder functionality --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index d362085149de4..09ca922e45db6 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -125,7 +125,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $this->errors = []; $cart = $this->customerCartProvider->resolve($customerId); - if (!$this->reorderHelper->canReorder($order->getId())) { + if (!$this->reorderHelper->isAllowed($order->getStore())) { $this->addError((string)__('Reorders are not allowed.'), self::ERROR_REORDER_NOT_AVAILABLE); return $this->prepareOutput($cart); } From 1ac6b5dcfe1b222822c1dcc374d1ef2d6a930535 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 20 Mar 2020 19:50:56 +0200 Subject: [PATCH 493/734] remove unstable cas on tests --- .../testsuite/Magento/Backend/Model/Dashboard/ChartTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php index c7d2e3ef17ec0..e496a0f07a653 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -60,11 +60,6 @@ public function getChartDataProvider(): array '7d', 'quantity' ], - [ - 19, - '1m', - 'quantity' - ], [ 16, '1y', From 7fb673c998218080fba355bcd8645ed8b4150afa Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Fri, 20 Mar 2020 13:17:22 -0500 Subject: [PATCH 494/734] MC-32201: Reorder functionality --- .../Magento/Sales/Model/Reorder/Reorder.php | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 09ca922e45db6..e9a21303921f8 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -7,6 +7,7 @@ namespace Magento\Sales\Model\Reorder; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; @@ -36,6 +37,7 @@ class Reorder * List of error messages and codes. */ private const MESSAGE_CODES = [ + 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, 'This product is out of stock' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, @@ -161,6 +163,7 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi $info->setQty($orderItem->getQtyOrdered()); try { + /** @var Product $product */ $product = $this->productRepository->getById($orderItem->getProductId(), false, null, true); } catch (NoSuchEntityException $e) { $this->addError( @@ -169,13 +172,22 @@ private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): voi ); return; } + $addProductResult = null; try { - $cart->addProduct($product, $info); + $addProductResult = $cart->addProduct($product, $info); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->addError($this->addCartItemError($product->getSku(), $e->getMessage())); + $this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage())); } catch (\Throwable $e) { $this->logger->critical($e); - $this->addError($this->addCartItemError($product->getSku()), self::ERROR_UNDEFINED); + $this->addError($this->getCartItemErrorMessage($orderItem, $product), self::ERROR_UNDEFINED); + } + + // error happens in case the result is string + if (is_string($addProductResult)) { + $errors = array_unique(explode("\n", $addProductResult)); + foreach ($errors as $error) { + $this->addError($this->getCartItemErrorMessage($orderItem, $product, $error)); + } } } } @@ -234,14 +246,18 @@ private function prepareOutput(CartInterface $cart): Data\ReorderOutput } /** - * Add error message for a cart item + * Get error message for a cart item * - * @param string $sku + * @param Item $item + * @param Product $product * @param string|null $message * @return string */ - private function addCartItemError(string $sku, string $message = null): string + private function getCartItemErrorMessage(Item $item, Product $product, string $message = null): string { + // try to get sku from line-item first. + // for complex product type: if custom option is not available it can cause error + $sku = $item->getSku() ?? $product->getData('sku'); return (string)($message ? __('Could not add the product with SKU "%1" to the shopping cart: %2', $sku, $message) : __('Could not add the product with SKU "%1" to the shopping cart', $sku)); From 91dfa0c3bf01b453bb1ce5fab767e4f0505a79d3 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Fri, 20 Mar 2020 13:43:05 -0500 Subject: [PATCH 495/734] MC-32201: Reorder functionality --- .../Magento/GraphQl/Sales/ReorderTest.php | 35 +++++++++++++++++++ .../_files/order_with_zero_qty_product.php | 28 +++++++++++++++ .../order_with_zero_qty_product_rollback.php | 7 ++++ 3 files changed, 70 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 94c89fc960b45..f6bfd9e7b7575 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -116,6 +116,41 @@ public function testSimpleProductOutOfStock() $this->assertWithDeletedProduct($productRepository, $product); } + /** + * Test reorder when simple product qty is 0, with allowed backorders configured to below 0 + * + * @magentoConfigFixture admin_store cataloginventory/item_options/backorders 1 + * @magentoConfigFixture admin_store cataloginventory/item_options/use_deferred_stock_update 0 + * @magentoApiDataFixture Magento/Sales/_files/order_with_zero_qty_product.php + * @throws \Exception + */ + public function testBackorderWithOutOfStock() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple' + ] + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2' + ] + ] + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + } + /** * Assert that simple product is not available. */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php new file mode 100644 index 0000000000000..3c0a2b35c5156 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set it out of stock +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product qty to zero +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php new file mode 100644 index 0000000000000..34cded299350d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_rollback.php'; From c724d3c4d436e6c293fb76a3557f61329ae9c1d7 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 20 Mar 2020 21:11:50 +0200 Subject: [PATCH 496/734] refactor assertion to avoid unstable data --- .../Magento/Backend/Model/Dashboard/ChartTest.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php index e496a0f07a653..284c3c9b5046e 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -39,7 +39,7 @@ protected function setUp() */ public function testGetByPeriodWithParam(int $expectedDataQty, string $period, string $chartParam): void { - $this->assertCount($expectedDataQty, $this->model->getByPeriod($period, $chartParam)); + $this->assertGreaterThan($expectedDataQty, $this->model->getByPeriod($period, $chartParam)); } /** @@ -51,22 +51,27 @@ public function getChartDataProvider(): array { return [ [ - 24, + 10, '24h', 'quantity' ], [ - 8, + 4, '7d', 'quantity' ], [ - 16, + 10, + '1m', + 'quantity' + ], + [ + 8, '1y', 'quantity' ], [ - 28, + 15, '2y', 'quantity' ] From c5a4692353ad84f1d16ebb05320dceab32ee9efd Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Fri, 20 Mar 2020 14:31:32 -0500 Subject: [PATCH 497/734] MC-31586: Customer address is duplicated after setBillingAddressOnCart GraphQL mutation. - reverting changes from https://github.com/magento/graphql-ce/pull/873 - Added functional tests --- .../SaveQuoteAddressToCustomerAddressBook.php | 104 ---------- .../Model/Cart/GetShippingAddress.php | 21 +-- .../Model/Cart/SetBillingAddressOnCart.php | 29 +-- .../Customer/SetBillingAddressOnCartTest.php | 2 +- .../Customer/SetShippingAddressOnCartTest.php | 177 ++++++++++++++++++ 5 files changed, 182 insertions(+), 151 deletions(-) delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php deleted file mode 100644 index c87101156327e..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart\Address; - -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Customer\Api\Data\RegionInterface; -use Magento\Customer\Api\Data\RegionInterfaceFactory; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Quote\Model\Quote\Address as QuoteAddress; - -/** - * Save Address to Customer Address Book. - */ -class SaveQuoteAddressToCustomerAddressBook -{ - /** - * @var AddressInterfaceFactory - */ - private $addressFactory; - - /** - * @var AddressRepositoryInterface - */ - private $addressRepository; - - /** - * @var RegionInterfaceFactory - */ - private $regionFactory; - - /** - * @param AddressInterfaceFactory $addressFactory - * @param AddressRepositoryInterface $addressRepository - * @param RegionInterfaceFactory $regionFactory - */ - public function __construct( - AddressInterfaceFactory $addressFactory, - AddressRepositoryInterface $addressRepository, - RegionInterfaceFactory $regionFactory - ) { - $this->addressFactory = $addressFactory; - $this->addressRepository = $addressRepository; - $this->regionFactory = $regionFactory; - } - - /** - * Save Address to Customer Address Book. - * - * @param QuoteAddress $quoteAddress - * @param int $customerId - * - * @return void - * @throws GraphQlInputException - */ - public function execute(QuoteAddress $quoteAddress, int $customerId): void - { - try { - /** @var AddressInterface $customerAddress */ - $customerAddress = $this->addressFactory->create(); - $customerAddress->setFirstname($quoteAddress->getFirstname()) - ->setLastname($quoteAddress->getLastname()) - ->setMiddlename($quoteAddress->getMiddlename()) - ->setPrefix($quoteAddress->getPrefix()) - ->setSuffix($quoteAddress->getSuffix()) - ->setVatId($quoteAddress->getVatId()) - ->setCountryId($quoteAddress->getCountryId()) - ->setCompany($quoteAddress->getCompany()) - ->setRegionId($quoteAddress->getRegionId()) - ->setFax($quoteAddress->getFax()) - ->setCity($quoteAddress->getCity()) - ->setPostcode($quoteAddress->getPostcode()) - ->setStreet($quoteAddress->getStreet()) - ->setTelephone($quoteAddress->getTelephone()) - ->setCustomerId($customerId); - - /** @var RegionInterface $region */ - $region = $this->regionFactory->create(); - $region->setRegionCode($quoteAddress->getRegionCode()) - ->setRegion($quoteAddress->getRegion()) - ->setRegionId($quoteAddress->getRegionId()); - $customerAddress->setRegion($region); - - $this->addressRepository->save($customerAddress); - } catch (InputException $inputException) { - $graphQlInputException = new GraphQlInputException(__($inputException->getMessage())); - $errors = $inputException->getErrors(); - foreach ($errors as $error) { - $graphQlInputException->addError(new GraphQlInputException(__($error->getMessage()))); - } - throw $graphQlInputException; - } catch (LocalizedException $exception) { - throw new GraphQlInputException(__($exception->getMessage()), $exception); - } - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index 2a57c281de183..f93962eb06849 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -12,7 +12,6 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote\Address; -use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Get shipping address @@ -24,21 +23,13 @@ class GetShippingAddress */ private $quoteAddressFactory; - /** - * @var SaveQuoteAddressToCustomerAddressBook - */ - private $saveQuoteAddressToCustomerAddressBook; - /** * @param QuoteAddressFactory $quoteAddressFactory - * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( - QuoteAddressFactory $quoteAddressFactory, - SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook + QuoteAddressFactory $quoteAddressFactory ) { $this->quoteAddressFactory = $quoteAddressFactory; - $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -96,25 +87,15 @@ private function createShippingAddress( if (null === $customerAddressId) { $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); - - // need to save address only for registered user and if save_in_address_book = true - if (0 !== $customerId - && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] === true - ) { - $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress( (int)$customerAddressId, $customerId ); } - return $shippingAddress; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 23fc35790ac0e..e600965923c14 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -13,7 +13,6 @@ use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote\Address; -use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Set billing address for a specified shopping cart @@ -30,24 +29,16 @@ class SetBillingAddressOnCart */ private $assignBillingAddressToCart; - /** - * @var SaveQuoteAddressToCustomerAddressBook - */ - private $saveQuoteAddressToCustomerAddressBook; - /** * @param QuoteAddressFactory $quoteAddressFactory * @param AssignBillingAddressToCart $assignBillingAddressToCart - * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - AssignBillingAddressToCart $assignBillingAddressToCart, - SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook + AssignBillingAddressToCart $assignBillingAddressToCart ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->assignBillingAddressToCart = $assignBillingAddressToCart; - $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -90,7 +81,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b ); } - $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput, $sameAsShipping); + $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } @@ -101,7 +92,6 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b * @param ContextInterface $context * @param int|null $customerAddressId * @param array $addressInput - * @param bool $sameAsShipping * @return Address * @throws GraphQlAuthorizationException * @throws GraphQlInputException @@ -110,21 +100,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b private function createBillingAddress( ContextInterface $context, ?int $customerAddressId, - ?array $addressInput, - $sameAsShipping + ?array $addressInput ): Address { if (null === $customerAddressId) { $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); - - $customerId = $context->getUserId(); - // need to save address only for registered user and if save_in_address_book = true - // and address is not same as shipping - if (0 !== $customerId - && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] && !$sameAsShipping - ) { - $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); @@ -136,7 +115,6 @@ private function createBillingAddress( ); } $errors = $billingAddress->validate(); - if (true !== $errors) { $e = new GraphQlInputException(__('Billing address errors')); foreach ($errors as $error) { @@ -144,7 +122,6 @@ private function createBillingAddress( } throw $e; } - return $billingAddress; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 4725a9012fc98..1af44012bb515 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -858,7 +858,7 @@ public function testSetNewBillingAddressWithSaveInAddressBook() $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $addresses); + self::assertCount(0, $addresses); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 2a19fb0d10d6a..cedbf4bf61d17 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -940,6 +940,99 @@ public function testSetNewShippingAddressWithSaveInAddressBook() $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + self::assertCount(0, $addresses); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testSetNewShippingAddressAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: true + } + customer_notes: "Test note" + } + ] + } + ) { + cart { + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + customer_notes + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $addresses); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); @@ -1094,4 +1187,88 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @return string + */ + private function getSetShippingMethodsQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode + ): string { + return <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [{ + carrier_code: "$shippingCarrierCode" + method_code: "$shippingMethodCode" + }] + }) { + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + carrier_title + method_title + amount { + value + currency + } + } + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function getSetPaymentMethodQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getPlaceOrderQuery(string $maskedQuoteId): string + { + return <<<QUERY +mutation { + placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { + order { + order_number + } + } +} +QUERY; + } } From a46f9458d4c89e9df130500c3734add210d98325 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Fri, 20 Mar 2020 15:08:30 -0500 Subject: [PATCH 498/734] MC-32378: Implement control over minimum_should_match for elasticsearch queries --- .../Magento/Elasticsearch7/etc/config.xml | 2 +- .../_files/products_for_search.php | 40 +++++++++---------- .../_files/products_for_search_rollback.php | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Elasticsearch7/etc/config.xml b/app/code/Magento/Elasticsearch7/etc/config.xml index c13586bb9e357..72657975faebb 100644 --- a/app/code/Magento/Elasticsearch7/etc/config.xml +++ b/app/code/Magento/Elasticsearch7/etc/config.xml @@ -14,7 +14,7 @@ <elasticsearch7_index_prefix>magento2</elasticsearch7_index_prefix> <elasticsearch7_enable_auth>0</elasticsearch7_enable_auth> <elasticsearch7_server_timeout>15</elasticsearch7_server_timeout> - <elasticsearch7_minimum_should_match></elasticsearch7_minimum_should_match> + <elasticsearch7_minimum_should_match/> </search> </catalog> </default> diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php index 8792a1060ebf2..4f271d2a78a39 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php @@ -12,63 +12,65 @@ use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Visibility; +$categoryId = 333; +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$attributeSet = $objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class); +$entityType = $objectManager->create(\Magento\Eav\Model\Entity\Type::class)->loadByCode('catalog_product'); +$defaultSetId = $objectManager->create(\Magento\Catalog\Model\Product::class)->getDefaultAttributeSetid(); + $products = [ [ 'type' => 'simple', - 'id' => 201, 'name' => 'search product 1', 'sku' => '24 MB06', 'status' => Status::STATUS_ENABLED, 'visibility' => Visibility::VISIBILITY_BOTH, - 'attribute_set' => 4, - 'website_ids' => [1], + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], 'price' => 10, - 'category_id' => 333, + 'category_id' => $categoryId, 'meta_title' => 'Key Title', 'meta_keyword' => 'meta keyword', 'meta_description' => 'meta description', ], [ 'type' => 'simple', - 'id' => 202, 'name' => 'search product 2', 'sku' => '24 MB04', 'status' => Status::STATUS_ENABLED, 'visibility' => Visibility::VISIBILITY_BOTH, - 'attribute_set' => 4, - 'website_ids' => [1], + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], 'price' => 10, - 'category_id' => 333, + 'category_id' => $categoryId, 'meta_title' => 'Last Title', 'meta_keyword' => 'meta keyword', 'meta_description' => 'meta description', ], [ 'type' => 'simple', - 'id' => 203, 'name' => 'search product 3', 'sku' => '24 MB02', 'status' => Status::STATUS_ENABLED, 'visibility' => Visibility::VISIBILITY_BOTH, - 'attribute_set' => 4, - 'website_ids' => [1], + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], 'price' => 20, - 'category_id' => 333, + 'category_id' => $categoryId, 'meta_title' => 'First Title', 'meta_keyword' => 'meta keyword', 'meta_description' => 'meta description', ], [ 'type' => 'simple', - 'id' => 204, 'name' => 'search product 4', 'sku' => '24 MB01', 'status' => Status::STATUS_ENABLED, 'visibility' => Visibility::VISIBILITY_BOTH, - 'attribute_set' => 4, - 'website_ids' => [1], + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], 'price' => 30, - 'category_id' => 333, + 'category_id' => $categoryId, 'meta_title' => 'A title', 'meta_keyword' => 'meta keyword', 'meta_description' => 'meta description', @@ -76,17 +78,15 @@ ]; /** @var CategoryLinkManagementInterface $categoryLinkManagement */ -$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(CategoryLinkManagementInterface::class); +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); $categoriesToAssign = []; foreach ($products as $data) { /** @var $product Product */ - $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(Product::class); + $product = $objectManager->create(Product::class); $product ->setTypeId($data['type']) - ->setId($data['id']) ->setAttributeSetId($data['attribute_set']) ->setWebsiteIds($data['website_ids']) ->setName($data['name']) diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php index b0cfcd7a3e5e3..120816c38232d 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php @@ -30,7 +30,7 @@ } } -include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; +include __DIR__ . '/../../Catalog/_files/category_rollback.php'; $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); From 722be2f340b2cc59a6eba395b4f30c052bc22319 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 00:35:56 +0100 Subject: [PATCH 499/734] Backward Compatible change of ActionGroup used for opening Product Edit form in Admin Panel --- ...p.xml => AdminProductPageOpenByIdActionGroup.xml} | 12 ++---------- .../Mftf/ActionGroup/_Deprecated_ActionGroup.xml | 6 ++++++ ...inCheckMediaRolesForFirstAddedImageViaApiTest.xml | 2 +- ...pleProductWithSpecialAndTierDiscountPriceTest.xml | 2 +- .../StorefrontConfigurableOptionsThumbImagesTest.xml | 6 +++--- ...leForConfigurableProductWithSpecialPricesTest.xml | 4 ++-- .../Test/Mftf/Test/SearchEntityResultsTest.xml | 4 ++-- ...StorefrontQuickSearchConfigurableChildrenTest.xml | 2 +- ...bleProductChildAssignedToSeparateCategoryTest.xml | 2 +- ...ownloadableProductSamplesAreNotAccessibleTest.xml | 2 +- ...DuringOrderCreationWithMultiWebsiteConfigTest.xml | 2 +- ...ntSeeProductImagesMatchingProductSwatchesTest.xml | 2 +- ...ionInShoppingCartForCustomerPhysicalQuoteTest.xml | 2 +- ...mationInShoppingCartForGuestPhysicalQuoteTest.xml | 2 +- ...ontDeleteBundleDynamicProductFromWishlistTest.xml | 2 +- ...icBundleProductFromShoppingCartToWishlistTest.xml | 2 +- 16 files changed, 26 insertions(+), 28 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/ActionGroup/{GoToProductPageViaIDActionGroup.xml => AdminProductPageOpenByIdActionGroup.xml} (57%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml similarity index 57% rename from app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml rename to app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml index 104ef83771e9d..d1d4add6f3bd2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml @@ -1,21 +1,13 @@ <?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="GoToProductPageViaIDActionGroup"> - <annotations> - <description>Goes to the Product edit page for the provided Product ID.</description> - </annotations> + <actionGroup name="AdminProductPageOpenByIdActionGroup"> <arguments> <argument name="productId" type="string"/> </arguments> <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> + <waitForPageLoad stepKey="waitForProductPage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml new file mode 100644 index 0000000000000..36ea97a435872 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToProductPageViaIDActionGroup" extends="AdminProductPageOpenByIdActionGroup" deprecated="Use AdminProductPageOpenByIdActionGroup instead"/> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml index c31054e3dc192..7d27bfb3a058b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml @@ -30,7 +30,7 @@ <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToSimpleProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProduct"> <argument name="productId" value="$$createSimpleProduct.id$$"/> </actionGroup> <actionGroup ref="AdminOpenProductImagesSectionActionGroup" stepKey="openProductImagesSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml index 6817969de65c3..d642f16160c5b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml @@ -34,7 +34,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openAdminProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> <argument name="productId" value="$createProduct.id$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml index 134fd89c2c813..059b90532f8f9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml @@ -87,7 +87,7 @@ <!-- ConfigProduct --> <!-- Go to Product Page (ConfigProduct) --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToConfigProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToConfigProduct"> <argument name="productId" value="$$createConfigProduct.id$$"/> </actionGroup> @@ -113,7 +113,7 @@ <!-- ChildProduct1 --> <!-- Go to Product Page (ChildProduct1) --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct1"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToChildProduct1"> <argument name="productId" value="$$createConfigChildProduct1.id$$"/> </actionGroup> @@ -138,7 +138,7 @@ <!-- ChildProduct2 --> <!-- Go to Product Page (ChildProduct2) --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct2"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToChildProduct2"> <argument name="productId" value="$$createConfigChildProduct2.id$$"/> </actionGroup> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml index db25ffc9c68b2..f8b477e796c9d 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml @@ -98,7 +98,7 @@ </after> <!-- Add special prices for products --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToFirstChildProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstChildProduct"> <argument name="productId" value="$$createFirstConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForFirstProduct"> @@ -106,7 +106,7 @@ </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveFirstProduct"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToSecondChildProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondChildProduct"> <argument name="productId" value="$$createSecondConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForSecondProduct"> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 890df17f113d5..f1c7c706a5123 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -203,7 +203,7 @@ <!-- Create and Assign Attribute to product1--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct1"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> <argument name="productId" value="$product1.id$"/> </actionGroup> <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> @@ -224,7 +224,7 @@ </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> <!-- Create and Assign Attribute to product2--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct2"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2"> <argument name="productId" value="$product2.id$"/> </actionGroup> <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml index 5c3f55c7212ad..954f53814f3c3 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml @@ -92,7 +92,7 @@ </actionGroup> <!-- Disable Child Product --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openSimpleProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openSimpleProduct"> <argument name="productId" value="$createSimpleProduct.id$"/> </actionGroup> <actionGroup ref="ToggleProductEnabledActionGroup" stepKey="disableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml index 9296ebc7cece5..ddb8190c076ee 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml @@ -101,7 +101,7 @@ </after> <!-- Go to the product page for the first product --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openConfigChildProduct1Page"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openConfigChildProduct1Page"> <argument name="productId" value="$$createConfigChildProduct1.id$$"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml index b641a2541ff98..919fe4b3c645e 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml @@ -87,7 +87,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Open Downloadable product from precondition --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createProduct.id$"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml index 4f0d777ad0f21..eb9d48cbbd567 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml @@ -37,7 +37,7 @@ <argument name="StoreGroup" value="customStoreGroup"/> <argument name="customStore" value="customStore"/> </actionGroup> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createProduct.id$$"/> </actionGroup> <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml index 8bfdb77cbe177..22ce176da8233 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml @@ -63,7 +63,7 @@ </actionGroup> <!-- Edit configurable product --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$$createSimpleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index 8caf49ca2c374..c35a6309f1f3d 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -42,7 +42,7 @@ <!-- Customer is created with default addresses: --> <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createSimpleProduct.id$"/> </actionGroup> <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index 388a254c7028b..d2246f07dd7cb 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -40,7 +40,7 @@ <field key="price">10.00</field> </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createSimpleProduct.id$"/> </actionGroup> <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml index 3ff3fe0f379ce..6238f0dcbff09 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml @@ -48,7 +48,7 @@ </createData> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml index 6af7c6eae2c2a..e07315e0ee0a4 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -46,7 +46,7 @@ <requiredEntity createDataKey="simpleProduct2"/> </createData> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> From efd31af7111c1a92f29f9ba7105d642a6a7157a5 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 20 Mar 2020 18:43:37 -0500 Subject: [PATCH 500/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas -- fix merge conflict --- .../Controller/Adminhtml/IndexTest.php | 360 +----------------- 1 file changed, 15 insertions(+), 345 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 789f9a56b68ce..e2ba275a5a438 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -6,44 +6,32 @@ namespace Magento\Customer\Controller\Adminhtml; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerNameGenerationInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Controller\RegistryConstants; use Magento\Customer\Model\EmailNotification; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; /** * @magentoAppArea adminhtml * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class IndexTest extends AbstractBackendController { /** * Base controller URL * * @var string */ - protected $_baseControllerUrl; + private $baseControllerUrl = 'backend/customer/index/'; /** @var CustomerRepositoryInterface */ - protected $customerRepository; + private $customerRepository; - /** @var AddressRepositoryInterface */ - protected $addressRepository; - - /** @var AccountManagementInterface */ - protected $accountManagement; - - /** @var \Magento\Framework\Data\Form\FormKey */ - protected $formKey; - - /**@var \Magento\Customer\Helper\View */ - protected $customerViewHelper; - - /** @var \Magento\TestFramework\ObjectManager */ - protected $objectManager; + /** @var CustomerNameGenerationInterface */ + private $customerViewHelper; /** * @inheritDoc @@ -51,24 +39,8 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle protected function setUp() { parent::setUp(); - $this->_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; - $this->customerRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\CustomerRepositoryInterface::class - ); - $this->addressRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AddressRepositoryInterface::class - ); - $this->accountManagement = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AccountManagementInterface::class - ); - $this->formKey = Bootstrap::getObjectManager()->get( - \Magento\Framework\Data\Form\FormKey::class - ); - - $this->objectManager = Bootstrap::getObjectManager(); - $this->customerViewHelper = $this->objectManager->get( - \Magento\Customer\Helper\View::class - ); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerViewHelper = $this->_objectManager->get(CustomerNameGenerationInterface::class); } /** @@ -79,144 +51,12 @@ protected function tearDown() /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + $this->_objectManager->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithEmptyPostData() - { - $this->getRequest()->setPostValue([])->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithInvalidFormData() - { - $post = ['account' => ['middlename' => 'test middlename', 'group_id' => 1]]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - /** @var \Magento\Backend\Model\Session $session */ - $session = $this->objectManager->get(\Magento\Backend\Model\Session::class); - /** - * Check that customer data were set to session - */ - $this->assertNotEmpty($session->getCustomerFormData()); - $this->assertArraySubset($post, $session->getCustomerFormData()); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); - } - - /** - * @magentoDataFixture Magento/Newsletter/_files/subscribers.php - */ - public function testSaveActionExistingCustomerUnsubscribeNewsletter() - { - $customerId = 1; - $websiteId = 1; - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(1, $subscriber->getStatus()); - - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'email' => 'customer@example.com', - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'sendemail_store_id' => 1 - ], - 'subscription_status' => [$websiteId => '0'] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(3, $subscriber->getStatus()); - - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->equalTo(['You saved the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); - } - - /** - * Ensure that an email is sent during save action - * - * @magentoConfigFixture current_store customer/account_information/change_email_template change_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionExistingCustomerChangeEmail() - { - $customerId = 1; - $newEmail = 'newcustomer@example.com'; - $transportBuilderMock = $this->prepareEmailMock( - 2, - 'change_email_template', - [ - 'name' => 'CustomerSupport', - 'email' => 'support@example.com', - ], - $customerId, - $newEmail - ); - $this->addEmailMockToClass($transportBuilderMock, EmailNotification::class); - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => $newEmail, - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - 'created_at' => '2000-01-01 00:00:00', - 'default_shipping' => '_item1', - 'default_billing' => 1, - ] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->_objectManager->get(Session::class)->getMessages(true); } /** @@ -265,83 +105,6 @@ public function testInlineEditChangeEmail() $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreException() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'password' => 'password', - ], - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - $this->assertArraySubset( - $post, - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreExceptionFormatFormData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '12/3/1996', - ], - ]; - $postCustomerFormatted = [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '1996-12-03', - ]; - - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - - $customerFormData = Bootstrap::getObjectManager() - ->get(\Magento\Backend\Model\Session::class) - ->getCustomerFormData(); - $this->assertEquals( - $postCustomerFormatted, - $customerFormData['customer'], - 'Customer form data should be formatted' - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -390,42 +153,6 @@ public function te1stNewActionWithCustomerData() $this->testNewAction(); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testDeleteAction() - { - $this->getRequest()->setParam('id', 1); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['You deleted the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testNotExistingCustomerDeleteAction() - { - $this->getRequest()->setParam('id', 2); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['No such entity with customerId = 2']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -437,63 +164,6 @@ public function testCartAction() $this->assertContains('<div id="customer_cart_grid"', $body); } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressSuccess() - { - $customerData = [ - 'customer' => [ - 'entity_id' => '1', - 'middlename' => 'new middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'new firstname', - 'lastname' => 'new lastname', - 'email' => 'example@domain.com', - 'default_shipping' => '_item1', - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - ], - 'address' => [ - '_item1' => [ - 'firstname' => 'update firstname', - 'lastname' => 'update lastname', - 'street' => ['update street'], - 'city' => 'update city', - 'country_id' => 'US', - 'region_id' => 10, - 'postcode' => '01001', - 'telephone' => '+7000000001', - ], - '_template_' => [ - 'firstname' => '', - 'lastname' => '', - 'street' => [], - 'city' => '', - 'country_id' => 'US', - 'postcode' => '', - 'telephone' => '', - ], - ], - ]; - /** - * set customer data - */ - $this->getRequest()->setParams($customerData)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/validate'); - $body = $this->getResponse()->getBody(); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - $this->assertEquals('{"error":0}', $body); - } - /** * @magentoDbIsolation enabled */ @@ -502,7 +172,7 @@ public function testResetPasswordActionNoCustomerId() // No customer ID in post, will just get redirected to base $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -514,7 +184,7 @@ public function testResetPasswordActionBadCustomerId() $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->getRequest()->setPostValue(['customer_id' => '789']); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -529,7 +199,7 @@ public function testResetPasswordActionSuccess() $this->equalTo(['The customer will receive an email with a link to reset password.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'edit')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'edit')); } /** From 2e273eb24ed4d180c7d21168ce370f899926694b Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 00:47:48 +0100 Subject: [PATCH 501/734] Replace manual `<amOnPage>` with `<actionGroup>` --- ...linePaymentIncludingTaxAndDiscountTest.xml | 6 +++-- ...undleProductToCartFromWishListPageTest.xml | 10 ++++---- ...CheckBundleProductOptionTierPricesTest.xml | 8 +++++-- ...oductPricesForCombinationOfOptionsTest.xml | 4 +++- .../Test/Mftf/Test/AddToCartCrossSellTest.xml | 10 +++++--- ...tomAttributeValuesAfterProductSaveTest.xml | 4 +++- .../AdminCloneProductWithDuplicateUrlTest.xml | 12 +++++++--- ...AdminCreateNewAttributeFromProductTest.xml | 4 +++- ...roductsImageInCaseOfMultipleStoresTest.xml | 12 +++++++--- ...lterByNameByStoreViewOnProductGridTest.xml | 4 +++- ...CustomizableOptionToProductWithSKUTest.xml | 4 +++- .../AdminMoveProductBetweenCategoriesTest.xml | 4 +++- ...egoryIndexerInUpdateOnScheduleModeTest.xml | 20 ++++++++++++---- ...ignedToCategoryWithoutCustomURLKeyTest.xml | 4 +++- ...AdminProductTypeSwitchingOnEditingTest.xml | 8 +++++-- ...dminRemoveCustomOptionsFromProductTest.xml | 4 +++- .../Test/AdminSimpleProductEditUiTest.xml | 6 +++-- ...ceForDifferentTimezonesForWebsitesTest.xml | 4 +++- ...ctAndProductCategoryPartialReindexTest.xml | 12 +++++++--- ...eroMaximumQtyAllowedInShoppingCartTest.xml | 4 +++- .../Mftf/Test/SearchEntityResultsTest.xml | 8 +++++-- ...minUrlForProductRewrittenCorrectlyTest.xml | 4 +++- ...tOnCheckoutPageDifferentStoreViewsTest.xml | 4 +++- ...riceInShoppingCartAfterProductSaveTest.xml | 4 +++- ...agesAndPricesToConfigurableProductTest.xml | 4 +++- .../AdminConfigurableProductCreateTest.xml | 4 +++- .../AdminConfigurableProductUpdateTest.xml | 8 +++++-- ...AdminProductTypeSwitchingOnEditingTest.xml | 12 +++++++--- ...bleProductPriceAdditionalStoreViewTest.xml | 24 ++++++++++++++----- ...ductsListWidgetConfigurableProductTest.xml | 4 +++- ...vailableToConfigureDisabledProductTest.xml | 8 +++++-- ...efrontVisibilityOfDuplicateProductTest.xml | 4 +++- ...AdminProductTypeSwitchingOnEditingTest.xml | 4 +++- .../Test/AdminCreatingShippingLabelTest.xml | 4 +++- ...inSystemIndexManagementGridChangesTest.xml | 4 +++- .../Mftf/Test/ShopByButtonInMobileTest.xml | 4 +++- ...hMapAssignedConfigProductIsCorrectTest.xml | 8 +++++-- ...ectnessInvoicedItemInBundleProductTest.xml | 4 +++- ...rWithSimpleProductCustomOptionFileTest.xml | 4 +++- ...ustomStoreShippingMethodTableRatesTest.xml | 4 +++- ...tImageColorWhenFilterByColorFilterTest.xml | 4 +++- ...FixedTaxValSavedForSpecificWebsiteTest.xml | 8 +++++-- .../Test/WishListWithDisabledProductTest.xml | 4 +++- 43 files changed, 210 insertions(+), 74 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml index 5efa5fd0db6b6..85d17a69ebae0 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml @@ -74,7 +74,7 @@ </after> <!-- Create a cart price rule with 10% discount for whole cart --> - <click selector="{{AdminMenuSection.marketing}}" stepKey="clickOnMarketing" /> + <click selector="{{AdminMenuSection.marketing}}" stepKey="clickOnMarketing"/> <waitForPageLoad stepKey="waitForMarketing"/> <click selector="{{CartPriceRulesSubmenuSection.cartPriceRules}}" stepKey="clickOnCartPriceRules"/> <waitForPageLoad stepKey="waitForCartPriceRules"/> @@ -93,7 +93,9 @@ <actionGroup ref="ChangeShippingTaxClassActionGroup" stepKey="changeShippingTaxClass"/> <!--Adding Special price to product--> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct.id$$)}}" stepKey="openAdminProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$simpleProduct.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml index 1498e52850fd5..aa06990f7af78 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml @@ -50,14 +50,16 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="createSimpleProduct2"/> </createData> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Delete created data --> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> <deleteData createDataKey="createCustomerViaTheStorefront" stepKey="deleteCustomerViaTheStorefront"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Log out --> <comment userInput="Log out" stepKey="commentLogOut"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> @@ -74,8 +76,8 @@ <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addToWishlistProduct"> <argument name="productVar" value="$$createProduct$$"/> </actionGroup> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createProduct.name$$)}}" stepKey="clickAddToCart" /> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createProduct.name$$)}}" stepKey="clickAddToCart"/> <waitForPageLoad stepKey="waitForProductBundlePage"/> <!-- See error message --> <comment userInput="See error message" stepKey="commentSeeErrorMessage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml index 0c9be915a7a8b..30369d447ab15 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml @@ -27,7 +27,9 @@ <!-- Add tier prices to simple products --> <!-- Simple product 1 --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPageProduct1"> + <argument name="productId" value="$$simpleProduct1CreateBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct1"> <argument name="group" value="ALL GROUPS"/> <argument name="quantity" value="5"/> @@ -35,7 +37,9 @@ <argument name="amount" value="50"/> </actionGroup> <!-- Simple product 2 --> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct2CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPageProduct2"> + <argument name="productId" value="$$simpleProduct2CreateBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct2"> <argument name="group" value="ALL GROUPS"/> <argument name="quantity" value="7"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 896c4f8f25fec..d388549745d51 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -40,7 +40,9 @@ <!--Add special price to simple product--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct5.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$simpleProduct5.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml index 9abad132d32db..749f0d25e1514 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AddToCartCrossSellTest"> <annotations> <features value="Catalog"/> @@ -42,7 +42,9 @@ </after> <!-- Go to simpleProduct1, add simpleProduct2 and simpleProduct3 as cross-sell--> - <amOnPage url="{{AdminProductEditPage.url($simpleProduct1.id$)}}" stepKey="goToProduct1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> + <argument name="productId" value="$simpleProduct1.id$"/> + </actionGroup> <click stepKey="openHeader1" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct2ToSimp1"> @@ -55,7 +57,9 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <!-- Go to simpleProduct3, add simpleProduct1 and simpleProduct2 as cross-sell--> - <amOnPage url="{{AdminProductEditPage.url($simpleProduct3.id$)}}" stepKey="goToProduct3"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct3"> + <argument name="productId" value="$simpleProduct3.id$"/> + </actionGroup> <click stepKey="openHeader2" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct1ToSimp3"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml index 5b510a7ecdb0b..8eec2ed40c8cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -49,7 +49,9 @@ </after> <!-- Open created product for edit --> - <amOnPage url="{{AdminProductEditPage.url($createSimpleProduct.id$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$createSimpleProduct.id$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Add created attribute to the product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml index a213f2af900cf..fb4013b94333e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml @@ -34,7 +34,9 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> <!--Save and duplicated the product once--> <comment userInput="Save and duplicated the product once" stepKey="commentSaveAndDuplicateProduct"/> @@ -45,7 +47,9 @@ <assertContains expectedType="string" expected="-1" actual="$grabDuplicatedProductUrlKey" stepKey="assertDuplicatedProductUrlKey1"/> <!--Add duplicated product to the simple product--> <comment userInput="Add duplicated product to the simple product" stepKey="commentAddProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToSimpleProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProductPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad1"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addCrossSellProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> @@ -63,7 +67,9 @@ <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('crosssell')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeCrossSellProduct"/> <!--Save and duplicated the product second time--> <comment userInput="Save and duplicated the product second time" stepKey="commentSaveAndDuplicateProduct1"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage1"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad2"/> <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductFormSecondTime"/> <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openProductSEOSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml index a39bc0bd39e2f..18fbcadf985c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml @@ -54,7 +54,9 @@ </actionGroup> <!--Go to created product page and create new attribute--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup" stepKey="createAttribute"> <argument name="attributeName" value="{{productDropDownAttribute.attribute_code}}"/> <argument name="attributeType" value="Dropdown"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml index 9ed0a8104faa1..b255fd17187da 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml @@ -40,12 +40,16 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <createData entity="SubCategory" stepKey="createSubCategory"/> <createData entity="NewRootCategory" stepKey="createRootCategory"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="visitAdminProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad0"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="['Default Category', $$createRootCategory.name$$, $$createSubCategory.name$$]" stepKey="fillCategory"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Add images to the product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="visitAdminProductPage2"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad1"/> <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> <argument name="image" value="ProductImage"/> @@ -80,7 +84,9 @@ <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickResetButton"/> <waitForPageLoad stepKey="waitForStorePageLoad"/> <!--Open product page on admin--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad2"/> <!--Enable the newly created website and save the product--> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct2"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml index 5ad3ee6f83e2d..7f973c99b39a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml @@ -28,7 +28,9 @@ <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToDefaultStoreView"> <argument name="storeView" value="_defaultStore.name"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index cfce9143f6cc6..cc21740cbc59f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -49,7 +49,9 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Change second product sku to first product sku--> - <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToProductEditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage1"> + <argument name="productId" value="$$createSecondProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku1"/> <!--Import customizable options and check--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index 3f7d612a1fdbc..d3d435b7451c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -68,7 +68,9 @@ <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSaveSuccessMessage"/> <!-- Assign <product1> to the <Sub1> --> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct.id$$)}}" stepKey="goToProduct"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$simpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="activateDropDownCategory"/> <fillField userInput="{{SimpleSubCategory.name}}" selector="{{AdminProductFormSection.searchCategory}}" stepKey="fillSearch"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index b547fbe69fbf7..beb2bb401e7c4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -74,7 +74,9 @@ <!-- Case: change product category from product page --> <!-- 1. Open Admin > Catalog > Products > Product A1 --> - <amOnPage url="{{AdminProductEditPage.url($$createProductA1.id$$)}}" stepKey="goToProductA1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductA1"> + <argument name="productId" value="$$createProductA1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- 2. Assign category A to product A1. Save product --> @@ -104,7 +106,9 @@ <see userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductA1"/> <!--6. Open Admin > Catalog > Products > Product A1. Unassign category A from product A1 --> - <amOnPage url="{{AdminProductEditPage.url($$createProductA1.id$$)}}" stepKey="OnPageProductA1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="OnPageProductA1"> + <argument name="productId" value="$$createProductA1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductA1PageLoad"/> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryA"> <argument name="categoryName" value="$$createCategoryA.name$$"/> @@ -144,7 +148,9 @@ <see userInput="$$createProductC2.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeNameProductC2"/> <!-- 11. Open product C1 in Admin. Make it disabled (Enable Product = No)--> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="goToProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductC1PageLoad"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOffEnableToggleAgain"/> <!-- Saved successfully --> @@ -200,7 +206,9 @@ <!-- 17. Repeat steps 10-16, but enable products instead. --> <!-- 17.11 Open product C1 in Admin. Make it enabled --> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="goToEditProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductC1Page"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOnEnableToggleAgain"/> @@ -258,7 +266,9 @@ <!-- Case: change product visibility --> <!-- 18. Repeat steps 10-17 but change product Visibility instead of product status --> <!-- 18.11 Open product C1 in Admin. Make it enabled --> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="editProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="editProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitProductC1Page"/> <selectOption selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Search" stepKey="changeVisibility"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml index 9babd94ef2641..c62c6b4b4c6a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml @@ -51,7 +51,9 @@ </after> <!-- Open product --> - <amOnPage url="{{AdminProductEditPage.url($createSimpleProductSecond.id$)}}" stepKey="openProductSecondEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductSecondEditPage"> + <argument name="productId" value="$createSimpleProductSecond.id$"/> + </actionGroup> <!-- switch store view --> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToStoreView"> <argument name="storeView" value="storeViewData.name"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index 8e8f3ebccafb1..bf5b3db1f1f33 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -38,7 +38,9 @@ </after> <!--Change product type to Downloadable--> <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToDownloadableProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToDownloadableProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> @@ -76,7 +78,9 @@ </annotations> <!--Change product type to Simple--> <comment userInput="Change product type to Simple Product" stepKey="commentCreateSimple"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml index 702525ac5bf04..f46a3aea0ef81 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml @@ -32,7 +32,9 @@ </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Edit Simple Product --> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProduct"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <!-- See 3 options are present --> <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertCustomOptionsField"> <argument name="option" value="ProductOptionField"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml index bc5a0319bae7a..c0f121edcf6bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml @@ -34,7 +34,9 @@ <!--check admin for valid Enable Status label--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="wait1"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeCheckboxEnableProductIsChecked"/> @@ -46,4 +48,4 @@ <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickEnableProductLabel"/> <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="dontSeeCheckboxEnableProductIsCheckedAfterLabelClick"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 59f0b2f5dd76e..2e8c6c456352f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -57,7 +57,9 @@ <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig1"/> <!--Set special price to created product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="setSpecialPriceToCreatedProduct"> <argument name="price" value="15"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index 7ef1619319289..1ef490983e467 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -104,7 +104,9 @@ </actionGroup> <!-- Unassign category M from Product B --> - <amOnPage url="{{AdminProductEditPage.url($$productB.id$$)}}" stepKey="amOnEditCategoryPageB"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditCategoryPageB"> + <argument name="productId" value="$$productB.id$$"/> + </actionGroup> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryM"> <argument name="categoryName" value="$$categoryM.name$$"/> </actionGroup> @@ -166,13 +168,17 @@ <!-- 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="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditProductPageA"> + <argument name="productId" value="$$productA.id$$"/> + </actionGroup> <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="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditProductPageC"> + <argument name="productId" value="$$productC.id$$"/> + </actionGroup> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryL"> <argument name="categoryName" value="$$categoryL.name$$"/> </actionGroup> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml index 43f16c0fa475e..edbf7451c6b6b 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml @@ -54,7 +54,9 @@ <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfigWithCorrectNumber"/> <!-- Go to product page --> - <amOnPage url="{{AdminProductEditPage.url($$createdProduct.id$$)}}" stepKey="openAdminProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$createdProduct.id$$"/> + </actionGroup> <!-- Validate zero value --> <actionGroup ref="AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup" stepKey="productValidateZeroValue"> <argument name="qty" value="0"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index f1c7c706a5123..03a1d89e0b47d 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -506,7 +506,9 @@ </createData> <!--Finish bundle creation--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Perform reindex and flush cache --> @@ -573,7 +575,9 @@ <!--Finish bundle creation--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Perform reindex and flush cache --> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 453f18003e694..a7b18dcd8a8c9 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -37,7 +37,9 @@ </after> <!--Open Created product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <!--Switch to Default Store view--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 1fff7501f578d..a5f0aaef49175 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -41,7 +41,9 @@ </actionGroup> <!--Go to created product page--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPage"/> <!--Switch to second store view and change the product name--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index 46c4abf4eab1a..9820babecccc5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -52,7 +52,9 @@ <!--Edit product price via admin panel--> <openNewTab stepKey="openNewTab"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <fillField userInput="120" selector="{{AdminProductFormSection.productPrice}}" stepKey="setNewPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeTab"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml index 543ead3f6732a..07d9719b24d96 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml @@ -38,7 +38,9 @@ </after> <!--Open edit product page--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProductCreateConfigurableProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct.id$$"/> + </actionGroup> <!--Open edit configuration wizard--> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml index 692ba32c6db28..8a34bf19a2d75 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml @@ -95,7 +95,9 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPage"/> <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPane"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index fdc467728451a..4d06452d05bbb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -158,7 +158,9 @@ <!--check admin for both options--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="wait2"/> <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeOption1Admin"/> <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct2.name$$" stepKey="seeOption2Admin"/> @@ -254,7 +256,9 @@ <!--go to admin and disable an option--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="wait2"/> <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisable"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index 361d58c147d38..0bccd2f76982a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -50,7 +50,9 @@ </after> <!--Add configurations to product--> <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToSimpleProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToSimpleProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> @@ -89,7 +91,9 @@ </annotations> <!--Delete product configurations--> <comment userInput="Delete product configuration" stepKey="commentDeleteConfigs"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> <conditionalClick selector="{{ AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption1Actions"/> @@ -157,7 +161,9 @@ </after> <!--Add configurations to product--> <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index a5f62da42575e..23c3de4675718 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -108,7 +108,9 @@ <see userInput="You saved the store view." stepKey="seeSaveMessage" /> <!--go to admin and open product edit page to disable product all store view --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitEditPage"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProductForAllStoreView"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton2"/> @@ -145,7 +147,9 @@ <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="enabledConfigProductSecondStore"/> <!--go to admin and open product edit page to enable child product for second store view --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage2"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitEditPage2"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher1"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView1"/> @@ -166,14 +170,18 @@ <see userInput="$$createConfigProduct.name$$" stepKey="assertProductPresent1"/> <!--go to admin and open child product1 and assign it to the second website --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1EditPage1"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild1EditPageToLoad"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct1InWebsitesSection"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite1"/> <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveUpdatedChild1Again"/> <!--go to admin again and open child product1 and enable for second store view--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1EditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild1EditPageToLoad1"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP1"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P1"/> @@ -185,14 +193,18 @@ <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="save2UpdatedChild1"/> <!--go to admin and open child product2 edit page and assign it to the second website --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2EditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild2EditPageToLoad"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct2InWebsitesSection"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite2"/> <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveUpdatedChild2"/> <!--go to admin again and open child product2 and enable for second store view--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2EditPage2"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild2EditPageToLoad1"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP2"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P2"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml index 3dc706645a33b..372aa03e4e152 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml @@ -78,7 +78,9 @@ <!-- A Cms page containing the New Products Widget gets created here via extends --> <!-- Modify the Configurable Product that we created in the before block --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index dbbeb4e252ef7..e0263c5c011b9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -109,13 +109,17 @@ </after> <!-- Disable child product --> <comment userInput="Disable child product" stepKey="disableChildProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForChildProductPageLoad"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Set the second product out of stock --> <comment userInput="Set the second product out of stock" stepKey="outOfStockChildProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSecondChildProductPageLoad"/> <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="outOfStockStatus"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProductForm"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 6befc15044cc5..aa9b0ac01872a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -49,7 +49,9 @@ </after> <!--Create attribute and options for product--> <comment userInput="Create attribute and options for product" stepKey="commentCreateAttributesAndOptions"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToConfigProductPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct1"> <argument name="image" value="MagentoLogo"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index f2b6dc9e8a809..07c84e3f8e6e0 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -54,7 +54,9 @@ </after> <!--Change product type to Downloadable--> <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToDownloadableProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToDownloadableProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeightForProduct"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index c0b602e772b54..a596f40efa66f 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -78,7 +78,9 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Add country of manufacture to product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <actionGroup ref="AdminFillProductCountryOfManufactureActionGroup" stepKey="fillCountryOfManufacture"> <argument name="countryId" value="DE"/> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml index 75b040a623451..30347ec1a64d0 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml @@ -44,7 +44,9 @@ <seeElement selector="{{AdminIndexManagementSection.columnScheduleStatus}}" stepKey="seeScheduleStatusColumn"/> <!--Adding Special price to product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml index 0e0eb352c8d33..b5fc483e251eb 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml @@ -45,7 +45,9 @@ <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickAttributeSetSave"/> <!-- Go to simple product edit page and set the product attribute to a value --> <comment userInput="Go to simple product edit page and set the product attribute to a value" stepKey="commentProductAttributeEdit" /> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$simpleProduct1.id$$"/> + </actionGroup> <selectOption selector="{{AdminProductFormSection.customSelectField($$attribute.attribute[attribute_code]$$)}}" userInput="option1" stepKey="selectAttribute"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Check storefront mobile view for shop by button is functioning as expected --> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index 72443a41d67f4..a9d1d7b11d43e 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -111,7 +111,9 @@ </after> <!-- Set Manufacturer's Suggested Retail Price to products--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToFirstChildProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp"/> @@ -119,7 +121,9 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondChildProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad1"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton1"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp1"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index d7d5036c45c8f..ee01abcc989de 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -52,7 +52,9 @@ </after> <!--Complete Bundle product creation--> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Run re-index task--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml index b694363960def..d1417ba1bc267 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml @@ -33,7 +33,9 @@ </after> <!--Add option to product.--> - <amOnPage url="{{AdminProductEditPage.url($simpleProduct.id$)}}" stepKey="navigateToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToProductEditPage"> + <argument name="productId" value="$simpleProduct.id$"/> + </actionGroup> <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="addOption"> <argument name="option" value="ProductOptionFile"/> </actionGroup> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index 5319992efa585..a4c155e9d3753 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -76,7 +76,9 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Assign product to custom website--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="UnassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index 18e9f82e74121..4287d2a91c4ee 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -41,7 +41,9 @@ <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToConfigProductPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!--Create visual swatch attribute--> <actionGroup ref="AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup" stepKey="addSwatchToProduct"> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml index b929fd3d304c0..b542d6a4d0364 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml @@ -70,7 +70,9 @@ </after> <!-- Go to product edit page and assign it to created website --> <comment userInput="Go to product edit page and assign it to created website" stepKey="assignProductToCreatedWebsite"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="navigateToProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct"> <argument name="website" value="{{NewWebSiteData.name}}"/> @@ -99,7 +101,9 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--See available websites only 'All Websites'--> <comment userInput="See available websites 'All Websites', 'Main Website' and Second website" stepKey="commentCheckWebsitesInProductPage"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductPageSecondTime"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductPageSecondTime"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoadSecondTime"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'All Websites')}}" stepKey="checkAllWebsitesInDropDown"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'Main Website')}}" stepKey="checkMainWebsiteInDropDown"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml index f9072402dbd73..227212cd05ae8 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml @@ -40,7 +40,9 @@ </actionGroup> <openNewTab stepKey="openNewTab"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminSetProductDisabledActionGroup" stepKey="disableProduct"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeSecondTab"/> From 7fc4e5c079270e8ff6586eb54df96d458447fd6b Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Fri, 20 Mar 2020 19:31:17 -0500 Subject: [PATCH 502/734] Integration test fix --- .../Backend/Model/Dashboard/ChartTest.php | 16 ++++++++++------ .../Sales/_files/order_list_with_invoice.php | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php index 284c3c9b5046e..25ac15342e13f 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -39,7 +39,11 @@ protected function setUp() */ public function testGetByPeriodWithParam(int $expectedDataQty, string $period, string $chartParam): void { - $this->assertGreaterThan($expectedDataQty, $this->model->getByPeriod($period, $chartParam)); + $ordersData = $this->model->getByPeriod($period, $chartParam); + $ordersCount = array_sum(array_map(function ($item) { + return $item['y']; + }, $ordersData)); + $this->assertGreaterThanOrEqual($expectedDataQty, $ordersCount); } /** @@ -51,27 +55,27 @@ public function getChartDataProvider(): array { return [ [ - 10, + 2, '24h', 'quantity' ], [ - 4, + 3, '7d', 'quantity' ], [ - 10, + 4, '1m', 'quantity' ], [ - 8, + 5, '1y', 'quantity' ], [ - 15, + 6, '2y', 'quantity' ] diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php index 06ddb18b009d1..d1f74c746b64f 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php @@ -55,7 +55,7 @@ 'base_grand_total' => 140.00, 'grand_total' => 140.00, 'subtotal' => 140.00, - 'created_at' => $dateTime->modify('-1 month')->format(DateTime::DATETIME_PHP_FORMAT), + 'created_at' => $dateTime->modify('first day of this month')->format(DateTime::DATETIME_PHP_FORMAT), ], [ 'increment_id' => '100000005', @@ -65,7 +65,7 @@ 'base_grand_total' => 150.00, 'grand_total' => 150.00, 'subtotal' => 150.00, - 'created_at' => $dateTime->modify('-1 year')->format(DateTime::DATETIME_PHP_FORMAT), + 'created_at' => $dateTime->modify('first day of january this year')->format(DateTime::DATETIME_PHP_FORMAT), ], [ 'increment_id' => '100000006', @@ -75,7 +75,7 @@ 'base_grand_total' => 160.00, 'grand_total' => 160.00, 'subtotal' => 160.00, - 'created_at' => $dateTime->modify('-2 year')->format(DateTime::DATETIME_PHP_FORMAT), + 'created_at' => $dateTime->modify('first day of january last year')->format(DateTime::DATETIME_PHP_FORMAT), ], ]; From 89016aaeafc79cac98c8a0b64d09ee9949b8ec2a Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Fri, 20 Mar 2020 19:36:30 -0500 Subject: [PATCH 503/734] MC-32201: Reorder functionality (test coverage) --- .../Magento/Sales/Model/Reorder/Reorder.php | 2 + .../ReorderConfigurableWithVariationsTest.php | 335 ++++++++++++++++++ ...order_with_two_configurable_variations.php | 95 +++++ ...h_two_configurable_variations_rollback.php | 8 + .../_files/order_with_two_simple_products.php | 1 - 5 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index e9a21303921f8..42a3932d9a85e 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -242,6 +242,8 @@ private function prepareOutput(CartInterface $cart): Data\ReorderOutput { $output = new Data\ReorderOutput($cart, $this->errors); $this->errors = []; + // we already show user errors, do not expose it to cart level + $cart->setHasError(false); return $output; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php new file mode 100644 index 0000000000000..d41c05730c0ae --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -0,0 +1,335 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder with and without products overlay in shopping cart. + */ +class ReorderConfigurableWithVariationsTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $this->cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $this->cartRepository->getForCustomer(self::CUSTOMER_ID); + $this->cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_configurable_variations.php + */ + public function testVariations() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple_20'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); + + $this->assertValidVariations(); + $this->assertWithOutOfStockVariation($productRepository, $product); + $this->assertWithDeletedVariation($productRepository, $product); + } + + /** + * Assert 2 variations of configurable product. + * + * @return void + * @throws NoSuchEntityException + */ + private function assertValidVariations(): void + { + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ] + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 2', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert reorder with out of stock variation. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return void + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException *@throws NoSuchEntityException + */ + private function assertWithOutOfStockVariation( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // make product available in stock but disable and make reorder + $product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] + ); + $productRepository->save($product); + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + $this->assetProductNotSalable($response); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert reorder with "out of stock" variation. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return void + * @throws \Magento\Framework\Exception\StateException + * @throws NoSuchEntityException + */ + private function assertWithDeletedVariation( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // delete a product and make reorder + /** @var \Magento\Framework\Registry $registry */ + $registry = Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + $productRepository->delete($product); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + $this->assetProductUndefined($response); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert that variation is not salable. + * + * @param array $response + * @return void + */ + private function assetProductNotSalable(array $response) + { + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => [ + 'orderNumber', + ], + 'code' => 'NOT_SALABLE', + 'message' => 'Could not add the product with SKU "configurable" to the shopping cart:' . + ' This product is out of stock.', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * Assert condition that variation is undefined. + * + * @param array $response + * @return void + */ + private function assetProductUndefined(array $response): void + { + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => [ + 'orderNumber', + ], + 'code' => 'UNDEFINED', + 'message' => 'Could not add the product with SKU "configurable" to the shopping cart: ' . + 'You need to choose options for your item.', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + message + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + ... on ConfigurableCartItem { + configurable_options { + option_label + value_label + } + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php new file mode 100644 index 0000000000000..1c83f0a683710 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require 'default_rollback.php'; + +$objectManager = Bootstrap::getObjectManager(); + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable.php'; +$configurableProduct = $productRepository->get($product->getSku()); + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +// configurable product +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class); +$configurableOptions = $options->setAttributeFilter($attribute->getId())->getItems(); +foreach ($configurableOptions as $option) { + $requestInfo[] = [ + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId(), + ], + ]; +} +$qtyOrdered = 1; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderConfigurableItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderConfigurableItem->setProductId($configurableProduct->getId())->setQtyOrdered($qtyOrdered); +$orderConfigurableItem->setBasePrice($configurableProduct->getPrice()); +$orderConfigurableItem->setPrice($configurableProduct->getPrice()); +$orderConfigurableItem->setRowTotal($configurableProduct->getPrice()); +$orderConfigurableItem->setParentItemId(null); +$orderConfigurableItem->setProductType('configurable'); +$configurableVariations = []; +foreach (array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()) as $key => $variationId) { + $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); + + /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ + $simpleProduct = $productRepository->getById($simpleProductId); + + $info = $requestInfo[$key]; + $info['product'] = $simpleProductId; + $info['item'] = $simpleProduct; + + $orderConfigurableParentItem = clone $orderConfigurableItem; + $orderConfigurableParentItem->setProductOptions(['info_buyRequest' => $info]); + $configurableItems[] = $orderConfigurableParentItem; +} + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false); +foreach ($configurableItems as $item) { + $order->addItem($item); +} +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php new file mode 100644 index 0000000000000..b6bc7c7113a85 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php index 077fa6c5f541e..ae99114490974 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php @@ -70,7 +70,6 @@ ->setName($secondProduct->getName()) ->setSku($secondProduct->getSku()) ->setStoreId(0) - ->setProductId($secondProduct->getId()) ->setSku($secondProductSku) ->setProductOptions(['info_buyRequest' => $requestInfo]); From 1d3cfbb0e20c1ffb32ed6365f12e58c038934360 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 02:33:05 +0100 Subject: [PATCH 504/734] Static Tests fix --- .../ActionGroup/AdminProductPageOpenByIdActionGroup.xml | 6 ++++++ .../Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml index d1d4add6f3bd2..ec0f6757d48b3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml @@ -1,4 +1,10 @@ <?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"> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml index 36ea97a435872..aff1de23af409 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml @@ -1,4 +1,10 @@ <?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"> From 8083b97c8ed38bef6e89026dc0bd22da05e6112d Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 04:27:19 +0100 Subject: [PATCH 505/734] Implement ActionInterface for /captcha/refresh --- .../Captcha/Controller/Refresh/Index.php | 74 ++++-- .../Unit/Controller/Refresh/IndexTest.php | 215 +++++++++--------- 2 files changed, 157 insertions(+), 132 deletions(-) diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index e401e03e9551f..db911788daacd 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -1,43 +1,67 @@ <?php /** - * Refreshes captcha and returns JSON encoded URL to image (AJAX action) - * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'} - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Captcha\Controller\Refresh; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Framework\App\Action\Context; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\JsonFactory as JsonResultFactory; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; +use Magento\Framework\View\LayoutInterface; -class Index extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface +/** + * Refreshes captcha and returns JSON encoded URL to image (AJAX action) + * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'} + */ +class Index implements HttpPostActionInterface { /** - * @var \Magento\Captcha\Helper\Data + * @var CaptchaHelper + */ + private $captchaHelper; + + /** + * @var JsonSerializer + */ + private $serializer; + + /** + * @var RequestInterface */ - protected $captchaHelper; + private $request; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var LayoutInterface */ - protected $serializer; + private $layout; /** - * @param Context $context - * @param \Magento\Captcha\Helper\Data $captchaHelper - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer - * @throws \RuntimeException + * @var JsonResultFactory + */ + private $jsonFactory; + + /** + * @param RequestInterface $request + * @param JsonResultFactory $jsonFactory + * @param CaptchaHelper $captchaHelper + * @param LayoutInterface $layout + * @param JsonSerializer $serializer */ public function __construct( - Context $context, - \Magento\Captcha\Helper\Data $captchaHelper, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + RequestInterface $request, + JsonResultFactory $jsonFactory, + CaptchaHelper $captchaHelper, + LayoutInterface $layout, + JsonSerializer $serializer ) { + $this->request = $request; + $this->jsonFactory = $jsonFactory; $this->captchaHelper = $captchaHelper; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); - parent::__construct($context); + $this->layout = $layout; + $this->serializer = $serializer; } /** @@ -45,10 +69,10 @@ public function __construct( */ public function execute() { - $formId = $this->_request->getPost('formId'); + $formId = $this->request->getPost('formId'); if (null === $formId) { $params = []; - $content = $this->_request->getContent(); + $content = $this->request->getContent(); if ($content) { $params = $this->serializer->unserialize($content); } @@ -57,9 +81,9 @@ public function execute() $captchaModel = $this->captchaHelper->getCaptcha($formId); $captchaModel->generate(); - $block = $this->_view->getLayout()->createBlock($captchaModel->getBlockName()); + $block = $this->layout->createBlock($captchaModel->getBlockName()); $block->setFormId($formId)->setIsAjax(true)->toHtml(); - $this->_response->representJson($this->serializer->serialize(['imgSrc' => $captchaModel->getImgSrc()])); - $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); + + return $this->jsonFactory->create(['imgSrc' => $captchaModel->getImgSrc()]); } } diff --git a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php index 99ac2e2d8fccc..386de37e146b7 100644 --- a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php @@ -5,133 +5,134 @@ */ namespace Magento\Captcha\Test\Unit\Controller\Refresh; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Captcha\Controller\Refresh\Index; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Captcha\Model\CaptchaInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; +use Magento\Framework\View\Element\BlockInterface; +use Magento\Framework\View\LayoutInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $captchaHelperMock; + private const STUB_FORM_ID = 'StubFormId'; + private const STUB_CAPTCHA_SOURCE = '/stub-captcha-source.jpg'; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $captchaMock; + /** @var MockObject|RequestInterface */ + private $requestMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; + /** @var MockObject|ResultJsonFactory */ + private $jsonResultFactoryMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; + /** @var MockObject|CaptchaHelper */ + private $captchaHelperMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $viewMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $layoutMock; + /** @var MockObject|LayoutInterface */ + private $layoutMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $flagMock; + /** @var MockObject|BlockInterface */ + private $blockMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $serializerMock; + /** @var MockObject|JsonSerializer */ + private $jsonSerializerMock; - /** - * @var \Magento\Captcha\Controller\Refresh\Index - */ - protected $model; + /** @var Index */ + private $refreshAction; protected function setUp() { - $this->captchaHelperMock = $this->createMock(\Magento\Captcha\Helper\Data::class); - $this->captchaMock = $this->createMock(\Magento\Captcha\Model\DefaultModel::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->contextMock = $this->createMock(\Magento\Framework\App\Action\Context::class); - $this->viewMock = $this->createMock(\Magento\Framework\App\ViewInterface::class); - $this->layoutMock = $this->createMock(\Magento\Framework\View\LayoutInterface::class); - $this->flagMock = $this->createMock(\Magento\Framework\App\ActionFlag::class); - $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); - - $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->requestMock)); - $this->contextMock->expects($this->any())->method('getView')->will($this->returnValue($this->viewMock)); - $this->contextMock->expects($this->any())->method('getResponse')->will($this->returnValue($this->responseMock)); - $this->contextMock->expects($this->any())->method('getActionFlag')->will($this->returnValue($this->flagMock)); - $this->viewMock->expects($this->any())->method('getLayout')->will($this->returnValue($this->layoutMock)); - - $this->model = new \Magento\Captcha\Controller\Refresh\Index( - $this->contextMock, + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->setMethods(['getPost', 'getContent']) + ->getMockForAbstractClass(); + $this->layoutMock = $this->getMockBuilder(LayoutInterface::class) + ->setMethods(['createBlock']) + ->getMockForAbstractClass(); + $this->blockMock = $this->getMockBuilder(BlockInterface::class) + ->setMethods(['setFormId', 'setIsAjax', 'toHtml']) + ->getMockForAbstractClass(); + $this->jsonResultFactoryMock = $this->createMock(ResultJsonFactory::class); + $this->jsonSerializerMock = $this->createMock(JsonSerializer::class); + $this->captchaHelperMock = $this->createMock(CaptchaHelper::class); + + $this->blockMock->method('setIsAjax') + ->willReturnSelf(); + + $this->layoutMock->method('createBlock') + ->willReturn($this->blockMock); + + $this->refreshAction = new Index( + $this->requestMock, + $this->jsonResultFactoryMock, $this->captchaHelperMock, - $this->serializerMock + $this->layoutMock, + $this->jsonSerializerMock ); } - /** - * @dataProvider executeDataProvider - * @param int $formId - * @param int $callsNumber - */ - public function testExecute($formId, $callsNumber) + public function testCaptchaGeneratedWhenPostDataContainsFormId() { - $content = ['formId' => $formId]; - $imgSource = ['imgSrc' => 'source']; - - $blockMethods = ['setFormId', 'setIsAjax', 'toHtml']; - $blockMock = $this->createPartialMock(\Magento\Captcha\Block\Captcha::class, $blockMethods); - - $this->requestMock->expects($this->any())->method('getPost')->with('formId')->will($this->returnValue($formId)); - $this->requestMock->expects($this->exactly($callsNumber))->method('getContent') - ->will($this->returnValue(json_encode($content))); - $this->captchaHelperMock->expects($this->any())->method('getCaptcha')->with($formId) - ->will($this->returnValue($this->captchaMock)); - $this->captchaMock->expects($this->once())->method('generate'); - $this->captchaMock->expects($this->once())->method('getBlockName')->will($this->returnValue('block')); - $this->captchaMock->expects($this->once())->method('getImgSrc')->will($this->returnValue('source')); - $this->layoutMock->expects($this->once())->method('createBlock')->with('block') - ->will($this->returnValue($blockMock)); - $blockMock->expects($this->any())->method('setFormId')->with($formId)->will($this->returnValue($blockMock)); - $blockMock->expects($this->any())->method('setIsAjax')->with(true)->will($this->returnValue($blockMock)); - $blockMock->expects($this->once())->method('toHtml'); - $this->responseMock->expects($this->once())->method('representJson')->with(json_encode($imgSource)); - $this->flagMock->expects($this->once())->method('set')->with('', 'no-postDispatch', true); - $this->serializerMock->expects($this->exactly($callsNumber)) - ->method('unserialize')->will($this->returnValue($content)); - $this->serializerMock->expects($this->once()) - ->method('serialize')->will($this->returnValue(json_encode($imgSource))); - - $this->model->execute(); + // Given + $this->requestMock->method('getPost') + ->with('formId') + ->willReturn(self::STUB_FORM_ID); + $this->blockMock->method('setFormId') + ->willReturnSelf(); + + // Expect + $this->requestMock->expects($this->never()) + ->method('getContent'); + $this->captchaHelperMock->expects($this->once()) + ->method('getCaptcha') + ->with(self::STUB_FORM_ID) + ->willReturn( + $this->getCaptchaModelMock(self::STUB_CAPTCHA_SOURCE) + ); + + // When + $this->refreshAction->execute(); + } + + public function testCaptchaFallsBackToRequestContentIfPostMissing() + { + // Given + $this->requestMock->method('getPost') + ->with('formId') + ->willReturn(null); + $this->blockMock->method('setFormId') + ->willReturnSelf(); + + // Expect + $this->requestMock->expects(self::once()) + ->method('getContent') + ->willReturn(null); + $this->captchaHelperMock->expects($this->once()) + ->method('getCaptcha') + ->with(null) + ->willReturn( + $this->getCaptchaModelMock(self::STUB_CAPTCHA_SOURCE) + ); + + // When + $this->refreshAction->execute(); + } /** - * @return array + * @param string $imageSource + * @return MockObject|CaptchaInterface */ - public function executeDataProvider() + private function getCaptchaModelMock(string $imageSource): CaptchaInterface { - return [ - [ - 'formId' => null, - 'callsNumber' => 1, - ], - [ - 'formId' => 1, - 'callsNumber' => 0, - ] - ]; + $modelMock = $this->getMockBuilder(CaptchaInterface::class) + ->setMethods(['generate', 'getBlockName', 'getImgSrc']) + ->getMockForAbstractClass(); + + $modelMock->method('getImgSrc') + ->willReturn($imageSource); + + return $modelMock; } } From e172c8367ba96b8e3ee5724648bf769c81cf45c0 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 04:30:12 +0100 Subject: [PATCH 506/734] Introduce more meaningful property name --- app/code/Magento/Captcha/Controller/Refresh/Index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index db911788daacd..a8a65fc1104fd 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -41,7 +41,7 @@ class Index implements HttpPostActionInterface /** * @var JsonResultFactory */ - private $jsonFactory; + private $jsonResultFactory; /** * @param RequestInterface $request @@ -58,7 +58,7 @@ public function __construct( JsonSerializer $serializer ) { $this->request = $request; - $this->jsonFactory = $jsonFactory; + $this->jsonResultFactory = $jsonFactory; $this->captchaHelper = $captchaHelper; $this->layout = $layout; $this->serializer = $serializer; @@ -84,6 +84,6 @@ public function execute() $block = $this->layout->createBlock($captchaModel->getBlockName()); $block->setFormId($formId)->setIsAjax(true)->toHtml(); - return $this->jsonFactory->create(['imgSrc' => $captchaModel->getImgSrc()]); + return $this->jsonResultFactory->create(['imgSrc' => $captchaModel->getImgSrc()]); } } From 35e3ae3be1af147c1105e8bfcba097ec3e71da73 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 10:23:16 +0100 Subject: [PATCH 507/734] Static Tests fix --- app/code/Magento/Captcha/Controller/Refresh/Index.php | 2 +- .../Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index a8a65fc1104fd..f587cc152b434 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -65,7 +65,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function execute() { diff --git a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php index 386de37e146b7..83886806b0820 100644 --- a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php @@ -117,7 +117,6 @@ public function testCaptchaFallsBackToRequestContentIfPostMissing() // When $this->refreshAction->execute(); - } /** From e71b22aed2a504d02e43c6c6302407f57cd50154 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 10:38:06 +0100 Subject: [PATCH 508/734] Fix failing Captcha test, extract Captcha MFTF test to separate file, add Cron Index --- .../Captcha/Controller/Refresh/Index.php | 3 +- .../Mftf/Test/CaptchaFormsDisplayingTest.xml | 49 ----------------- .../CaptchaWithDisabledGuestCheckoutTest.xml | 54 +++++++++++++++++++ 3 files changed, 56 insertions(+), 50 deletions(-) create mode 100644 app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index f587cc152b434..2959fe278d736 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -84,6 +84,7 @@ public function execute() $block = $this->layout->createBlock($captchaModel->getBlockName()); $block->setFormId($formId)->setIsAjax(true)->toHtml(); - return $this->jsonResultFactory->create(['imgSrc' => $captchaModel->getImgSrc()]); + $result = $this->jsonResultFactory->create(); + return $result->setData(['imgSrc' => $captchaModel->getImgSrc()]); } } diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml index b1c3ed52f163b..71763edc35a3b 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml @@ -65,53 +65,4 @@ <scrollToTopOfPage stepKey="ScrollToTop"/> <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> </test> - <test name="CaptchaWithDisabledGuestCheckoutTest"> - <annotations> - <features value="Captcha"/> - <stories value="MC-5602 - CAPTCHA doesn't appear in login popup after refreshing page."/> - <title value="Captcha is displaying on login form with disabled guest checkout"/> - <description value="Captcha is displaying on login form with disabled guest checkout"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-96691"/> - <group value="captcha"/> - </annotations> - <before> - <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/> - <magentoCLI command="config:set customer/captcha/failed_attempts_login 1" stepKey="decreaseLoginAttempt"/> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <after> - <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/> - <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> - </after> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> - <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> - <fillField selector="{{StorefrontCustomerSignInPopupFormSection.password}}" userInput="incorrectPassword" stepKey="fillIncorrectCustomerPassword"/> - <click selector="{{StorefrontCustomerSignInPopupFormSection.signIn}}" stepKey="clickSignIn"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.errorMessage}}" stepKey="seeErrorMessage"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart2"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton2"/> - </test> </tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml new file mode 100644 index 0000000000000..54a2b27431c8e --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CaptchaWithDisabledGuestCheckoutTest"> + <annotations> + <features value="Captcha"/> + <stories value="MC-5602 - CAPTCHA doesn't appear in login popup after refreshing page."/> + <title value="Captcha is displaying on login form with disabled guest checkout"/> + <description value="Captcha is displaying on login form with disabled guest checkout"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-96691"/> + <group value="captcha"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/> + <magentoCLI command="config:set customer/captcha/failed_attempts_login 1" stepKey="decreaseLoginAttempt"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + <after> +<!-- <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/>--> +<!-- <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/>--> +<!-- <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>--> +<!-- <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/>--> + </after> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> + <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> + <fillField selector="{{StorefrontCustomerSignInPopupFormSection.password}}" userInput="incorrectPassword" stepKey="fillIncorrectCustomerPassword"/> + <click selector="{{StorefrontCustomerSignInPopupFormSection.signIn}}" stepKey="clickSignIn"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.errorMessage}}" stepKey="seeErrorMessage"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart2"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton2"/> + </test> +</tests> From bb9517abb3924898a83bb7cef8076d539a892735 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 10:39:08 +0100 Subject: [PATCH 509/734] Uncomment elements that I used for debugging --- .../Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml index 54a2b27431c8e..5f1d75c0bb713 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml @@ -21,10 +21,10 @@ <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> -<!-- <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/>--> -<!-- <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/>--> -<!-- <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>--> -<!-- <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/>--> + <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/> + <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> </after> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> From 06f78cf37aac2b0e1173f2d6494a369627e15406 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 11:21:20 +0100 Subject: [PATCH 510/734] Add missing Captcha --- .../Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml index 5f1d75c0bb713..6b263157a0d35 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaWithDisabledGuestCheckoutTest.xml @@ -1,4 +1,11 @@ <?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="CaptchaWithDisabledGuestCheckoutTest"> From 7c482c78833dcaba6bd4be88d56ff01f42b5e243 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 11:29:02 +0100 Subject: [PATCH 511/734] Fix failing Unit Test and refactor main class --- .../Captcha/Controller/Refresh/Index.php | 33 ++++++++++++++----- .../Unit/Controller/Refresh/IndexTest.php | 7 ++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index 2959fe278d736..968f02a87290f 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Captcha\Controller\Refresh; use Magento\Captcha\Helper\Data as CaptchaHelper; @@ -68,6 +70,26 @@ public function __construct( * @inheritdoc */ public function execute() + { + $formId = $this->getRequestFormId(); + + $captchaModel = $this->captchaHelper->getCaptcha($formId); + $captchaModel->generate(); + + $block = $this->layout->createBlock($captchaModel->getBlockName()); + $block->setFormId($formId)->setIsAjax(true)->toHtml(); + + $result = $this->jsonResultFactory->create(); + + return $result->setData(['imgSrc' => $captchaModel->getImgSrc()]); + } + + /** + * Returns requested Form ID + * + * @return string|null + */ + private function getRequestFormId(): ?string { $formId = $this->request->getPost('formId'); if (null === $formId) { @@ -76,15 +98,10 @@ public function execute() if ($content) { $params = $this->serializer->unserialize($content); } - $formId = isset($params['formId']) ? $params['formId'] : null; - } - $captchaModel = $this->captchaHelper->getCaptcha($formId); - $captchaModel->generate(); - $block = $this->layout->createBlock($captchaModel->getBlockName()); - $block->setFormId($formId)->setIsAjax(true)->toHtml(); + $formId = $params['formId'] ?? null; + } - $result = $this->jsonResultFactory->create(); - return $result->setData(['imgSrc' => $captchaModel->getImgSrc()]); + return $formId !== null ? (string)$formId : null; } } diff --git a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php index 83886806b0820..c99d4a996adec 100644 --- a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php @@ -9,6 +9,7 @@ use Magento\Captcha\Helper\Data as CaptchaHelper; use Magento\Captcha\Model\CaptchaInterface; use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Json as ResultJson; use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; use Magento\Framework\View\Element\BlockInterface; @@ -27,6 +28,9 @@ class IndexTest extends TestCase /** @var MockObject|ResultJsonFactory */ private $jsonResultFactoryMock; + /** @var MockObject|ResultJson */ + private $jsonResultMock; + /** @var MockObject|CaptchaHelper */ private $captchaHelperMock; @@ -54,6 +58,9 @@ protected function setUp() ->setMethods(['setFormId', 'setIsAjax', 'toHtml']) ->getMockForAbstractClass(); $this->jsonResultFactoryMock = $this->createMock(ResultJsonFactory::class); + $this->jsonResultMock = $this->createMock(ResultJson::class); + $this->jsonResultFactoryMock->method('create') + ->willReturn($this->jsonResultMock); $this->jsonSerializerMock = $this->createMock(JsonSerializer::class); $this->captchaHelperMock = $this->createMock(CaptchaHelper::class); From 14ea469a3b30592ead4fe619c780cda1c21aad6e Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 21 Mar 2020 12:42:23 +0100 Subject: [PATCH 512/734] Rollback redundant step --- .../Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml index ec0f6757d48b3..61626c0ea5662 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml @@ -14,6 +14,5 @@ </arguments> <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> - <waitForPageLoad stepKey="waitForProductPage"/> </actionGroup> </actionGroups> From b4b22a40e2a153ace29fb9c105d261d12a63f688 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sat, 21 Mar 2020 17:06:33 +0200 Subject: [PATCH 513/734] fix code style failed tests --- .../Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index 6f2a5b2163fda..fe660f01c8244 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -284,7 +284,7 @@ protected function _initFormValues() * * @param string $html * - * @return string + * @return string */ protected function _afterToHtml($html) { From e535d373ddad664d7484acf841f94f50fd85c8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Wed, 15 Jan 2020 11:37:06 +0100 Subject: [PATCH 514/734] Cleanup ObjectManager usage - Magento_Bundle --- .../Catalog/Product/View/Type/Bundle.php | 85 +- .../Magento/Bundle/Model/LinkManagement.php | 174 ++-- .../Bundle/Model/Product/SaveHandler.php | 35 +- .../Model/ResourceModel/Indexer/Stock.php | 67 +- .../Bundle/Model/ResourceModel/Option.php | 70 +- .../Sales/Order/Pdf/Items/AbstractItems.php | 46 +- .../Sales/Order/Pdf/Items/Creditmemo.php | 133 ++- .../Model/Sales/Order/Pdf/Items/Invoice.php | 129 +-- .../Model/Sales/Order/Pdf/Items/Shipment.php | 135 ++- .../Bundle/Pricing/Adjustment/Calculator.php | 75 +- .../Pricing/Price/BundleOptionPrice.php | 53 +- .../Bundle/Pricing/Price/FinalPrice.php | 65 +- .../Test/Unit/Model/LinkManagementTest.php | 957 ++++++++++-------- .../Order/Pdf/Items/AbstractItemsTest.php | 182 ++-- .../Unit/Pricing/Price/FinalPriceTest.php | 160 +-- 15 files changed, 1261 insertions(+), 1105 deletions(-) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index fa488b073f515..2e9b8ba413af3 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -6,20 +6,30 @@ namespace Magento\Bundle\Block\Catalog\Product\View\Type; use Magento\Bundle\Model\Option; +use Magento\Bundle\Model\Product\Price; +use Magento\Bundle\Model\Product\PriceFactory; +use Magento\Bundle\Model\Product\Type; +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Block\Product\View\AbstractView; use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\Catalog\Pricing\Price\RegularPrice; +use Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; +use Magento\Framework\Json\EncoderInterface; +use Magento\Framework\Locale\FormatInterface; +use Magento\Framework\Stdlib\ArrayUtils; /** * Catalog bundle product info block * * @api - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView +class Bundle extends AbstractView { - /** * @var array */ @@ -33,17 +43,17 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView protected $catalogProduct; /** - * @var \Magento\Bundle\Model\Product\PriceFactory + * @var PriceFactory */ protected $productPriceFactory; /** - * @var \Magento\Framework\Json\EncoderInterface + * @var EncoderInterface */ protected $jsonEncoder; /** - * @var \Magento\Framework\Locale\FormatInterface + * @var FormatInterface */ protected $localeFormat; @@ -63,22 +73,24 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView private $optionsPosition = []; /** - * @param \Magento\Catalog\Block\Product\Context $context - * @param \Magento\Framework\Stdlib\ArrayUtils $arrayUtils + * @param Context $context + * @param ArrayUtils $arrayUtils * @param \Magento\Catalog\Helper\Product $catalogProduct - * @param \Magento\Bundle\Model\Product\PriceFactory $productPrice - * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder - * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @param PriceFactory $productPrice + * @param EncoderInterface $jsonEncoder + * @param FormatInterface $localeFormat * @param array $data + * @param CollectionProcessor|null $catalogRuleProcessor */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Framework\Stdlib\ArrayUtils $arrayUtils, + Context $context, + ArrayUtils $arrayUtils, \Magento\Catalog\Helper\Product $catalogProduct, - \Magento\Bundle\Model\Product\PriceFactory $productPrice, - \Magento\Framework\Json\EncoderInterface $jsonEncoder, - \Magento\Framework\Locale\FormatInterface $localeFormat, - array $data = [] + PriceFactory $productPrice, + EncoderInterface $jsonEncoder, + FormatInterface $localeFormat, + array $data = [], + ?CollectionProcessor $catalogRuleProcessor = null ) { $this->catalogProduct = $catalogProduct; $this->productPriceFactory = $productPrice; @@ -89,22 +101,8 @@ public function __construct( $arrayUtils, $data ); - } - - /** - * Return catalog rule processor or creates processor if it does not exist - * - * @deprecated 100.2.0 - * @return \Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor - */ - private function getCatalogRuleProcessor() - { - if ($this->catalogRuleProcessor === null) { - $this->catalogRuleProcessor = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor::class); - } - - return $this->catalogRuleProcessor; + $this->catalogRuleProcessor = $catalogRuleProcessor ?? ObjectManager::getInstance() + ->get(CollectionProcessor::class); } /** @@ -120,7 +118,7 @@ public function getOptions($stripSelection = false) { if (!$this->options) { $product = $this->getProduct(); - /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + /** @var Type $typeInstance */ $typeInstance = $product->getTypeInstance(); $typeInstance->setStoreFilter($product->getStoreId(), $product); @@ -130,7 +128,7 @@ public function getOptions($stripSelection = false) $typeInstance->getOptionsIds($product), $product ); - $this->getCatalogRuleProcessor()->addPriceData($selectionCollection); + $this->catalogRuleProcessor->addPriceData($selectionCollection); $selectionCollection->addTierPriceData(); $this->options = $optionCollection->appendSelections( @@ -151,10 +149,7 @@ public function getOptions($stripSelection = false) public function hasOptions() { $this->getOptions(); - if (empty($this->options) || !$this->getProduct()->isSalable()) { - return false; - } - return true; + return !(empty($this->options) || !$this->getProduct()->isSalable()); } /** @@ -255,7 +250,7 @@ private function getSelectionItemData(Product $product, Product $selection) ->getOptionSelectionAmount($selection) ->getValue(); - $selection = [ + return [ 'qty' => $qty, 'customQty' => $selection->getSelectionCanChangeQty(), 'optionId' => $selection->getId(), @@ -275,8 +270,6 @@ private function getSelectionItemData(Product $product, Product $selection) 'name' => $selection->getName(), 'canApplyMsrp' => false, ]; - - return $selection; } /** @@ -371,16 +364,16 @@ private function getOptionItemData(Option $option, Product $product, $position) */ private function getConfigData(Product $product, array $options) { - $isFixedPrice = $this->getProduct()->getPriceType() == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED; + $isFixedPrice = $this->getProduct()->getPriceType() == Price::PRICE_TYPE_FIXED; $productAmount = $product ->getPriceInfo() - ->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->getPrice(FinalPrice::PRICE_CODE) ->getPriceWithoutOption(); $baseProductAmount = $product ->getPriceInfo() - ->getPrice(\Magento\Catalog\Pricing\Price\RegularPrice::PRICE_CODE) + ->getPrice(RegularPrice::PRICE_CODE) ->getAmount(); $config = [ diff --git a/app/code/Magento/Bundle/Model/LinkManagement.php b/app/code/Magento/Bundle/Model/LinkManagement.php index 8c85c06c7342d..43a6532a16b2d 100644 --- a/app/code/Magento/Bundle/Model/LinkManagement.php +++ b/app/code/Magento/Bundle/Model/LinkManagement.php @@ -1,36 +1,49 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model; +use Magento\Bundle\Api\Data\LinkInterface; +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterface; +use Magento\Bundle\Api\ProductLinkManagementInterface; +use Magento\Bundle\Model\Product\Type; +use Magento\Bundle\Model\ResourceModel\Bundle; +use Magento\Bundle\Model\ResourceModel\BundleFactory; +use Magento\Bundle\Model\ResourceModel\Option\CollectionFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\StoreManagerInterface; /** + * Class used to manage bundle products links. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterface +class LinkManagement implements ProductLinkManagementInterface { /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @var \Magento\Bundle\Api\Data\LinkInterfaceFactory + * @var LinkInterfaceFactory */ protected $linkFactory; /** - * @var \Magento\Bundle\Model\ResourceModel\BundleFactory + * @var BundleFactory */ protected $bundleFactory; @@ -40,12 +53,17 @@ class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterfa protected $bundleSelection; /** - * @var \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory + * @var CollectionFactory */ protected $optionCollection; /** - * @var \Magento\Framework\Api\DataObjectHelper + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var DataObjectHelper */ protected $dataObjectHelper; @@ -56,21 +74,23 @@ class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterfa /** * @param ProductRepositoryInterface $productRepository - * @param \Magento\Bundle\Api\Data\LinkInterfaceFactory $linkFactory - * @param \Magento\Bundle\Model\SelectionFactory $bundleSelection - * @param \Magento\Bundle\Model\ResourceModel\BundleFactory $bundleFactory - * @param \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory $optionCollection - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param LinkInterfaceFactory $linkFactory + * @param SelectionFactory $bundleSelection + * @param BundleFactory $bundleFactory + * @param CollectionFactory $optionCollection + * @param StoreManagerInterface $storeManager + * @param DataObjectHelper $dataObjectHelper + * @param MetadataPool $metadataPool */ public function __construct( ProductRepositoryInterface $productRepository, - \Magento\Bundle\Api\Data\LinkInterfaceFactory $linkFactory, - \Magento\Bundle\Model\SelectionFactory $bundleSelection, - \Magento\Bundle\Model\ResourceModel\BundleFactory $bundleFactory, - \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory $optionCollection, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + LinkInterfaceFactory $linkFactory, + SelectionFactory $bundleSelection, + BundleFactory $bundleFactory, + CollectionFactory $optionCollection, + StoreManagerInterface $storeManager, + DataObjectHelper $dataObjectHelper, + MetadataPool $metadataPool ) { $this->productRepository = $productRepository; $this->linkFactory = $linkFactory; @@ -79,15 +99,16 @@ public function __construct( $this->optionCollection = $optionCollection; $this->storeManager = $storeManager; $this->dataObjectHelper = $dataObjectHelper; + $this->metadataPool = $metadataPool; } /** - * {@inheritdoc} + * @inheritDoc */ public function getChildren($productSku, $optionId = null) { $product = $this->productRepository->get($productSku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException(__('This is implemented for bundle products only.')); } @@ -96,7 +117,7 @@ public function getChildren($productSku, $optionId = null) if (!$option->getSelections() || ($optionId !== null && $option->getOptionId() != $optionId)) { continue; } - /** @var \Magento\Catalog\Model\Product $selection */ + /** @var Product $selection */ foreach ($option->getSelections() as $selection) { $childrenList[] = $this->buildLink($selection, $product); } @@ -105,32 +126,33 @@ public function getChildren($productSku, $optionId = null) } /** - * {@inheritdoc} + * @inheritDoc */ - public function addChildByProductSku($sku, $optionId, \Magento\Bundle\Api\Data\LinkInterface $linkedProduct) + public function addChildByProductSku($sku, $optionId, LinkInterface $linkedProduct) { - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $this->productRepository->get($sku, true); return $this->addChild($product, $optionId, $linkedProduct); } /** - * {@inheritdoc} + * @inheritDoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function saveChild( $sku, - \Magento\Bundle\Api\Data\LinkInterface $linkedProduct + LinkInterface $linkedProduct ) { $product = $this->productRepository->get($sku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException( __('The product with the "%1" SKU isn\'t a bundle product.', [$product->getSku()]) ); } - /** @var \Magento\Catalog\Model\Product $linkProductModel */ + /** @var Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { throw new InputException(__('The bundle product can\'t contain another composite product.')); @@ -140,7 +162,7 @@ public function saveChild( throw new InputException(__('The product link needs an ID field entered. Enter and try again.')); } - /** @var \Magento\Bundle\Model\Selection $selectionModel */ + /** @var Selection $selectionModel */ $selectionModel = $this->bundleSelection->create(); $selectionModel->load($linkedProduct->getId()); if (!$selectionModel->getId()) { @@ -151,7 +173,7 @@ public function saveChild( ) ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $selectionModel = $this->mapProductLinkToSelectionModel( $selectionModel, $linkedProduct, @@ -169,17 +191,21 @@ public function saveChild( } /** - * @param \Magento\Bundle\Model\Selection $selectionModel - * @param \Magento\Bundle\Api\Data\LinkInterface $productLink + * Fill selection model with product link data + * + * @param Selection $selectionModel + * @param LinkInterface $productLink * @param string $linkedProductId * @param string $parentProductId - * @return \Magento\Bundle\Model\Selection + * + * @return Selection + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function mapProductLinkToSelectionModel( - \Magento\Bundle\Model\Selection $selectionModel, - \Magento\Bundle\Api\Data\LinkInterface $productLink, + Selection $selectionModel, + LinkInterface $productLink, $linkedProductId, $parentProductId ) { @@ -214,21 +240,22 @@ protected function mapProductLinkToSelectionModel( } /** - * {@inheritdoc} + * @inheritDoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function addChild( - \Magento\Catalog\Api\Data\ProductInterface $product, + ProductInterface $product, $optionId, - \Magento\Bundle\Api\Data\LinkInterface $linkedProduct + LinkInterface $linkedProduct ) { - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException( __('The product with the "%1" SKU isn\'t a bundle product.', $product->getSku()) ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $options = $this->optionCollection->create(); @@ -246,10 +273,10 @@ public function addChild( ); } - /* @var $resource \Magento\Bundle\Model\ResourceModel\Bundle */ + /* @var $resource Bundle */ $resource = $this->bundleFactory->create(); $selections = $resource->getSelectionsData($product->getData($linkField)); - /** @var \Magento\Catalog\Model\Product $linkProductModel */ + /** @var Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { throw new InputException(__('The bundle product can\'t contain another composite product.')); @@ -267,9 +294,9 @@ public function addChild( [$linkedProduct->getSku(), $product->getSku()] ) ); - } else { - return $this->bundleSelection->create()->load($linkProductModel->getEntityId()); } + + return $this->bundleSelection->create()->load($linkProductModel->getEntityId()); } } } @@ -294,13 +321,13 @@ public function addChild( } /** - * {@inheritdoc} + * @inheritDoc */ public function removeChild($sku, $optionId, $childSku) { $product = $this->productRepository->get($sku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException(__('The product with the "%1" SKU isn\'t a bundle product.', $sku)); } @@ -308,7 +335,7 @@ public function removeChild($sku, $optionId, $childSku) $usedProductIds = []; $removeSelectionIds = []; foreach ($this->getOptions($product) as $option) { - /** @var \Magento\Bundle\Model\Selection $selection */ + /** @var Selection $selection */ foreach ($option->getSelections() as $selection) { if ((strcasecmp($selection->getSku(), $childSku) == 0) && ($selection->getOptionId() == $optionId)) { $removeSelectionIds[] = $selection->getSelectionId(); @@ -319,12 +346,12 @@ public function removeChild($sku, $optionId, $childSku) } } if (empty($removeSelectionIds)) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + throw new NoSuchEntityException( __("The bundle product doesn't exist. Review your request and try again.") ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - /* @var $resource \Magento\Bundle\Model\ResourceModel\Bundle */ + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + /* @var $resource Bundle */ $resource = $this->bundleFactory->create(); $resource->dropAllUnneededSelections($product->getData($linkField), $excludeSelectionIds); $resource->removeProductRelations($product->getData($linkField), array_unique($usedProductIds)); @@ -333,26 +360,29 @@ public function removeChild($sku, $optionId, $childSku) } /** - * @param \Magento\Catalog\Model\Product $selection - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Bundle\Api\Data\LinkInterface + * Build bundle link between two products + * + * @param Product $selection + * @param Product $product + * + * @return LinkInterface */ - private function buildLink(\Magento\Catalog\Model\Product $selection, \Magento\Catalog\Model\Product $product) + private function buildLink(Product $selection, Product $product) { $selectionPriceType = $selectionPrice = null; - /** @var \Magento\Bundle\Model\Selection $product */ + /** @var Selection $product */ if ($product->getPriceType()) { $selectionPriceType = $selection->getSelectionPriceType(); $selectionPrice = $selection->getSelectionPriceValue(); } - /** @var \Magento\Bundle\Api\Data\LinkInterface $link */ + /** @var LinkInterface $link */ $link = $this->linkFactory->create(); $this->dataObjectHelper->populateWithArray( $link, $selection->getData(), - \Magento\Bundle\Api\Data\LinkInterface::class + LinkInterface::class ); $link->setIsDefault($selection->getIsDefault()) ->setId($selection->getSelectionId()) @@ -364,12 +394,15 @@ private function buildLink(\Magento\Catalog\Model\Product $selection, \Magento\C } /** - * @param \Magento\Catalog\Api\Data\ProductInterface $product - * @return \Magento\Bundle\Api\Data\OptionInterface[] + * Get bundle product options + * + * @param ProductInterface $product + * + * @return OptionInterface[] */ - private function getOptions(\Magento\Catalog\Api\Data\ProductInterface $product) + private function getOptions(ProductInterface $product) { - /** @var \Magento\Bundle\Model\Product\Type $productTypeInstance */ + /** @var Type $productTypeInstance */ $productTypeInstance = $product->getTypeInstance(); $productTypeInstance->setStoreFilter( $product->getStoreId(), @@ -383,19 +416,6 @@ private function getOptions(\Magento\Catalog\Api\Data\ProductInterface $product) $product ); - $options = $optionCollection->appendSelections($selectionCollection, true); - return $options; - } - - /** - * Get MetadataPool instance - * @return MetadataPool - */ - private function getMetadataPool() - { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; + return $optionCollection->appendSelections($selectionCollection, true); } } diff --git a/app/code/Magento/Bundle/Model/Product/SaveHandler.php b/app/code/Magento/Bundle/Model/Product/SaveHandler.php index 99e8188146bbb..8c2727a71aac1 100644 --- a/app/code/Magento/Bundle/Model/Product/SaveHandler.php +++ b/app/code/Magento/Bundle/Model/Product/SaveHandler.php @@ -3,58 +3,59 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Product; +use Magento\Bundle\Api\Data\OptionInterface; use Magento\Bundle\Model\Option\SaveAction; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository; use Magento\Bundle\Api\ProductLinkManagementInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\EntityManager\Operation\ExtensionInterface; /** - * Class SaveHandler + * Bundle product save handler */ class SaveHandler implements ExtensionInterface { /** * @var OptionRepository */ - protected $optionRepository; + private $optionRepository; /** * @var ProductLinkManagementInterface */ - protected $productLinkManagement; + private $productLinkManagement; /** - * @var MetadataPool + * @var SaveAction */ - private $metadataPool; + private $optionSave; /** - * @var SaveAction + * @var MetadataPool */ - private $optionSave; + private $metadataPool; /** * @param OptionRepository $optionRepository * @param ProductLinkManagementInterface $productLinkManagement * @param SaveAction $optionSave - * @param MetadataPool|null $metadataPool + * @param MetadataPool $metadataPool */ public function __construct( OptionRepository $optionRepository, ProductLinkManagementInterface $productLinkManagement, SaveAction $optionSave, - MetadataPool $metadataPool = null + MetadataPool $metadataPool ) { $this->optionRepository = $optionRepository; $this->productLinkManagement = $productLinkManagement; $this->optionSave = $optionSave; - $this->metadataPool = $metadataPool - ?: ObjectManager::getInstance()->get(MetadataPool::class); + $this->metadataPool = $metadataPool; } /** @@ -69,7 +70,7 @@ public function __construct( */ public function execute($entity, $arguments = []) { - /** @var \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions */ + /** @var OptionInterface[] $bundleProductOptions */ $bundleProductOptions = $entity->getExtensionAttributes()->getBundleProductOptions() ?: []; //Only processing bundle products. if ($entity->getTypeId() !== Type::TYPE_CODE || empty($bundleProductOptions)) { @@ -101,7 +102,8 @@ public function execute($entity, $arguments = []) * Remove option product links * * @param string $entitySku - * @param \Magento\Bundle\Api\Data\OptionInterface $option + * @param OptionInterface $option + * * @return void */ protected function removeOptionLinks($entitySku, $option) @@ -120,6 +122,7 @@ protected function removeOptionLinks($entitySku, $option) * @param object $entity * @param array $options * @param array $newOptionsIds + * * @return void */ private function saveOptions($entity, array $options, array $newOptionsIds = []): void @@ -137,6 +140,7 @@ private function saveOptions($entity, array $options, array $newOptionsIds = []) * Get options ids from array of the options entities. * * @param array $options + * * @return array */ private function getOptionIds(array $options): array @@ -144,7 +148,7 @@ private function getOptionIds(array $options): array $optionIds = []; if (!empty($options)) { - /** @var \Magento\Bundle\Api\Data\OptionInterface $option */ + /** @var OptionInterface $option */ foreach ($options as $option) { if ($option->getOptionId()) { $optionIds[] = $option->getOptionId(); @@ -161,6 +165,7 @@ private function getOptionIds(array $options): array * @param ProductInterface $entity * @param array $existingOptionsIds * @param array $optionIds + * * @return void */ private function processRemovedOptions(ProductInterface $entity, array $existingOptionsIds, array $optionIds): void diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php index 803fbae067e9d..6808081506dd7 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php @@ -3,65 +3,65 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\ResourceModel\Indexer; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full; -use Magento\Framework\App\ObjectManager; +use Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Indexer\Table\StrategyInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; /** * Bundle Stock Status Indexer Resource Model * - * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock +class Stock extends DefaultStock { /** - * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher + * @var ActiveTableSwitcher */ private $activeTableSwitcher; /** - * @var \Magento\Bundle\Model\ResourceModel\Indexer\StockStatusSelectBuilder + * @var StockStatusSelectBuilder */ private $stockStatusSelectBuilder; /** - * @var \Magento\Bundle\Model\ResourceModel\Indexer\BundleOptionStockDataSelectBuilder + * @var BundleOptionStockDataSelectBuilder */ private $bundleOptionStockDataSelectBuilder; /** - * Class constructor - * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy - * @param \Magento\Eav\Model\Config $eavConfig - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param null $connectionName - * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher - * @param StockStatusSelectBuilder|null $stockStatusSelectBuilder - * @param BundleOptionStockDataSelectBuilder|null $bundleOptionStockDataSelectBuilder + * @param Context $context + * @param StrategyInterface $tableStrategy + * @param Config $eavConfig + * @param ScopeConfigInterface $scopeConfig + * @param ActiveTableSwitcher $activeTableSwitcher + * @param StockStatusSelectBuilder $stockStatusSelectBuilder + * @param BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder + * @param string $connectionName */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy, - \Magento\Eav\Model\Config $eavConfig, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - $connectionName = null, - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null, - StockStatusSelectBuilder $stockStatusSelectBuilder = null, - BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder = null + Context $context, + StrategyInterface $tableStrategy, + Config $eavConfig, + ScopeConfigInterface $scopeConfig, + ActiveTableSwitcher $activeTableSwitcher, + StockStatusSelectBuilder $stockStatusSelectBuilder, + BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder, + $connectionName = null ) { parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName); - $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class); - - $this->stockStatusSelectBuilder = $stockStatusSelectBuilder ?: ObjectManager::getInstance() - ->get(StockStatusSelectBuilder::class); - - $this->bundleOptionStockDataSelectBuilder = $bundleOptionStockDataSelectBuilder ?: ObjectManager::getInstance() - ->get(BundleOptionStockDataSelectBuilder::class); + $this->activeTableSwitcher = $activeTableSwitcher; + $this->stockStatusSelectBuilder = $stockStatusSelectBuilder; + $this->bundleOptionStockDataSelectBuilder = $bundleOptionStockDataSelectBuilder; } /** @@ -79,6 +79,7 @@ protected function _getBundleOptionTable() * * @param int|array $entityIds * @param bool $usePrimaryTable use primary or temporary index table + * * @return $this */ protected function _prepareBundleOptionStockData($entityIds = null, $usePrimaryTable = false) @@ -122,6 +123,7 @@ protected function _prepareBundleOptionStockData($entityIds = null, $usePrimaryT * * @param int|array $entityIds * @param bool $usePrimaryTable use primary or temporary index table + * * @return \Magento\Framework\DB\Select */ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false) @@ -171,6 +173,7 @@ protected function _prepareIndexTable($entityIds = null) * Update Stock status index by product ids * * @param array|int $entityIds + * * @return $this */ protected function _updateIndex($entityIds) diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option.php b/app/code/Magento/Bundle/Model/ResourceModel/Option.php index 7babd0b349f02..1260233b5bcb3 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option.php @@ -3,20 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\ResourceModel; +use Magento\Bundle\Model\Option\Validator; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; /** * Bundle Option Resource Model */ -class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Option extends AbstractDb { /** - * @var \Magento\Bundle\Model\Option\Validator + * @var Validator */ private $validator; @@ -31,22 +37,23 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb private $entityManager; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Bundle\Model\Option\Validator $validator + * @param Context $context + * @param Validator $validator + * @param MetadataPool $metadataPool + * @param EntityManager $entityManager * @param string $connectionName - * @param EntityManager|null $entityManager */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Bundle\Model\Option\Validator $validator, - $connectionName = null, - EntityManager $entityManager = null + Context $context, + Validator $validator, + MetadataPool $metadataPool, + EntityManager $entityManager, + $connectionName = null ) { parent::__construct($context, $connectionName); $this->validator = $validator; - - $this->entityManager = $entityManager - ?: ObjectManager::getInstance()->get(EntityManager::class); + $this->metadataPool = $metadataPool; + $this->entityManager = $entityManager; } /** @@ -60,7 +67,10 @@ protected function _construct() } /** + * Remove selections by option id + * * @param int $optionId + * * @return int */ public function removeOptionSelections($optionId) @@ -74,10 +84,11 @@ public function removeOptionSelections($optionId) /** * After save process * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) + protected function _afterSave(AbstractModel $object) { parent::_afterSave($object); @@ -90,7 +101,7 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) $connection = $this->getConnection(); $connection->delete($this->getTable('catalog_product_bundle_option_value'), $condition); - $data = new \Magento\Framework\DataObject(); + $data = new DataObject(); $data->setOptionId($object->getId()) ->setStoreId($object->getStoreId()) ->setParentProductId($object->getParentId()) @@ -112,10 +123,10 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) /** * After delete process * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return $this */ - protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object) + protected function _afterDelete(AbstractModel $object) { parent::_afterDelete($object); @@ -136,6 +147,7 @@ protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object) * * @param int $productId * @param int $storeId + * * @return array */ public function getSearchableData($productId, $storeId) @@ -148,7 +160,7 @@ public function getSearchableData($productId, $storeId) 'option_title_default.title' ); $bind = ['store_id' => $storeId, 'product_id' => $productId]; - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $select = $connection->select() ->from( ['opt' => $this->getMainTable()], @@ -180,7 +192,7 @@ public function getSearchableData($productId, $storeId) } /** - * {@inheritdoc} + * @inheritDoc */ public function getValidationRulesBeforeSave() { @@ -188,21 +200,9 @@ public function getValidationRulesBeforeSave() } /** - * Get MetadataPool instance - * @return MetadataPool - */ - private function getMetadataPool() - { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; - } - - /** - * {@inheritdoc} + * @inheritDoc */ - public function save(\Magento\Framework\Model\AbstractModel $object) + public function save(AbstractModel $object) { $this->entityManager->save($object); diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php index 30e37e54a21db..441bc0dd9de89 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php @@ -6,8 +6,14 @@ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; use Magento\Catalog\Model\Product\Type\AbstractType; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Tax\Helper\Data; /** * Order pdf items renderer @@ -24,30 +30,28 @@ abstract class AbstractItems extends \Magento\Sales\Model\Order\Pdf\Items\Abstra private $serializer; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json $serializer */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->serializer = $serializer; parent::__construct( $context, $registry, diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php index 3d7d05396e0fc..1ed3fc76ddee0 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order creditmemo pdf default items renderer @@ -16,36 +25,34 @@ class Creditmemo extends AbstractItems /** * Core string * - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param Json $serializer + * @param StringUtils $string + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $string, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + Json $serializer, + StringUtils $string, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $string; parent::__construct( @@ -54,10 +61,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -98,19 +105,17 @@ public function draw() } // draw selection attributes - if ($childItem->getOrderItem()->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 38, true, true), - 'feed' => $x, - ]; + if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 38, true, true), + 'feed' => $x, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; - $prevOptionId = $attributes['option_id']; - } + $line = []; + $prevOptionId = $attributes['option_id']; } // draw product titles @@ -152,7 +157,7 @@ public function draw() // draw QTY $text = $childItem->getQty() * 1; $line[] = [ - 'text' => $childItem->getQty() * 1, + 'text' => $text, 'feed' => $x, 'font' => 'bold', 'align' => 'center', @@ -177,40 +182,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 40, - true, - true - ), - 'font' => 'italic', - 'feed' => $leftBound, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 30, true, true) as $subValue) { - $text[] = $subValue; - } - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 40, + true, + true + ), + 'font' => 'italic', + 'feed' => $leftBound, + ]; - $lines[][] = ['text' => $text, 'feed' => $leftBound + 5]; + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 30, true, true) as $subValue) { + $text[] = $subValue; + } } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => $leftBound + 5]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index 1827c2249dda3..64e9f56dd65bc 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order invoice pdf default items renderer @@ -14,36 +23,36 @@ class Invoice extends AbstractItems { /** - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** * Constructor * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $coreString - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param StringUtils $coreString + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $coreString, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + StringUtils $coreString, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $coreString; parent::__construct( @@ -52,10 +61,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -94,19 +103,17 @@ public function draw() $drawItems[$optionId] = ['lines' => [], 'height' => 15]; } - if ($childItem->getOrderItem()->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 45, true, true), - 'feed' => 35, - ]; + if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 45, true, true), + 'feed' => 35, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; - $prevOptionId = $attributes['option_id']; - } + $line = []; + $prevOptionId = $attributes['option_id']; } /* in case Product name is longer than 80 chars - it is written in a few lines */ @@ -146,40 +153,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 40, - true, - true - ), - 'font' => 'italic', - 'feed' => 35, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 30, true, true) as $subValue) { - $text[] = $subValue; - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 40, + true, + true + ), + 'font' => 'italic', + 'feed' => 35, + ]; + + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 30, true, true) as $subValue) { + $text[] = $subValue; } - - $lines[][] = ['text' => $text, 'feed' => 40]; } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => 40]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php index 08ccf0618d03e..8a293b63541a2 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order shipment pdf items renderer @@ -14,36 +23,34 @@ class Shipment extends AbstractItems { /** - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param StringUtils $string + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $string, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + StringUtils $string, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $string; parent::__construct( @@ -52,10 +59,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -95,24 +102,22 @@ public function draw() $drawItems[$optionId] = ['lines' => [], 'height' => 15]; } - if ($childItem->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 60, true, true), - 'feed' => 60, - ]; + if ($childItem->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 60, true, true), + 'feed' => 60, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; + $line = []; - $prevOptionId = $attributes['option_id']; - } + $prevOptionId = $attributes['option_id']; } - if ($this->isShipmentSeparately() && $childItem->getParentItem() || - !$this->isShipmentSeparately() && !$childItem->getParentItem() + if (($this->isShipmentSeparately() && $childItem->getParentItem()) || + (!$this->isShipmentSeparately() && !$childItem->getParentItem()) ) { if (isset($shipItems[$childItem->getId()])) { $qty = $shipItems[$childItem->getId()]->getQty() * 1; @@ -153,40 +158,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 70, - true, - true - ), - 'font' => 'italic', - 'feed' => 60, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 50, true, true) as $subValue) { - $text[] = $subValue; - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 70, + true, + true + ), + 'font' => 'italic', + 'feed' => 60, + ]; + + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 50, true, true) as $subValue) { + $text[] = $subValue; } - - $lines[][] = ['text' => $text, 'feed' => 65]; } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => 65]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php index 04a6ee0bd459b..3051394eaf512 100644 --- a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php +++ b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php @@ -3,14 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Pricing\Adjustment; +use Magento\Bundle\Model\Option; use Magento\Bundle\Model\Product\Price; use Magento\Bundle\Pricing\Price\BundleSelectionFactory; +use Magento\Bundle\Pricing\Price\BundleSelectionPrice; use Magento\Catalog\Model\Product; use Magento\Framework\Pricing\Adjustment\Calculator as CalculatorBase; use Magento\Framework\Pricing\Amount\AmountFactory; +use Magento\Framework\Pricing\Amount\AmountInterface; use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Store\Model\Store; @@ -51,7 +55,7 @@ class Calculator implements BundleCalculatorInterface protected $priceCurrency; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface[] + * @var AmountInterface[] */ private $optionAmount = []; @@ -66,7 +70,7 @@ class Calculator implements BundleCalculatorInterface * @param BundleSelectionFactory $bundleSelectionFactory * @param TaxHelper $taxHelper * @param PriceCurrencyInterface $priceCurrency - * @param SelectionPriceListProviderInterface|null $selectionPriceListProvider + * @param SelectionPriceListProviderInterface $selectionPriceListProvider */ public function __construct( CalculatorBase $calculator, @@ -74,7 +78,7 @@ public function __construct( BundleSelectionFactory $bundleSelectionFactory, TaxHelper $taxHelper, PriceCurrencyInterface $priceCurrency, - SelectionPriceListProviderInterface $selectionPriceListProvider = null + SelectionPriceListProviderInterface $selectionPriceListProvider ) { $this->calculator = $calculator; $this->amountFactory = $amountFactory; @@ -91,7 +95,9 @@ public function __construct( * @param SaleableInterface $saleableItem * @param null|bool|string|array $exclude * @param null|array $context - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getAmount($amount, SaleableInterface $saleableItem, $exclude = null, $context = []) @@ -105,7 +111,8 @@ public function getAmount($amount, SaleableInterface $saleableItem, $exclude = n * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMinRegularAmount($amount, Product $saleableItem, $exclude = null) { @@ -118,7 +125,8 @@ public function getMinRegularAmount($amount, Product $saleableItem, $exclude = n * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMaxAmount($amount, Product $saleableItem, $exclude = null) { @@ -131,7 +139,8 @@ public function getMaxAmount($amount, Product $saleableItem, $exclude = null) * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMaxRegularAmount($amount, Product $saleableItem, $exclude = null) { @@ -146,7 +155,8 @@ public function getMaxRegularAmount($amount, Product $saleableItem, $exclude = n * @param bool $searchMin * @param float $baseAmount * @param bool $useRegularPrice - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getOptionsAmount( Product $saleableItem, @@ -173,7 +183,8 @@ public function getOptionsAmount( * * @param float $amount * @param Product $saleableItem - * @return \Magento\Framework\Pricing\Amount\AmountInterface|void + * + * @return AmountInterface|void */ public function getAmountWithoutOption($amount, Product $saleableItem) { @@ -194,29 +205,13 @@ public function getAmountWithoutOption($amount, Product $saleableItem) */ protected function getSelectionAmounts(Product $bundleProduct, $searchMin, $useRegularPrice = false) { - return $this->getSelectionPriceListProvider()->getPriceList($bundleProduct, $searchMin, $useRegularPrice); - } - - /** - * Get selection price list provider. - * - * @return SelectionPriceListProviderInterface - * @deprecated 100.2.0 - */ - private function getSelectionPriceListProvider() - { - if (null === $this->selectionPriceListProvider) { - $this->selectionPriceListProvider = \Magento\Framework\App\ObjectManager::getInstance() - ->get(SelectionPriceListProviderInterface::class); - } - - return $this->selectionPriceListProvider; + return $this->selectionPriceListProvider->getPriceList($bundleProduct, $searchMin, $useRegularPrice); } /** * Check this option if it should be skipped * - * @param \Magento\Bundle\Model\Option $option + * @param Option $option * @param bool $canSkipRequiredOption * @return bool * @deprecated 100.2.0 @@ -265,9 +260,9 @@ protected function getBundleOptions(Product $saleableItem) * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function calculateBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude = null) { @@ -282,14 +277,14 @@ public function calculateBundleAmount($basePriceValue, $bundleProduct, $selectio * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ protected function calculateFixedBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude) { $fullAmount = $basePriceValue; - /** @var $option \Magento\Bundle\Model\Option */ + /** @var $option Option */ foreach ($selectionPriceList as $selectionPrice) { $fullAmount += ($selectionPrice->getValue() * $selectionPrice->getQuantity()); } @@ -301,9 +296,9 @@ protected function calculateFixedBundleAmount($basePriceValue, $bundleProduct, $ * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude) @@ -328,7 +323,7 @@ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $store = $bundleProduct->getStore(); $roundingMethod = $this->taxHelper->getCalculationAlgorithm($store); foreach ($amountList as $amountInfo) { - /** @var \Magento\Framework\Pricing\Amount\AmountInterface $itemAmount */ + /** @var AmountInterface $itemAmount */ $itemAmount = $amountInfo['amount']; $qty = $amountInfo['quantity']; @@ -366,10 +361,10 @@ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, /** * Create selection price list for the retrieved options * - * @param \Magento\Bundle\Model\Option $option + * @param Option $option * @param Product $bundleProduct * @param bool $useRegularPrice - * @return \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] + * @return BundleSelectionPrice[] */ public function createSelectionPriceList($option, $bundleProduct, $useRegularPrice = false) { @@ -399,10 +394,10 @@ public function createSelectionPriceList($option, $bundleProduct, $useRegularPri /** * Find minimal or maximal price for existing options * - * @param \Magento\Bundle\Model\Option $option - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param Option $option + * @param BundleSelectionPrice[] $selectionPriceList * @param bool $searchMin - * @return \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] + * @return BundleSelectionPrice[] * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function processOptions($option, $selectionPriceList, $searchMin = true) diff --git a/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php b/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php index 1c724caaa28d8..a76112da0ca4e 100644 --- a/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php @@ -3,12 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Pricing\Price; +use Magento\Bundle\Model\ResourceModel\Option\Collection; +use Magento\Bundle\Model\Selection; use Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface; use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\Amount\AmountInterface; use Magento\Framework\Pricing\Price\AbstractPrice; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Pricing\PriceCurrencyInterface; /** * Bundle option price model with final price. @@ -25,12 +30,6 @@ class BundleOptionPrice extends AbstractPrice implements BundleOptionPriceInterf */ protected $calculator; - /** - * @var BundleSelectionFactory - * @deprecated - */ - protected $selectionFactory; - /** * @var float|bool|null */ @@ -45,26 +44,23 @@ class BundleOptionPrice extends AbstractPrice implements BundleOptionPriceInterf * @param Product $saleableItem * @param float $quantity * @param BundleCalculatorInterface $calculator - * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency - * @param BundleSelectionFactory $bundleSelectionFactory - * @param BundleOptions|null $bundleOptions + * @param PriceCurrencyInterface $priceCurrency + * @param BundleOptions $bundleOptions */ public function __construct( Product $saleableItem, $quantity, BundleCalculatorInterface $calculator, - \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, - BundleSelectionFactory $bundleSelectionFactory, - BundleOptions $bundleOptions = null + PriceCurrencyInterface $priceCurrency, + BundleOptions $bundleOptions ) { - $this->selectionFactory = $bundleSelectionFactory; parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); $this->product->setQty($this->quantity); - $this->bundleOptions = $bundleOptions ?: ObjectManager::getInstance()->get(BundleOptions::class); + $this->bundleOptions = $bundleOptions; } /** - * {@inheritdoc} + * @inheritDoc */ public function getValue() { @@ -75,25 +71,10 @@ public function getValue() return $this->value; } - /** - * Getter for maximal price of options. - * - * @return bool|float - * @deprecated - */ - public function getMaxValue() - { - if (null === $this->maximalPrice) { - $this->maximalPrice = $this->bundleOptions->calculateOptions($this->product, false); - } - - return $this->maximalPrice; - } - /** * Get Options with attached Selections collection. * - * @return \Magento\Bundle\Model\ResourceModel\Option\Collection + * @return Collection */ public function getOptions() { @@ -103,8 +84,9 @@ public function getOptions() /** * Get selection amount. * - * @param \Magento\Bundle\Model\Selection $selection - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @param Selection $selection + * + * @return AmountInterface */ public function getOptionSelectionAmount($selection) { @@ -119,6 +101,7 @@ public function getOptionSelectionAmount($selection) * Calculate maximal or minimal options value. * * @param bool $searchMin + * * @return bool|float */ protected function calculateOptions($searchMin = true) @@ -129,7 +112,7 @@ protected function calculateOptions($searchMin = true) /** * Get minimal amount of bundle price with options * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getAmount() { diff --git a/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php b/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php index c41f6a4868ab1..66f618c0fcfa5 100644 --- a/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php @@ -3,15 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Pricing\Price; -use Magento\Catalog\Model\Product; -use Magento\Framework\Pricing\Adjustment\CalculatorInterface; -use Magento\Catalog\Pricing\Price\CustomOptionPrice; use Magento\Bundle\Model\Product\Price; -use Magento\Framework\App\ObjectManager; use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\CustomOptionPrice; +use Magento\Framework\Pricing\Adjustment\CalculatorInterface; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; /** * Final price model @@ -19,27 +21,27 @@ class FinalPrice extends \Magento\Catalog\Pricing\Price\FinalPrice implements FinalPriceInterface { /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $maximalPrice; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $minimalPrice; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $priceWithoutOption; /** - * @var \Magento\Bundle\Pricing\Price\BundleOptionPrice + * @var BundleOptionPrice */ protected $bundleOptionPrice; /** - * @var \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface + * @var ProductCustomOptionRepositoryInterface */ private $productOptionRepository; @@ -47,15 +49,18 @@ class FinalPrice extends \Magento\Catalog\Pricing\Price\FinalPrice implements Fi * @param Product $saleableItem * @param float $quantity * @param CalculatorInterface $calculator - * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + * @param PriceCurrencyInterface $priceCurrency + * @param ProductCustomOptionRepositoryInterface $productOptionRepository */ public function __construct( Product $saleableItem, $quantity, CalculatorInterface $calculator, - \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + PriceCurrencyInterface $priceCurrency, + ProductCustomOptionRepositoryInterface $productOptionRepository ) { parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); + $this->productOptionRepository = $productOptionRepository; } /** @@ -65,49 +70,33 @@ public function __construct( */ public function getValue() { - return parent::getValue() + - $this->getBundleOptionPrice()->getValue(); + return parent::getValue() + $this->getBundleOptionPrice()->getValue(); } /** * Returns max price * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getMaximalPrice() { if (!$this->maximalPrice) { $price = $this->getBasePrice()->getValue(); if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) { - /** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */ + /** @var CustomOptionPrice $customOptionPrice */ $customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE); $price += $customOptionPrice->getCustomOptionRange(false); } $this->maximalPrice = $this->calculator->getMaxAmount($price, $this->product); } - return $this->maximalPrice; - } - /** - * Return ProductCustomOptionRepository - * - * @return ProductCustomOptionRepositoryInterface - * @deprecated 100.1.0 - */ - private function getProductOptionRepository() - { - if (!$this->productOptionRepository) { - $this->productOptionRepository = ObjectManager::getInstance()->get( - ProductCustomOptionRepositoryInterface::class - ); - } - return $this->productOptionRepository; + return $this->maximalPrice; } /** * Returns min price * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getMinimalPrice() { @@ -117,7 +106,7 @@ public function getMinimalPrice() /** * Returns price amount * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getAmount() { @@ -125,7 +114,7 @@ public function getAmount() $price = parent::getValue(); if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) { $this->loadProductCustomOptions(); - /** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */ + /** @var CustomOptionPrice $customOptionPrice */ $customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE); $price += $customOptionPrice->getCustomOptionRange(true); } @@ -143,7 +132,7 @@ private function loadProductCustomOptions() { if (!$this->product->getOptions()) { $options = []; - foreach ($this->getProductOptionRepository()->getProductOptions($this->product) as $option) { + foreach ($this->productOptionRepository->getProductOptions($this->product) as $option) { $option->setProduct($this->product); $options[] = $option; } @@ -152,9 +141,9 @@ private function loadProductCustomOptions() } /** - * get bundle product price without any option + * Get bundle product price without any option * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getPriceWithoutOption() { @@ -167,7 +156,7 @@ public function getPriceWithoutOption() /** * Returns option price * - * @return \Magento\Bundle\Pricing\Price\BundleOptionPrice + * @return BundleOptionPrice */ protected function getBundleOptionPrice() { diff --git a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php index ccc8c52d5022f..d67613a46ad66 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php @@ -7,174 +7,198 @@ namespace Magento\Bundle\Test\Unit\Model; +use Exception; +use Magento\Bundle\Api\Data\LinkInterface; +use Magento\Bundle\Api\Data\LinkInterfaceFactory; use Magento\Bundle\Model\LinkManagement; +use Magento\Bundle\Model\Option; +use Magento\Bundle\Model\ResourceModel\Bundle; +use Magento\Bundle\Model\ResourceModel\BundleFactory; +use Magento\Bundle\Model\ResourceModel\Option\Collection as OptionCollection; +use Magento\Bundle\Model\ResourceModel\Option\CollectionFactory as OptionCollectionFactory; +use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; +use Magento\Bundle\Model\Selection; +use Magento\Bundle\Model\SelectionFactory; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\EntityManager\EntityMetadata; +use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class LinkManagementTest + * Test class for \Magento\Bundle\Model\LinkManagement * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LinkManagementTest extends \PHPUnit\Framework\TestCase +class LinkManagementTest extends TestCase { /** - * @var \Magento\Bundle\Model\LinkManagement + * @var LinkManagement */ - protected $model; + private $model; /** - * @var \Magento\Catalog\Model\ProductRepository|\PHPUnit_Framework_MockObject_MockObject + * @var ProductRepository|MockObject */ - protected $productRepository; + private $productRepository; /** - * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $product; + private $product; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkInterfaceFactory|MockObject */ - protected $linkFactory; + private $linkFactory; /** - * @var \Magento\Catalog\Model\Product\Type\Interceptor|\PHPUnit_Framework_MockObject_MockObject + * @var Type|MockObject */ - protected $productType; + private $productType; /** - * @var \Magento\Bundle\Model\ResourceModel\Option\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var OptionCollection|MockObject */ - protected $optionCollection; + private $optionCollection; /** - * @var \Magento\Bundle\Model\ResourceModel\Selection\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var SelectionCollection|MockObject */ - protected $selectionCollection; + private $selectionCollection; /** - * @var \Magento\Bundle\Model\Option|\PHPUnit_Framework_MockObject_MockObject + * @var Option|MockObject */ - protected $option; + private $option; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\SelectionFactory + * @var SelectionFactory|MockObject */ - protected $bundleSelectionMock; + private $bundleSelectionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\ResourceModel\BundleFactory + * @var BundleFactory|MockObject */ - protected $bundleFactoryMock; + private $bundleFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory + * @var OptionCollectionFactory|MockObject */ - protected $optionCollectionFactoryMock; + private $optionCollectionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface|MockObject */ - protected $storeManagerMock; + private $storeManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkInterface|MockObject */ - protected $link; + private $link; /** - * @var int + * @var MockObject */ - protected $storeId = 2; + private $dataObjectHelperMock; /** - * @var array + * @var MetadataPool|MockObject */ - protected $optionIds = [1, 2, 3]; + private $metadataPoolMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var EntityMetadata|MockObject */ - protected $dataObjectHelperMock; + private $metadataMock; /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var int */ - protected $metadataPoolMock; + private $storeId = 2; /** - * @var \Magento\Framework\EntityManager\EntityMetadata|\PHPUnit_Framework_MockObject_MockObject + * @var array */ - protected $metadataMock; + private $optionIds = [1, 2, 3]; /** * @var string */ - protected $linkField = 'product_id'; + private $linkField = 'product_id'; + /** + * @inheritDoc + */ protected function setUp() { $helper = new ObjectManager($this); - $this->productRepository = $this->getMockBuilder(\Magento\Catalog\Model\ProductRepository::class) + $this->productRepository = $this->getMockBuilder(ProductRepository::class) ->setMethods(['get']) ->disableOriginalConstructor() ->getMock(); - $this->productType = $this->getMockBuilder(\Magento\Bundle\Model\Product\Type\Interceptor::class) + $this->productType = $this->getMockBuilder(\Magento\Bundle\Model\Product\Type::class) ->setMethods(['getOptionsCollection', 'setStoreFilter', 'getSelectionsCollection', 'getOptionsIds']) ->disableOriginalConstructor() ->getMock(); - $this->option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class) + $this->option = $this->getMockBuilder(Option::class) ->setMethods(['getSelections', 'getOptionId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $this->optionCollection = $this->getMockBuilder(\Magento\Bundle\Model\ResourceModel\Option\Collection::class) + $this->optionCollection = $this->getMockBuilder(OptionCollection::class) ->setMethods(['appendSelections']) ->disableOriginalConstructor() ->getMock(); $this->selectionCollection = $this->getMockBuilder( - \Magento\Bundle\Model\ResourceModel\Selection\Collection::class + SelectionCollection::class )->disableOriginalConstructor()->getMock(); - $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->product = $this->getMockBuilder(Product::class) ->setMethods(['getTypeInstance', 'getStoreId', 'getTypeId', '__wakeup', 'getId', 'getData']) ->disableOriginalConstructor() ->getMock(); - $this->link = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $this->link = $this->getMockBuilder(LinkInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->linkFactory = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterfaceFactory::class) + $this->linkFactory = $this->getMockBuilder(LinkInterfaceFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); $this->bundleSelectionMock = $this->createPartialMock( - \Magento\Bundle\Model\SelectionFactory::class, + SelectionFactory::class, ['create'] ); $this->bundleFactoryMock = $this->createPartialMock( - \Magento\Bundle\Model\ResourceModel\BundleFactory::class, + BundleFactory::class, ['create'] ); $this->optionCollectionFactoryMock = $this->createPartialMock( - \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory::class, + OptionCollectionFactory::class, ['create'] ); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->metadataPoolMock = $this->getMockBuilder(MetadataPool::class) ->disableOriginalConstructor() ->getMock(); - $this->metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) + $this->metadataMock = $this->getMockBuilder(EntityMetadata::class) ->disableOriginalConstructor() ->getMock(); - $this->metadataPoolMock->expects($this->any())->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + $this->metadataPoolMock->method('getMetadata') + ->with(ProductInterface::class) ->willReturn($this->metadataMock); - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + $this->dataObjectHelperMock = $this->getMockBuilder(DataObjectHelper::class) ->disableOriginalConstructor() ->getMock(); $this->model = $helper->getObject( - LinkManagement::class, [ 'productRepository' => $this->productRepository, @@ -184,12 +208,9 @@ protected function setUp() 'optionCollection' => $this->optionCollectionFactoryMock, 'storeManager' => $this->storeManagerMock, 'dataObjectHelper' => $this->dataObjectHelperMock, + 'metadataPool' => $this->metadataPoolMock ] ); - $refClass = new \ReflectionClass(LinkManagement::class); - $refProperty = $refClass->getProperty('metadataPool'); - $refProperty->setAccessible(true); - $refProperty->setValue($this->model, $this->metadataPoolMock); } public function testGetChildren() @@ -198,31 +219,45 @@ public function testGetChildren() $this->getOptions(); - $this->productRepository->expects($this->any())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('bundle')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('bundle'); - $this->productType->expects($this->once())->method('setStoreFilter')->with( - $this->equalTo($this->storeId), - $this->product - ); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->with($this->equalTo($this->optionIds), $this->equalTo($this->product)) - ->will($this->returnValue($this->selectionCollection)); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionIds)); + $this->productType->expects($this->once()) + ->method('setStoreFilter') + ->with( + $this->equalTo($this->storeId), + $this->product + ); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->with( + $this->equalTo($this->optionIds), + $this->equalTo($this->product) + ) + ->willReturn($this->selectionCollection); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn($this->optionIds); - $this->optionCollection->expects($this->once())->method('appendSelections') + $this->optionCollection->expects($this->once()) + ->method('appendSelections') ->with($this->equalTo($this->selectionCollection)) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); - $this->option->expects($this->any())->method('getSelections')->willReturn([$this->product]); - $this->product->expects($this->any())->method('getData')->willReturn([]); + $this->option->method('getSelections') + ->willReturn([$this->product]); + $this->product->method('getData') + ->willReturn([]); $this->dataObjectHelperMock->expects($this->once()) ->method('populateWithArray') - ->with($this->link, $this->anything(), \Magento\Bundle\Api\Data\LinkInterface::class) + ->with($this->link, $this->anything(), LinkInterface::class) ->willReturnSelf(); $this->link->expects($this->once())->method('setIsDefault')->willReturnSelf(); $this->link->expects($this->once())->method('setQty')->willReturnSelf(); @@ -241,27 +276,42 @@ public function testGetChildrenWithOptionId() $this->getOptions(); - $this->productRepository->expects($this->any())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('bundle')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('bundle'); - $this->productType->expects($this->once())->method('setStoreFilter')->with( - $this->equalTo($this->storeId), - $this->product - ); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->with($this->equalTo($this->optionIds), $this->equalTo($this->product)) - ->will($this->returnValue($this->selectionCollection)); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionIds)); + $this->productType->expects($this->once()) + ->method('setStoreFilter') + ->with( + $this->equalTo($this->storeId), + $this->product + ); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->with( + $this->equalTo($this->optionIds), + $this->equalTo($this->product) + ) + ->willReturn($this->selectionCollection); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn($this->optionIds); - $this->optionCollection->expects($this->once())->method('appendSelections') + $this->optionCollection->expects($this->once()) + ->method('appendSelections') ->with($this->equalTo($this->selectionCollection)) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); - $this->option->expects($this->any())->method('getOptionId')->will($this->returnValue(10)); - $this->option->expects($this->once())->method('getSelections')->willReturn([1, 2]); + $this->option->method('getOptionId') + ->willReturn(10); + $this->option->expects($this->once()) + ->method('getSelections') + ->willReturn([1, 2]); $this->dataObjectHelperMock->expects($this->never())->method('populateWithArray'); @@ -275,10 +325,14 @@ public function testGetChildrenException() { $productSku = 'productSku'; - $this->productRepository->expects($this->once())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->expects($this->once()) + ->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('simple')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('simple'); $this->assertEquals([$this->link], $this->model->getChildren($productSku)); } @@ -288,13 +342,14 @@ public function testGetChildrenException() */ public function testAddChildToNotBundleProduct() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getOptionId') + ->willReturn(1); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); $this->model->addChild($productMock, 1, $productLink); } @@ -303,306 +358,312 @@ public function testAddChildToNotBundleProduct() */ public function testAddChildNonExistingOption() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getOptionId')->willReturn(1); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $emptyOption = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $emptyOption = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); $emptyOption->expects($this->once()) ->method('getId') - ->will($this->returnValue(null)); + ->willReturn(null); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($emptyOption)); + ->willReturn($emptyOption); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + $this->optionCollectionFactoryMock + ->method('create') + ->willReturn($optionsCollectionMock); $this->model->addChild($productMock, 1, $productLink); } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage The bundle product can't contain another composite product. */ public function testAddChildLinkedProductIsComposite() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) - ->method('getData') + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(true)); - $this->productRepository - ->expects($this->once()) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(true); + $this->productRepository->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock + ->method('getStore') + ->willReturn($store); + $store->method('getId') + ->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo('1')) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData')->with($this->linkField)->willReturn([]); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + $this->bundleFactoryMock->expects($this->once())->method('create')->willReturn($bundle); $this->model->addChild($productMock, 1, $productLink); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testAddChildProductAlreadyExistsInOption() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); $productMock = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, + Product::class, ['getTypeId', 'getCopyFromView', 'getData', 'getTypeInstance', 'getSku'] ); $productMock->expects($this->once())->method('getTypeId')->willReturn( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + Type::TYPE_BUNDLE ); - $productMock->expects($this->any()) - ->method('getData') + $productMock->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $productMock->expects($this->any())->method('getCopyFromView')->will($this->returnValue(false)); - - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getEntityId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->once()) + $productMock->method('getCopyFromView') + ->willReturn(false); + + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getEntityId') + ->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once()) + ->method('getId') + ->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 12, 'parent_product_id' => 'product_id'], ['option_id' => 1, 'product_id' => 13, 'parent_product_id' => 'product_id'], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); - $bundle->expects($this->once())->method('getSelectionsData') + $bundle = $this->createMock(Bundle::class); + $bundle->expects($this->once()) + ->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); $this->model->addChild($productMock, 1, $productLink); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testAddChildCouldNotSave() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); $this->productRepository ->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 11], ['option_id' => 1, 'product_id' => 12], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, ['save']); + $selection = $this->createPartialMock(Selection::class, ['save']); $selection->expects($this->once())->method('save') - ->will( - $this->returnCallback( - function () { - throw new \Exception('message'); - } - ) + ->willReturnCallback( + static function () { + throw new Exception('message'); + } ); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->addChild($productMock, 1, $productLink); } public function testAddChild() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(false); $this->productRepository ->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 11], ['option_id' => 1, 'product_id' => 12], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once())->method('create')->willReturn($bundle); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, ['save', 'getId']); + $selection = $this->createPartialMock(Selection::class, ['save', 'getId']); $selection->expects($this->once())->method('save'); - $selection->expects($this->once())->method('getId')->will($this->returnValue(42)); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $selection->expects($this->once())->method('getId')->willReturn(42); + $this->bundleSelectionMock->expects($this->once())->method('create')->willReturn($selection); $result = $this->model->addChild($productMock, 1, $productLink); $this->assertEquals(42, $result); } @@ -621,51 +682,51 @@ public function testSaveChild() $parentProductId = 32; $bundleProductSku = 'bundleProductSku'; - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $productLink->expects($this->any())->method('getPosition')->will($this->returnValue($position)); - $productLink->expects($this->any())->method('getQty')->will($this->returnValue($qty)); - $productLink->expects($this->any())->method('getPriceType')->will($this->returnValue($priceType)); - $productLink->expects($this->any())->method('getPrice')->will($this->returnValue($price)); - $productLink->expects($this->any())->method('getCanChangeQuantity') - ->will($this->returnValue($canChangeQuantity)); - $productLink->expects($this->any())->method('getIsDefault')->will($this->returnValue($isDefault)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue($optionId)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getId')->willReturn($id); + $productLink->method('getOptionId')->willReturn($optionId); + $productLink->method('getPosition')->willReturn($position); + $productLink->method('getQty')->willReturn($qty); + $productLink->method('getPriceType')->willReturn($priceType); + $productLink->method('getPrice')->willReturn($price); + $productLink->method('getCanChangeQuantity') + ->willReturn($canChangeQuantity); + $productLink->method('getIsDefault')->willReturn($isDefault); + $productLink->method('getSelectionId')->willReturn($optionId); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($parentProductId); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue($linkProductId)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn($linkProductId); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(false); $this->productRepository ->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); + ->willReturn($productMock); $this->productRepository ->expects($this->at(1)) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'save', 'getId', 'load', @@ -678,10 +739,11 @@ public function testSaveChild() 'setSelectionPriceValue', 'setSelectionCanChangeQty', 'setIsDefault' - ]); + ] + ); $selection->expects($this->once())->method('save'); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue($id)); + $selection->expects($this->once())->method('load')->with($id)->willReturnSelf(); + $selection->method('getId')->willReturn($id); $selection->expects($this->once())->method('setProductId')->with($linkProductId); $selection->expects($this->once())->method('setParentProductId')->with($parentProductId); $selection->expects($this->once())->method('setOptionId')->with($optionId); @@ -692,12 +754,12 @@ public function testSaveChild() $selection->expects($this->once())->method('setSelectionCanChangeQty')->with($canChangeQuantity); $selection->expects($this->once())->method('setIsDefault')->with($isDefault); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once())->method('create')->willReturn($selection); $this->assertTrue($this->model->saveChild($bundleProductSku, $productLink)); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testSaveChildFailedToSave() { @@ -705,40 +767,45 @@ public function testSaveChildFailedToSave() $linkProductId = 45; $parentProductId = 32; - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSelectionId')->willReturn(1); $bundleProductSku = 'bundleProductSku'; - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any())->method('getId')->will($this->returnValue($parentProductId)); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock->method('getId') + ->willReturn($parentProductId); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue($linkProductId)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn($linkProductId); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore') + ->willReturn($store); + $store->method('getId') + ->willReturn(0); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'save', 'getId', 'load', @@ -752,144 +819,159 @@ public function testSaveChildFailedToSave() 'setSelectionPriceValue', 'setSelectionCanChangeQty', 'setIsDefault' - ]); - $mockException = $this->createMock(\Exception::class); - $selection->expects($this->once())->method('save')->will($this->throwException($mockException)); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue($id)); - $selection->expects($this->once())->method('setProductId')->with($linkProductId); - - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + ] + ); + $mockException = $this->createMock(Exception::class); + $selection->expects($this->once()) + ->method('save') + ->willThrowException($mockException); + $selection->expects($this->once()) + ->method('load') + ->with($id) + ->willReturnSelf(); + $selection->method('getId') + ->willReturn($id); + $selection->expects($this->once()) + ->method('setProductId') + ->with($linkProductId); + + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->saveChild($bundleProductSku, $productLink); } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithoutId() { - $bundleProductSku = "bundleSku"; + $bundleProductSku = 'bundleSku'; $linkedProductSku = 'simple'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue(null)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn(null); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); $this->model->saveChild($bundleProductSku, $productLink); } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage The product link with the "12345" ID field wasn't found. Verify the ID and try again. */ public function testSaveChildWithInvalidId() { $id = 12345; $linkedProductSku = 'simple'; - $bundleProductSku = "bundleProductSku"; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); - - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $bundleProductSku = 'bundleProductSku'; + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); + + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'getId', 'load', - ]); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue(null)); + ] + ); + $selection->expects($this->once()) + ->method('load') + ->with($id) + ->willReturnSelf(); + $selection->method('getId')->willReturn(null); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->saveChild($bundleProductSku, $productLink); } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithCompositeProductLink() { - $bundleProductSku = "bundleProductSku"; + $bundleProductSku = 'bundleProductSku'; $id = 12; $linkedProductSku = 'simple'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(true)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(true); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); $this->model->saveChild($bundleProductSku, $productLink); } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithSimpleProduct() { $id = 12; $linkedProductSku = 'simple'; - $bundleProductSku = "bundleProductSku"; + $bundleProductSku = 'bundleProductSku'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_SIMPLE); - $this->productRepository->expects($this->once())->method('get')->with($bundleProductSku) + $this->productRepository->expects($this->once()) + ->method('get') + ->with($bundleProductSku) ->willReturn($productMock); $this->model->saveChild($bundleProductSku, $productLink); @@ -897,34 +979,33 @@ public function testSaveChildWithSimpleProduct() public function testRemoveChild() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + $this->productRepository->method('get')->willReturn($this->product); + $bundle = $this->createMock(Bundle::class); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); $productSku = 'productSku'; $optionId = 1; $productId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku)); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->willReturn($productId); - - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); - $this->metadataMock->expects($this->any())->method('getLinkField')->willReturn($this->linkField); - $this->product->expects($this->any()) - ->method('getData') + $selection->method('getSku')->willReturn($childSku); + $selection->method('getOptionId')->willReturn($optionId); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn($productId); + + $this->option->method('getSelections')->willReturn([$selection]); + $this->metadataMock->method('getLinkField')->willReturn($this->linkField); + $this->product->method('getData') ->with($this->linkField) ->willReturn(3); @@ -935,18 +1016,16 @@ public function testRemoveChild() } /** - * @expectedException \Magento\Framework\Exception\InputException + * @ex@expectedException \Magento\Framework\Exception\InputException */ public function testRemoveChildForbidden() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); $this->model->removeChild($productSku, $optionId, $childSku); } @@ -955,28 +1034,26 @@ public function testRemoveChildForbidden() */ public function testRemoveChildInvalidOptionId() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku)); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId + 1)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->will($this->returnValue(1)); + $selection->method('getSku')->willReturn($childSku); + $selection->method('getOptionId')->willReturn($optionId + 1); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn(1); - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); + $this->option->method('getSelections')->willReturn([$selection]); $this->model->removeChild($productSku, $optionId, $childSku); } @@ -985,61 +1062,71 @@ public function testRemoveChildInvalidOptionId() */ public function testRemoveChildInvalidChildSku() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku . '_invalid')); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->will($this->returnValue(1)); + $selection->method('getSku')->willReturn($childSku . '_invalid'); + $selection->method('getOptionId')->willReturn($optionId); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn(1); - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); + $this->option->method('getSelections') + ->willReturn([$selection]); $this->model->removeChild($productSku, $optionId, $childSku); } private function getOptions() { - $this->product->expects($this->any())->method('getTypeInstance')->will($this->returnValue($this->productType)); - $this->product->expects($this->once())->method('getStoreId')->will($this->returnValue($this->storeId)); - $this->productType->expects($this->once())->method('setStoreFilter') + $this->product->method('getTypeInstance') + ->willReturn($this->productType); + $this->product->expects($this->once()) + ->method('getStoreId') + ->willReturn($this->storeId); + $this->productType->expects($this->once()) + ->method('setStoreFilter') ->with($this->equalTo($this->storeId), $this->equalTo($this->product)); - $this->productType->expects($this->once())->method('getOptionsCollection') + $this->productType->expects($this->once()) + ->method('getOptionsCollection') ->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionCollection)); + ->willReturn($this->optionCollection); } public function getRemoveOptions() { - $this->product->expects($this->any())->method('getTypeInstance')->will($this->returnValue($this->productType)); - $this->product->expects($this->once())->method('getStoreId')->will($this->returnValue(1)); + $this->product->method('getTypeInstance') + ->willReturn($this->productType); + $this->product->expects($this->once()) + ->method('getStoreId') + ->willReturn(1); $this->productType->expects($this->once())->method('setStoreFilter'); $this->productType->expects($this->once())->method('getOptionsCollection') ->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionCollection)); + ->willReturn($this->optionCollection); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue([1, 2, 3])); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn([1, 2, 3]); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->will($this->returnValue([])); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->willReturn([]); - $this->optionCollection->expects($this->any())->method('appendSelections') + $this->optionCollection->method('appendSelections') ->with($this->equalTo([]), true) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php index 3e9aeaed5c5b4..3c4dbf2e85274 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php @@ -3,50 +3,71 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items; -class AbstractItemsTest extends \PHPUnit\Framework\TestCase +use Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Item; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class AbstractItemsTest extends TestCase { - /** @var \Magento\Sales\Model\Order\Item|\PHPUnit_Framework_MockObject_MockObject */ - protected $orderItem; + /** + * @var Shipment + */ + private $model; - /** @var \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment $model */ - protected $model; + /** + * @var Json|MockObject + */ + private $serializerMock; - /** @var \Magento\Framework\Serialize\Serializer\Json $serializer */ - protected $serializer; + /** + * @var Item|MockObject + */ + private $orderItemMock; protected function setUp() { - $this->orderItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + $this->orderItemMock = $this->createPartialMock( + Item::class, ['getProductOptions', '__wakeup', 'getParentItem', 'getOrderItem', 'getOrderItemId', 'getId'] ); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); + $objectManager = new ObjectManager($this); + $this->serializerMock = $this->createMock(Json::class); $this->model = $objectManager->getObject( - \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment::class, + Shipment::class, [ - 'serializer' => $this->serializer + 'serializer' => $this->serializerMock ] ); } /** * @dataProvider getChildrenEmptyItemsDataProvider + * + * @param string $class + * @param string $method + * @param string $returnClass */ public function testGetChildrenEmptyItems($class, $method, $returnClass) { $salesModel = $this->createPartialMock($returnClass, ['getAllItems', '__wakeup']); - $salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue([])); + $salesModel->expects($this->once())->method('getAllItems')->willReturn([]); $item = $this->createPartialMock($class, [$method, 'getOrderItem', '__wakeup']); - $item->expects($this->once())->method($method)->will($this->returnValue($salesModel)); - $item->expects($this->once())->method('getOrderItem')->will($this->returnValue($this->orderItem)); - $this->orderItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $item->expects($this->once())->method($method)->willReturn($salesModel); + $item->expects($this->once())->method('getOrderItem')->willReturn($this->orderItemMock); + $this->orderItemMock->method('getId')->willReturn(1); - $this->assertSame(null, $this->model->getChildren($item)); + $this->assertNull($this->model->getChildren($item)); } /** @@ -56,9 +77,9 @@ public function getChildrenEmptyItemsDataProvider() { return [ [ - \Magento\Sales\Model\Order\Invoice\Item::class, + Invoice\Item::class, 'getInvoice', - \Magento\Sales\Model\Order\Invoice::class + Invoice::class ], [ \Magento\Sales\Model\Order\Shipment\Item::class, @@ -66,38 +87,40 @@ public function getChildrenEmptyItemsDataProvider() \Magento\Sales\Model\Order\Shipment::class ], [ - \Magento\Sales\Model\Order\Creditmemo\Item::class, + Creditmemo\Item::class, 'getCreditmemo', - \Magento\Sales\Model\Order\Creditmemo::class + Creditmemo::class ] ]; } /** * @dataProvider getChildrenDataProvider + * + * @param bool $parentItem */ public function testGetChildren($parentItem) { if ($parentItem) { - $parentItem = $this->createPartialMock(\Magento\Sales\Model\Order\Item::class, ['getId', '__wakeup']); - $parentItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $parentItem = $this->createPartialMock(Item::class, ['getId', '__wakeup']); + $parentItem->method('getId')->willReturn(1); } - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItemId')->will($this->returnValue(2)); - $this->orderItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItemId')->willReturn(2); + $this->orderItemMock->method('getId')->willReturn(1); - $salesModel = $this->createPartialMock(\Magento\Sales\Model\Order\Invoice::class, ['getAllItems', '__wakeup']); - $salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue([$this->orderItem])); + $salesModel = $this->createPartialMock(Invoice::class, ['getAllItems', '__wakeup']); + $salesModel->expects($this->once())->method('getAllItems')->willReturn([$this->orderItemMock]); $item = $this->createPartialMock( - \Magento\Sales\Model\Order\Invoice\Item::class, + Invoice\Item::class, ['getInvoice', 'getOrderItem', '__wakeup'] ); - $item->expects($this->once())->method('getInvoice')->will($this->returnValue($salesModel)); - $item->expects($this->any())->method('getOrderItem')->will($this->returnValue($this->orderItem)); + $item->expects($this->once())->method('getInvoice')->willReturn($salesModel); + $item->method('getOrderItem')->willReturn($this->orderItemMock); - $this->assertSame([2 => $this->orderItem], $this->model->getChildren($item)); + $this->assertSame([2 => $this->orderItemMock], $this->model->getChildren($item)); } /** @@ -113,11 +136,14 @@ public function getChildrenDataProvider() /** * @dataProvider isShipmentSeparatelyWithoutItemDataProvider + * + * @param array $productOptions + * @param bool $result */ public function testIsShipmentSeparatelyWithoutItem($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->isShipmentSeparately()); } @@ -136,23 +162,27 @@ public function isShipmentSeparatelyWithoutItemDataProvider() /** * @dataProvider isShipmentSeparatelyWithItemDataProvider + * + * @param array $productOptions + * @param bool $result + * @param bool $parentItem */ public function testIsShipmentSeparatelyWithItem($productOptions, $result, $parentItem) { if ($parentItem) { $parentItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + Item::class, ['getProductOptions', '__wakeup'] ); - $parentItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $parentItem->method('getProductOptions')->willReturn($productOptions); } else { - $this->orderItem->expects($this->any())->method('getProductOptions') - ->will($this->returnValue($productOptions)); + $this->orderItemMock->method('getProductOptions') + ->willReturn($productOptions); } - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); - $this->assertSame($result, $this->model->isShipmentSeparately($this->orderItem)); + $this->assertSame($result, $this->model->isShipmentSeparately($this->orderItemMock)); } /** @@ -170,11 +200,14 @@ public function isShipmentSeparatelyWithItemDataProvider() /** * @dataProvider isChildCalculatedWithoutItemDataProvider + * + * @param array $productOptions + * @param bool $result */ public function testIsChildCalculatedWithoutItem($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->isChildCalculated()); } @@ -193,23 +226,27 @@ public function isChildCalculatedWithoutItemDataProvider() /** * @dataProvider isChildCalculatedWithItemDataProvider + * + * @param array $productOptions + * @param bool $result + * @param bool $parentItem */ public function testIsChildCalculatedWithItem($productOptions, $result, $parentItem) { if ($parentItem) { $parentItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + Item::class, ['getProductOptions', '__wakeup'] ); - $parentItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $parentItem->method('getProductOptions')->willReturn($productOptions); } else { - $this->orderItem->expects($this->any())->method('getProductOptions') - ->will($this->returnValue($productOptions)); + $this->orderItemMock->method('getProductOptions') + ->willReturn($productOptions); } - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); - $this->assertSame($result, $this->model->isChildCalculated($this->orderItem)); + $this->assertSame($result, $this->model->isChildCalculated($this->orderItemMock)); } /** @@ -227,11 +264,13 @@ public function isChildCalculatedWithItemDataProvider() /** * @dataProvider getBundleOptionsDataProvider + * @param array $productOptions + * @param array|string $result */ public function testGetBundleOptions($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->getBundleOptions()); } @@ -248,8 +287,8 @@ public function getBundleOptionsDataProvider() public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); - $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); + $this->orderItemMock->method('getProductOptions')->willReturn([]); + $this->assertNull($this->model->getSelectionAttributes($this->orderItemMock)); } public function testGetSelectionAttributesWithBundle() @@ -258,13 +297,12 @@ public function testGetSelectionAttributesWithBundle() $options = ['bundle_selection_attributes' => $bundleAttributes]; $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; - $this->serializer->expects($this->any()) - ->method('unserialize') + $this->serializerMock->method('unserialize') ->with($bundleAttributes) - ->will($this->returnValue($unserializedResult)); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + ->willReturn($unserializedResult); + $this->orderItemMock->method('getProductOptions')->willReturn($options); - $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItemMock)); } public function testGetOrderOptions() @@ -274,28 +312,32 @@ public function testGetOrderOptions() 'additional_options' => ['additional_options'], 'attributes_info' => ['attributes_info'], ]; - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertEquals(['attributes_info', 'options', 'additional_options'], $this->model->getOrderOptions()); } public function testGetOrderItem() { - $this->model->setItem($this->orderItem); - $this->assertSame($this->orderItem, $this->model->getOrderItem()); + $this->model->setItem($this->orderItemMock); + $this->assertSame($this->orderItemMock, $this->model->getOrderItem()); } /** * @dataProvider canShowPriceInfoDataProvider + * + * @param bool $parentItem + * @param array $productOptions + * @param bool $result */ public function testCanShowPriceInfo($parentItem, $productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); - $this->assertSame($result, $this->model->canShowPriceInfo($this->orderItem)); + $this->assertSame($result, $this->model->canShowPriceInfo($this->orderItemMock)); } /** diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php index 4463709391102..d21971b821691 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php @@ -3,121 +3,156 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Test\Unit\Pricing\Price; +use Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface; use Magento\Bundle\Pricing\Price\BundleOptionPrice; +use Magento\Bundle\Pricing\Price\FinalPrice; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\BasePrice; use Magento\Catalog\Pricing\Price\CustomOptionPrice; use Magento\Bundle\Model\Product\Price; use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Pricing\PriceInfo\Base; +use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * @SuppressWarnings(PHPMD) + * Test class for \Magento\Bundle\Pricing\Price\FinalPrice + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class FinalPriceTest extends \PHPUnit\Framework\TestCase +class FinalPriceTest extends TestCase { - /** @var \Magento\Bundle\Pricing\Price\FinalPrice */ - protected $finalPrice; + /** + * @var FinalPrice + */ + private $finalPrice; - /** @var ObjectManagerHelper */ - protected $objectManagerHelper; + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; - /** @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $saleableInterfaceMock; + /** + * @var SaleableInterface|MockObject + */ + private $saleableInterfaceMock; - /** @var float */ - protected $quantity = 1.; + /** + * @var float + */ + private $quantity = 1.; - /** @var float*/ - protected $baseAmount; + /** + * @var BundleCalculatorInterface|MockObject + */ + private $bundleCalculatorMock; - /** @var \Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $bundleCalculatorMock; + /** + * @var PriceCurrencyInterface|MockObject + */ + private $priceCurrencyMock; - /** @var \Magento\Framework\Pricing\PriceInfo\Base |\PHPUnit_Framework_MockObject_MockObject */ - protected $priceInfoMock; + /** + * @var ProductCustomOptionRepositoryInterface|MockObject + */ + private $productOptionRepositoryMock; - /** @var \Magento\Catalog\Pricing\Price\BasePrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $basePriceMock; + /** + * @var float + */ + private $baseAmount; - /** @var BundleOptionPrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $bundleOptionMock; + /** + * @var Base|MockObject + */ + private $priceInfoMock; - /** @var CustomOptionPrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $customOptionPriceMock; + /** + * @var BasePrice|MockObject + */ + private $basePriceMock; /** - * @var PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var BundleOptionPrice|MockObject */ - protected $priceCurrencyMock; + private $bundleOptionMock; /** - * @var ProductCustomOptionRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomOptionPrice|MockObject */ - private $productOptionRepositoryMock; + private $customOptionPriceMock; /** * @return void */ protected function prepareMock() { - $this->saleableInterfaceMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->saleableInterfaceMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getPriceType', 'getPriceInfo']) ->getMock(); $this->bundleCalculatorMock = $this->createMock( - \Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface::class + BundleCalculatorInterface::class ); - $this->basePriceMock = $this->createMock(\Magento\Catalog\Pricing\Price\BasePrice::class); - $this->basePriceMock->expects($this->any()) - ->method('getValue') - ->will($this->returnValue($this->baseAmount)); + $this->basePriceMock = $this->createMock(BasePrice::class); + $this->basePriceMock->method('getValue') + ->willReturn($this->baseAmount); - $this->bundleOptionMock = $this->getMockBuilder(\Magento\Bundle\Pricing\Price\BundleOptionPrice::class) + $this->bundleOptionMock = $this->getMockBuilder(BundleOptionPrice::class) ->disableOriginalConstructor() ->getMock(); - $this->customOptionPriceMock = $this->getMockBuilder(\Magento\Catalog\Pricing\Price\CustomOptionPrice::class) + $this->customOptionPriceMock = $this->getMockBuilder(CustomOptionPrice::class) ->disableOriginalConstructor() ->getMock(); - $this->priceInfoMock = $this->createMock(\Magento\Framework\Pricing\PriceInfo\Base::class); + $this->priceInfoMock = $this->createMock(Base::class); $this->priceInfoMock->expects($this->atLeastOnce()) ->method('getPrice') - ->will($this->returnValueMap([ - [\Magento\Catalog\Pricing\Price\BasePrice::PRICE_CODE, $this->basePriceMock], - [BundleOptionPrice::PRICE_CODE, $this->bundleOptionMock], - [CustomOptionPrice::PRICE_CODE, $this->customOptionPriceMock], - ])); + ->willReturnMap( + [ + [BasePrice::PRICE_CODE, $this->basePriceMock], + [BundleOptionPrice::PRICE_CODE, $this->bundleOptionMock], + [CustomOptionPrice::PRICE_CODE, $this->customOptionPriceMock], + ] + ); $this->saleableInterfaceMock->expects($this->once()) ->method('getPriceInfo') - ->will($this->returnValue($this->priceInfoMock)); - - $this->priceCurrencyMock = $this->createMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class); - - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->finalPrice = new \Magento\Bundle\Pricing\Price\FinalPrice( - $this->saleableInterfaceMock, - $this->quantity, - $this->bundleCalculatorMock, - $this->priceCurrencyMock - ); + ->willReturn($this->priceInfoMock); + $this->priceCurrencyMock = $this->createMock(PriceCurrencyInterface::class); $this->productOptionRepositoryMock = $this->getMockForAbstractClass( ProductCustomOptionRepositoryInterface::class ); - $reflection = new \ReflectionClass(get_class($this->finalPrice)); - $reflectionProperty = $reflection->getProperty('productOptionRepository'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->finalPrice, $this->productOptionRepositoryMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->finalPrice = $this->objectManagerHelper->getObject( + FinalPrice::class, + [ + 'saleableItem' => $this->saleableInterfaceMock, + 'quantity' => $this->quantity, + 'calculator' => $this->bundleCalculatorMock, + 'priceCurrency' => $this->priceCurrencyMock, + 'productOptionRepository' => $this->productOptionRepositoryMock + ] + ); } /** + * @param $baseAmount + * @param $optionsValue + * @param $result * @dataProvider getValueDataProvider */ public function testGetValue($baseAmount, $optionsValue, $result) @@ -126,7 +161,7 @@ public function testGetValue($baseAmount, $optionsValue, $result) $this->prepareMock(); $this->bundleOptionMock->expects($this->once()) ->method('getValue') - ->will($this->returnValue($optionsValue)); + ->willReturn($optionsValue); $this->assertSame($result, $this->finalPrice->getValue()); } @@ -144,6 +179,7 @@ public function getValueDataProvider() } /** + * @param $baseAmount * @dataProvider getValueDataProvider */ public function testGetMaximalPrice($baseAmount) @@ -155,7 +191,7 @@ public function testGetMaximalPrice($baseAmount) $this->bundleCalculatorMock->expects($this->once()) ->method('getMaxAmount') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMaximalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMaximalPrice()); @@ -179,7 +215,7 @@ public function testGetMaximalPriceFixedBundleWithOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getMaxAmount') ->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMaximalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMaximalPrice()); @@ -192,7 +228,7 @@ public function testGetMinimalPriceFixedBundleWithOption() $result = 7; $this->prepareMock(); $customOptions = [ - $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductCustomOptionInterface::class) + $this->getMockBuilder(ProductCustomOptionInterface::class) ->setMethods(['setProduct']) ->getMockForAbstractClass() ]; @@ -213,7 +249,7 @@ public function testGetMinimalPriceFixedBundleWithOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getAmount') ->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMinimalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMinimalPrice()); @@ -231,7 +267,7 @@ public function testGetMinimalPrice($baseAmount) $this->bundleCalculatorMock->expects($this->once()) ->method('getAmount') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMinimalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMinimalPrice()); @@ -244,7 +280,7 @@ public function testGetPriceWithoutOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getAmountWithoutOption') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getPriceWithoutOption()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getPriceWithoutOption()); From 2e3128f56508aeeafb8229fe36241782135408ae Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sat, 21 Mar 2020 18:29:18 +0200 Subject: [PATCH 515/734] Added the MFTF test that checks attribute won't save with HTML tags in labels --- ...ctAttributeDefaultStoreViewActionGroup.xml | 16 ++++++ ...ttributeWithHtmlTagsInLabelActionGroup.xml | 29 +++++++++++ .../Test/Mftf/Data/ProductAttributeData.xml | 5 ++ .../AdminCreateProductAttributeSection.xml | 5 ++ ...uctAttributeLabelDontAllowHtmlTagsTest.xml | 50 +++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml new file mode 100644 index 0000000000000..92fa687a442ee --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminFillProductAttributeDefaultStoreViewActionGroup"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <fillField selector="{{ManageLabelsSection.DefaultStoreLabel}}" userInput="{{value}}" stepKey="fillDefaultStoreViewLabel"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml new file mode 100644 index 0000000000000..26751f9659005 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml @@ -0,0 +1,29 @@ +<?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="SaveProductAttributeWithHtmlTagsInLabelActionGroup"> + <annotations> + <description>Clicks on Save. Validates that the HTML tags issue is present.</description> + </annotations> + + <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + + <!-- See the error message on the "Manage Labels Tab" --> + <seeElement selector="#attribute-labels-table .mage-error" stepKey="seeHtmlTagsUsageErrorMessage1"/> + + <!-- Click on "Properties" tab on left menu --> + <click selector="{{AttributePropertiesSection.propertiesTab}}" stepKey="clickPropertiesTab"/> + + <!-- See the error message on the "Properties" Tab --> + <seeElement selector=".field-attribute_label .mage-error" stepKey="seeHtmlTagsUsageErrorMessage2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index e4a91902cb79b..d918ad35873ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -406,6 +406,11 @@ <data key="frontend_label">Size</data> <data key="attribute_code" unique="suffix">size_attr</data> </entity> + <entity name="productAttributeWithHtmlTagsInLabel" extends="newProductAttribute" type="ProductAttribute"> + <data key="default_label" unique="suffix">Attribute Default label <span></data> + <data key="default_store_label" unique="suffix">Attribute Store label <span> </data> + <data key="frontend_input">text</data> + </entity> <!-- Product attribute from file "export_import_configurable_product.csv" --> <entity name="ProductAttributeWithTwoOptionsForExportImport" extends="productAttributeDropdownTwoOptions" type="ProductAttribute"> <data key="attribute_code">attribute</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 0934e39dcb062..07f9b66966b6c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -50,6 +50,11 @@ <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> </section> + <section name="ManageLabelsSection"> + <element name="PageTitle" type="text" selector="//span[text()='Manage Titles (Size, Color, etc.)']" /> + <element name="ManageLabelsTab" selector="#product_attribute_tabs_labels" type="button"/> + <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> + </section> <section name="WYSIWYGProductAttributeSection"> <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml new file mode 100644 index 0000000000000..1e6add929ffa0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml @@ -0,0 +1,50 @@ +<?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="AdminProductAttributeLabelDontAllowHtmlTagsTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Attribute label must not contain HTML tags"/> + <title value="Product Attribute label musts not contain HTML tags"/> + <description value="Test whenever HTML tags are allowed for a product attribute label"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Log out --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Go to Stores > Attributes > Product , click "Add New Attribute" --> + <actionGroup ref="AdminOpenProductAttributePageActionGroup" stepKey="openProductAttributePage"/> + <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="createNewAttribute"/> + + <!-- Input value for Default Label --> + <actionGroup ref="AdminFillProductAttributePropertiesActionGroup" stepKey="fillAttributeDefaultLabel"> + <argument name="attributeName" value="{{productAttributeWithHtmlTagsInLabel.default_label}}"/> + <argument name="attributeType" value="{{productAttributeWithHtmlTagsInLabel.frontend_input}}" /> + </actionGroup> + + <!-- Click on "Manage Labels" tab on left menu --> + <click selector="{{ManageLabelsSection.ManageLabelsTab}}" stepKey="clickManageLabelsTab"/> + + <!-- Input value for Default Store View --> + <actionGroup ref="AdminFillProductAttributeDefaultStoreViewActionGroup" stepKey="fillAttributeDefaultStoreViewLabel"> + <argument name="value" value="{{productAttributeWithHtmlTagsInLabel.default_store_label}}"/> + </actionGroup> + + <!-- Save Product Attribute --> + <actionGroup ref="SaveProductAttributeWithHtmlTagsInLabelActionGroup" stepKey="saveAttribute"/> + </test> +</tests> From 9a8d94d6600a1802813b9a990e448b957ca391d0 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sat, 21 Mar 2020 19:13:35 +0200 Subject: [PATCH 516/734] Admin area: Make the customer addresses under Addresses Tab show in IE11 --- .../Customer/view/adminhtml/web/template/default-address.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 8d68b93445b85..2e904678296eb 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -20,7 +20,7 @@ </if> <if args="address.street"> <text args="address.street" if="_.isString(address.street)"/> - <text args="_.values(address.street).filter(value => _.isString(value)).join(', ')" + <text args="_.filter(_.values(address.street), function (value) { return _.isString(value)}).join(', ')" ifnot="_.isString(address.street)"/> <br/> </if> From e121c90be9a5d665c72920baad468085739e02c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Sat, 21 Mar 2020 22:41:39 +0100 Subject: [PATCH 517/734] Cleanup ObjectManager usage - Magento_Sitemap --- .../Block/Adminhtml/Grid/Renderer/Link.php | 59 ++++++++------ .../Controller/Adminhtml/Sitemap/Delete.php | 51 +++++------- .../Controller/Adminhtml/Sitemap/Generate.php | 17 ++-- .../Controller/Adminhtml/Sitemap/Save.php | 50 +++++++----- .../Sitemap/Model/Config/Backend/Robots.php | 14 ++-- .../Unit/Model/Config/Backend/RobotsTest.php | 80 ++++++++++--------- 6 files changed, 141 insertions(+), 130 deletions(-) diff --git a/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php b/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php index b4e54104bdfb4..5779caf529e33 100644 --- a/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php +++ b/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php @@ -3,49 +3,55 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Block\Adminhtml\Grid\Renderer; -use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Backend\Block\Context; +use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; use Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; +use Magento\Framework\Filesystem; +use Magento\Sitemap\Model\Sitemap; +use Magento\Sitemap\Model\SitemapFactory; /** * Sitemap grid link column renderer */ -class Link extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer +class Link extends AbstractRenderer { /** - * @var \Magento\Framework\Filesystem $filesystem + * @var Filesystem */ - protected $_filesystem; + private $filesystem; /** - * @var \Magento\Sitemap\Model\SitemapFactory + * @var SitemapFactory */ - protected $_sitemapFactory; + private $sitemapFactory; /** * @var DocumentRoot */ - protected $documentRoot; + private $documentRoot; /** - * @param \Magento\Backend\Block\Context $context - * @param \Magento\Sitemap\Model\SitemapFactory $sitemapFactory - * @param \Magento\Framework\Filesystem $filesystem - * @param array $data + * @param Context $context + * @param SitemapFactory $sitemapFactory + * @param Filesystem $filesystem * @param DocumentRoot $documentRoot + * @param array $data */ public function __construct( - \Magento\Backend\Block\Context $context, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory, - \Magento\Framework\Filesystem $filesystem, - array $data = [], - DocumentRoot $documentRoot = null + Context $context, + SitemapFactory $sitemapFactory, + Filesystem $filesystem, + DocumentRoot $documentRoot, + array $data = [] ) { - $this->_sitemapFactory = $sitemapFactory; - $this->_filesystem = $filesystem; - $this->documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); + $this->sitemapFactory = $sitemapFactory; + $this->filesystem = $filesystem; + $this->documentRoot = $documentRoot; parent::__construct($context, $data); } @@ -53,19 +59,20 @@ public function __construct( /** * Prepare link to display in grid * - * @param \Magento\Framework\DataObject $row + * @param DataObject $row + * * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { - /** @var $sitemap \Magento\Sitemap\Model\Sitemap */ - $sitemap = $this->_sitemapFactory->create(); + /** @var $sitemap Sitemap */ + $sitemap = $this->sitemapFactory->create(); $sitemap->setStoreId($row->getStoreId()); - $url = $this->escapeHtml($sitemap->getSitemapUrl($row->getSitemapPath(), $row->getSitemapFilename())); + $url = $this->_escaper->escapeHtml($sitemap->getSitemapUrl($row->getSitemapPath(), $row->getSitemapFilename())); $fileName = preg_replace('/^\//', '', $row->getSitemapPath() . $row->getSitemapFilename()); $documentRootPath = $this->documentRoot->getPath(); - $directory = $this->_filesystem->getDirectoryRead($documentRootPath); + $directory = $this->filesystem->getDirectoryRead($documentRootPath); if ($directory->isFile($fileName)) { return sprintf('<a href="%1$s">%1$s</a>', $url); } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php index 1c807cbfc194e..560797cfc7453 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php @@ -3,40 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Backend\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem; +use Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Sitemap\Model\SitemapFactory; /** * Controller class Delete. Represents adminhtml request flow for a sitemap deletion */ -class Delete extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements HttpPostActionInterface +class Delete extends Sitemap implements HttpPostActionInterface { /** - * @var \Magento\Framework\Filesystem + * @var SitemapFactory */ - private $filesystem; + private $sitemapFactory; /** - * @var \Magento\Sitemap\Model\SitemapFactory + * @var Filesystem */ - private $sitemapFactory; + private $filesystem; /** - * Constructor - * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Sitemap\Model\SitemapFactory|null $sitemapFactory + * @param Context $context + * @param SitemapFactory $sitemapFactory + * @param Filesystem $filesystem */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory = null + Context $context, + SitemapFactory $sitemapFactory, + Filesystem $filesystem ) { parent::__construct($context); - $this->sitemapFactory = $sitemapFactory ?: ObjectManager::getInstance() - ->get(\Magento\Sitemap\Model\SitemapFactory::class); + $this->sitemapFactory = $sitemapFactory; + $this->filesystem = $filesystem; } /** @@ -46,7 +51,7 @@ public function __construct( */ public function execute() { - $directory = $this->getFilesystem()->getDirectoryWrite(DirectoryList::ROOT); + $directory = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); // check if we know what should be deleted $id = $this->getRequest()->getParam('sitemap_id'); if ($id) { @@ -86,20 +91,4 @@ public function execute() // go to grid $this->_redirect('adminhtml/*/'); } - - /** - * The getter function to get Filesystem object for real application code - * - * @return \Magento\Framework\Filesystem - * @deprecated 100.2.0 - */ - private function getFilesystem() - { - if (null === $this->filesystem) { - $this->filesystem = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Filesystem::class - ); - } - return $this->filesystem; - } } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 5cfc7349888f3..7d7ba87940fc7 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -3,35 +3,36 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Area; use Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Store\Model\App\Emulation; -use Magento\Framework\App\ObjectManager; /** * Generate sitemap file */ class Generate extends Sitemap implements HttpGetActionInterface { - /** @var \Magento\Store\Model\App\Emulation $appEmulation */ + /** + * @var Emulation + */ private $appEmulation; /** - * Generate constructor. * @param Action\Context $context - * @param \Magento\Store\Model\App\Emulation|null $appEmulation + * @param Emulation $appEmulation */ public function __construct( Action\Context $context, - Emulation $appEmulation = null + Emulation $appEmulation ) { parent::__construct($context); - $this->appEmulation = $appEmulation ?: ObjectManager::getInstance() - ->get(\Magento\Store\Model\App\Emulation::class); + $this->appEmulation = $appEmulation; } /** @@ -51,7 +52,7 @@ public function execute() try { $this->appEmulation->startEnvironmentEmulation( $sitemap->getStoreId(), - \Magento\Framework\App\Area::AREA_FRONTEND, + Area::AREA_FRONTEND, true ); $sitemap->generateXml(); diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index 5230de0429778..1543fc8df933c 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -3,20 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Controller; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Filesystem; use Magento\Framework\Validator\StringLength; use Magento\MediaStorage\Model\File\Validator\AvailablePath; +use Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Sitemap\Helper\Data; use Magento\Sitemap\Model\SitemapFactory; /** * Save sitemap controller. */ -class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements HttpPostActionInterface +class Save extends Sitemap implements HttpPostActionInterface { /** * Maximum length of sitemap filename @@ -34,12 +41,12 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements Http private $pathValidator; /** - * @var \Magento\Sitemap\Helper\Data + * @var Data */ private $sitemapHelper; /** - * @var \Magento\Framework\Filesystem + * @var Filesystem */ private $filesystem; @@ -53,24 +60,24 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements Http * @param Context $context * @param StringLength $stringValidator * @param AvailablePath $pathValidator - * @param \Magento\Sitemap\Helper\Data $sitemapHelper - * @param \Magento\Framework\Filesystem $filesystem + * @param Data $sitemapHelper + * @param Filesystem $filesystem * @param SitemapFactory $sitemapFactory */ public function __construct( Context $context, - StringLength $stringValidator = null, - AvailablePath $pathValidator = null, - \Magento\Sitemap\Helper\Data $sitemapHelper = null, - \Magento\Framework\Filesystem $filesystem = null, - SitemapFactory $sitemapFactory = null + StringLength $stringValidator, + AvailablePath $pathValidator, + Data $sitemapHelper, + Filesystem $filesystem, + SitemapFactory $sitemapFactory ) { parent::__construct($context); - $this->stringValidator = $stringValidator ?: $this->_objectManager->get(StringLength::class); - $this->pathValidator = $pathValidator ?: $this->_objectManager->get(AvailablePath::class); - $this->sitemapHelper = $sitemapHelper ?: $this->_objectManager->get(\Magento\Sitemap\Helper\Data::class); - $this->filesystem = $filesystem ?: $this->_objectManager->get(\Magento\Framework\Filesystem::class); - $this->sitemapFactory = $sitemapFactory ?: $this->_objectManager->get(SitemapFactory::class); + $this->stringValidator = $stringValidator; + $this->pathValidator = $pathValidator; + $this->sitemapHelper = $sitemapHelper; + $this->filesystem = $filesystem; + $this->sitemapFactory = $sitemapFactory; } /** @@ -115,11 +122,12 @@ protected function validatePath(array $data) * Clear sitemap * * @param \Magento\Sitemap\Model\Sitemap $model + * * @return void */ protected function clearSiteMap(\Magento\Sitemap\Model\Sitemap $model) { - /** @var \Magento\Framework\Filesystem $directory */ + /** @var Filesystem $directory */ $directory = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); if ($this->getRequest()->getParam('sitemap_id')) { @@ -169,12 +177,13 @@ protected function saveData($data) * Get result after saving data * * @param string|bool $id - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ protected function getResult($id) { - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(Controller\ResultFactory::TYPE_REDIRECT); + if ($id) { // check if 'Save and Continue' if ($this->getRequest()->getParam('back')) { @@ -194,19 +203,20 @@ protected function getResult($id) 'adminhtml/*/edit', ['sitemap_id' => $this->getRequest()->getParam('sitemap_id')] ); + return $resultRedirect; } /** * Save action * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect */ public function execute() { // check if data sent $data = $this->getRequest()->getPostValue(); - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(Controller\ResultFactory::TYPE_REDIRECT); if ($data) { if (!$this->validatePath($data)) { diff --git a/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php b/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php index 7a6d28259bfed..9b9aa6fb05e5b 100644 --- a/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php +++ b/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Model\Config\Backend; use Magento\Framework\App\Cache\TypeListInterface; @@ -14,7 +16,6 @@ use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Registry; use Magento\Robots\Model\Config\Value as RobotsValue; -use Magento\Store\Model\StoreResolver; use Magento\Store\Model\StoreManagerInterface; /** @@ -41,27 +42,22 @@ class Robots extends Value implements IdentityInterface * @param Registry $registry * @param ScopeConfigInterface $config * @param TypeListInterface $cacheTypeList - * @param StoreResolver $storeResolver - * @param StoreManagerInterface|null $storeManager + * @param StoreManagerInterface $storeManager * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Context $context, Registry $registry, ScopeConfigInterface $config, TypeListInterface $cacheTypeList, - StoreResolver $storeResolver, - StoreManagerInterface $storeManager = null, + StoreManagerInterface $storeManager, AbstractResource $resource = null, AbstractDb $resourceCollection = null, array $data = [] ) { - $this->storeManager = $storeManager ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(StoreManagerInterface::class); + $this->storeManager = $storeManager; parent::__construct( $context, diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php index cbf353d0a93c7..2ba83aa0ec14f 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php @@ -3,75 +3,83 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Test\Unit\Model\Config\Backend; -class RobotsTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Model\Context; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Robots\Model\Config\Value; +use Magento\Sitemap\Model\Config\Backend\Robots; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class RobotsTest extends TestCase { /** - * @var \Magento\Sitemap\Model\Config\Backend\Robots + * @var Robots */ private $model; /** - * @var \Magento\Framework\Model\Context|\PHPUnit_Framework_MockObject_MockObject - */ - private $context; - - /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ - private $registry; + private $contextMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ - private $scopeConfig; + private $registryMock; /** - * @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - private $typeList; + private $scopeConfigMock; /** - * @var \Magento\Store\Model\StoreResolver|\PHPUnit_Framework_MockObject_MockObject + * @var TypeListInterface|MockObject */ - private $storeResolver; + private $cacheTypeListMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - private $storeManager; + private $storeManagerMock; protected function setUp() { - $this->context = $this->getMockBuilder(\Magento\Framework\Model\Context::class) + $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); - $this->registry = $this->getMockBuilder(\Magento\Framework\Registry::class) + $this->registryMock = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); - $this->typeList = $this->getMockBuilder(\Magento\Framework\App\Cache\TypeListInterface::class) + $this->cacheTypeListMock = $this->getMockBuilder(TypeListInterface::class) ->getMockForAbstractClass(); - $this->storeResolver = $this->getMockBuilder(\Magento\Store\Model\StoreResolver::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->getMockForAbstractClass(); - $this->model = new \Magento\Sitemap\Model\Config\Backend\Robots( - $this->context, - $this->registry, - $this->scopeConfig, - $this->typeList, - $this->storeResolver, - $this->storeManager + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Robots::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'config' => $this->scopeConfigMock, + 'cacheTypeList' => $this->cacheTypeListMock, + 'storeManager' => $this->storeManagerMock + ] ); } @@ -82,9 +90,9 @@ public function testGetIdentities() { $storeId = 1; - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMock(); + $storeMock = $this->getMockBuilder(StoreInterface::class)->getMock(); - $this->storeManager->expects($this->once()) + $this->storeManagerMock->expects($this->once()) ->method('getStore') ->willReturn($storeMock); @@ -93,7 +101,7 @@ public function testGetIdentities() ->willReturn($storeId); $expected = [ - \Magento\Robots\Model\Config\Value::CACHE_TAG . '_' . $storeId, + Value::CACHE_TAG . '_' . $storeId, ]; $this->assertEquals($expected, $this->model->getIdentities()); } From 770e9fadfc0f8aa5a064e3bf5fcb4870be3dbfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Sat, 21 Mar 2020 22:52:09 +0100 Subject: [PATCH 518/734] Cleanup ObjectManager usage - Magento_SendFriend --- .../Controller/Product/Sendmail.php | 87 +++++++++++-------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/SendFriend/Controller/Product/Sendmail.php b/app/code/Magento/SendFriend/Controller/Product/Sendmail.php index 696c235899370..bff4e7edd14f6 100644 --- a/app/code/Magento/SendFriend/Controller/Product/Sendmail.php +++ b/app/code/Magento/SendFriend/Controller/Product/Sendmail.php @@ -3,31 +3,43 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\SendFriend\Controller\Product; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Session; +use Magento\Framework\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Registry; +use Magento\SendFriend\Controller\Product; use Magento\SendFriend\Model\CaptchaValidator; +use Magento\SendFriend\Model\SendFriend; /** * Class Sendmail. Represents request flow logic of 'sendmail' feature * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Sendmail extends \Magento\SendFriend\Controller\Product implements HttpPostActionInterface +class Sendmail extends Product implements HttpPostActionInterface { /** - * @var \Magento\Catalog\Api\CategoryRepositoryInterface + * @var CategoryRepositoryInterface */ - protected $categoryRepository; + private $categoryRepository; /** - * @var \Magento\Catalog\Model\Session + * @var Session */ - protected $catalogSession; + private $catalogSession; /** * @var CaptchaValidator @@ -35,50 +47,49 @@ class Sendmail extends \Magento\SendFriend\Controller\Product implements HttpPos private $captchaValidator; /** - * Sendmail class construct - * - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator - * @param \Magento\SendFriend\Model\SendFriend $sendFriend - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository - * @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository - * @param \Magento\Catalog\Model\Session $catalogSession + * @param Context $context + * @param Registry $coreRegistry + * @param Validator $formKeyValidator + * @param SendFriend $sendFriend + * @param ProductRepositoryInterface $productRepository + * @param CategoryRepositoryInterface $categoryRepository + * @param Session $catalogSession * @param CaptchaValidator|null $captchaValidator */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator, - \Magento\SendFriend\Model\SendFriend $sendFriend, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, - \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository, - \Magento\Catalog\Model\Session $catalogSession, - CaptchaValidator $captchaValidator = null + Context $context, + Registry $coreRegistry, + Validator $formKeyValidator, + SendFriend $sendFriend, + ProductRepositoryInterface $productRepository, + CategoryRepositoryInterface $categoryRepository, + Session $catalogSession, + CaptchaValidator $captchaValidator ) { parent::__construct($context, $coreRegistry, $formKeyValidator, $sendFriend, $productRepository); $this->categoryRepository = $categoryRepository; $this->catalogSession = $catalogSession; - $this->captchaValidator = $captchaValidator ?: ObjectManager::getInstance()->create(CaptchaValidator::class); + $this->captchaValidator = $captchaValidator; } /** * Send Email Post Action * - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $product = $this->_initProduct(); $data = $this->getRequest()->getPostValue(); if (!$product || !$data) { - /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ + /** @var Forward $resultForward */ $resultForward = $this->resultFactory->create(ResultFactory::TYPE_FORWARD); $resultForward->forward('noroute'); return $resultForward; @@ -108,23 +119,23 @@ public function execute() if ($validate === true) { $this->sendFriend->send(); - $this->messageManager->addSuccess(__('The link to a friend was sent.')); + $this->messageManager->addSuccessMessage(__('The link to a friend was sent.')); $url = $product->getProductUrl(); $resultRedirect->setUrl($this->_redirect->success($url)); return $resultRedirect; - } else { - if (is_array($validate)) { - foreach ($validate as $errorMessage) { - $this->messageManager->addError($errorMessage); - } - } else { - $this->messageManager->addError(__('We found some problems with the data.')); + } + + if (is_array($validate)) { + foreach ($validate as $errorMessage) { + $this->messageManager->addErrorMessage($errorMessage); } + } else { + $this->messageManager->addErrorMessage(__('We found some problems with the data.')); } - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + } catch (LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Some emails were not sent.')); + $this->messageManager->addExceptionMessage($e, __('Some emails were not sent.')); } // save form data From b8706386d35445eec3ca309e08c917262ed84761 Mon Sep 17 00:00:00 2001 From: konarshankar07 <konar.shankar2013@gmail.com> Date: Sun, 22 Mar 2020 11:09:03 +0530 Subject: [PATCH 519/734] Partial Implementaion of ASI issue --- app/code/Magento/MediaGallery/Model/Asset.php | 9 ++++ .../Test/Unit/Model/DataExtractorTest.php | 42 ++++++++++++++----- .../Magento/MediaGallery/etc/db_schema.xml | 1 + .../MediaGallery/etc/db_schema_whitelist.json | 3 +- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/MediaGallery/Model/Asset.php b/app/code/Magento/MediaGallery/Model/Asset.php index f6e1c00044a79..8e232ff0336d9 100644 --- a/app/code/Magento/MediaGallery/Model/Asset.php +++ b/app/code/Magento/MediaGallery/Model/Asset.php @@ -24,6 +24,7 @@ class Asset extends AbstractExtensibleModel implements AssetInterface private const CONTENT_TYPE = 'content_type'; private const WIDTH = 'width'; private const HEIGHT = 'height'; + private const SIZE = 'size'; private const CREATED_AT = 'created_at'; private const UPDATED_AT = 'updated_at'; @@ -89,6 +90,14 @@ public function getHeight(): int return (int) $this->getData(self::HEIGHT); } + /** + * @inheritdoc + */ + public function getSize(): int + { + return (int) $this->getData(self::SIZE); + } + /** * @inheritdoc */ diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php index 7d57f32449f56..b412834f13267 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -9,10 +9,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaGallery\Model\Asset; +use Magento\MediaGallery\Model\DataExtractor; use Magento\MediaGallery\Model\Keyword; use Magento\MediaGalleryApi\Api\Data\AssetInterface; -use Magento\MediaGalleryApi\Api\Data\KeywordInterface; -use Magento\MediaGallery\Model\DataExtractor; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -54,8 +53,27 @@ public function testExtractData(string $class, $interfaceClass, array $expectedD 'data' => $data, ] ); - $receivedData = $this->dataExtractor->extract($model, $interfaceClass); - $this->checkValues($expectedData, $receivedData, $model); + if ($interfaceClass) { + $receivedData = $this->dataExtractor->extract($model, $interfaceClass); + $this->checkAssetValues($expectedData, $receivedData, $model); + } else { + $receivedData = $this->dataExtractor->extract($model); + $this->checkKeyWordValues($expectedData, $receivedData, $model); + } + } + + /** + * @param array $expectedData + * @param array $data + * @param object $model + */ + protected function checkAssetValues(array $expectedData, array $data, $model) + { + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); + $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); + } + $this->assertEquals(array_keys($expectedData), array_keys($data)); } /** @@ -63,13 +81,13 @@ public function testExtractData(string $class, $interfaceClass, array $expectedD * @param array $data * @param object $model */ - protected function checkValues(array $expectedData, array $data, $model) + protected function checkKeyWordValues(array $expectedData, array $data, $model) { foreach ($expectedData as $expectedDataKey => $expectedDataItem) { $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); } - $this->assertEquals(array_keys($expectedData), array_keys($expectedData)); + $this->assertEquals(array_keys($expectedData), array_keys(array_slice($data, 0, 2))); } /** @@ -102,13 +120,17 @@ public function assetProvider() 'value' => 'content_type', 'method' => 'getContentType', ], + 'height' => [ + 'value' => 4, + 'method' => 'getHeight', + ], 'width' => [ 'value' => 3, 'method' => 'getWidth', ], - 'height' => [ - 'value' => 4, - 'method' => 'getHeight', + 'size' => [ + 'value' => 300, + 'method' => 'getSize', ], 'created_at' => [ 'value' => '2019-11-28 10:40:09', @@ -122,7 +144,7 @@ public function assetProvider() ], 'Keyword conversion without interface' => [ Keyword::class, - null, + '', [ 'id' => [ 'value' => 2, diff --git a/app/code/Magento/MediaGallery/etc/db_schema.xml b/app/code/Magento/MediaGallery/etc/db_schema.xml index fac1342528f2c..8cc88de724ca6 100644 --- a/app/code/Magento/MediaGallery/etc/db_schema.xml +++ b/app/code/Magento/MediaGallery/etc/db_schema.xml @@ -14,6 +14,7 @@ <column xsi:type="varchar" name="content_type" length="255" nullable="true" comment="Content Type"/> <column xsi:type="int" name="width" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Width"/> <column xsi:type="int" name="height" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Height"/> + <column xsi:type="int" name="size" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Size"/> <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At"/> <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At"/> <constraint xsi:type="primary" referenceId="PRIMARY"> diff --git a/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json index 10db10d5dd5db..9e187e6dea4a5 100644 --- a/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json +++ b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json @@ -8,6 +8,7 @@ "content_type": true, "width": true, "height": true, + "size": true, "created_at": true, "updated_at": true }, @@ -53,4 +54,4 @@ "MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID_MEDIA_GALLERY_ASSET_ID": true } } -} \ No newline at end of file +} From 6156274af1cb43a267cb1bedf25ecca143448c76 Mon Sep 17 00:00:00 2001 From: konarshankar07 <konar.shankar2013@gmail.com> Date: Sun, 22 Mar 2020 11:14:38 +0530 Subject: [PATCH 520/734] Added missing changes from previous commit --- .../Magento/MediaGalleryApi/Api/Data/AssetInterface.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php index affae296ca530..240e748059974 100644 --- a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php +++ b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php @@ -65,6 +65,13 @@ public function getHeight(): int; */ public function getWidth(): int; + /** + * Retrieve full licensed asset's width + * + * @return int + */ + public function getSize(): int; + /** * Get created at * From e31dafdd66aed1a64ea9f54d494dbadba558fb3a Mon Sep 17 00:00:00 2001 From: konarshankar07 <konar.shankar2013@gmail.com> Date: Sun, 22 Mar 2020 11:16:39 +0530 Subject: [PATCH 521/734] Updated PHPDOC block --- app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php index 240e748059974..5b45eb41cc727 100644 --- a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php +++ b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php @@ -66,7 +66,7 @@ public function getHeight(): int; public function getWidth(): int; /** - * Retrieve full licensed asset's width + * Retrieve full licensed asset's size * * @return int */ From 2ad68314bb7771ef1a552d23c39e31724ededda9 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sun, 22 Mar 2020 10:32:13 +0200 Subject: [PATCH 522/734] Added missing copyright to the file --- .../view/adminhtml/web/template/default-address.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 2e904678296eb..6dd154726010c 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -1,3 +1,9 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <div data-bind="attr: {class: contentClass}"> <div data-bind="attr: {class: 'fieldset-wrapper address-information ' + defaultAddressClass}"> <address> From ae2038eb74ad2f084b5b01f05f6215c12f243edf Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Sun, 22 Mar 2020 11:25:26 +0200 Subject: [PATCH 523/734] Refactored the MFTF tests according to the review --- ...ctAttributeDefaultStoreViewActionGroup.xml | 2 +- ...teToNewProductAttributePageActionGroup.xml | 19 ++++++++++++++++++ .../AdminSaveProductAttributeActionGroup.xml | 20 +++++++++++++++++++ ...uteHtmlTagsValidationErrorActionGroup.xml} | 8 ++------ .../AdminCreateProductAttributeSection.xml | 5 ----- ...minProductAttributeManageLabelsSection.xml | 15 ++++++++++++++ ...uctAttributeLabelDontAllowHtmlTagsTest.xml | 20 +++++++------------ 7 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml rename app/code/Magento/Catalog/Test/Mftf/ActionGroup/{SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml => AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml} (67%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml index 92fa687a442ee..42d205f0fb397 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml @@ -11,6 +11,6 @@ <arguments> <argument name="value" type="string"/> </arguments> - <fillField selector="{{ManageLabelsSection.DefaultStoreLabel}}" userInput="{{value}}" stepKey="fillDefaultStoreViewLabel"/> + <fillField selector="{{AdminProductAttributeManageLabelsSection.DefaultStoreLabel}}" userInput="{{value}}" stepKey="fillDefaultStoreViewLabel"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml new file mode 100644 index 0000000000000..b7dbcc2c86dc4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToNewProductAttributePageActionGroup"> + <annotations> + <description>Go to the create new product attribute page</description> + </annotations> + + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForAttributePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..956dc3bf6fa52 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveProductAttributeActionGroup"> + <annotations> + <description>Clicks on Save button to save the attribute.</description> + </annotations> + + <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml similarity index 67% rename from app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml rename to app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml index 26751f9659005..df57b4ae2093e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeWithHtmlTagsInLabelActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml @@ -8,15 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SaveProductAttributeWithHtmlTagsInLabelActionGroup"> + <actionGroup name="AssertSeeProductAttributeValidationErrorActionGroup"> <annotations> - <description>Clicks on Save. Validates that the HTML tags issue is present.</description> + <description>Validates that the HTML tags issue is present on the attribute page after save.</description> </annotations> - <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForAttributeToSave"/> - <!-- See the error message on the "Manage Labels Tab" --> <seeElement selector="#attribute-labels-table .mage-error" stepKey="seeHtmlTagsUsageErrorMessage1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 07f9b66966b6c..0934e39dcb062 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -50,11 +50,6 @@ <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> </section> - <section name="ManageLabelsSection"> - <element name="PageTitle" type="text" selector="//span[text()='Manage Titles (Size, Color, etc.)']" /> - <element name="ManageLabelsTab" selector="#product_attribute_tabs_labels" type="button"/> - <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> - </section> <section name="WYSIWYGProductAttributeSection"> <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml new file mode 100644 index 0000000000000..048016620c94d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductAttributeManageLabelsSection"> + <element name="ManageLabelsTab" selector="#product_attribute_tabs_labels" type="button"/> + <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml index 1e6add929ffa0..b967403b0eb80 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml @@ -18,33 +18,27 @@ <group value="catalog"/> </annotations> <before> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <!-- Log out --> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> </after> - <!-- Go to Stores > Attributes > Product , click "Add New Attribute" --> - <actionGroup ref="AdminOpenProductAttributePageActionGroup" stepKey="openProductAttributePage"/> - <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="createNewAttribute"/> + <actionGroup ref="AdminNavigateToNewProductAttributePageActionGroup" stepKey="openProductAttributePage"/> - <!-- Input value for Default Label --> <actionGroup ref="AdminFillProductAttributePropertiesActionGroup" stepKey="fillAttributeDefaultLabel"> <argument name="attributeName" value="{{productAttributeWithHtmlTagsInLabel.default_label}}"/> <argument name="attributeType" value="{{productAttributeWithHtmlTagsInLabel.frontend_input}}" /> </actionGroup> - <!-- Click on "Manage Labels" tab on left menu --> - <click selector="{{ManageLabelsSection.ManageLabelsTab}}" stepKey="clickManageLabelsTab"/> + <click selector="{{AdminProductAttributeManageLabelsSection.ManageLabelsTab}}" stepKey="clickManageLabelsTab"/> - <!-- Input value for Default Store View --> <actionGroup ref="AdminFillProductAttributeDefaultStoreViewActionGroup" stepKey="fillAttributeDefaultStoreViewLabel"> <argument name="value" value="{{productAttributeWithHtmlTagsInLabel.default_store_label}}"/> </actionGroup> - <!-- Save Product Attribute --> - <actionGroup ref="SaveProductAttributeWithHtmlTagsInLabelActionGroup" stepKey="saveAttribute"/> + <actionGroup ref="AdminSaveProductAttributeActionGroup" stepKey="saveAttribute"/> + + <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeLabelsForHtmlTags"/> </test> </tests> From b112b54ce14a89fa74e122e7c45a1a6d61a1bdd3 Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi <a.beziazychnyi@atwix.com> Date: Sun, 22 Mar 2020 17:15:21 +0200 Subject: [PATCH 524/734] magento/magento2: fixes PHPDocs for module Magento_reports --- .../Model/ResourceModel/Product/Sold/Collection.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php index bca9b8662715a..bc8a67c2addff 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php @@ -12,6 +12,7 @@ namespace Magento\Reports\Model\ResourceModel\Product\Sold; use Magento\Framework\DB\Select; +use Zend_Db_Select_Exception; /** * Data collection. @@ -25,9 +26,10 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Order\Collection /** * Set Date range to collection. * - * @param int $from - * @param int $to + * @param string $from + * @param string $to * @return $this + * @throws Zend_Db_Select_Exception */ public function setDateRange($from, $to) { @@ -49,6 +51,7 @@ public function setDateRange($from, $to) * @param string $from * @param string $to * @return $this + * @throws Zend_Db_Select_Exception */ public function addOrderedQty($from = '', $to = '') { From 34c1818adcc67b063a54887a658b6df4994cf5a0 Mon Sep 17 00:00:00 2001 From: tna <truongngocanh2794@gmail.com> Date: Sun, 22 Mar 2020 22:42:16 +0700 Subject: [PATCH 525/734] Fix bug 16315: Change function's name --- .../Catalog/Model/Indexer/Product/Flat/Action/Row.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index 4bc985c58d768..5ea2b636933a3 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -85,7 +85,7 @@ public function execute($id = null) $ids = [$id]; $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); - $storeIds = $this->getProductAvailableStores($id); + $storeIds = $this->getAssignedStoreIdsOfProduct($id); $stores = $this->_storeManager->getStores(); foreach ($stores as $store) { @@ -137,12 +137,12 @@ public function execute($id = null) } /** - * Get list store id where product is enable + * Get list store id where the product is enable * * @param int $productId * @return array */ - private function getProductAvailableStores($productId) + private function getAssignedStoreIdsOfProduct($productId) { $select = $this->_connection->select(); $select->from(['e' => $this->_productIndexerHelper->getTable('store')], ['e.store_id']) From bcf33735a44b16c228bccfbc913af9bdbd2fcd07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Sun, 22 Mar 2020 22:32:59 +0100 Subject: [PATCH 526/734] Implement ActionInterface for /robots/index/index --- .../Magento/Robots/Controller/Index/Index.php | 12 +++-- .../Test/Unit/Controller/Index/IndexTest.php | 45 ++++++++++--------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Robots/Controller/Index/Index.php b/app/code/Magento/Robots/Controller/Index/Index.php index 679066d723dce..3a7eb152afdd2 100644 --- a/app/code/Magento/Robots/Controller/Index/Index.php +++ b/app/code/Magento/Robots/Controller/Index/Index.php @@ -3,17 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Robots\Controller\Index; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory; /** * Processes request to robots.txt file and returns robots.txt content as result */ -class Index extends Action +class Index implements HttpGetActionInterface { /** * @var PageFactory @@ -21,16 +22,12 @@ class Index extends Action private $resultPageFactory; /** - * @param Context $context * @param PageFactory $resultPageFactory */ public function __construct( - Context $context, PageFactory $resultPageFactory ) { $this->resultPageFactory = $resultPageFactory; - - parent::__construct($context); } /** @@ -44,6 +41,7 @@ public function execute() $resultPage = $this->resultPageFactory->create(true); $resultPage->addHandle('robots_index_index'); $resultPage->setHeader('Content-Type', 'text/plain'); + return $resultPage; } } diff --git a/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php index d3a7a97c7ea80..9ba2a33257751 100644 --- a/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php @@ -3,40 +3,42 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Robots\Test\Unit\Controller\Index; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\Robots\Controller\Index\Index; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Index */ - private $contextMock; + private $controller; /** - * @var \Magento\Framework\Controller\Result\RawFactory|\PHPUnit_Framework_MockObject_MockObject + * @var PageFactory|MockObject */ - private $resultPageFactory; - - /** - * @var \Magento\Robots\Controller\Index\Index - */ - private $controller; + private $resultPageFactoryMock; protected function setUp() { - $this->contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultPageFactory = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + $this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->controller = new \Magento\Robots\Controller\Index\Index( - $this->contextMock, - $this->resultPageFactory + $objectManager = new ObjectManager($this); + $this->controller = $objectManager->getObject( + Index::class, + [ + 'resultPageFactory' => $this->resultPageFactoryMock + ] ); } @@ -45,7 +47,7 @@ protected function setUp() */ public function testExecute() { - $resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); $resultPageMock->expects($this->once()) @@ -55,13 +57,12 @@ public function testExecute() ->method('setHeader') ->with('Content-Type', 'text/plain'); - $this->resultPageFactory->expects($this->any()) - ->method('create') + $this->resultPageFactoryMock->method('create') ->with(true) ->willReturn($resultPageMock); $this->assertInstanceOf( - \Magento\Framework\View\Result\Page::class, + Page::class, $this->controller->execute() ); } From 57db694f5c53141854574b61777cd6de45459a35 Mon Sep 17 00:00:00 2001 From: konarshankar07 <konar.shankar2013@gmail.com> Date: Mon, 23 Mar 2020 11:23:04 +0530 Subject: [PATCH 527/734] Code review changes --- .../MediaGallery/Test/Unit/Model/DataExtractorTest.php | 4 ++-- app/code/Magento/MediaGallery/etc/db_schema.xml | 2 +- app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php index b412834f13267..f70e4ccdae22c 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -67,7 +67,7 @@ public function testExtractData(string $class, $interfaceClass, array $expectedD * @param array $data * @param object $model */ - protected function checkAssetValues(array $expectedData, array $data, $model) + private function checkAssetValues(array $expectedData, array $data, $model) { foreach ($expectedData as $expectedDataKey => $expectedDataItem) { $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); @@ -81,7 +81,7 @@ protected function checkAssetValues(array $expectedData, array $data, $model) * @param array $data * @param object $model */ - protected function checkKeyWordValues(array $expectedData, array $data, $model) + private function checkKeyWordValues(array $expectedData, array $data, $model) { foreach ($expectedData as $expectedDataKey => $expectedDataItem) { $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); diff --git a/app/code/Magento/MediaGallery/etc/db_schema.xml b/app/code/Magento/MediaGallery/etc/db_schema.xml index 8cc88de724ca6..8bcad28de2a8e 100644 --- a/app/code/Magento/MediaGallery/etc/db_schema.xml +++ b/app/code/Magento/MediaGallery/etc/db_schema.xml @@ -14,7 +14,7 @@ <column xsi:type="varchar" name="content_type" length="255" nullable="true" comment="Content Type"/> <column xsi:type="int" name="width" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Width"/> <column xsi:type="int" name="height" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Height"/> - <column xsi:type="int" name="size" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Size"/> + <column xsi:type="int" name="size" padding="10" unsigned="true" nullable="false" identity="false" comment="Asset file size in bytes"/> <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At"/> <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At"/> <constraint xsi:type="primary" referenceId="PRIMARY"> diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php index 5b45eb41cc727..0f4b78a6dc603 100644 --- a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php +++ b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php @@ -66,7 +66,7 @@ public function getHeight(): int; public function getWidth(): int; /** - * Retrieve full licensed asset's size + * Retrieve asset file size in bytes * * @return int */ From 768e5def16f8934357b80c0e633cafda37ff86e5 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 23 Mar 2020 08:51:51 +0200 Subject: [PATCH 528/734] MC-32546: Website Payments Pro Hosted Solution redirect to home page --- .../Paypal/Controller/Hostedpro/Cancel.php | 29 ++++++++++++++++-- .../Controller/Hostedpro/ReturnAction.php | 30 +++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php b/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php index 629431e13879c..981d63b88225c 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php @@ -1,15 +1,24 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Controller\Hostedpro; +use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Paypal\Helper\Checkout; -class Cancel extends \Magento\Framework\App\Action\Action +/** + * PayPal Hostedpro cancel controller. + */ +class Cancel extends Action implements CsrfAwareActionInterface, HttpGetActionInterface { /** * @var Checkout @@ -40,4 +49,20 @@ public function execute() $this->_redirect('checkout', ['_fragment' => 'payment']); } + + /** + * @inheritDoc + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } } diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php index 1643f480d25e9..2451247b57617 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php @@ -1,12 +1,22 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Controller\Hostedpro; -class ReturnAction extends \Magento\Framework\App\Action\Action +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +/** + * PayPal Hostedpro return controller. + */ +class ReturnAction extends Action implements CsrfAwareActionInterface, HttpPostActionInterface { /** * When a customer return to website from gateway. @@ -21,4 +31,20 @@ public function execute() $this->_redirect('checkout/onepage/success'); } } + + /** + * @inheritDoc + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } } From 711713b79a25d0e4254c2d606f9554d7d3758f30 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 23 Mar 2020 10:33:40 +0200 Subject: [PATCH 529/734] MC-32405: [Magento Cloud] - Cannot install a clean instance with pre-defined stores configuration in config.php | Forward port - MDVA-19809 --- .../App/Config/Source/InitialConfigSource.php | 3 +- .../Config/Source/InitialConfigSourceTest.php | 175 ++++++++++++++++++ .../Magento/Store/_files/dump_config.php | 65 +++++++ 3 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php diff --git a/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php b/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php index d6e98c0ddcdaa..4598a70e99397 100644 --- a/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php +++ b/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php @@ -52,6 +52,7 @@ public function __construct( /** * Return whole config data from config file for specified config type. + * * Ignore $path argument due to config source must return all config data * * @param string $path @@ -65,7 +66,7 @@ public function get($path = '') * * @see \Magento\Store\Model\Config\Importer To import store configs */ - if ($this->deploymentConfig->isAvailable()) { + if ($this->deploymentConfig->isAvailable() || $this->deploymentConfig->isDbAvailable()) { return []; } diff --git a/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php b/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php new file mode 100644 index 0000000000000..282b2d1cc24bb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php @@ -0,0 +1,175 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\App\Config\Source; + +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\App\DeploymentConfig\Writer; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Filesystem; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test that initial scopes config are loaded if database is available + */ +class InitialConfigSourceTest extends TestCase +{ + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var ConfigFilePool + */ + private $configFilePool; + + /** + * @var FileReader + */ + private $reader; + + /** + * @var Writer + */ + private $writer; + + /** + * @var array + */ + private $config; + + /** + * @var array + */ + private $envConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->reader = $objectManager->get(FileReader::class); + $this->writer = $objectManager->get(Writer::class); + $this->filesystem = $objectManager->get(Filesystem::class); + $this->configFilePool = $objectManager->get(ConfigFilePool::class); + $this->storeManager = $objectManager->get(StoreManagerInterface::class); + $this->config = $this->loadConfig(); + $this->envConfig = $this->loadEnvConfig(); + $this->loadDumpConfig(); + $this->storeManager->reinitStores(); + } + + /** + * @inheritdoc + */ + public function tearDown() + { + $this->clearConfig(ConfigFilePool::APP_CONFIG); + $this->clearConfig(ConfigFilePool::APP_ENV); + $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $this->config]); + $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); + $this->storeManager->reinitStores(); + } + + /** + * Test that initial scopes config are loaded if database is available + * + * @param array $websites + * @param string $defaultWebsite + * @param bool $offline + * @throws \Magento\Framework\Exception\LocalizedException + * @dataProvider getDefaultDataProvider + */ + public function testGetWebsites(array $websites, string $defaultWebsite, bool $offline = false): void + { + if ($offline) { + // remove application environment config for emulate work without db + $this->clearConfig(ConfigFilePool::APP_ENV); + } + $this->assertEquals($defaultWebsite, $this->storeManager->getWebsite()->getCode()); + $this->assertEquals($websites, array_keys($this->storeManager->getWebsites(true, true)), '', 0.0, 10, true); + } + + /** + * @return array + */ + public function getDefaultDataProvider(): array + { + return [ + [ + [ + 'admin', + 'base', + ], + 'base', + false + ], + [ + [ + 'admin', + 'main', + ], + 'main', + true + ] + ]; + } + + private function clearConfig(string $type): void + { + $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( + $this->configFilePool->getPath($type), + "<?php\n return [];\n" + ); + } + + /** + * @return void + */ + private function loadDumpConfig(): void + { + $data = array_replace_recursive( + $this->config, + $this->getDumpConfig() + ); + $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $data], true); + } + + /** + * @return array + */ + private function getDumpConfig(): array + { + return require __DIR__ . '/../../../_files/dump_config.php'; + } + + /** + * @return array + */ + private function loadConfig(): array + { + return $this->reader->load(ConfigFilePool::APP_CONFIG); + } + + /** + * @return array + */ + private function loadEnvConfig(): array + { + return $this->reader->load(ConfigFilePool::APP_ENV); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php b/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php new file mode 100644 index 0000000000000..7e4337ee65200 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'scopes' => [ + 'websites' => [ + 'admin' => [ + 'website_id' => '0', + 'code' => 'admin', + 'name' => 'Admin', + 'sort_order' => '0', + 'default_group_id' => '0', + 'is_default' => '0', + ], + 'main' => [ // base website code was changed to main + 'website_id' => '1', + 'code' => 'main', + 'name' => 'Main Website', + 'sort_order' => '0', + 'default_group_id' => '1', + 'is_default' => '1', + ], + ], + 'groups' => [ + 0 => [ + 'group_id' => '0', + 'website_id' => '0', + 'code' => 'default', + 'name' => 'Default', + 'root_category_id' => '0', + 'default_store_id' => '0', + ], + 1 => [ + 'group_id' => '1', + 'website_id' => '1', + 'code' => 'main_website_store', + 'name' => 'Main Website Store', + 'root_category_id' => '2', + 'default_store_id' => '1', + ], + ], + 'stores' => [ + 'admin' => [ + 'store_id' => '0', + 'code' => 'admin', + 'website_id' => '0', + 'group_id' => '0', + 'name' => 'Admin', + 'sort_order' => '0', + 'is_active' => '1', + ], + 'default' => [ + 'store_id' => '1', + 'code' => 'default', + 'website_id' => '1', + 'group_id' => '1', + 'name' => 'Default Store View', + 'sort_order' => '0', + 'is_active' => '1', + ], + ], + ] +]; From 7c5185423f8e595f0589b36d1f61b9c58975790b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 23 Mar 2020 10:43:05 +0200 Subject: [PATCH 530/734] MC-32638: [MFTF] AdminCreateSimpleProductWithDatetimeAttributeTest fails because of bad design --- ...CreateSimpleProductWithDatetimeAttributeTest.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml index fe5b70b8e4ca7..2141f44113057 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -19,8 +19,9 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> + <after> <deleteData createDataKey="createDatetimeAttribute" stepKey="deleteDatetimeAttribute"/> <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> @@ -45,13 +46,15 @@ <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addDatetimeAttribute"> <argument name="attributeCode" value="$createDatetimeAttribute.attribute_code$"/> </actionGroup> - <!-- Flush config cache to reset product attributes in attribute set --> - <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> <!-- Save the product --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!-- Flush config cache to reset product attributes in attribute set --> + <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> + <reloadPage stepKey="reloadProductEditPage"/> <!-- Check default value --> - <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="goToAttributesSection"/> - <click selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="openAttributesSection"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="waitAttributesSectionAppears"/> + <conditionalClick selector="{{AdminProductAttributesSection.sectionHeader}}" dependentSelector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" visible="false" stepKey="openAttributesSection"/> + <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="scrollToAttributesSection"/> <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" stepKey="waitForSlideOutAttributes"/> <seeInField selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" userInput="$generateDefaultValue" stepKey="checkDefaultValue"/> <!-- Check datetime grid filter --> From 32b6ce68c8b5a2b4ef1dee3a8a4ca793162f4355 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 23 Mar 2020 11:08:35 +0200 Subject: [PATCH 531/734] improved the MFTF tests --- ...buteHtmlTagsValidationErrorActionGroup.xml | 25 ------------------- ...uctAttributeValidationErrorActionGroup.xml | 24 ++++++++++++++++++ .../Page/AdminProductAttributeFormPage.xml | 1 + ...uctAttributeLabelDontAllowHtmlTagsTest.xml | 12 ++++++++- 4 files changed, 36 insertions(+), 26 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml deleted file mode 100644 index df57b4ae2093e..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeHtmlTagsValidationErrorActionGroup.xml +++ /dev/null @@ -1,25 +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="AssertSeeProductAttributeValidationErrorActionGroup"> - <annotations> - <description>Validates that the HTML tags issue is present on the attribute page after save.</description> - </annotations> - - <!-- See the error message on the "Manage Labels Tab" --> - <seeElement selector="#attribute-labels-table .mage-error" stepKey="seeHtmlTagsUsageErrorMessage1"/> - - <!-- Click on "Properties" tab on left menu --> - <click selector="{{AttributePropertiesSection.propertiesTab}}" stepKey="clickPropertiesTab"/> - - <!-- See the error message on the "Properties" Tab --> - <seeElement selector=".field-attribute_label .mage-error" stepKey="seeHtmlTagsUsageErrorMessage2"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml new file mode 100644 index 0000000000000..590828fca8185 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.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="AssertSeeProductAttributeValidationErrorActionGroup"> + <annotations> + <description>Check whenever the validation is present for the product attribute</description> + </annotations> + + <arguments> + <argument name="selector" type="string"/> + <argument name="message" type="string"/> + </arguments> + + <seeElement selector="{{selector}}" stepKey="seeValidationDomElement"/> + <see userInput="{{message}}" selector="{{selector}}" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml index fab87f90f86dd..3acaaf4e1775d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml @@ -9,5 +9,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ProductAttributePage" url="catalog/product_attribute/new/" area="admin" module="Catalog"> <section name="AdminCreateProductAttributeSection"/> + <section name="AdminProductAttributeManageLabelsSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml index b967403b0eb80..25b314e18e211 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml @@ -39,6 +39,16 @@ <actionGroup ref="AdminSaveProductAttributeActionGroup" stepKey="saveAttribute"/> - <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeLabelsForHtmlTags"/> + <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeStoreViewLabelForHtmlTags"> + <argument name="selector" value="#attribute-labels-table .mage-error"/> + <argument name="message" value="HTML tags are not allowed"/> + </actionGroup> + + <click selector="{{AttributePropertiesSection.propertiesTab}}" stepKey="clickPropertiesTab"/> + + <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeLabelForHtmlTags"> + <argument name="selector" value=".field-attribute_label .mage-error"/> + <argument name="message" value="HTML tags are not allowed"/> + </actionGroup> </test> </tests> From 9c5528ff66f77990dd05370c8ddade8e522a4d0e Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir <s.zhovnir@atwix.com> Date: Mon, 23 Mar 2020 11:29:49 +0200 Subject: [PATCH 532/734] Fixed the wrong behavior for a prompt modal when a user clicks on the overlay --- app/code/Magento/Ui/view/base/web/js/modal/prompt.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/prompt.js b/app/code/Magento/Ui/view/base/web/js/modal/prompt.js index 443d35f1b0ded..84e9494a8e781 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/prompt.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/prompt.js @@ -111,6 +111,7 @@ define([ _create: function () { this.options.focus = this.options.promptField; this.options.validation = this.options.validation && this.options.validationRules.length; + this.options.outerClickHandler = this.options.outerClickHandler || _.bind(this.closeModal, this, false); this._super(); this.modal.find(this.options.modalContent).append(this.getFormTemplate()); this.modal.find(this.options.modalCloseBtn).off().on('click', _.bind(this.closeModal, this, false)); From 196e353adbd04e59d8603ca1c03e8ab88cea8d78 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 23 Mar 2020 14:24:51 +0200 Subject: [PATCH 533/734] refactored MFTF tests --- ...oductAttributePageSwitchTabActionGroup.xml | 21 ++++++++++++++++ ...uctAttributeValidationErrorActionGroup.xml | 6 ++--- ...ationErrorOnManageLabelsTabActionGroup.xml | 24 +++++++++++++++++++ ...minProductAttributeManageLabelsSection.xml | 1 - ...uctAttributeLabelDontAllowHtmlTagsTest.xml | 14 ++++++----- 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml new file mode 100644 index 0000000000000..f3fcab490b3d9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductAttributePageSwitchTabActionGroup"> + <annotations> + <description>Switches the active tab on the Product Attribute New/Edit Page</description> + </annotations> + <arguments> + <argument name="tabName" type="string"/> + </arguments> + + <click selector="#product_attribute_tabs a[title='{{tabName}}']" stepKey="changeProductAttributeActiveTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml index 590828fca8185..b612e49cd05b6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml @@ -8,13 +8,13 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertSeeProductAttributeValidationErrorActionGroup"> + <actionGroup name="AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup"> <annotations> - <description>Check whenever the validation is present for the product attribute</description> + <description>Check whenever the validation error is present for the product attribute in the "Properties" Tab</description> </annotations> <arguments> - <argument name="selector" type="string"/> + <argument name="selector" type="string" defaultValue=".field-attribute_label .mage-error"/> <argument name="message" type="string"/> </arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml new file mode 100644 index 0000000000000..a4fb1e4a4a007 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.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="AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup"> + <annotations> + <description>Check whenever the validation error is present for the product attribute in the "Manage Labels" Tab</description> + </annotations> + + <arguments> + <argument name="selector" type="string" defaultValue="#attribute-labels-table .mage-error"/> + <argument name="message" type="string"/> + </arguments> + + <seeElement selector="{{selector}}" stepKey="seeValidationDomElement"/> + <see userInput="{{message}}" selector="{{selector}}" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml index 048016620c94d..c5e91854468e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml @@ -9,7 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeManageLabelsSection"> - <element name="ManageLabelsTab" selector="#product_attribute_tabs_labels" type="button"/> <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml index 25b314e18e211..f3981e7b8f76a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml @@ -31,7 +31,9 @@ <argument name="attributeType" value="{{productAttributeWithHtmlTagsInLabel.frontend_input}}" /> </actionGroup> - <click selector="{{AdminProductAttributeManageLabelsSection.ManageLabelsTab}}" stepKey="clickManageLabelsTab"/> + <actionGroup ref="AdminProductAttributePageSwitchTabActionGroup" stepKey="makeManageLabelsTabActive"> + <argument name="tabName" value="Manage Labels"/> + </actionGroup> <actionGroup ref="AdminFillProductAttributeDefaultStoreViewActionGroup" stepKey="fillAttributeDefaultStoreViewLabel"> <argument name="value" value="{{productAttributeWithHtmlTagsInLabel.default_store_label}}"/> @@ -39,15 +41,15 @@ <actionGroup ref="AdminSaveProductAttributeActionGroup" stepKey="saveAttribute"/> - <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeStoreViewLabelForHtmlTags"> - <argument name="selector" value="#attribute-labels-table .mage-error"/> + <actionGroup ref="AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup" stepKey="validateAttributeStoreViewLabelForHtmlTags"> <argument name="message" value="HTML tags are not allowed"/> </actionGroup> - <click selector="{{AttributePropertiesSection.propertiesTab}}" stepKey="clickPropertiesTab"/> + <actionGroup ref="AdminProductAttributePageSwitchTabActionGroup" stepKey="makePropertiesTabActive"> + <argument name="tabName" value="Properties"/> + </actionGroup> - <actionGroup ref="AssertSeeProductAttributeValidationErrorActionGroup" stepKey="validateAttributeLabelForHtmlTags"> - <argument name="selector" value=".field-attribute_label .mage-error"/> + <actionGroup ref="AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup" stepKey="validateAttributeLabelForHtmlTags"> <argument name="message" value="HTML tags are not allowed"/> </actionGroup> </test> From eb26773b3353119234c4010d830ccc57076160db Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Fri, 20 Mar 2020 17:26:54 -0500 Subject: [PATCH 534/734] MC-23540: Elasticsearch 7.x.x Upgrade --- .../Elasticsearch/SearchAdapter/Mapper.php | 65 ++++++ .../Test/Unit/SearchAdapter/MapperTest.php | 204 ++++++++++++++++++ .../Test/Legacy/_files/obsolete_classes.php | 1 - 3 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php create mode 100644 app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php b/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php new file mode 100644 index 0000000000000..d76086ee2f809 --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter; + +use Magento\Framework\Search\RequestInterface; +use Magento\Framework\Search\Request\Query\BoolExpression as BoolQuery; +use Magento\Elasticsearch\SearchAdapter\Query\Builder as QueryBuilder; +use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; +use Magento\Elasticsearch\SearchAdapter\Filter\Builder as FilterBuilder; +use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper as Elasticsearch5Mapper; + +/** + * Mapper class for Elasticsearch2 + * + * @api + * @since 100.1.0 + * @deprecated because of EOL for Elasticsearch2 + */ +class Mapper extends Elasticsearch5Mapper +{ + /** + * @param QueryBuilder $queryBuilder + * @param MatchQueryBuilder $matchQueryBuilder + * @param FilterBuilder $filterBuilder + */ + public function __construct( + QueryBuilder $queryBuilder, + MatchQueryBuilder $matchQueryBuilder, + FilterBuilder $filterBuilder + ) { + $this->queryBuilder = $queryBuilder; + $this->matchQueryBuilder = $matchQueryBuilder; + $this->filterBuilder = $filterBuilder; + } + + /** + * Build adapter dependent query + * + * @param RequestInterface $request + * @return array + * @since 100.1.0 + */ + public function buildQuery(RequestInterface $request) + { + $searchQuery = $this->queryBuilder->initQuery($request); + $searchQuery['body']['query'] = array_merge( + $searchQuery['body']['query'], + $this->processQuery( + $request->getQuery(), + [], + BoolQuery::QUERY_CONDITION_MUST + ) + ); + + $searchQuery['body']['query']['bool']['minimum_should_match'] = 1; + + $searchQuery = $this->queryBuilder->initAggregations($request, $searchQuery); + return $searchQuery; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php new file mode 100644 index 0000000000000..1e552e395c37f --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php @@ -0,0 +1,204 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Test\Unit\SearchAdapter; + +use Magento\Elasticsearch\SearchAdapter\Mapper; +use Magento\Elasticsearch\SearchAdapter\Query\Builder as QueryBuilder; +use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; +use Magento\Elasticsearch\SearchAdapter\Filter\Builder as FilterBuilder; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MapperTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Mapper + */ + protected $model; + + /** + * @var QueryBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $queryBuilder; + + /** + * @var MatchQueryBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $matchQueryBuilder; + + /** + * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $filterBuilder; + + /** + * Setup method + * @return void + */ + protected function setUp() + { + $this->queryBuilder = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Query\Builder::class) + ->setMethods([ + 'initQuery', + 'initAggregations', + ]) + ->disableOriginalConstructor() + ->getMock(); + $this->matchQueryBuilder = $this->getMockBuilder( + \Magento\Elasticsearch\SearchAdapter\Query\Builder\Match::class + ) + ->setMethods(['build']) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilder = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Filter\Builder::class) + ->disableOriginalConstructor() + ->getMock(); + $this->queryBuilder->expects($this->any()) + ->method('initQuery') + ->willReturn([ + 'body' => [ + 'query' => [], + ], + ]); + $this->queryBuilder->expects($this->any()) + ->method('initAggregations') + ->willReturn([ + 'body' => [ + 'query' => [], + ], + ]); + $this->matchQueryBuilder->expects($this->any()) + ->method('build') + ->willReturn([]); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + \Magento\Elasticsearch\SearchAdapter\Mapper::class, + [ + 'queryBuilder' => $this->queryBuilder, + 'matchQueryBuilder' => $this->matchQueryBuilder, + 'filterBuilder' => $this->filterBuilder + ] + ); + } + + /** + * Test buildQuery() method with exception + * @expectedException \InvalidArgumentException + */ + public function testBuildQueryFailure() + { + $request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $query = $this->getMockBuilder(\Magento\Framework\Search\Request\QueryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $query->expects($this->atLeastOnce()) + ->method('getType') + ->willReturn('unknown'); + + $this->model->buildQuery($request); + } + + /** + * Test buildQuery() method + * + * @param string $queryType + * @param string $queryMock + * @param string $referenceType + * @param string $filterMock + * @dataProvider buildQueryDataProvider + */ + public function testBuildQuery($queryType, $queryMock, $referenceType, $filterMock) + { + $request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $query = $this->getMockBuilder($queryMock) + ->setMethods(['getMust', 'getMustNot', 'getType', 'getShould', 'getReferenceType', 'getReference']) + ->disableOriginalConstructor() + ->getMock(); + $matchQuery = $this->getMockBuilder(\Magento\Framework\Search\Request\Query\Match::class) + ->disableOriginalConstructor() + ->getMock(); + $filterQuery = $this->getMockBuilder($filterMock) + ->disableOriginalConstructor() + ->getMock(); + $request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + + $query->expects($this->atLeastOnce()) + ->method('getType') + ->willReturn($queryType); + $query->expects($this->any()) + ->method('getMust') + ->willReturn([$matchQuery]); + $query->expects($this->any()) + ->method('getShould') + ->willReturn([]); + $query->expects($this->any()) + ->method('getMustNot') + ->willReturn([]); + $query->expects($this->any()) + ->method('getReferenceType') + ->willReturn($referenceType); + $query->expects($this->any()) + ->method('getReference') + ->willReturn($filterQuery); + $matchQuery->expects($this->any()) + ->method('getType') + ->willReturn('matchQuery'); + $filterQuery->expects($this->any()) + ->method('getType') + ->willReturn('matchQuery'); + $filterQuery->expects($this->any()) + ->method('getType') + ->willReturn('matchQuery'); + $this->filterBuilder->expects(($this->any())) + ->method('build') + ->willReturn([ + 'bool' => [ + 'must' => [], + ], + ]); + + $this->model->buildQuery($request); + } + + /** + * @return array + */ + public function buildQueryDataProvider() + { + return [ + [ + 'matchQuery', \Magento\Framework\Search\Request\Query\Match::class, + 'query', \Magento\Framework\Search\Request\QueryInterface::class, + ], + [ + 'boolQuery', \Magento\Framework\Search\Request\Query\BoolExpression::class, + 'query', \Magento\Framework\Search\Request\QueryInterface::class, + ], + [ + 'filteredQuery', \Magento\Framework\Search\Request\Query\Filter::class, + 'query', \Magento\Framework\Search\Request\QueryInterface::class, + ], + [ + 'filteredQuery', \Magento\Framework\Search\Request\Query\Filter::class, + 'filter', \Magento\Framework\Search\Request\FilterInterface::class, + ], + ]; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 885a84e790b70..40009eb10a6c7 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4249,7 +4249,6 @@ ['Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper'], ['Magento\Elasticsearch\Model\Client\Elasticsearch'], ['Magento\Elasticsearch\SearchAdapter\Aggregation\Interval'], - ['Magento\Elasticsearch\SearchAdapter\Mapper'], ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType'], ['Magento\Elasticsearch\Model\Adapter\DataMapperInterface'], ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy'], From 6b6a2f7dee40b5bf6daada05e6e52020fe015d1a Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 23 Mar 2020 15:14:12 +0200 Subject: [PATCH 535/734] refactored MFTF tests --- .../AdminProductAttributePageSwitchTabActionGroup.xml | 2 +- ...ctAttributeValidationErrorOnManageLabelsTabActionGroup.xml | 4 +--- ...uctAttributeValidationErrorOnPropertiesTabActionGroup.xml} | 4 +--- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 1 + .../Test/Mftf/Section/AdminEditProductAttributesSection.xml | 1 + .../Mftf/Section/AdminProductAttributeManageLabelsSection.xml | 1 + 6 files changed, 6 insertions(+), 7 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/ActionGroup/{AssertSeeProductAttributeValidationErrorActionGroup.xml => AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml} (72%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml index f3fcab490b3d9..cbef1b61fe44c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml @@ -16,6 +16,6 @@ <argument name="tabName" type="string"/> </arguments> - <click selector="#product_attribute_tabs a[title='{{tabName}}']" stepKey="changeProductAttributeActiveTab"/> + <click selector="{{AdminEditProductAttributesSection.tabButton(tabName)}}" stepKey="changeProductAttributeActiveTab"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml index a4fb1e4a4a007..370d30003baf8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml @@ -14,11 +14,9 @@ </annotations> <arguments> - <argument name="selector" type="string" defaultValue="#attribute-labels-table .mage-error"/> <argument name="message" type="string"/> </arguments> - <seeElement selector="{{selector}}" stepKey="seeValidationDomElement"/> - <see userInput="{{message}}" selector="{{selector}}" stepKey="seeValidationMessage"/> + <see userInput="{{message}}" selector="{{AdminProductAttributeManageLabelsSection.attributeStoreLabelValidationError}}" stepKey="seeValidationMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml similarity index 72% rename from app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml rename to app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml index b612e49cd05b6..5e0da379bdccb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml @@ -14,11 +14,9 @@ </annotations> <arguments> - <argument name="selector" type="string" defaultValue=".field-attribute_label .mage-error"/> <argument name="message" type="string"/> </arguments> - <seeElement selector="{{selector}}" stepKey="seeValidationDomElement"/> - <see userInput="{{message}}" selector="{{selector}}" stepKey="seeValidationMessage"/> + <see userInput="{{message}}" selector="{{AttributePropertiesSection.attributeLabelValidationError}}" stepKey="seeValidationMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 0934e39dcb062..671133d560576 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -30,6 +30,7 @@ <element name="dropdownNthOptionAdmin" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(3) input" parameterized="true"/> <element name="dropdownNthOptionDefaultStoreView" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(4) input" parameterized="true"/> <element name="dropdownNthOptionDelete" type="button" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) button[title='Delete']" parameterized="true"/> + <element name="attributeLabelValidationError" type="text" selector=".field-attribute_label .mage-error"/> </section> <section name="AttributeDeleteModalSection"> <element name="confirm" type="button" selector=".modal-popup.confirm .action-accept"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml index b243fbfd6034a..ad4ab57f8de2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml @@ -22,5 +22,6 @@ <element name="ProductDataMayBeLostConfirmButton" type="button" selector="//aside[contains(@class,'_show')]//button[.='Change Input Type']"/> <element name="defaultLabel" type="text" selector="//td[contains(text(), '{{attributeName}}')]/following-sibling::td[contains(@class, 'col-frontend_label')]" parameterized="true"/> <element name="formByStoreId" type="block" selector="//form[contains(@action,'store/{{store_id}}')]" parameterized="true"/> + <element name="tabButton" type="text" selector="#product_attribute_tabs a[title='{{tabName}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml index c5e91854468e2..e1863576b8ecf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeManageLabelsSection"> <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> + <element name="attributeStoreLabelValidationError" type="text" selector="#attribute-labels-table .mage-error"/> </section> </sections> From ca38be25388a06197e60ae76a6619433ece17446 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 23 Mar 2020 16:19:55 +0200 Subject: [PATCH 536/734] MC-24244: [MFTF Test] Instant purchase negative scenarios --- .../Braintree/Test/Mftf/Data/ConfigData.xml | 23 ++ ...chaseFunctionalityNegativeScenarioTest.xml | 227 ++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml create mode 100644 app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..a5bc8b7d8054c --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,23 @@ +<?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="Enable3DSecureBraintree"> + <data key="path">payment/braintree/verify_3dsecure</data> + <data key="value">1</data> + </entity> + <entity name="Disable3DSecureBraintree"> + <data key="path">payment/braintree/verify_3dsecure</data> + <data key="value">0</data> + </entity> + <entity name="DisableVaultBraintree"> + <data key="path">payment/braintree_cc_vault/active</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml new file mode 100644 index 0000000000000..68a2cf0d3262c --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml @@ -0,0 +1,227 @@ +<?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="StorefrontInstantPurchaseFunctionalityNegativeScenarioTest"> + <annotations> + <features value="InstantPurchase"/> + <stories value="Using Instant Purchase - Negative Scenario"/> + <title value="Checks negative Instant Purchase functionality scenario"/> + <description value="Checks that Instant Purchase button does not appears in a different situation"/> + <useCaseId value="MAGETWO-84214"/> + <testCaseId value="MC-25949"/> + <severity value="CRITICAL"/> + <group value="instant_purchase"/> + <group value="vault"/> + <group value="braintree"/> + </annotations> + <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> + <!-- Configure Braintree payment method --> + <createData entity="BraintreeConfig" stepKey="configureBraintreePayment"/> + <!-- Enable Braintree with Vault --> + <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintreeAndVault"/> + <!-- Create customers: without address, with address, with saved shipping and billing --> + <createData entity="Simple_Customer_Without_Address" stepKey="customerWithoutAddress"/> + <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customerWithAddress"/> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="customerWithDefaultAddress"/> + <!-- Create all product variations --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> + <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> + <!-- Create Bundle Product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Create Downloadable Product --> + <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + <!-- Create Grouped Product --> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="createLinkForGroupedProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Log in as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- Customer placed order from storefront with payment method --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStep"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethod"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoad"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSection"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardData"> + <argument name="cartData" value="VisaDefaultCard"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardData"/> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUse"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </before> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="customerWithoutAddress" stepKey="deleteCustomerWithoutAddress"/> + <deleteData createDataKey="customerWithAddress" stepKey="deleteCustomerWithAddress"/> + <deleteData createDataKey="customerWithDefaultAddress" stepKey="deleteCustomerWithDefaultAddress"/> + <!-- Set configs to default --> + <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="restoreToDefaultFlatRate"/> + <magentoCLI command="config:set {{Disable3DSecureBraintree.path}} {{Disable3DSecureBraintree.value}}" stepKey="restoreToDefault3DSecureVerification"/> + <createData entity="DefaultBraintreeConfig" stepKey="defaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollBackCustomBraintreeConfigurationData"/> + <!-- Remove created products/attributes --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <!-- Remove Downloadable Product --> + <magentoCLI command="downloadable:domains:remove static.magento.com" stepKey="removeDownloadableDomain"/> + <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <!-- Remove Configurable Product --> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + </after> + <!-- 1. Ensure customer is a guest --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <!-- 2. Browse all product pages and verify that the "Instant Purchase" button does not appear --> + <!-- Simple product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openProductPage"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPage"/> + <!-- Virtual product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openVirtualProductPage"> + <argument name="product" value="$createVirtualProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnVirtualProductPage"/> + <!-- Downloadable Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openDownloadableProductPage"> + <argument name="product" value="$createDownloadableProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnDownloadableProductPage"/> + <!-- Bundle Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openBundleProductPage"> + <argument name="product" value="$createBundleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForPageLoad stepKey="waitForBundleProductPageLoad"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnBundleProductPage"/> + <!-- Grouped product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openGroupedProductPage"> + <argument name="product" value="$createGroupedProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnGroupedProductPage"/> + <!-- Configurable Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="product" value="$createConfigProductCreateConfigurableProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnConfigurableProductPage"/> + <!-- 3. Log in as a customer without address --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithoutAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithoutAddress$"/> + </actionGroup> + <!-- 4. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageAgain"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageAgain"/> + <!-- 5. Log in as a customer with address --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithoutAddress"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithAddress$"/> + </actionGroup> + <!-- 6. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageOneMoreTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageOneMoreTime"/> + <!-- 7. Log in as a customer with default address --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddress"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 8. Browse simple product page and check that Instant Purchase button show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForFourthTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButton"/> + <!-- 9-10. Configure Braintree Payment Method(without Vault). Configure 3d Secure Verification --> + <magentoCLI command="config:set {{DisableVaultBraintree.path}} {{DisableVaultBraintree.value}}" stepKey="disableVault"/> + <magentoCLI command="config:set {{Enable3DSecureBraintree.path}} {{Enable3DSecureBraintree.value}}" stepKey="enable3DSecureVerification"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressAgain"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAgain"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 11. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForFifthTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageForFifthTime"/> + <!-- 12. Disable all supported payment methods --> + <createData entity="DefaultBraintreeConfig" stepKey="restoreToDefaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="restoreToDefaultBraintreeConfigurationData"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressOneMoreTime"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontOneMoreTime"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 13. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForSixthTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageForSixthTime"/> + <!-- 14. Reenable supported payment method (without 3d secure for Braintree) --> + <magentoCLI command="config:set {{Disable3DSecureBraintree.path}} {{Disable3DSecureBraintree.value}}" stepKey="disable3DSecureVerification"/> + <createData entity="BraintreeConfig" stepKey="reenableBraintreePayment"/> + <createData entity="CustomBraintreeConfigurationData" stepKey="reenableBraintreeAndVault"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressForFourthTime"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontFinalTime"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 15. Browse simple product page and check that Instant Purchase button show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForSeventhTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonAgain"/> + <!-- 16. Disable shipping method for customer with default address --> + <magentoCLI command="config:set {{DisableFlatRateConfigData.path}} {{DisableFlatRateConfigData.value}}" stepKey="disableFlatRate"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressForFifthTime"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontForFifthTime"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 17. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageFinalTime"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageFinalTime"/> + </test> +</tests> From 9eb5d52d6ffad536ebc6a8184ca7ef21e6107f3f Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Mon, 23 Mar 2020 10:01:42 -0500 Subject: [PATCH 537/734] MC-32201: Reorder functionality (test coverage) --- .../Magento/GraphQl/Sales/ReorderTest.php | 41 +++++++- .../Sales/_files/order_with_1_qty_product.php | 28 ++++++ .../order_with_1_qty_product_rollback.php | 7 ++ .../order_with_two_simple_products_qty_10.php | 99 +++++++++++++++++++ ...th_two_simple_products_qty_10_rollback.php | 8 ++ .../_files/order_with_zero_qty_product.php | 6 +- 6 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index f6bfd9e7b7575..43d04efe80f0c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -119,8 +119,6 @@ public function testSimpleProductOutOfStock() /** * Test reorder when simple product qty is 0, with allowed backorders configured to below 0 * - * @magentoConfigFixture admin_store cataloginventory/item_options/backorders 1 - * @magentoConfigFixture admin_store cataloginventory/item_options/use_deferred_stock_update 0 * @magentoApiDataFixture Magento/Sales/_files/order_with_zero_qty_product.php * @throws \Exception */ @@ -151,6 +149,45 @@ public function testBackorderWithOutOfStock() $this->assertResponseFields($response['reorderItems'], $expectedResponse); } + /** + * Test reorder with low stock for simple product + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_1_qty_product.php + * @throws \Exception + */ + public function testReorderWithLowStock() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => ['orderNumber'], + 'code' => 'INSUFFICIENT_STOCK', + 'message' => 'Could not add the product with SKU "simple" to the shopping cart: ' + . 'The requested qty is not available', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 10, + 'items' => [ + [ + 'quantity' => 10, + 'product' => [ + 'sku' => 'simple-2' + ] + ] + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse['cart']['total_quantity'] = 20; + $expectedResponse['cart']['items'][0]['quantity'] = 20; + + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + } + /** * Assert that simple product is not available. */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php new file mode 100644 index 0000000000000..162eca7d68209 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_qty_10.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set qty to 1 +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product qty to 1 +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 1, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php new file mode 100644 index 0000000000000..34cded299350d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php new file mode 100644 index 0000000000000..40e5a168655a2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(10); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(10) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php new file mode 100644 index 0000000000000..b6bc7c7113a85 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php index 3c0a2b35c5156..cdcda1d5dc2c4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +use Magento\CatalogInventory\Model\Stock; use Magento\TestFramework\Helper\Bootstrap; require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products.php'; @@ -12,7 +13,7 @@ /** @var $order \Magento\Sales\Model\Order */ $order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); -// load product and set it out of stock +// load product and set qty to 0 /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ $productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); $productSku = 'simple'; @@ -23,6 +24,9 @@ [ 'use_config_manage_stock' => 1, 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_YES_NONOTIFY, ] ); $productRepository->save($product); From eef736d952d1e1bdebaaf4e0cd2fae85c661cafb Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Mon, 23 Mar 2020 11:10:05 -0500 Subject: [PATCH 538/734] MC-32201: Reorder functionality (test coverage) --- .../Sales/ReorderMultipleProductsTest.php | 215 ++++++++++++++++++ .../order_with_different_types_of_product.php | 206 +++++++++++++++++ ...th_different_types_of_product_rollback.php | 8 + 3 files changed, 429 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php new file mode 100644 index 0000000000000..d87e3564ca980 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php @@ -0,0 +1,215 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder with different types of product. + */ +class ReorderMultipleProductsTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_different_types_of_product.php + */ + public function testMutplipleProducts() + { + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 5, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable-1', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + [ + 'quantity' => 1, + 'product' => + [ + 'sku' => 'virtual_product', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'bundle-product-radio-required-option', + ], + 'bundle_options' => [ + [ + 'label' => 'Radio Options', + 'type' => 'radio', + 'values' => [ + [ + 'label' => 'Simple Product', + 'price' => 10, + 'quantity' => 1, + ], + ], + ], + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'downloadable-product', + ], + 'links' => [ + [ + 'title' => 'Downloadable Product Link', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + message + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + ... on ConfigurableCartItem { + configurable_options { + option_label + value_label + } + } + ... on BundleCartItem { + bundle_options { + label + type + values { + label + price + quantity + } + } + } + ... on DownloadableCartItem { + links { + title + } + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php new file mode 100644 index 0000000000000..563f38e9dfa06 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php @@ -0,0 +1,206 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; +$simpleProduct = $secondProduct; + +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable.php'; +$configurableProduct = $productRepository->get($product->getSku()); +// this change is require for compatibility with downloadable product fixture, +// it sets id=1 for product that causes rewrite of configurable product +$configurableProduct->setId(111); +$configurableProduct->setUrlKey('configurable-product-new'); +$productRepository->save($configurableProduct); + +require __DIR__ . '/../../../Magento/GraphQl/Catalog/_files/virtual_product.php'; +$virtualProduct = $productRepository->get($product->getSku()); + +require __DIR__ . '/../../../Magento/Bundle/_files/bundle_product_radio_required_option.php'; +$bundleProduct = $productRepository->get($bundleProduct->getSku()); + +require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable.php'; +$downloadableProduct = $productRepository->get($product->getSku()); + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +// simple product item +$simpleProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$simpleProductItem->setProductId($simpleProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($simpleProduct->getPrice()) + ->setPrice($simpleProduct->getPrice()) + ->setRowTotal($simpleProduct->getPrice()) + ->setProductType($simpleProduct->getTypeId()) + ->setName($simpleProduct->getName()) + ->setSku($simpleProduct->getSku()) + ->setStoreId(0) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +// configurable product + +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class); +$option = $options->setAttributeFilter($attribute->getId()) + ->getFirstItem(); + +$requestInfo = [ + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId(), + ], +]; + +$qtyOrdered = 1; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderConfigurableItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderConfigurableItem->setProductId($configurableProduct->getId())->setQtyOrdered($qtyOrdered); +$orderConfigurableItem->setBasePrice($configurableProduct->getPrice()); +$orderConfigurableItem->setPrice($configurableProduct->getPrice()); +$orderConfigurableItem->setRowTotal($configurableProduct->getPrice()); +$orderConfigurableItem->setParentItemId(null); +$orderConfigurableItem->setProductType('configurable'); +$orderConfigurableItem->setProductOptions(['info_buyRequest' => $requestInfo]); +if ($configurableProduct->getExtensionAttributes() + && (array)$configurableProduct->getExtensionAttributes()->getConfigurableProductLinks() +) { + $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); + /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ + $simpleProduct = $productRepository->getById($simpleProductId); + $requestInfo['product'] = $simpleProductId; + $requestInfo['item'] = $simpleProduct; + $orderConfigurableItem->setProductOptions(['info_buyRequest' => $requestInfo]); +} + +// virtual product +$virtualProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$virtualProductItem->setProductId($virtualProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($virtualProduct->getPrice()) + ->setPrice($virtualProduct->getPrice()) + ->setRowTotal($virtualProduct->getPrice()) + ->setProductType($virtualProduct->getTypeId()) + ->setName($virtualProduct->getName()) + ->setSku($virtualProduct->getSku()) + ->setStoreId(0) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +// downloadable product +/** @var $linkCollection \Magento\Downloadable\Model\ResourceModel\Link\Collection */ +$linkCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Downloadable\Model\Link::class +)->getCollection()->addProductToFilter( + $downloadableProduct->getId() +)->addTitleToResult( + $downloadableProduct->getStoreId() +)->addPriceToResult( + $downloadableProduct->getStore()->getWebsiteId() +); + +/** @var $link \Magento\Downloadable\Model\Link */ +$links = $linkCollection->getItems(); +$requestInfo = [ + 'qty' => 1, + 'links' => array_keys($links) +]; + +$downloadableProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$downloadableProductItem->setProductId($downloadableProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($downloadableProduct->getPrice()) + ->setPrice($downloadableProduct->getPrice()) + ->setRowTotal($downloadableProduct->getPrice()) + ->setProductType($downloadableProduct->getTypeId()) + ->setName($downloadableProduct->getName()) + ->setSku($downloadableProduct->getSku()) + ->setStoreId($downloadableProduct->getStoreId()) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +// bundle product +/** @var $typeInstance \Magento\Bundle\Model\Product\Type */ +$typeInstance = $bundleProduct->getTypeInstance(); +$typeInstance->setStoreFilter($bundleProduct->getStoreId(), $bundleProduct); +$optionCollection = $typeInstance->getOptionsCollection($bundleProduct); + +$bundleOptions = []; +$bundleOptionsQty = []; +foreach ($optionCollection as $option) { + /** @var $option \Magento\Bundle\Model\Option */ + $selectionsCollection = $typeInstance->getSelectionsCollection([$option->getId()], $bundleProduct); + if ($option->isMultiSelection()) { + $bundleOptions[$option->getId()] = array_column($selectionsCollection->toArray(), 'selection_id'); + } else { + $bundleOptions[$option->getId()] = $selectionsCollection->getFirstItem()->getSelectionId(); + } + $bundleOptionsQty[$option->getId()] = 1; +} + +$requestInfo = [ + 'product' => $bundleProduct->getId(), + 'bundle_option' => $bundleOptions, + 'bundle_option_qty' => $bundleOptionsQty, + 'qty' => 1, +]; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderBundleItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderBundleItem->setProductId($bundleProduct->getId()); +$orderBundleItem->setQtyOrdered(1); +$orderBundleItem->setBasePrice($bundleProduct->getPrice()); +$orderBundleItem->setPrice($bundleProduct->getPrice()); +$orderBundleItem->setRowTotal($bundleProduct->getPrice()); +$orderBundleItem->setProductType($bundleProduct->getTypeId()); +$orderBundleItem->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($simpleProductItem); +$order->addItem($orderConfigurableItem); +$order->addItem($virtualProductItem); +$order->addItem($orderBundleItem); +$order->addItem($downloadableProductItem); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php new file mode 100644 index 0000000000000..6223dd13d0bf4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'default_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; From 06aead4055bd2597c95241496618dd8d99cf27b0 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Mon, 23 Mar 2020 16:49:31 -0500 Subject: [PATCH 539/734] MC-32201: Reorder functionality (test coverage) --- .../GraphQl/Sales/ReorderConfigurableWithVariationsTest.php | 1 - .../Sales/_files/order_with_two_configurable_variations.php | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php index d41c05730c0ae..9b8445efdce39 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -265,7 +265,6 @@ private function assetProductUndefined(array $response): void $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); } - /** * @param string $email * @param string $password diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php index 1c83f0a683710..90d8e858b7908 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php @@ -7,8 +7,6 @@ use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; -require 'default_rollback.php'; - $objectManager = Bootstrap::getObjectManager(); require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; @@ -54,7 +52,9 @@ $orderConfigurableItem->setParentItemId(null); $orderConfigurableItem->setProductType('configurable'); $configurableVariations = []; -foreach (array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()) as $key => $variationId) { +foreach (array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()) + as $key => $variationId +) { $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ From 4e70c4d9c08994dc263d876cdeafb9aeb49a1e3f Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Mon, 23 Mar 2020 19:51:21 -0500 Subject: [PATCH 540/734] MC-32201: Reorder functionality --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 42a3932d9a85e..5c852618894d2 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -144,6 +144,8 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu $this->addError($e->getMessage()); } + $cart = $this->cartRepository->get($cart->getId()); + return $this->prepareOutput($cart); } From 5482cdfb366c45cd4d8704ace4634c34117226c6 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 23 Mar 2020 22:37:35 -0500 Subject: [PATCH 541/734] MC-13825: [2.4.x] Migrate ZF2 components to Laminas -- fix merge conflict --- composer.json | 3 - composer.lock | 820 ++++++++++++++++++++------------------------------ 2 files changed, 322 insertions(+), 501 deletions(-) diff --git a/composer.json b/composer.json index 421e29123151b..0ecf3878fa7ff 100644 --- a/composer.json +++ b/composer.json @@ -65,9 +65,6 @@ "laminas/laminas-uri": "^2.5.1", "laminas/laminas-validator": "^2.6.0", "laminas/laminas-view": "~2.11.2", - "league/flysystem": "^1.0", - "league/flysystem-aws-s3-v3": "^1.0", - "league/flysystem-azure-blob-storage": "^0.1.6", "magento/composer": "1.6.x-dev", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", diff --git a/composer.lock b/composer.lock index 39e6fe4845513..51bceb357611d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,92 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "92b62c12f24800168ad73c8c41bb31e1", + "content-hash": "60eacd9a8d639c5c851fa8d443b3433f", "packages": [ - { - "name": "aws/aws-sdk-php", - "version": "3.133.32", - "source": { - "type": "git", - "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "c4bd227436446f02d2b9963f3ea4ae6dc380420b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c4bd227436446f02d2b9963f3ea4ae6dc380420b", - "reference": "c4bd227436446f02d2b9963f3ea4ae6dc380420b", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-simplexml": "*", - "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "^2.5", - "php": ">=5.5" - }, - "require-dev": { - "andrewsville/php-token-reflection": "^1.4", - "aws/aws-php-sns-message-validator": "~1.0", - "behat/behat": "~3.0", - "doctrine/cache": "~1.4", - "ext-dom": "*", - "ext-openssl": "*", - "ext-pcntl": "*", - "ext-sockets": "*", - "nette/neon": "^2.3", - "phpunit/phpunit": "^4.8.35|^5.4.3", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3" - }, - "suggest": { - "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", - "doctrine/cache": "To use the DoctrineCacheAdapter", - "ext-curl": "To send requests using cURL", - "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", - "ext-sockets": "To use client-side monitoring" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Aws\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" - } - ], - "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", - "keywords": [ - "amazon", - "aws", - "cloud", - "dynamodb", - "ec2", - "glacier", - "s3", - "sdk" - ], - "time": "2020-03-09T18:10:46+00:00" - }, { "name": "braintree/braintree_php", "version": "3.35.0", @@ -341,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.9.3", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b" + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/1291a16ce3f48bfdeca39d64fca4875098af4d7b", - "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b", + "url": "https://api.github.com/repos/composer/composer/zipball/b912a45da3e2b22f5cb5a23e441b697a295ba011", + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011", "shasum": "" }, "require": { @@ -363,17 +279,17 @@ "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0" + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { "symfony/console": "2.8.38" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7", - "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" + "phpspec/prophecy": "^1.10", + "symfony/phpunit-bridge": "^3.4" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", @@ -386,7 +302,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -417,7 +333,7 @@ "dependency", "package" ], - "time": "2020-02-04T11:58:49+00:00" + "time": "2020-03-13T19:34:27+00:00" }, { "name": "composer/semver", @@ -1667,16 +1583,16 @@ }, { "name": "laminas/laminas-feed", - "version": "2.12.0", + "version": "2.12.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-feed.git", - "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602" + "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/64d25e18a6ea3db90c27fe2d6b95630daa1bf602", - "reference": "64d25e18a6ea3db90c27fe2d6b95630daa1bf602", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/c9356994eb80d0f6b46d7e12ba048d450bf0cd72", + "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72", "shasum": "" }, "require": { @@ -1697,7 +1613,7 @@ "laminas/laminas-http": "^2.7", "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", "laminas/laminas-validator": "^2.10.1", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20", "psr/http-message": "^1.0.1" }, "suggest": { @@ -1730,7 +1646,7 @@ "feed", "laminas" ], - "time": "2019-12-31T16:46:54+00:00" + "time": "2020-03-23T10:40:31+00:00" }, { "name": "laminas/laminas-filter", @@ -1803,16 +1719,16 @@ }, { "name": "laminas/laminas-form", - "version": "2.14.3", + "version": "2.14.4", "source": { "type": "git", "url": "https://github.com/laminas/laminas-form.git", - "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd" + "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-form/zipball/012aae01366cb8c8fb64e39a887363ef82f388dd", - "reference": "012aae01366cb8c8fb64e39a887363ef82f388dd", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/8b985f74bfe32910edb4ba9503877c4310228cd2", + "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2", "shasum": "" }, "require": { @@ -1841,7 +1757,7 @@ "laminas/laminas-text": "^2.6", "laminas/laminas-validator": "^2.6", "laminas/laminas-view": "^2.6.2", - "phpunit/phpunit": "^5.7.23 || ^6.5.3" + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" }, "suggest": { "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", @@ -1881,7 +1797,7 @@ "form", "laminas" ], - "time": "2019-12-31T16:56:34+00:00" + "time": "2020-03-18T22:38:54+00:00" }, { "name": "laminas/laminas-http", @@ -2007,16 +1923,16 @@ }, { "name": "laminas/laminas-i18n", - "version": "2.10.1", + "version": "2.10.2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-i18n.git", - "reference": "815be447f1c77f70a86bf24d00087fcb975b39ff" + "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/815be447f1c77f70a86bf24d00087fcb975b39ff", - "reference": "815be447f1c77f70a86bf24d00087fcb975b39ff", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/9699c98d97d2f519def3da8d4128dfe6e6ad11bf", + "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf", "shasum": "" }, "require": { @@ -2078,7 +1994,7 @@ "i18n", "laminas" ], - "time": "2019-12-31T17:07:17+00:00" + "time": "2020-03-20T11:57:14+00:00" }, { "name": "laminas/laminas-inputfilter", @@ -3161,16 +3077,16 @@ }, { "name": "laminas/laminas-validator", - "version": "2.13.1", + "version": "2.13.2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-validator.git", - "reference": "36702f033486bf1953e254f5299aad205302e79d" + "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/36702f033486bf1953e254f5299aad205302e79d", - "reference": "36702f033486bf1953e254f5299aad205302e79d", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", + "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", "shasum": "" }, "require": { @@ -3236,7 +3152,7 @@ "laminas", "validator" ], - "time": "2020-01-15T09:59:30+00:00" + "time": "2020-03-16T11:38:27+00:00" }, { "name": "laminas/laminas-view", @@ -3381,178 +3297,6 @@ ], "time": "2020-01-07T22:58:31+00:00" }, - { - "name": "league/flysystem", - "version": "1.0.65", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "8f17b3ba67097aafb8318cd5c553b1acf7c891c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8f17b3ba67097aafb8318cd5c553b1acf7c891c8", - "reference": "8f17b3ba67097aafb8318cd5c553b1acf7c891c8", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "php": ">=5.5.9" - }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" - }, - "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.26" - }, - "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Flysystem\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Filesystem abstraction: Many filesystems, one API.", - "keywords": [ - "Cloud Files", - "WebDAV", - "abstraction", - "aws", - "cloud", - "copy.com", - "dropbox", - "file systems", - "files", - "filesystem", - "filesystems", - "ftp", - "rackspace", - "remote", - "s3", - "sftp", - "storage" - ], - "time": "2020-03-08T18:53:20+00:00" - }, - { - "name": "league/flysystem-aws-s3-v3", - "version": "1.0.24", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570", - "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570", - "shasum": "" - }, - "require": { - "aws/aws-sdk-php": "^3.0.0", - "league/flysystem": "^1.0.40", - "php": ">=5.5.0" - }, - "require-dev": { - "henrikbjorn/phpspec-code-coverage": "~1.0.1", - "phpspec/phpspec": "^2.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Flysystem\\AwsS3v3\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Flysystem adapter for the AWS S3 SDK v3.x", - "time": "2020-02-23T13:31:58+00:00" - }, - { - "name": "league/flysystem-azure-blob-storage", - "version": "0.1.6", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem-azure-blob-storage.git", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-azure-blob-storage/zipball/97215345f3c42679299ba556a4d16d4847ee7f6d", - "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.5", - "league/flysystem": "^1.0", - "microsoft/azure-storage-blob": "^1.1", - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\Flysystem\\AzureBlobStorage\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "time": "2019-06-07T20:42:16+00:00" - }, { "name": "magento/composer", "version": "1.6.x-dev", @@ -3715,94 +3459,6 @@ ], "time": "2019-11-26T15:09:40+00:00" }, - { - "name": "microsoft/azure-storage-blob", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/Azure/azure-storage-blob-php.git", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-blob-php/zipball/6a333cd28a3742c3e99e79042dc6510f9f917919", - "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919", - "shasum": "" - }, - "require": { - "microsoft/azure-storage-common": "~1.4", - "php": ">=5.6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "MicrosoftAzure\\Storage\\Blob\\": "src/Blob" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } - ], - "description": "This project provides a set of PHP client libraries that make it easy to access Microsoft Azure Storage Blob APIs.", - "keywords": [ - "azure", - "blob", - "php", - "sdk", - "storage" - ], - "time": "2020-01-02T07:18:59+00:00" - }, - { - "name": "microsoft/azure-storage-common", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/Azure/azure-storage-common-php.git", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Azure/azure-storage-common-php/zipball/be4df800761d0d0fa91a9460c7f42517197d57a0", - "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "~6.0", - "php": ">=5.6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "MicrosoftAzure\\Storage\\Common\\": "src/Common" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Azure Storage PHP Client Library", - "email": "dmsh@microsoft.com" - } - ], - "description": "This project provides a set of common code shared by Azure Storage Blob, Table, Queue and File PHP client libraries.", - "keywords": [ - "azure", - "common", - "php", - "sdk", - "storage" - ], - "time": "2020-01-02T07:15:54+00:00" - }, { "name": "monolog/monolog", "version": "1.25.3", @@ -3837,88 +3493,29 @@ "sentry/sentry": "^0.13", "swiftmailer/swiftmailer": "^5.3|^6.0" }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "time": "2019-12-20T14:15:16+00:00" - }, - { - "name": "mtdowling/jmespath.php", - "version": "2.5.0", - "source": { - "type": "git", - "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "52168cb9472de06979613d365c7f1ab8798be895" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", - "reference": "52168cb9472de06979613d365c7f1ab8798be895", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/polyfill-mbstring": "^1.4" - }, - "require-dev": { - "composer/xdebug-handler": "^1.2", - "phpunit/phpunit": "^4.8.36|^7.5.15" + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, - "bin": [ - "bin/jp.php" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "JmesPath\\": "src/" - }, - "files": [ - "src/JmesPath.php" - ] + "Monolog\\": "src/Monolog" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3926,17 +3523,19 @@ ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Declaratively specify how to extract elements from a JSON document", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", "keywords": [ - "json", - "jsonpath" + "log", + "logging", + "psr-3" ], - "time": "2019-12-30T18:03:34+00:00" + "time": "2019-12-20T14:15:16+00:00" }, { "name": "paragonie/random_compat", @@ -3985,16 +3584,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.12.2", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "3b953109fdfc821c1979bc829c8b7421721fef82" + "reference": "bbade402cbe84c69b718120911506a3aa2bae653" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3b953109fdfc821c1979bc829c8b7421721fef82", - "reference": "3b953109fdfc821c1979bc829c8b7421721fef82", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653", + "reference": "bbade402cbe84c69b718120911506a3aa2bae653", "shasum": "" }, "require": { @@ -4063,7 +3662,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2019-12-30T03:11:08+00:00" + "time": "2020-03-20T21:48:09+00:00" }, { "name": "pelago/emogrifier", @@ -4263,16 +3862,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.25", + "version": "2.0.26", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0" + "reference": "09655fcc1f8bab65727be036b28f6f20311c126c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c18159618ed7cd7ff721ac1a8fec7860a475d2f0", - "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/09655fcc1f8bab65727be036b28f6f20311c126c", + "reference": "09655fcc1f8bab65727be036b28f6f20311c126c", "shasum": "" }, "require": { @@ -4351,7 +3950,7 @@ "x.509", "x509" ], - "time": "2020-02-25T04:16:50+00:00" + "time": "2020-03-13T04:15:39+00:00" }, { "name": "psr/container", @@ -5711,23 +5310,23 @@ }, { "name": "allure-framework/allure-php-api", - "version": "1.1.7", + "version": "1.1.8", "source": { "type": "git", "url": "https://github.com/allure-framework/allure-php-commons.git", - "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18" + "reference": "5ae2deac1c7e1b992cfa572167370de45bdd346d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", - "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", + "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/5ae2deac1c7e1b992cfa572167370de45bdd346d", + "reference": "5ae2deac1c7e1b992cfa572167370de45bdd346d", "shasum": "" }, "require": { "jms/serializer": "^0.16 || ^1.0", "php": ">=5.4.0", "ramsey/uuid": "^3.0", - "symfony/http-foundation": "^2.0 || ^3.0 || ^4.0" + "symfony/http-foundation": "^2.0 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { "phpunit/phpunit": "^4.0.0" @@ -5760,7 +5359,7 @@ "php", "report" ], - "time": "2020-02-05T16:43:19+00:00" + "time": "2020-03-13T10:47:35+00:00" }, { "name": "allure-framework/allure-phpunit", @@ -5812,18 +5411,102 @@ ], "time": "2017-11-03T13:08:21+00:00" }, + { + "name": "aws/aws-sdk-php", + "version": "3.133.42", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "4f0259689e66237e429f9fc1d879b2bcdce6dadb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4f0259689e66237e429f9fc1d879b2bcdce6dadb", + "reference": "4f0259689e66237e429f9fc1d879b2bcdce6dadb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2020-03-23T18:17:07+00:00" + }, { "name": "behat/gherkin", - "version": "v4.6.1", + "version": "v4.6.2", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759" + "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/25bdcaf37898b4a939fa3031d5d753ced97e4759", - "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/51ac4500c4dc30cbaaabcd2f25694299df666a31", + "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31", "shasum": "" }, "require": { @@ -5869,7 +5552,7 @@ "gherkin", "parser" ], - "time": "2020-02-27T11:29:57+00:00" + "time": "2020-03-17T14:03:26+00:00" }, { "name": "cache/cache", @@ -6057,16 +5740,16 @@ }, { "name": "codeception/phpunit-wrapper", - "version": "6.8.0", + "version": "6.8.1", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "20e054e9cee8de0523322367bcccde8e6a3db263" + "reference": "ca6e94c6dadc19db05698d4e0d84214e570ce045" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/20e054e9cee8de0523322367bcccde8e6a3db263", - "reference": "20e054e9cee8de0523322367bcccde8e6a3db263", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/ca6e94c6dadc19db05698d4e0d84214e570ce045", + "reference": "ca6e94c6dadc19db05698d4e0d84214e570ce045", "shasum": "" }, "require": { @@ -6085,7 +5768,7 @@ "type": "library", "autoload": { "psr-4": { - "Codeception\\PHPUnit\\": "src\\" + "Codeception\\PHPUnit\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6099,7 +5782,7 @@ } ], "description": "PHPUnit classes used by Codeception", - "time": "2020-01-03T08:01:16+00:00" + "time": "2020-03-20T08:05:05+00:00" }, { "name": "codeception/stub", @@ -7598,6 +7281,90 @@ ], "time": "2017-05-10T09:20:27+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.66", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/021569195e15f8209b1c4bebb78bd66aa4f08c21", + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.26" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2020-03-17T18:58:12+00:00" + }, { "name": "lusitanian/oauth", "version": "v0.8.11", @@ -7835,6 +7602,63 @@ "homepage": "http://vfs.bovigo.org/", "time": "2019-10-30T15:31:00+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" + }, + "require-dev": { + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2019-12-30T18:03:34+00:00" + }, { "name": "mustache/mustache", "version": "v2.13.0", @@ -8514,20 +8338,20 @@ }, { "name": "phpoption/phpoption", - "version": "1.7.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959" + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", - "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae", + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0" + "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.3", @@ -8565,7 +8389,7 @@ "php", "type" ], - "time": "2019-12-15T19:35:24+00:00" + "time": "2020-03-21T18:07:53+00:00" }, { "name": "phpspec/prophecy", @@ -8632,16 +8456,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.14", + "version": "0.12.18", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2" + "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37bdd26a80235d0f9045b49f4151102b7831cbe2", - "reference": "37bdd26a80235d0f9045b49f4151102b7831cbe2", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286", + "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286", "shasum": "" }, "require": { @@ -8667,7 +8491,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-03-02T22:29:43+00:00" + "time": "2020-03-22T16:51:47+00:00" }, { "name": "phpunit/php-code-coverage", From e964252cbc88021a83ff57504e23cab64c23bd46 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Mon, 23 Mar 2020 23:22:56 -0500 Subject: [PATCH 542/734] MC-31586: Customer address is duplicated after setBillingAddressOnCart GraphQL mutation. - static fix --- app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index f93962eb06849..f0b69885b6e40 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -14,7 +14,7 @@ use Magento\Quote\Model\Quote\Address; /** - * Get shipping address + * Model for getting shipping address */ class GetShippingAddress { From 19b01134b56cdd300eee1f0f6f3733b84b43f633 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 24 Mar 2020 14:59:16 +0200 Subject: [PATCH 543/734] MC-32697: [2.4] AbstractSimpleObjectBuilder.php throws exception when Interceptor instance comes. --- .../Framework/Api/SortOrderBuilderTest.php | 40 +++++++++++++++++++ .../Api/AbstractSimpleObjectBuilder.php | 16 ++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Api/SortOrderBuilderTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/SortOrderBuilderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/SortOrderBuilderTest.php new file mode 100644 index 0000000000000..20b2528eb7918 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/SortOrderBuilderTest.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Api; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test of building the Data Object + */ +class SortOrderBuilderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var SortOrderBuilder + */ + private $interceptedBuilder; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->interceptedBuilder = Bootstrap::getObjectManager()->get(SortOrderBuilder::class . '\Interceptor'); + } + + /** + * Test Builder successfully creates object when Interceptor instance is provided. + * + * @return void + */ + public function testCreate(): void + { + $this->assertEquals(SortOrder::class, get_class($this->interceptedBuilder->create())); + } +} diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php index d9a972ac237f5..d34e5a0bf6d66 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Api; /** @@ -44,6 +46,8 @@ public function create() } /** + * Overwrite data in Object. + * * @param string $key * @param mixed $value * @@ -60,12 +64,16 @@ protected function _set($key, $value) * * @return string */ - protected function _getDataObjectType() + protected function _getDataObjectType(): string { $currentClass = get_class($this); - $builderSuffix = 'Builder'; - $dataObjectType = substr($currentClass, 0, -strlen($builderSuffix)); - return $dataObjectType; + $suffix = 'Builder'; + $interceptorSuffix = '\Interceptor'; + if (false !== strpos($currentClass, $interceptorSuffix, -strlen($interceptorSuffix))) { + $suffix .= $interceptorSuffix; + } + + return substr($currentClass, 0, -strlen($suffix)); } /** From 2516175b3c1a9c53a5ae3d269d4f11e706bd2301 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Tue, 24 Mar 2020 15:37:39 +0200 Subject: [PATCH 544/734] Add follow symlinks to support linked folders --- lib/internal/Magento/Framework/Filesystem/Driver/File.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index 27df19f66c7aa..48d9e34848dbd 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -254,7 +254,10 @@ private function mkdirRecursive($path, $permissions = 0777) public function readDirectory($path) { try { - $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $flags = \FilesystemIterator::SKIP_DOTS | + \FilesystemIterator::UNIX_PATHS | + \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + $iterator = new \FilesystemIterator($path, $flags); $result = []; /** @var \FilesystemIterator $file */ From 6c1de59f3c1457620a33b7acd18d11b6ca5fce6e Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Tue, 24 Mar 2020 09:37:51 -0500 Subject: [PATCH 545/734] MC-32201: Reorder functionality (test coverage) --- .../order_with_different_types_of_product.php | 22 +++++++++++++------ ...th_different_types_of_product_rollback.php | 3 ++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php index 563f38e9dfa06..6f7f26099c0b1 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php @@ -39,7 +39,9 @@ $payment->setMethod('checkmo'); $customerIdFromFixture = 1; -// simple product item +/** + * simple product + */ $simpleProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); $requestInfo = [ 'qty' => 1 @@ -55,8 +57,9 @@ ->setStoreId(0) ->setProductOptions(['info_buyRequest' => $requestInfo]); -// configurable product - +/** + * configurable product + */ /** @var \Magento\Eav\Model\Config $eavConfig */ $eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ @@ -95,7 +98,9 @@ $orderConfigurableItem->setProductOptions(['info_buyRequest' => $requestInfo]); } -// virtual product +/** + * virtual product + */ $virtualProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); $requestInfo = [ 'qty' => 1 @@ -110,8 +115,9 @@ ->setSku($virtualProduct->getSku()) ->setStoreId(0) ->setProductOptions(['info_buyRequest' => $requestInfo]); - -// downloadable product +/** + * downloadable product + */ /** @var $linkCollection \Magento\Downloadable\Model\ResourceModel\Link\Collection */ $linkCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Downloadable\Model\Link::class @@ -142,7 +148,9 @@ ->setStoreId($downloadableProduct->getStoreId()) ->setProductOptions(['info_buyRequest' => $requestInfo]); -// bundle product +/** + * bundle product + */ /** @var $typeInstance \Magento\Bundle\Model\Product\Type */ $typeInstance = $bundleProduct->getTypeInstance(); $typeInstance->setStoreFilter($bundleProduct->getStoreId(), $bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php index 6223dd13d0bf4..3acfda5455ae8 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php @@ -4,5 +4,6 @@ * See COPYING.txt for license details. */ -require 'default_rollback.php'; require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable_rollback.php'; +require 'default_rollback.php'; From 0458eb931ebcf31837efe69293dc4ebceebd5372 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 24 Mar 2020 09:40:17 -0500 Subject: [PATCH 546/734] MC-32387: Add pagination support to categoryList query - review comments - add caching --- .../Model/Resolver/Category/CategoriesIdentity.php | 8 +++++--- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php index dd18c463b98de..0a679eb064226 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; +use Magento\Catalog\Model\Category; use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface; /** @@ -15,7 +16,7 @@ class CategoriesIdentity implements IdentityInterface { /** @var string */ - private $cacheTag = \Magento\Catalog\Model\Category::CACHE_TAG; + private $cacheTag = Category::CACHE_TAG; /** * Get category IDs from resolved data @@ -26,8 +27,9 @@ class CategoriesIdentity implements IdentityInterface public function getIdentities(array $resolvedData): array { $ids = []; - if (!empty($resolvedData)) { - foreach ($resolvedData as $category) { + $resolvedCategories = $resolvedData['items'] ?? $resolvedData; + if (!empty($resolvedCategories)) { + foreach ($resolvedCategories as $category) { $ids[] = sprintf('%s_%s', $this->cacheTag, $category['id']); } if (!empty($ids)) { diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 8783e9394c7e4..f77b301d61e28 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -21,7 +21,7 @@ type Query { filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional.") currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1.") - ): CategoryResult @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoriesQuery") + ): CategoryResult @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoriesQuery") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") } type Price @doc(description: "Price is deprecated, replaced by ProductPrice. The Price object defines the price of a product as well as any tax-related adjustments.") { @@ -138,8 +138,8 @@ type CategoryTree implements CategoryInterface @doc(description: "Category Tree children: [CategoryTree] @doc(description: "Child categories tree.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } -type CategoryResult @doc(description: "Collection of CategoryTree object and pagination information.") { - items: [CategoryTree] @doc(description: "List of categories that match filter criteria.") +type CategoryResult @doc(description: "A collection of CategoryTree objects and pagination information.") { + items: [CategoryTree] @doc(description: "A list of categories that match the filter criteria.") page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.") total_count: Int @doc(description: "The total number of categories that match the criteria.") } From 9c6f13cbbc8cfd5ebab0f870f6ab0935cc8adf69 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 24 Mar 2020 18:31:11 +0200 Subject: [PATCH 547/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...AppearsOnProductPagePreviewActionGroup.xml | 22 ++ ...urableProductWithSeveralAttributesTest.xml | 5 +- ...hVisualSwatchAttributePrependMediaTest.xml | 260 ++++++++++++++++++ .../Mftf/Data/SwatchAttributeOptionData.xml | 8 + 4 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml new file mode 100644 index 0000000000000..9a35253412072 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup"> + <annotations> + <description>Validate that the Product Image is present on preview and correct on frontend product page.</description> + </annotations> + <arguments> + <argument name="productImage" type="string" defaultValue="{{MagentoLogo.file}}"/> + </arguments> + + <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear"/> + <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery"/> + <seeElement selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="seeCorrectProductImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml index a39352ce16189..61f5f9779b207 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml @@ -11,7 +11,7 @@ <test name="StorefrontGalleryConfigurableProductWithSeveralAttributesTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Verify functionality of updating Gallery items on 'view Product' Storefront page for Configurable Product with several attributes of different types"/> + <stories value="Prepend variation media on storefront"/> <title value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> <description value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> <severity value="AVERAGE"/> @@ -20,6 +20,7 @@ <group value="configurableProduct"/> <group value="swatch"/> </annotations> + <before> <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> <!--Create 1 configurable product with 2 variations--> @@ -165,7 +166,7 @@ </after> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> - <argument name="productUrl" value="$$createConfigurableProduct.custom_attributes[url_key]$$"/> + <argument name="productUrl" value="$createConfigurableProduct.custom_attributes[url_key]$"/> </actionGroup> <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml new file mode 100644 index 0000000000000..944659d176b50 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -0,0 +1,260 @@ +<?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="StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Prepend variation media on storefront"/> + <title value="Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media"/> + <description value="Verify functionality of updating Gallery items on 'view Product' Storefront page for Configurable Product with Visual Swatch input type attribute. Verify that Configurable Product media in Gallery is prepended with media from selected variation"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-26184"/> + <group value="catalog"/> + <group value="configurableProduct"/> + <group value="swatch"/> + </annotations> + + <before> + <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> + <!--Create 1 configurable product --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> + <!-- Create product swatch attribute with 2 variations --> + <createData entity="VisualSwatchProductAttributeForm" stepKey="createVisualSwatchAttribute"/> + <createData entity="SwatchProductAttributeOption1" stepKey="swatchAttributeFirstOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption2" stepKey="swatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption3" stepKey="swatchAttributeThirdOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSwatchAttributeFirsOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSwatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getSwatchAttributeThirdOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </getData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Open configurable product edit page --> + <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> + + <!-- Add attributes to configurable product--> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> + + <!-- Find Swatch attribute in grid and select it --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForSwatchAttribute"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createVisualSwatchAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForSwatchAttribute"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectSwatchAttribute"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToSelectOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createVisualSwatchAttribute.frontend_label[0]$)}}" stepKey="selectAllSwatchAttributeOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToApplyQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextToProceedToSummary"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickGenerateProductsButton"/> + + <!-- Load media for configurable product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProduct"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProduct"> + <argument name="image" value="Magento3"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="image" value="{{TestImageNew.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"/> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> + + <!-- Load media for configurable product variation option1 --> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$swatchAttributeFirstOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption1"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1"> + <argument name="image" value="{{placeholderSmallImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1"/> + + <!-- Load media for configurable product variation option3 --> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption3"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$swatchAttributeThirdOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption3"> + <argument name="image" value="{{Magento3.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption3"/> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption3"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption3"> + <argument name="image" value="{{MagentoLogo.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption3"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption3"/> + + <!-- Reindex invalidated indices after product attribute has been created --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + </before> + + <after> + <createData entity="DefaultProductVideoConfig" stepKey="resetStoreDefaultVideoConfig"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> + <argument name="product" value="$createConfigurableProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForDeleteSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 4 record(s) have been deleted." stepKey="seeDeleteSuccessMessage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductVisualSwatchAttribute"> + <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + </after> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="productUrl" value="$createConfigurableProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case0"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case0"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case0"/> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase0"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase0"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase0"> + <expectedResult type="variable">getListOfThumbnailsCase0</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase0</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase0"/> + + <!--CASE 1: Selected options = B1; Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase1"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase1[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case1"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case1"/> + <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase1[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case1"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case1"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case1"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case1"/> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase1"> + <argument name="productImage" value="{{MagentoLogo.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase1"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase1"> + <expectedResult type="variable">getListOfThumbnailsCase1</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase1</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase1"/> + + <!--CASE 2: Selected options = B2; Expected media : C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase2"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case2"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case2"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case2"/> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase2"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase2"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase2"> + <expectedResult type="variable">getListOfThumbnailsCase2</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase2</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase2"/> + + <!--CASE 3: Selected options = B3; Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase3"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case3"/> + <assertRegExp expected="|{{TestImageAdobe.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case3"/> + <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase3[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case3"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[3]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case3"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[4]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case3"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[5]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case3"/> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase3"> + <argument name="productImage" value="{{Magento3.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase3"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase3"> + <expectedResult type="variable">getListOfThumbnailsCase3</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase3</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase3"/> + + <!--CASE 4: Selected options = none; Expected media : C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="unselectThirdOptionCase4"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> + <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[0]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case4"/> + <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[1]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case4"/> + <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[2]" + expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case4"/> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase4"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase4"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase4"> + <expectedResult type="variable">getListOfThumbnailsCase4</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase4</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase4"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml index a46bc0544b642..34f367ea663c0 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml @@ -24,4 +24,12 @@ <requiredEntity type="StoreLabel">Option2Store0</requiredEntity> <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> </entity> + <entity name="SwatchProductAttributeOption3" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-option3-</data> + <data key="is_default">true</data> + <data key="sort_order">2</data> + <requiredEntity type="StoreLabel">Option3Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option3Store1</requiredEntity> + </entity> </entities> From e5d980d751b04c7e3a8cd107c1be17308fee59f1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 24 Mar 2020 18:32:34 +0200 Subject: [PATCH 548/734] MC-32697: [2.4] AbstractSimpleObjectBuilder.php throws exception when Interceptor instance comes. --- .../Magento/Framework/Api/AbstractSimpleObjectBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php index d34e5a0bf6d66..cf0a887249049 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php @@ -64,7 +64,7 @@ protected function _set($key, $value) * * @return string */ - protected function _getDataObjectType(): string + protected function _getDataObjectType() { $currentClass = get_class($this); $suffix = 'Builder'; From 7c77f6f173a78183a94af426b03770cd95f32f14 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Tue, 24 Mar 2020 19:51:03 +0200 Subject: [PATCH 549/734] Cover changes with integration test --- .../Framework/Filesystem/Driver/FileTest.php | 26 +++++++++++++++++++ .../Framework/Filesystem/Driver/File.php | 5 +++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php index c8719ea7e0a0a..5dec995f83827 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php @@ -64,6 +64,32 @@ protected function tearDown(): void $this->removeGeneratedDirectory(); } + /** + * Tests read directory with symlynked folders. + * + * @return void + */ + public function testReadDirectoryRecursivelyWithSymlinkedFolders(): void + { + $sourceDirectory = $this->generatedPath . '/source'; + $destinationDirectory = $this->generatedPath . '/destination'; + + $this->driver->createDirectory($sourceDirectory); + $this->driver->createDirectory($sourceDirectory . '/directory1'); + $this->driver->createDirectory($destinationDirectory); + + $linkName = $destinationDirectory . '/link'; + $this->driver->symlink($sourceDirectory, $linkName); + + $paths = [ + $destinationDirectory . '/link' . '/directory1', + $destinationDirectory . '/link' + + ]; + $actual = $this->driver->readDirectoryRecursively($destinationDirectory); + $this->assertEquals($paths, $actual); + } + /** * Tests directory recursive read. * diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index 48d9e34848dbd..aec9a64a132fe 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -949,7 +949,10 @@ protected function getScheme($scheme = null) public function readDirectoryRecursively($path = null) { $result = []; - $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $flags = \FilesystemIterator::SKIP_DOTS | + \FilesystemIterator::UNIX_PATHS | + \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + try { $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, $flags), From ade9a6fadf9dd7ee1bb736ba0c49065de6cd2cf6 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Tue, 24 Mar 2020 15:21:28 -0500 Subject: [PATCH 550/734] MC-32201: Reorder functionality (test coverage) --- .../ReorderConfigurableWithVariationsTest.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php index 9b8445efdce39..2d691ed15926f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -75,7 +75,6 @@ public function testVariations() $this->assertValidVariations(); $this->assertWithOutOfStockVariation($productRepository, $product); - $this->assertWithDeletedVariation($productRepository, $product); } /** @@ -158,16 +157,18 @@ private function assertWithOutOfStockVariation( /** * Assert reorder with "out of stock" variation. * - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository - * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_configurable_variations.php * @return void * @throws \Magento\Framework\Exception\StateException * @throws NoSuchEntityException */ - private function assertWithDeletedVariation( - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, - \Magento\Catalog\Api\Data\ProductInterface $product - ): void { + public function testWithDeletedVariation(): void { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple_20'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); // delete a product and make reorder /** @var \Magento\Framework\Registry $registry */ $registry = Bootstrap::getObjectManager() From f873398518b915e067e77f59130bde0142c15547 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Tue, 24 Mar 2020 15:48:50 -0500 Subject: [PATCH 551/734] MC-32201: Reorder functionality (test coverage) --- .../ReorderConfigurableWithVariationsTest.php | 1 + .../order_with_1_qty_product_rollback.php | 2 +- ...th_different_types_of_product_rollback.php | 5 +- .../order_with_simple_product_rollback.php | 94 +------------------ ...order_with_two_configurable_variations.php | 5 +- ...h_two_configurable_variations_rollback.php | 1 + 6 files changed, 12 insertions(+), 96 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php index 2d691ed15926f..e4b911d67ba93 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -14,6 +14,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * Test Reorder with and without products overlay in shopping cart. */ class ReorderConfigurableWithVariationsTest extends GraphQlAbstract diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php index 34cded299350d..db665e644238c 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php @@ -4,4 +4,4 @@ * See COPYING.txt for license details. */ -require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_rollback.php'; +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php index 3acfda5455ae8..83a666f8e1896 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php @@ -5,5 +5,8 @@ */ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable_rollback.php'; +require __DIR__ . '/../../../Magento/GraphQl/Catalog/_files/virtual_product_rollback.php'; +require __DIR__ . '/../../../Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php'; require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable_rollback.php'; -require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php index 1e8e9beccc87f..6e4390330bcd2 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php @@ -6,94 +6,6 @@ use Magento\Sales\Api\OrderRepositoryInterface; -require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; - -/** \Magento\Customer\Model\Customer $customer */ -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; -$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null)->setAddressType('shipping'); - -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); -$payment->setMethod('checkmo'); -$customerIdFromFixture = 1; - -$optionValuesByType = [ - 'field' => 'Test value', - 'date_time' => [ - 'year' => '2015', - 'month' => '9', - 'day' => '9', - 'hour' => '2', - 'minute' => '2', - 'day_part' => 'am', - 'date_internal' => '', - ], - 'drop_down' => '3-1-select', - 'radio' => '4-1-radio', -]; - -$requestInfo = ['options' => [], 'qty' => 1]; -$productOptions = $product->getOptions(); -foreach ($productOptions as $option) { - $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; -} - -/** @var \Magento\Sales\Model\Order\Item $orderItem */ -$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); -$orderItem->setProductId($product->getId()); -$orderItem->setQtyOrdered(1); -$orderItem->setBasePrice($product->getPrice()); -$orderItem->setPrice($product->getPrice()); -$orderItem->setRowTotal($product->getPrice()); -$orderItem->setProductType($product->getTypeId()); -$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); -$orderItem->setName($product->getName()); -$orderItem->setSku($product->getSku()); -$orderItem->setStoreId(0); -// create second order item - -$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); -$requestInfo = [ - 'qty' => 1 -]; -$orderItem2->setProductId($secondProduct->getId()) - ->setQtyOrdered(1) - ->setBasePrice($secondProduct->getPrice()) - ->setPrice($secondProduct->getPrice()) - ->setRowTotal($secondProduct->getPrice()) - ->setProductType($secondProduct->getTypeId()) - ->setName($secondProduct->getName()) - ->setSku($secondProduct->getSku()) - ->setStoreId(0) - ->setProductId($secondProduct->getId()) - ->setSku($secondProductSku) - ->setProductOptions(['info_buyRequest' => $requestInfo]); - -/** @var \Magento\Sales\Model\Order $order */ -$order = $objectManager->create(\Magento\Sales\Model\Order::class); -$order->setIncrementId('100001001'); -$order->setState(\Magento\Sales\Model\Order::STATE_NEW); -$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); -$order->setCustomerIsGuest(false); -$order->setCustomerId($customer->getId()); -$order->setCustomerEmail($customer->getEmail()); -$order->setCustomerFirstname($customer->getName()); -$order->setCustomerLastname($customer->getLastname()); -$order->setBillingAddress($billingAddress); -$order->setShippingAddress($shippingAddress); -$order->setAddresses([$billingAddress, $shippingAddress]); -$order->setPayment($payment); -$order->addItem($orderItem); -$order->addItem($orderItem2); -$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); -$order->setSubtotal(100); -$order->setBaseSubtotal(100); -$order->setBaseGrandTotal(100); - -$orderRepository = $objectManager->create(OrderRepositoryInterface::class); -$orderRepository->save($order); +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php index 90d8e858b7908..9c639612348a4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php @@ -52,9 +52,8 @@ $orderConfigurableItem->setParentItemId(null); $orderConfigurableItem->setProductType('configurable'); $configurableVariations = []; -foreach (array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()) - as $key => $variationId -) { +$producLinks = array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); +foreach ($producLinks as $key => $variationId) { $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php index b6bc7c7113a85..0950ba3579a7e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php @@ -5,4 +5,5 @@ */ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable_rollback.php'; require 'default_rollback.php'; From e849f395d0fc704437b7b62a2f8e6d2682c7c4a6 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 25 Mar 2020 00:20:15 +0100 Subject: [PATCH 552/734] Implement ActionInterface for /version/ --- .../Version/Controller/Index/Index.php | 17 +++++----- .../Test/Unit/Controller/Index/IndexTest.php | 31 +++---------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Version/Controller/Index/Index.php b/app/code/Magento/Version/Controller/Index/Index.php index 53bcd4b4ff700..79dde0e025f01 100644 --- a/app/code/Magento/Version/Controller/Index/Index.php +++ b/app/code/Magento/Version/Controller/Index/Index.php @@ -8,15 +8,14 @@ namespace Magento\Version\Controller\Index; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\App\ResponseInterface; /** * Magento Version controller */ -class Index extends Action implements HttpGetActionInterface +class Index implements HttpGetActionInterface { const DEV_PREFIX = 'dev-'; @@ -24,15 +23,19 @@ class Index extends Action implements HttpGetActionInterface * @var ProductMetadataInterface */ protected $productMetadata; + /** + * @var ResponseInterface + */ + private $response; /** - * @param Context $context + * @param ResponseInterface $response * @param ProductMetadataInterface $productMetadata */ - public function __construct(Context $context, ProductMetadataInterface $productMetadata) + public function __construct(ResponseInterface $response, ProductMetadataInterface $productMetadata) { $this->productMetadata = $productMetadata; - parent::__construct($context); + $this->response = $response; } /** @@ -48,7 +51,7 @@ public function execute(): void return; } - $this->getResponse()->setBody( + $this->response->setBody( $this->productMetadata->getName() . '/' . $this->getMajorMinorVersion($versionParts) . ' (' . $this->productMetadata->getEdition() . ')' diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php index 3fc2cecabe990..bb7e407c750d3 100644 --- a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php @@ -7,10 +7,8 @@ namespace Magento\Version\Test\Unit\Controller\Index; -use Magento\Framework\App\Action\Context; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Version\Controller\Index\Index as VersionIndex; use PHPUnit\Framework\TestCase; @@ -19,12 +17,7 @@ class IndexTest extends TestCase /** * @var VersionIndex */ - private $model; - - /** - * @var Context - */ - private $contextMock; + private $versionController; /** * @var ProductMetadataInterface @@ -41,10 +34,6 @@ class IndexTest extends TestCase */ protected function setUp() { - $this->contextMock = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getEdition', 'getVersion']) @@ -55,19 +44,7 @@ protected function setUp() ->setMethods(['setBody', 'sendResponse']) ->getMock(); - $this->contextMock->expects($this->any()) - ->method('getResponse') - ->willReturn($this->responseMock); - - $objectManager = new ObjectManager($this); - - $this->model = $objectManager->getObject( - VersionIndex::class, - [ - 'context' => $this->contextMock, - 'productMetadata' => $this->productMetadataMock - ] - ); + $this->versionController = new VersionIndex($this->responseMock, $this->productMetadataMock); } /** @@ -82,7 +59,7 @@ public function testGitBasedInstallationDoesNotReturnVersion(): void $this->responseMock->expects($this->never()) ->method('setBody'); - $this->assertNull($this->model->execute()); + $this->assertNull($this->versionController->execute()); } /** @@ -98,6 +75,6 @@ public function testCommunityVersionDisplaysMajorMinorVersionAndEditionName(): v ->with('Magento/2.3 (Community)') ->will($this->returnSelf()); - $this->model->execute(); + $this->versionController->execute(); } } From 3d4a8bbd53b8efde6938afcac521554a605f3656 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 25 Mar 2020 01:13:07 +0100 Subject: [PATCH 553/734] Implement ActionInterface for /swagger/ --- .../Swagger/Controller/Index/Index.php | 26 +++++++++---------- .../Test/Unit/Controller/Index/IndexTest.php | 26 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Swagger/Controller/Index/Index.php b/app/code/Magento/Swagger/Controller/Index/Index.php index 162367aaf81f9..3e09b19ede9e4 100644 --- a/app/code/Magento/Swagger/Controller/Index/Index.php +++ b/app/code/Magento/Swagger/Controller/Index/Index.php @@ -5,40 +5,40 @@ */ namespace Magento\Swagger\Controller\Index; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\View\Page\Config as PageConfig; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory as PageFactory; + /** * Class Index * * @package Magento\Swagger\Controller\Index */ -class Index extends \Magento\Framework\App\Action\Action +class Index implements HttpGetActionInterface { /** - * @var \Magento\Framework\View\Page\Config + * @var PageConfig */ private $pageConfig; /** - * @var \Magento\Framework\View\Result\PageFactory + * @var PageFactory */ private $pageFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\View\Page\Config $pageConfig - * @param \Magento\Framework\View\Result\PageFactory $pageFactory + * @param PageConfig $pageConfig + * @param PageFactory $pageFactory */ - public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\View\Page\Config $pageConfig, - \Magento\Framework\View\Result\PageFactory $pageFactory - ) { - parent::__construct($context); + public function __construct(PageConfig $pageConfig, PageFactory $pageFactory) + { $this->pageConfig = $pageConfig; $this->pageFactory = $pageFactory; } /** - * @return \Magento\Framework\View\Result\Page + * @inheritDoc */ public function execute() { diff --git a/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php index c409cd6ca7504..abfc40d7e5437 100644 --- a/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php @@ -8,29 +8,29 @@ namespace Magento\Swagger\Test\Unit\Controller\Index; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\View\Page\Config as PageConfig; +use Magento\Framework\View\Result\PageFactory; +use Magento\Swagger\Controller\Index\Index; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { public function testExecute() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $pageConfigMock = $this->getMockBuilder(\Magento\Framework\View\Page\Config::class) + /** @var MockObject|PageConfig $pageConfigMock */ + $pageConfigMock = $this->getMockBuilder(PageConfig::class) ->disableOriginalConstructor() ->getMock(); - $resultPageFactory = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + /** @var MockObject|PageFactory $resultPageFactory */ + $resultPageFactory = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->getMock(); $pageConfigMock->expects($this->once())->method('addBodyClass')->with('swagger-section'); $resultPageFactory->expects($this->once())->method('create'); - $model = $objectManager->getObject( - \Magento\Swagger\Controller\Index\Index::class, - [ - 'pageConfig' => $pageConfigMock, - 'pageFactory' => $resultPageFactory - ] - ); - $model->execute(); + $indexAction = new Index($pageConfigMock, $resultPageFactory); + $indexAction->execute(); } } From 5c433fec3aff6e94ed756b196853ab51d567f040 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 25 Mar 2020 01:53:39 +0100 Subject: [PATCH 554/734] Remove leftovers after core code --- app/code/Magento/Swagger/Controller/Index/Index.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Swagger/Controller/Index/Index.php b/app/code/Magento/Swagger/Controller/Index/Index.php index 3e09b19ede9e4..c486989c0b1bc 100644 --- a/app/code/Magento/Swagger/Controller/Index/Index.php +++ b/app/code/Magento/Swagger/Controller/Index/Index.php @@ -3,18 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Swagger\Controller\Index; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\View\Page\Config as PageConfig; -use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory as PageFactory; -/** - * Class Index - * - * @package Magento\Swagger\Controller\Index - */ class Index implements HttpGetActionInterface { /** From a67d063657a7fefa632a122f6a0a01554659f4f5 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 25 Mar 2020 02:21:26 +0100 Subject: [PATCH 555/734] Fix failing Integration test --- .../Version/Controller/Index/Index.php | 42 +++++++++---------- .../Test/Unit/Controller/Index/IndexTest.php | 37 ++++++++-------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Version/Controller/Index/Index.php b/app/code/Magento/Version/Controller/Index/Index.php index 79dde0e025f01..2c874c601ea08 100644 --- a/app/code/Magento/Version/Controller/Index/Index.php +++ b/app/code/Magento/Version/Controller/Index/Index.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -8,12 +7,12 @@ namespace Magento\Version\Controller\Index; -use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\ProductMetadataInterface; -use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\RawFactory as RawResponseFactory; /** - * Magento Version controller + * Magento Version controller: Sets the response body to ProductName/Major.MinorVersion (Edition). */ class Index implements HttpGetActionInterface { @@ -22,40 +21,41 @@ class Index implements HttpGetActionInterface /** * @var ProductMetadataInterface */ - protected $productMetadata; + private $productMetadata; + /** - * @var ResponseInterface + * @var RawResponseFactory */ - private $response; + private $rawFactory; /** - * @param ResponseInterface $response + * @param RawResponseFactory $rawFactory * @param ProductMetadataInterface $productMetadata */ - public function __construct(ResponseInterface $response, ProductMetadataInterface $productMetadata) + public function __construct(RawResponseFactory $rawFactory, ProductMetadataInterface $productMetadata) { + $this->rawFactory = $rawFactory; $this->productMetadata = $productMetadata; - $this->response = $response; } /** - * Sets the response body to ProductName/Major.MinorVersion (Edition). - * - * @return void + * @inheritDoc */ - public function execute(): void + public function execute() { + $rawResponse = $this->rawFactory->create(); + $version = $this->productMetadata->getVersion(); $versionParts = explode('.', $version); - if ($this->isGitBasedInstallation($version) || !$this->isCorrectVersion($versionParts)) { - return; + if (!$this->isGitBasedInstallation($version) && $this->isCorrectVersion($versionParts)) { + $rawResponse->setContents( + $this->productMetadata->getName() . '/' . + $this->getMajorMinorVersion($versionParts) . + ' (' . $this->productMetadata->getEdition() . ')' + ); } - $this->response->setBody( - $this->productMetadata->getName() . '/' . - $this->getMajorMinorVersion($versionParts) . - ' (' . $this->productMetadata->getEdition() . ')' - ); + return $rawResponse; } /** diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php index bb7e407c750d3..69aed915bd513 100644 --- a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php @@ -9,25 +9,25 @@ use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; use Magento\Version\Controller\Index\Index as VersionIndex; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class IndexTest extends TestCase { - /** - * @var VersionIndex - */ + /** @var VersionIndex */ private $versionController; - /** - * @var ProductMetadataInterface - */ + /** @var MockObject|ProductMetadataInterface */ private $productMetadataMock; - /** - * @var ResponseInterface - */ - private $responseMock; + /** @var MockObject|RawFactory */ + private $rawResponseFactoryMock; + + /** @var MockObject|Raw */ + private $rawResponseMock; /** * Prepare test preconditions @@ -39,12 +39,11 @@ protected function setUp() ->setMethods(['getName', 'getEdition', 'getVersion']) ->getMock(); - $this->responseMock = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setBody', 'sendResponse']) - ->getMock(); + $this->rawResponseFactoryMock = $this->createPartialMock(RawFactory::class, ['create']); + $this->rawResponseMock = $this->createPartialMock(Raw::class, ['setContents']); + $this->rawResponseFactoryMock->method('create')->willReturn($this->rawResponseMock); - $this->versionController = new VersionIndex($this->responseMock, $this->productMetadataMock); + $this->versionController = new VersionIndex($this->rawResponseFactoryMock, $this->productMetadataMock); } /** @@ -56,10 +55,10 @@ public function testGitBasedInstallationDoesNotReturnVersion(): void ->method('getVersion') ->willReturn('dev-2.3'); - $this->responseMock->expects($this->never()) - ->method('setBody'); + $this->rawResponseMock->expects($this->never()) + ->method('setContents'); - $this->assertNull($this->versionController->execute()); + $this->versionController->execute(); } /** @@ -71,7 +70,7 @@ public function testCommunityVersionDisplaysMajorMinorVersionAndEditionName(): v $this->productMetadataMock->expects($this->any())->method('getEdition')->willReturn('Community'); $this->productMetadataMock->expects($this->any())->method('getName')->willReturn('Magento'); - $this->responseMock->expects($this->once())->method('setBody') + $this->rawResponseMock->expects($this->once())->method('setContents') ->with('Magento/2.3 (Community)') ->will($this->returnSelf()); From 03a9a241ee14b0b9761d1e40db0370e22e999389 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Tue, 24 Mar 2020 22:13:08 -0500 Subject: [PATCH 556/734] MC-32201: Reorder functionality (test coverage) --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 1 + .../Sales/ReorderConfigurableWithVariationsTest.php | 8 ++------ .../Magento/GraphQl/Sales/ReorderMultipleProductsTest.php | 1 - .../Magento/GraphQl/Sales/ReorderOverlayProductTest.php | 1 - .../testsuite/Magento/GraphQl/Sales/ReorderTest.php | 6 ------ .../order_with_different_types_of_product_rollback.php | 1 + .../Sales/_files/order_with_simple_product_rollback.php | 1 + .../order_with_two_simple_products_qty_10_rollback.php | 2 ++ .../_files/order_with_two_simple_products_rollback.php | 2 ++ 9 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 5c852618894d2..21f5b6944e640 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -40,6 +40,7 @@ class Reorder 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, 'This product is out of stock' => self::ERROR_NOT_SALABLE, + 'There are no source items' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php index e4b911d67ba93..eae61fea3cbaf 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -134,7 +134,8 @@ private function assertValidVariations(): void * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\StateException *@throws NoSuchEntityException + * @throws \Magento\Framework\Exception\StateException + * @throws NoSuchEntityException */ private function assertWithOutOfStockVariation( \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, @@ -200,8 +201,6 @@ private function assetProductNotSalable(array $response) 'orderNumber', ], 'code' => 'NOT_SALABLE', - 'message' => 'Could not add the product with SKU "configurable" to the shopping cart:' . - ' This product is out of stock.', ], ], 'cart' => [ @@ -241,8 +240,6 @@ private function assetProductUndefined(array $response): void 'orderNumber', ], 'code' => 'UNDEFINED', - 'message' => 'Could not add the product with SKU "configurable" to the shopping cart: ' . - 'You need to choose options for your item.', ], ], 'cart' => [ @@ -311,7 +308,6 @@ protected function getQuery($orderNumber): string userInputErrors { path code - message } cart { email diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php index d87e3564ca980..0dec95ed44c51 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php @@ -174,7 +174,6 @@ protected function getQuery($orderNumber): string userInputErrors { path code - message } cart { email diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php index 05b5e5b74b149..53c6bd4cfb212 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php @@ -190,7 +190,6 @@ protected function getQuery($orderNumber): string userInputErrors { path code - message } cart { email diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 43d04efe80f0c..433cbca150b09 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -163,8 +163,6 @@ public function testReorderWithLowStock() [ 'path' => ['orderNumber'], 'code' => 'INSUFFICIENT_STOCK', - 'message' => 'Could not add the product with SKU "simple" to the shopping cart: ' - . 'The requested qty is not available', ], ], 'cart' => [ @@ -199,8 +197,6 @@ private function assertProductNotAvailable() [ 'path' => ['orderNumber'], 'code' => 'NOT_SALABLE', - 'message' => 'Could not add the product with SKU "simple" to the shopping cart: ' - . 'Product that you are trying to add is not available.', ], ], 'cart' => [ @@ -270,7 +266,6 @@ private function assertWithDeletedProduct( [ 'path' => ['orderNumber'], 'code' => 'PRODUCT_NOT_FOUND', - 'message' => 'Could not find a product with ID "' . $productId . '"', ], ], 'cart' => [ @@ -327,7 +322,6 @@ protected function getQuery($orderNumber): string userInputErrors { path code - message } cart { email diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php index 83a666f8e1896..e7acd7961e85e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php @@ -10,3 +10,4 @@ require __DIR__ . '/../../../Magento/GraphQl/Catalog/_files/virtual_product_rollback.php'; require __DIR__ . '/../../../Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php'; require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php index 6e4390330bcd2..e242a8035250a 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php @@ -9,3 +9,4 @@ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php index b6bc7c7113a85..4980c3ca265df 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php @@ -5,4 +5,6 @@ */ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php index b6bc7c7113a85..4980c3ca265df 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php @@ -5,4 +5,6 @@ */ require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; require 'default_rollback.php'; From b19ebfc628f6b0ed6962cbce8ca6b830dc6a47ac Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 25 Mar 2020 08:24:18 +0200 Subject: [PATCH 557/734] MC-32546: Website Payments Pro Hosted Solution redirect to home page --- app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php | 3 ++- app/code/Magento/Paypal/Model/Ipn.php | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php index 2451247b57617..bb8b5f8fa0b46 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php @@ -8,6 +8,7 @@ namespace Magento\Paypal\Controller\Hostedpro; use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\App\Request\InvalidRequestException; @@ -16,7 +17,7 @@ /** * PayPal Hostedpro return controller. */ -class ReturnAction extends Action implements CsrfAwareActionInterface, HttpPostActionInterface +class ReturnAction extends Action implements CsrfAwareActionInterface, HttpPostActionInterface, HttpGetActionInterface { /** * When a customer return to website from gateway. diff --git a/app/code/Magento/Paypal/Model/Ipn.php b/app/code/Magento/Paypal/Model/Ipn.php index 0d7d5518f9c7d..4c128adfbd00e 100644 --- a/app/code/Magento/Paypal/Model/Ipn.php +++ b/app/code/Magento/Paypal/Model/Ipn.php @@ -8,6 +8,7 @@ use Exception; use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender; use Magento\Sales\Model\Order\Email\Sender\OrderSender; @@ -301,6 +302,9 @@ protected function _registerPaymentCapture($skipFraudDetection = false) $payment->setParentTransactionId($parentTransactionId); $payment->setShouldCloseParentTransaction('Completed' === $this->getRequestData('auth_status')); $payment->setIsTransactionClosed(0); + if ($this->_order->getState() === Order::STATE_PENDING_PAYMENT) { + $this->_order->setState(Order::STATE_PROCESSING); + } $payment->registerCaptureNotification( $this->getRequestData('mc_gross'), $skipFraudDetection && $parentTransactionId From 37daacb93ce148541551183288193d70f91f9227 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Wed, 25 Mar 2020 08:34:25 +0200 Subject: [PATCH 558/734] MC-32325: New customer creation via the admin does not honor default customer group for multi websites --- .../Unit/ViewModel/Customer/WebsiteTest.php | 105 ++++++++++++++++++ .../Customer/ViewModel/Customer/Website.php | 74 ++++++++++++ .../adminhtml/web/js/form/element/website.js | 30 +++++ .../view/base/ui_component/customer_form.xml | 9 +- 4 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php create mode 100644 app/code/Magento/Customer/ViewModel/Customer/Website.php create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js diff --git a/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php new file mode 100644 index 0000000000000..50862fa36f0f9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\ViewModel\Customer; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\ViewModel\Customer\Website as CustomerWebsite; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Test for customer's website view model + */ +class WebsiteTest extends TestCase +{ + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** + * @var CustomerWebsite + */ + private $customerWebsite; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + protected function setUp() + { + $this->systemStore = $this->createMock(SystemStore::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->customerWebsite = $this->objectManagerHelper->getObject( + CustomerWebsite::class, + [ + 'systemStore' => $this->systemStore, + 'scopeConfig' => $this->scopeConfig + ] + ); + } + + /** + * Test that method return correct array of options + * + * @param array $options + * @dataProvider dataProviderOptionsArray + * @return void + */ + public function testToOptionArray(array $options): void + { + $this->scopeConfig->method('getValue') + ->willReturn(1); + + $this->systemStore->method('getWebsiteValuesForForm') + ->willReturn([ + [ + 'label' => 'Main Website', + 'value' => '1', + ], + [ + 'label' => 'Second Website', + 'value' => '2', + ], + ]); + + $this->assertEquals($options, $this->customerWebsite->toOptionArray()); + } + + /** + * Data provider for testToOptionArray test + * + * @return array + */ + public function dataProviderOptionsArray(): array + { + return [ + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => '1', + 'group_id' => '1', + ], + [ + 'label' => 'Second Website', + 'value' => '2', + 'group_id' => '1', + ], + ], + ], + ]; + } +} diff --git a/app/code/Magento/Customer/ViewModel/Customer/Website.php b/app/code/Magento/Customer/ViewModel/Customer/Website.php new file mode 100644 index 0000000000000..bb25f4766aa77 --- /dev/null +++ b/app/code/Magento/Customer/ViewModel/Customer/Website.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\ViewModel\Customer; + +use Magento\Customer\Model\GroupManagement; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Customer's website view model + */ +class Website implements OptionSourceInterface +{ + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Store constructor. + * + * @param SystemStore $systemStore + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + SystemStore $systemStore, + ScopeConfigInterface $scopeConfig + ) { + $this->systemStore = $systemStore; + $this->scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function toOptionArray(): array + { + return $this->getWebsiteOptions(); + } + + /** + * Adding group ID to options list + * + * @return array + */ + private function getWebsiteOptions(): array + { + $options = $this->systemStore->getWebsiteValuesForForm(); + foreach ($options as $key => $option) { + $websiteId = $option['value']; + $groupId = $this->scopeConfig->getValue( + GroupManagement::XML_PATH_DEFAULT_ID, + ScopeInterface::SCOPE_WEBSITE, + $websiteId + ); + $options[$key]['group_id'] = $groupId; + } + + return $options; + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js new file mode 100644 index 0000000000000..7c2f6489a51d3 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js @@ -0,0 +1,30 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/element/website', + 'uiRegistry' +], function (Website, registry) { + 'use strict'; + + return Website.extend({ + /** + * On value change handler. + * + * @param {String} value + */ + onUpdate: function (value) { + var groupIdFieldKey = 'group_id', + groupId = registry.get('index = ' + groupIdFieldKey), + option = this.getOption(value); + + if (groupId) { + groupId.value(option[groupIdFieldKey]); + } + + return this._super(); + } + }); +}); diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index d5c7154a30f54..b9487037da2cc 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -117,7 +117,7 @@ <visible>false</visible> </settings> </field> - <field name="website_id" component="Magento_Ui/js/form/element/website" formElement="select"> + <field name="website_id" component="Magento_Customer/js/form/element/website" formElement="select"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> @@ -136,6 +136,13 @@ <link name="customerId">${ $.provider }:data.customer.entity_id</link> </imports> </settings> + <formElements> + <select> + <settings> + <options class="Magento\Customer\ViewModel\Customer\Website"/> + </settings> + </select> + </formElements> </field> <field name="prefix" formElement="input"> <argument name="data" xsi:type="array"> From ab2f9fdf5fd28fc9f015b2d747845cc323167335 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 25 Mar 2020 10:49:24 +0200 Subject: [PATCH 559/734] MC-30588: Adding new non-searchable product attribute invalidates indexes --- .../Indexer/Fulltext/Plugin/Attribute.php | 83 +++++++---- .../Indexer/Fulltext/Plugin/AttributeTest.php | 43 ++++-- .../Indexer/Fulltext/Plugin/AttributeTest.php | 139 ++++++++++++++++++ 3 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php index 86dccf8cfe559..7b5d43ece922d 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php @@ -6,6 +6,11 @@ namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin; use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\Framework\Model\AbstractModel; +use Magento\Catalog\Model\ResourceModel\Attribute as AttributeResourceModel; +use Magento\Framework\Search\Request\Config; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Catalog\Api\Data\EavAttributeInterface; /** * Catalog search indexer plugin for catalog attribute. @@ -13,7 +18,7 @@ class Attribute extends AbstractPlugin { /** - * @var \Magento\Framework\Search\Request\Config + * @var Config */ private $config; @@ -33,12 +38,12 @@ class Attribute extends AbstractPlugin private $saveIsNew; /** - * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry - * @param \Magento\Framework\Search\Request\Config $config + * @param IndexerRegistry $indexerRegistry + * @param Config $config */ public function __construct( - \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, - \Magento\Framework\Search\Request\Config $config + IndexerRegistry $indexerRegistry, + Config $config ) { parent::__construct($indexerRegistry); $this->config = $config; @@ -47,36 +52,32 @@ public function __construct( /** * Check if indexer invalidation is needed on attribute save (searchable flag change) * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Framework\Model\AbstractModel $attribute + * @param AttributeResourceModel $subject + * @param AbstractModel $attribute * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSave( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Framework\Model\AbstractModel $attribute + AttributeResourceModel $subject, + AbstractModel $attribute ) { $this->saveIsNew = $attribute->isObjectNew(); - $this->saveNeedInvalidation = ( - $attribute->dataHasChangedFor('is_searchable') - || $attribute->dataHasChangedFor('is_filterable') - || $attribute->dataHasChangedFor('is_visible_in_advanced_search') - ); + $this->saveNeedInvalidation = $this->shouldInvalidateSearchIndex($attribute); } /** * Invalidate indexer on attribute save (searchable flag change) * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Catalog\Model\ResourceModel\Attribute $result + * @param AttributeResourceModel $subject + * @param AttributeResourceModel $result * - * @return \Magento\Catalog\Model\ResourceModel\Attribute + * @return AttributeResourceModel * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Catalog\Model\ResourceModel\Attribute $result + AttributeResourceModel $subject, + AttributeResourceModel $result ) { if ($this->saveNeedInvalidation) { $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate(); @@ -91,15 +92,15 @@ public function afterSave( /** * Check if indexer invalidation is needed on searchable attribute delete * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Framework\Model\AbstractModel $attribute + * @param AttributeResourceModel $subject + * @param AbstractModel $attribute * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeDelete( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Framework\Model\AbstractModel $attribute + AttributeResourceModel $subject, + AbstractModel $attribute ) { $this->deleteNeedInvalidation = !$attribute->isObjectNew() && $attribute->getIsSearchable(); } @@ -107,19 +108,45 @@ public function beforeDelete( /** * Invalidate indexer on searchable attribute delete * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Catalog\Model\ResourceModel\Attribute $result + * @param AttributeResourceModel $subject + * @param AttributeResourceModel $result * - * @return \Magento\Catalog\Model\ResourceModel\Attribute + * @return AttributeResourceModel * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterDelete( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Catalog\Model\ResourceModel\Attribute $result + AttributeResourceModel $subject, + AttributeResourceModel $result ) { if ($this->deleteNeedInvalidation) { $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate(); } return $result; } + + /** + * Check if catalogsearch_fulltext index should be invalidated. + * + * @param AbstractModel $attribute + * @return bool + */ + private function shouldInvalidateSearchIndex( + AbstractModel $attribute + ):bool { + $shouldInvalidate = false; + $fields = [ + EavAttributeInterface::IS_SEARCHABLE, + EavAttributeInterface::IS_FILTERABLE, + EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH, + ]; + foreach ($fields as $field) { + if ($this->saveIsNew && $attribute->getData($field) + || !$this->saveIsNew && $attribute->dataHasChangedFor($field)) { + $shouldInvalidate = true; + break; + } + } + + return $shouldInvalidate; + } } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php index 70ae4a75924d4..fc2b353afaf03 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php @@ -64,7 +64,7 @@ protected function setUp() ); $this->attributeMock = $this->createPartialMock( \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['dataHasChangedFor', 'isObjectNew', 'getIsSearchable'] + ['dataHasChangedFor', 'isObjectNew', 'getIsSearchable', 'getData'] ); $this->config = $this->getMockBuilder(\Magento\Framework\Search\Request\Config::class) ->disableOriginalConstructor() @@ -85,7 +85,7 @@ public function testBeforeSave() ->method('isObjectNew') ->willReturn(true); $this->attributeMock->expects($this->once()) - ->method('dataHasChangedFor') + ->method('getData') ->with('is_searchable') ->willReturn(true); $this->assertEquals( @@ -102,22 +102,34 @@ public function testAfterSaveNoInvalidation() ); } - public function testAfterSaveWithInvalidation() + /** + * Test afterSave with invalidation. + * + * @param bool $saveNeedInvalidation + * @param bool $saveIsNew + * @dataProvider afterSaveDataProvider + */ + public function testAfterSaveWithInvalidation(bool $saveNeedInvalidation, bool $saveIsNew) { $model = $this->objectManager->getObject( Attribute::class, [ 'indexerRegistry' => $this->indexerRegistryMock, 'config' => $this->config, - 'saveNeedInvalidation' => true, - 'saveIsNew' => true + 'saveNeedInvalidation' => $saveNeedInvalidation, + 'saveIsNew' => $saveIsNew, ] ); - $this->indexerMock->expects($this->once())->method('invalidate'); - $this->prepareIndexer(); - $this->config->expects($this->once()) - ->method('reset'); + if ($saveNeedInvalidation) { + $this->indexerMock->expects($this->once())->method('invalidate'); + $this->prepareIndexer(); + } + + if ($saveIsNew || $saveNeedInvalidation) { + $this->config->expects($this->once()) + ->method('reset'); + } $this->assertEquals( $this->subjectMock, @@ -125,6 +137,19 @@ public function testAfterSaveWithInvalidation() ); } + /** + * @return array + */ + public function afterSaveDataProvider(): array + { + return [ + 'save_new_with_invalidation' => ['saveNeedInvalidation' => true, 'isNew' => true], + 'save_new_without_invalidation' => ['saveNeedInvalidation' => false, 'isNew' => true], + 'update_existing_with_inalidation' => ['saveNeedInvalidation' => true, 'isNew' => false], + 'update_existing_without_inalidation' => ['saveNeedInvalidation' => false, 'isNew' => false], + ]; + } + public function testBeforeDelete() { $this->attributeMock->expects($this->once()) diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php new file mode 100644 index 0000000000000..176ed2c0e425b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\CatalogSearch\Model\Indexer\Fulltext\Processor; +use Magento\Catalog\Api\Data\EavAttributeInterface; + +/** + * Check catalogsearch_fulltext index status after create product attribute. + */ +class AttributeTest extends TestCase +{ + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @var Attribute + */ + private $attribute; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->indexerProcessor = Bootstrap::getObjectManager()->create(Processor::class); + $this->attribute = Bootstrap::getObjectManager()->create(Attribute::class); + } + + /** + * Check index status on clean database. + * + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + */ + public function testCheckIndexStatusOnCleanDatabase(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after create non searchable attribute. + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterCreateNonSearchableAttribute(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after non searchable attribute update. + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterNonSearchableAttributeUpdate(): void + { + $this->attribute->load('dropdown_attribute', 'attribute_code'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setIsGlobal(1)->save(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after create searchable attribute. + * + * @return void + * @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterCreateSearchableAttribute(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isInvalid()); + } + + /** + * Check index status after update non searchable attribute to searchable. + * + * @param string $field + * @return void + * @dataProvider searchableAttributesDataProvider + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + */ + public function testCheckIndexStatusAfterUpdateNonSearchableAttributeToSearchable(string $field): void + { + $this->indexerProcessor->reindexAll(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + $this->attribute->loadByCode(Product::ENTITY, 'dropdown_attribute'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setData($field, true)->save(); + $this->assertFalse($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * @return array + */ + public function searchableAttributesDataProvider(): array + { + return [ + [EavAttributeInterface::IS_SEARCHABLE], + [EavAttributeInterface::IS_FILTERABLE], + [EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH] + ]; + } + + /** + * Check index status after update searchable attribute to non searchable. + * + * @return void + * @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterUpdateSearchableAttributeToNonSearchable(): void + { + $this->attribute->loadByCode(Product::ENTITY, 'test_catalog_view'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setIsFilterable(false)->save(); + $this->assertFalse($this->indexerProcessor->getIndexer()->isValid()); + } +} From 633f0732b44930da1359482abb993520ea90068b Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 25 Mar 2020 11:05:40 +0200 Subject: [PATCH 560/734] MC-32546: Website Payments Pro Hosted Solution redirect to home page --- app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php b/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php index 53022e2db93f5..3de60edf1e648 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php @@ -53,6 +53,7 @@ protected function setUp() 'getEmailSent', 'save', 'getState', + 'setState', ]; $this->_orderMock = $this->createPartialMock(\Magento\Sales\Model\OrderFactory::class, $methods); $this->_orderMock->expects($this->any())->method('create')->will($this->returnSelf()); @@ -149,9 +150,12 @@ public function testPaymentReviewRegisterPaymentFraud() ->will($this->returnValue(true)); $this->_orderMock->expects($this->any())->method('getPayment')->will($this->returnValue($paymentMock)); $this->_orderMock->expects($this->any())->method('canFetchPaymentReviewUpdate')->will($this->returnValue(true)); - $this->_orderMock->expects($this->once())->method('getState')->will( + $this->_orderMock->expects($this->any())->method('getState')->will( $this->returnValue(Order::STATE_PENDING_PAYMENT) ); + $this->_orderMock->expects($this->once()) + ->method('setState') + ->with(Order::STATE_PROCESSING); $this->_paypalInfo->expects($this->once()) ->method('importToPayment') ->with( From 577a27ee25312b6eed02aa528fc96505b886ab28 Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Tue, 24 Mar 2020 11:45:11 +0200 Subject: [PATCH 561/734] Added improvements to category url key validation logic --- .../CategoryUrlPathAutogeneratorObserver.php | 74 ++++++++++++++----- .../Magento/CatalogUrlRewrite/i18n/en_US.csv | 2 + .../Controller/Adminhtml/CategoryTest.php | 66 +++++++++++++++++ 3 files changed, 123 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 0d0b0fb995706..401f2c9629f76 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -7,12 +7,14 @@ namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Model\Category; +use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Model\Store; /** @@ -83,7 +85,7 @@ public function __construct( * * @param \Magento\Framework\Event\Observer $observer * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -106,18 +108,61 @@ public function execute(\Magento\Framework\Event\Observer $observer) * Update Url Key * * @param Category $category - * @param string $urlKey - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param string|null $urlKey + * @return void + * @throws LocalizedException + * @throws NoSuchEntityException + */ + private function updateUrlKey(Category $category, ?string $urlKey): void + { + $this->validateUrlKey($category, $urlKey); + $category->setUrlKey($urlKey) + ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category)); + if (!$category->isObjectNew()) { + $category->getResource()->saveAttribute($category, 'url_path'); + if ($category->dataHasChangedFor('url_path')) { + $this->updateUrlPathForChildren($category); + } + } + } + + /** + * Validate URL key value + * + * @param Category $category + * @param string|null $urlKey + * @return void + * @throws LocalizedException */ - private function updateUrlKey($category, $urlKey) + private function validateUrlKey(Category $category, ?string $urlKey): void { + if (empty($urlKey) && !empty($category->getName()) && !empty($category->getUrlKey())) { + throw new LocalizedException( + __( + 'Invalid URL key. The "%1" URL key can not be used to generate Latin URL key. ' . + 'Please use Latin letters and numbers to avoid generating URL key issues.', + $category->getUrlKey() + ) + ); + } + + if (empty($urlKey) && !empty($category->getName())) { + throw new LocalizedException( + __( + 'Invalid URL key. The "%1" category name can not be used to generate Latin URL key. ' . + 'Please add URL key or change category name using Latin letters and numbers to avoid generating ' . + 'URL key issues.', + $category->getName() + ) + ); + } + if (empty($urlKey)) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key')); + throw new LocalizedException(__('Invalid URL key')); } if (in_array($urlKey, $this->getInvalidValues())) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __( 'URL key "%1" matches a reserved endpoint name (%2). Use another URL key.', $urlKey, @@ -125,15 +170,6 @@ private function updateUrlKey($category, $urlKey) ) ); } - - $category->setUrlKey($urlKey) - ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category)); - if (!$category->isObjectNew()) { - $category->getResource()->saveAttribute($category, 'url_path'); - if ($category->dataHasChangedFor('url_path')) { - $this->updateUrlPathForChildren($category); - } - } } /** @@ -199,7 +235,7 @@ protected function isGlobalScope($storeId) * @param Category $category * @param Category|null $parentCategory * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ protected function updateUrlPathForCategory(Category $category, Category $parentCategory = null) { diff --git a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv index 1dddaa458a16c..7f1e1cd086408 100644 --- a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv +++ b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv @@ -7,4 +7,6 @@ "Create Permanent Redirect for URLs if URL Key Changed","Create Permanent Redirect for URLs if URL Key Changed" "Generate ""category/product"" URL Rewrites","Generate ""category/product"" URL Rewrites" "URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key.","URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key." +"Invalid URL key. The ""%1"" URL key can not be used to generate Latin URL key. Please use Latin letters and numbers to avoid generating URL key issues.","Invalid URL key. The ""%1"" URL key can not be used to generate Latin URL key. Please use Latin letters and numbers to avoid generating URL key issues." +"Invalid URL key. The ""%1"" category name can not be used to generate Latin URL key. Please add URL key or change category name using Latin letters and numbers to avoid generating URL key issues.","Invalid URL key. The ""%1"" category name can not be used to generate Latin URL key. Please add URL key or change category name using Latin letters and numbers to avoid generating URL key issues." "<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.","<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them." diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index e89e5aae92cf5..d4bcf576709fc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -890,4 +890,70 @@ public function testSaveWithCustomBackendNameAction(): void MessageInterface::TYPE_ERROR ); } + + /** + * Verify that the category cannot be saved if category name can not be converted to Latin (like Thai) + * + * @return void + */ + public function testSaveWithThaiCategoryNameAction(): void + { + $categoryName = 'ประเภท'; + $errorMessage = 'Invalid URL key. The "%1" category name can not be used to generate Latin URL key. ' . + 'Please add URL key or change category name using Latin letters and numbers to avoid generating ' . + 'URL key issues.'; + $inputData = [ + 'name' => $categoryName, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1 + ], + 'is_active' => '1', + 'include_in_menu' => '1', + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch('backend/catalog/category/save'); + $this->assertSessionMessages( + $this->equalTo( + [ + (string)__($errorMessage, $categoryName) + ] + ), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Verify that the category cannot be saved if category URL key can not be converted to Latin (like Thai) + * + * @return void + */ + public function testSaveWithThaiCategoryUrlKeyAction(): void + { + $categoryUrlKey = 'ประเภท'; + $errorMessage = 'Invalid URL key. The "%1" URL key can not be used to generate Latin URL key. ' . + 'Please use Latin letters and numbers to avoid generating URL key issues.'; + $inputData = [ + 'name' => 'category name', + 'url_key' => $categoryUrlKey, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1 + ], + 'is_active' => '1', + 'include_in_menu' => '1', + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch('backend/catalog/category/save'); + $this->assertSessionMessages( + $this->equalTo( + [ + (string)__($errorMessage, $categoryUrlKey) + ] + ), + MessageInterface::TYPE_ERROR + ); + } } From 1ce8a7c0e8da9de0f4239e9356df58054abbee75 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 25 Mar 2020 15:44:01 +0200 Subject: [PATCH 562/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...AppearsOnProductPagePreviewActionGroup.xml | 2 +- ...hVisualSwatchAttributePrependMediaTest.xml | 21 ++++--------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml index 9a35253412072..2aa3a608a0760 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml @@ -17,6 +17,6 @@ <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear"/> <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery"/> - <seeElement selector="{{StorefrontProductMediaSection.productImage(productImage)}}" stepKey="seeCorrectProductImage"/> + <seeElement selector="{{StorefrontProductMediaSection.productImageActive(productImage)}}" stepKey="seeCorrectProductImage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index 944659d176b50..daf200da74e34 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -51,24 +51,11 @@ <!-- Open configurable product edit page --> <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> - <!-- Add attributes to configurable product--> + <!-- Add attribute to configurable product--> <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> - - <!-- Find Swatch attribute in grid and select it --> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindSwatchAttribute"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForSwatchAttribute"/> - <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createVisualSwatchAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForSwatchAttribute"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForSwatchAttribute"/> - <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectSwatchAttribute"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToSelectOptions"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createVisualSwatchAttribute.frontend_label[0]$)}}" stepKey="selectAllSwatchAttributeOptions"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToApplyQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextToProceedToSummary"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickGenerateProductsButton"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createProductConfigurations"> + <argument name="attributeCode" value="$createVisualSwatchAttribute.attribute_code$"/> + </actionGroup> <!-- Load media for configurable product --> <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProduct"> From 88b2b44b8c4bc93e573bf6c080a5874e4a71041b Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 25 Mar 2020 09:47:42 -0500 Subject: [PATCH 563/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Added code fix --- .../Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php | 5 ++++- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index f0b69885b6e40..c3e16dcea5f97 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -8,9 +8,9 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Model\Quote\Address; /** @@ -49,6 +49,8 @@ public function execute(ContextInterface $context, array $shippingAddressInput): if ($addressInput) { $addressInput['customer_notes'] = $shippingAddressInput['customer_notes'] ?? ''; + $addressInput['save_in_address_book'] = isset($shippingAddressInput['address']['save_in_address_book']) + ? (bool) $shippingAddressInput['address']['save_in_address_book'] : true; } if (null === $customerAddressId && null === $addressInput) { @@ -63,6 +65,7 @@ public function execute(ContextInterface $context, array $shippingAddressInput): ); } + $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); return $shippingAddress; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index e600965923c14..2eaee2dd594aa 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -56,6 +56,12 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; + + if ($addressInput) { + $addressInput['save_in_address_book'] = isset($billingAddressInput['address']['save_in_address_book']) + ? (bool) $billingAddressInput['address']['save_in_address_book'] : true; + } + // Need to keep this for BC of `use_for_shipping` field $sameAsShipping = isset($billingAddressInput['use_for_shipping']) ? (bool)$billingAddressInput['use_for_shipping'] : false; From b10b2aa541e24f0a768616cf9857672a4514d9dd Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 25 Mar 2020 17:29:30 +0200 Subject: [PATCH 564/734] MC-24244: [MFTF Test] Instant purchase negative scenarios --- ...chaseFunctionalityNegativeScenarioTest.xml | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml index 68a2cf0d3262c..5f806aca004d4 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml @@ -10,10 +10,9 @@ <test name="StorefrontInstantPurchaseFunctionalityNegativeScenarioTest"> <annotations> <features value="InstantPurchase"/> - <stories value="Using Instant Purchase - Negative Scenario"/> + <stories value="Using Instant Purchase"/> <title value="Checks negative Instant Purchase functionality scenario"/> - <description value="Checks that Instant Purchase button does not appears in a different situation"/> - <useCaseId value="MAGETWO-84214"/> + <description value="Checks that Instant Purchase button does not appear in a different situation"/> <testCaseId value="MC-25949"/> <severity value="CRITICAL"/> <group value="instant_purchase"/> @@ -55,6 +54,8 @@ <requiredEntity createDataKey="createGroupedProduct"/> <requiredEntity createDataKey="createSimpleProduct"/> </createData> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> <!-- Log in as a customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> <argument name="Customer" value="$customerWithDefaultAddress$"/> @@ -104,7 +105,7 @@ <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterTest"/> </after> <!-- 1. Ensure customer is a guest --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> @@ -147,27 +148,27 @@ <argument name="Customer" value="$customerWithoutAddress$"/> </actionGroup> <!-- 4. Browse simple product page and check that Instant Purchase button does not show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageAgain"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithoutAddress"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageAgain"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductAsCustomerWithoutAddress"/> <!-- 5. Log in as a customer with address --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithoutAddress"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithAddressLoginToStorefront"> <argument name="Customer" value="$customerWithAddress$"/> </actionGroup> <!-- 6. Browse simple product page and check that Instant Purchase button does not show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageOneMoreTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithAddress"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageOneMoreTime"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageAsCustomerWithAddress"/> <!-- 7. Log in as a customer with default address --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddress"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefront"> <argument name="Customer" value="$customerWithDefaultAddress$"/> </actionGroup> <!-- 8. Browse simple product page and check that Instant Purchase button show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForFourthTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithDefaultAddress"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButton"/> @@ -175,53 +176,53 @@ <magentoCLI command="config:set {{DisableVaultBraintree.path}} {{DisableVaultBraintree.value}}" stepKey="disableVault"/> <magentoCLI command="config:set {{Enable3DSecureBraintree.path}} {{Enable3DSecureBraintree.value}}" stepKey="enable3DSecureVerification"/> <!-- New session should be started --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressAgain"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAgain"> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfter3dSecureEnabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfter3dSecureEnabled"> <argument name="Customer" value="$customerWithDefaultAddress$"/> </actionGroup> <!-- 11. Browse simple product page and check that Instant Purchase button does not show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForFifthTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWith3dSecureEnabled"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageForFifthTime"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageWith3dSecureEnabled"/> <!-- 12. Disable all supported payment methods --> <createData entity="DefaultBraintreeConfig" stepKey="restoreToDefaultBraintreeConfig"/> <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="restoreToDefaultBraintreeConfigurationData"/> <!-- New session should be started --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressOneMoreTime"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontOneMoreTime"> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterPaymentMethodDisabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterPaymentMethodDisabled"> <argument name="Customer" value="$customerWithDefaultAddress$"/> </actionGroup> <!-- 13. Browse simple product page and check that Instant Purchase button does not show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForSixthTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWhilePaymentMethodDisabled"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageForSixthTime"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductWhilePaymentMethodDisabled"/> <!-- 14. Reenable supported payment method (without 3d secure for Braintree) --> <magentoCLI command="config:set {{Disable3DSecureBraintree.path}} {{Disable3DSecureBraintree.value}}" stepKey="disable3DSecureVerification"/> <createData entity="BraintreeConfig" stepKey="reenableBraintreePayment"/> <createData entity="CustomBraintreeConfigurationData" stepKey="reenableBraintreeAndVault"/> <!-- New session should be started --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressForFourthTime"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontFinalTime"> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterReenablePaymentMethod"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterReenablePaymentMethod"> <argument name="Customer" value="$customerWithDefaultAddress$"/> </actionGroup> <!-- 15. Browse simple product page and check that Instant Purchase button show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageForSeventhTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithReenabledPaymentMethod"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonAgain"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonWithReenabledPaymentMethod"/> <!-- 16. Disable shipping method for customer with default address --> <magentoCLI command="config:set {{DisableFlatRateConfigData.path}} {{DisableFlatRateConfigData.value}}" stepKey="disableFlatRate"/> <!-- New session should be started --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddressForFifthTime"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontForFifthTime"> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterFlatRateDisabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterFlatRateDisabled"> <argument name="Customer" value="$customerWithDefaultAddress$"/> </actionGroup> <!-- 17. Browse simple product page and check that Instant Purchase button does not show up --> - <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageFinalTime"> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWhileFlatRateDisabled"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageFinalTime"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageWhileFlatRateDisabled"/> </test> </tests> From 610cec542aae547acb3b7a2500791a832751f8e7 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Wed, 25 Mar 2020 11:48:48 -0500 Subject: [PATCH 565/734] MC-32201: Reorder functionality (test coverage) --- .../GraphQl/Sales/ReorderConfigurableWithVariationsTest.php | 3 ++- .../testsuite/Magento/GraphQl/Sales/ReorderTest.php | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php index eae61fea3cbaf..42f19517c077d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -164,7 +164,8 @@ private function assertWithOutOfStockVariation( * @throws \Magento\Framework\Exception\StateException * @throws NoSuchEntityException */ - public function testWithDeletedVariation(): void { + public function testWithDeletedVariation(): void + { /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ $productRepository = Bootstrap::getObjectManager() ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php index 433cbca150b09..1a2fa55126267 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -254,10 +254,7 @@ private function assertWithDeletedProduct( ->get(\Magento\Framework\Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - - $productId = $product->getId(); $productRepository->delete($product); - $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); From 81eac1ed3100bb28c5ea907673781de403a91e25 Mon Sep 17 00:00:00 2001 From: ruslankostiv <rkostiv@adobe.com> Date: Wed, 25 Mar 2020 12:39:16 -0500 Subject: [PATCH 566/734] MC-32201: Reorder functionality (test coverage) --- ...rontGalleryConfigurableProductWithSeveralAttributesTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml index a39352ce16189..62b6a7ac77c67 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml @@ -19,6 +19,9 @@ <group value="catalog"/> <group value="configurableProduct"/> <group value="swatch"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> From 9e48a5813c1485ca947155b57dfaefcae6a6a4e3 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Wed, 25 Mar 2020 13:06:52 -0500 Subject: [PATCH 567/734] MC-32792: Get Categories server side API operation to improve in 2.4 --- .../ResourceModel/Category/Collection.php | 112 +++++++++++++----- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index 657daca13055e..8599d827a44ba 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -5,8 +5,11 @@ */ namespace Magento\Catalog\Model\ResourceModel\Category; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product\Visibility; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DB\Select; use Magento\Store\Model\ScopeInterface; /** @@ -68,6 +71,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ private $scopeConfig; + /** + * @var Visibility + */ + private $catalogProductVisibility; + /** * Constructor * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory @@ -96,7 +104,8 @@ public function __construct( \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null, + Visibility $catalogProductVisibility = null ) { parent::__construct( $entityFactory, @@ -113,6 +122,8 @@ public function __construct( ); $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->catalogProductVisibility = $catalogProductVisibility ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(Visibility::class); } /** @@ -122,7 +133,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Catalog\Model\Category::class, \Magento\Catalog\Model\ResourceModel\Category::class); + $this->_init(Category::class, \Magento\Catalog\Model\ResourceModel\Category::class); } /** @@ -259,6 +270,7 @@ protected function _loadProductCount() * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function loadProductCount($items, $countRegular = true, $countAnchor = true) { @@ -310,34 +322,14 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr if ($countAnchor) { // Retrieve Anchor categories product counts + $categoryIds = array_keys($anchor); + $countSelect = $this->getProductsCountQuery($categoryIds, (bool)$websiteId); + $categoryProductsCount = $this->_conn->fetchPairs($countSelect); foreach ($anchor as $item) { - if ($allChildren = $item->getAllChildren()) { - $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; - $select = $this->_conn->select(); - $select->from( - ['main_table' => $this->getProductTable()], - new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') - )->joinInner( - ['e' => $this->getTable('catalog_category_entity')], - 'main_table.category_id=e.entity_id', - [] - )->where( - '(e.entity_id = :entity_id OR e.path LIKE :c_path)' - ); - if ($websiteId) { - $select->join( - ['w' => $this->getProductWebsiteTable()], - 'main_table.product_id = w.product_id', - [] - )->where( - 'w.website_id = ?', - $websiteId - ); - } - $item->setProductCount((int)$this->_conn->fetchOne($select, $bind)); - } else { - $item->setProductCount(0); - } + $productsCount = isset($categoriesProductsCount[$item->getId()]) + ? (int)$categoryProductsCount[$item->getId()] + : $this->getProductsCountFromCategoryTable($item, $websiteId); + $item->setProductCount($productsCount); } } return $this; @@ -513,4 +505,66 @@ public function getProductTable() } return $this->_productTable; } + + /** + * @param Category $item + * @param string $websiteId + * @return int + */ + private function getProductsCountFromCategoryTable(Category $item, string $websiteId): int + { + $productCount = 0; + + if ($item->getAllChildren()) { + $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; + $select = $this->_conn->select(); + $select->from( + ['main_table' => $this->getProductTable()], + new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') + )->joinInner( + ['e' => $this->getTable('catalog_category_entity')], + 'main_table.category_id=e.entity_id', + [] + )->where( + '(e.entity_id = :entity_id OR e.path LIKE :c_path)' + ); + if ($websiteId) { + $select->join( + ['w' => $this->getProductWebsiteTable()], + 'main_table.product_id = w.product_id', + [] + )->where( + 'w.website_id = ?', + $websiteId + ); + } + $productCount = (int)$this->_conn->fetchOne($select, $bind); + } + return $productCount; + } + + /** + * Get query for retrieve count of products per category + * + * @param array $categoryIds + * @return Select + */ + private function getProductsCountQuery(array $categoryIds, $addVisibilityFilter = true): Select + { + $categoryTable = $this->getTable('catalog_category_product_index'); + $select = $this->_conn->select() + ->from( + ['cat_index' => $categoryTable], + ['category_id' => 'cat_index.category_id', 'count' => 'count(cat_index.product_id)'] + ) + ->where('cat_index.category_id in (?)', \array_map('\intval', $categoryIds)); + if (true === $addVisibilityFilter) { + $select->where('cat_index.visibility in (?)', $this->catalogProductVisibility->getVisibleInSiteIds()); + } + if (count($categoryIds) > 1) { + $select->group('cat_index.category_id'); + } + + return $select; + } } From 38d1556b06a32623a3ea7bedbc676e4a732fdc0c Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Mon, 10 Feb 2020 21:25:12 +0100 Subject: [PATCH 568/734] Prevent resizing an image if it was already resized before. --- .../MediaStorage/Service/ImageResize.php | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index d061ddbd3dc46..461d3df47523f 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -294,7 +294,7 @@ private function makeImage(string $originalImagePath, array $imageParams): Image } /** - * Resize image. + * Resize image if not already resized before * * @param array $imageParams * @param string $originalImagePath @@ -303,13 +303,48 @@ private function makeImage(string $originalImagePath, array $imageParams): Image private function resize(array $imageParams, string $originalImagePath, string $originalImageName) { unset($imageParams['id']); - $image = $this->makeImage($originalImagePath, $imageParams); $imageAsset = $this->assertImageFactory->create( [ 'miscParams' => $imageParams, 'filePath' => $originalImageName, ] ); + $imageAssetPath = $imageAsset->getPath(); + $usingDbAsStorage = $this->fileStorageDatabase->checkDbUsage(); + $mediaStorageFilename = $this->mediaDirectory->getRelativePath($imageAssetPath); + + $alreadyResized = $usingDbAsStorage ? + $this->fileStorageDatabase->fileExists($mediaStorageFilename) : + $this->mediaDirectory->isFile($imageAssetPath); + + if (!$alreadyResized) { + $this->generateResizedImage( + $imageParams, + $originalImagePath, + $imageAssetPath, + $usingDbAsStorage, + $mediaStorageFilename + ); + } + } + + /** + * Generate resized image + * + * @param array $imageParams + * @param string $originalImagePath + * @param string $imageAssetPath + * @param bool $usingDbAsStorage + * @param string $mediaStorageFilename + */ + private function generateResizedImage( + array $imageParams, + string $originalImagePath, + string $imageAssetPath, + bool $usingDbAsStorage, + string $mediaStorageFilename + ) { + $image = $this->makeImage($originalImagePath, $imageParams); if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { $image->resize($imageParams['image_width'], $imageParams['image_height']); @@ -335,11 +370,10 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->watermark($this->getWatermarkFilePath($imageParams['watermark_file'])); } - $image->save($imageAsset->getPath()); + $image->save($imageAssetPath); - if ($this->fileStorageDatabase->checkDbUsage()) { - $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); - $this->fileStorageDatabase->saveFile($mediastoragefilename); + if ($usingDbAsStorage) { + $this->fileStorageDatabase->saveFile($mediaStorageFilename); } } From 801b4b3147afe1e2b5f2ae30c2bf585c144829c7 Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Mon, 10 Feb 2020 21:45:26 +0100 Subject: [PATCH 569/734] Cleanup. --- .../MediaStorage/Service/ImageResize.php | 30 ++++++++----------- .../Test/Unit/Service/ImageResizeTest.php | 2 -- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index 461d3df47523f..d5ce1a7e20f98 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -15,17 +15,18 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Image; use Magento\Framework\Image\Factory as ImageFactory; use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; use Magento\Framework\App\State; use Magento\Framework\View\ConfigInterface as ViewConfig; -use \Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Store\Model\StoreManagerInterface; use Magento\Theme\Model\Config\Customization as ThemeCustomizationConfig; -use Magento\Theme\Model\ResourceModel\Theme\Collection; +use Magento\Theme\Model\ResourceModel\Theme\Collection as ThemeCollection; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\MediaStorage\Helper\File\Storage\Database; +use Magento\MediaStorage\Helper\File\Storage\Database as FileStorageDatabase; use Magento\Theme\Model\Theme; /** @@ -76,24 +77,20 @@ class ImageResize private $themeCustomizationConfig; /** - * @var Collection + * @var ThemeCollection */ private $themeCollection; /** - * @var Filesystem + * @var WriteInterface */ private $mediaDirectory; /** - * @var Filesystem - */ - private $filesystem; - - /** - * @var Database + * @var FileStorageDatabase */ private $fileStorageDatabase; + /** * @var StoreManagerInterface */ @@ -108,9 +105,9 @@ class ImageResize * @param ViewConfig $viewConfig * @param AssertImageFactory $assertImageFactory * @param ThemeCustomizationConfig $themeCustomizationConfig - * @param Collection $themeCollection + * @param ThemeCollection $themeCollection * @param Filesystem $filesystem - * @param Database $fileStorageDatabase + * @param FileStorageDatabase $fileStorageDatabase * @param StoreManagerInterface $storeManager * @throws \Magento\Framework\Exception\FileSystemException * @internal param ProductImage $gallery @@ -125,9 +122,9 @@ public function __construct( ViewConfig $viewConfig, AssertImageFactory $assertImageFactory, ThemeCustomizationConfig $themeCustomizationConfig, - Collection $themeCollection, + ThemeCollection $themeCollection, Filesystem $filesystem, - Database $fileStorageDatabase = null, + FileStorageDatabase $fileStorageDatabase = null, StoreManagerInterface $storeManager = null ) { $this->appState = $appState; @@ -140,9 +137,8 @@ public function __construct( $this->themeCustomizationConfig = $themeCustomizationConfig; $this->themeCollection = $themeCollection; $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $this->filesystem = $filesystem; $this->fileStorageDatabase = $fileStorageDatabase ?: - ObjectManager::getInstance()->get(Database::class); + ObjectManager::getInstance()->get(FileStorageDatabase::class); $this->storeManager = $storeManager ?? ObjectManager::getInstance()->get(StoreManagerInterface::class); } diff --git a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php index f0e1efa7806e4..4a17245396240 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php @@ -24,8 +24,6 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** - * Class ImageResizeTest - * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ From 2d1a3c1777a0fd7f4e2e8b7289701c1e7106594d Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Tue, 11 Feb 2020 20:37:01 +0100 Subject: [PATCH 570/734] Added unit tests. --- .../Test/Unit/Service/ImageResizeTest.php | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php index 4a17245396240..29ab475948221 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php @@ -247,6 +247,9 @@ public function testResizeFromThemesMediaStorageDatabase() $this->databaseMock->expects($this->any()) ->method('checkDbUsage') ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(false)); $this->productImageMock->expects($this->any()) ->method('getCountUsedProductImages') @@ -287,6 +290,9 @@ public function testResizeFromImageNameMediaStorageDatabase() $this->databaseMock->expects($this->any()) ->method('checkDbUsage') ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(false)); $this->mediaDirectoryMock->expects($this->any()) ->method('isFile') @@ -316,4 +322,65 @@ public function testResizeFromImageNameMediaStorageDatabase() $this->service->resizeFromImageName($this->testfilename); } + + public function testSkipResizingAlreadyResizedImageOnDisk() + { + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + + $this->mediaDirectoryMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + + $this->themeCollectionMock->expects($this->any()) + ->method('loadRegisteredThemes') + ->willReturn( + [ new DataObject(['id' => '0']) ] + ); + $this->themeCustomizationConfigMock->expects($this->any()) + ->method('getStoresByThemes') + ->willReturn( + ['0' => []] + ); + + $this->imageFactoryMock->expects($this->never()) + ->method('create'); + + $this->service->resizeFromImageName($this->testfilename); + } + + public function testSkipResizingAlreadyResizedImageInDatabase() + { + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(true)); + + $this->mediaDirectoryMock->expects($this->any()) + ->method('isFile') + ->with($this->testfilepath) + ->willReturnOnConsecutiveCalls( + $this->returnValue(false), + $this->returnValue(true) + ); + + $this->themeCollectionMock->expects($this->any()) + ->method('loadRegisteredThemes') + ->willReturn( + [ new DataObject(['id' => '0']) ] + ); + $this->themeCustomizationConfigMock->expects($this->any()) + ->method('getStoresByThemes') + ->willReturn( + ['0' => []] + ); + + $this->databaseMock->expects($this->never()) + ->method('saveFile'); + + $this->service->resizeFromImageName($this->testfilename); + } } From 9aab5bfe668ef5d1aa3aa32c47d06e0f2c3ec8f8 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 25 Mar 2020 16:45:11 -0500 Subject: [PATCH 571/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Added code fixes from review --- .../Model/Cart/GetShippingAddress.php | 3 - .../Model/Cart/SetShippingAddressesOnCart.php | 9 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- .../Customer/SetBillingAddressOnCartTest.php | 88 ++++++++++++++++- .../Customer/SetShippingAddressOnCartTest.php | 98 ++++++++++++++++++- 5 files changed, 188 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index c3e16dcea5f97..c68b987fe8a96 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -49,8 +49,6 @@ public function execute(ContextInterface $context, array $shippingAddressInput): if ($addressInput) { $addressInput['customer_notes'] = $shippingAddressInput['customer_notes'] ?? ''; - $addressInput['save_in_address_book'] = isset($shippingAddressInput['address']['save_in_address_book']) - ? (bool) $shippingAddressInput['address']['save_in_address_book'] : true; } if (null === $customerAddressId && null === $addressInput) { @@ -65,7 +63,6 @@ public function execute(ContextInterface $context, array $shippingAddressInput): ); } - $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); return $shippingAddress; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index e058913dde1d3..3fb6b8d8ae26e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -10,7 +10,6 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote\Address; /** * Set single shipping address for a specified shopping cart @@ -49,7 +48,13 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('You cannot specify multiple shipping addresses.') ); } - $shippingAddressInput = current($shippingAddressesInput); + $shippingAddressInput = current($shippingAddressesInput) ?? null; + $customerAddressId = $shippingAddressInput['customer_address_id'] ?? null; + + if ($shippingAddressInput && !$customerAddressId) { + $shippingAddressInput['address']['save_in_address_book'] = isset($shippingAddressInput['address']['save_in_address_book']) + ? (bool) $shippingAddressInput['address']['save_in_address_book'] : true; + } $shippingAddress = $this->getShippingAddress->execute($context, $shippingAddressInput); diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index d334d56c85aac..d776b48b6a740 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -112,7 +112,7 @@ input CartAddressInput { postcode: String country_code: String! telephone: String! - save_in_address_book: Boolean + save_in_address_book: Boolean @doc(description: "Determines whether to save the address in the customer's address book. The default value is true") } input SetShippingMethodsOnCartInput { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 1af44012bb515..b7376e91f705e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -954,6 +954,88 @@ public function testSetBillingAddressAndPlaceOrder() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetBillingAddressWithDefaultValueOfSaveInAddressBookAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + same_as_shipping: true + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "88776" + country_code: "US" + telephone: "88776655" + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + $this->assertCount(1, $addresses); + $this->assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -1208,9 +1290,9 @@ private function getSetShippingMethodsQuery( ): string { return <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$shippingCarrierCode" method_code: "$shippingMethodCode" @@ -1251,7 +1333,7 @@ private function getSetPaymentMethodQuery( payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index cedbf4bf61d17..9256c1c41b49f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -1046,6 +1046,98 @@ public function testSetNewShippingAddressAndPlaceOrder() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testSetNewShippingAddressWithDefaultValueOfSaveInAddressBookAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + customer_notes: "Test note" + } + ] + } + ) { + cart { + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + customer_notes + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + $this->assertCount(1, $addresses); + $this->assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + $this->assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -1201,9 +1293,9 @@ private function getSetShippingMethodsQuery( ): string { return <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$shippingCarrierCode" method_code: "$shippingMethodCode" @@ -1244,7 +1336,7 @@ private function getSetPaymentMethodQuery( payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code From e8040657005ad97b98b0264a9527928790d39482 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 25 Mar 2020 16:49:19 -0500 Subject: [PATCH 572/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Fixed extra space on schema --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index d776b48b6a740..1ca00d5ef7bdc 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -112,7 +112,7 @@ input CartAddressInput { postcode: String country_code: String! telephone: String! - save_in_address_book: Boolean @doc(description: "Determines whether to save the address in the customer's address book. The default value is true") + save_in_address_book: Boolean @doc(description: "Determines whether to save the address in the customer's address book. The default value is true") } input SetShippingMethodsOnCartInput { From 11c3eeaf6d2cd5d096aac2952eb145eaf4a2ba49 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 25 Mar 2020 23:58:30 +0100 Subject: [PATCH 573/734] =?UTF-8?q?Reduce=20the=20complexity=20proudly=20i?= =?UTF-8?q?ntroduced=20`(=E2=95=AF=C2=B0=E2=96=A1=C2=B0)=E2=95=AF=EF=B8=B5?= =?UTF-8?q?=20=E2=94=BB=E2=94=81=E2=94=BB`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Api/ProductRepositoryInterfaceTest.php | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 09b7dd163e1ce..44f8dc5498cb8 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -389,65 +389,65 @@ public function testCreate($product) public function externalVideoDataProvider(): array { return [ - [ + 'youtube-external-video' => [ [ - [ - 'media_type' => 'external-video', - 'disabled' => false, - 'label' => 'Test Video Created', - 'types' => [], - 'position' => 1, - 'content' => [ - 'type' => 'image/png', - 'name' => 'thumbnail.png', - 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' - . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Created', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', ], - 'extension_attributes' => [ - 'video_content' => [ - 'media_type' => 'external-video', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/', - 'video_title' => 'Video title', - 'video_description' => 'Video description', - 'video_metadata' => 'Video meta', - ], - ], - ] + ], ] ], - [ + 'vimeo-external-video' => [ [ - [ - 'media_type' => 'external-video', - 'disabled' => false, - 'label' => 'Test Video Updated', - 'types' => [], - 'position' => 1, - 'content' => [ - 'type' => 'image/png', - 'name' => 'thumbnail.png', - 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' - . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', - ], - 'extension_attributes' => [ - 'video_content' => [ - 'media_type' => 'external-video', - 'video_provider' => 'vimeo', - 'video_url' => 'https://www.vimeo.com/', - 'video_title' => 'Video title', - 'video_description' => 'Video description', - 'video_metadata' => 'Video meta', - ], + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Updated', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'vimeo', + 'video_url' => 'https://www.vimeo.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', ], - ] + ], ] ] ]; } + /** + * Returns the array of data for Video thumbnail + * + * @return array|string[] + */ + private function getVideoThumbnailStub(): array + { + return [ + 'type' => 'image/png', + 'name' => 'thumbnail.png', + 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' + . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + ]; + } + /** * Test create/ update product with external video media gallery entry * @@ -463,7 +463,7 @@ public function testCreateWithExternalVideo(array $mediaGalleryData) ] ); - $simpleProductBaseData['media_gallery_entries'] = $mediaGalleryData; + $simpleProductBaseData['media_gallery_entries'] = [$mediaGalleryData]; $response = $this->saveProduct($simpleProductBaseData); $this->assertEquals( From 52604a6bc6cf79facaf1873ec6f45249ee3a0704 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 26 Mar 2020 08:55:18 +0200 Subject: [PATCH 574/734] MC-24023: Placing order in Admin for more stock than is available is allowed and does not show error --- .../Initializer/StockItem.php | 4 ++++ .../Model/StockStateProvider.php | 9 ++++++-- .../CatalogInventory/etc/adminhtml/di.xml | 5 ----- ...vailableToConfigureDisabledProductTest.xml | 9 ++++++-- .../Block/Adminhtml/Order/Create/Messages.php | 21 +++++++++++++++++++ .../AdminAddSelectedProductToOrderTest.xml | 2 +- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php index 7a46780f2d783..f104552b4e0fc 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php @@ -118,6 +118,10 @@ public function initialize( $product->getStore()->getWebsiteId() ); + if ($result->getHasError() === true && in_array($result->getErrorCode(), ['qty_available', 'out_stock'])) { + $quoteItem->setHasError(true); + } + /* We need to ensure that any possible plugin will not erase the data */ $backOrdersQty = $this->stockStateProvider->checkQuoteItemQty($stockItem, $rowQty, $qtyForCheck, $qty) ->getItemBackorders(); diff --git a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php index 6851b05aa56a6..b57518b681aa2 100644 --- a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php +++ b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php @@ -14,7 +14,7 @@ use Magento\Framework\Math\Division as MathDivision; /** - * Interface StockStateProvider + * Provider stocks state */ class StockStateProvider implements StockStateProviderInterface { @@ -156,6 +156,7 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ if (!$stockItem->getIsInStock()) { $result->setHasError(true) + ->setErrorCode('out_stock') ->setMessage(__('This product is out of stock.')) ->setQuoteMessage(__('Some of the products are out of stock.')) ->setQuoteMessageIndex('stock'); @@ -165,7 +166,11 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ if (!$this->checkQty($stockItem, $summaryQty) || !$this->checkQty($stockItem, $qty)) { $message = __('The requested qty is not available'); - $result->setHasError(true)->setMessage($message)->setQuoteMessage($message)->setQuoteMessageIndex('qty'); + $result->setHasError(true) + ->setErrorCode('qty_available') + ->setMessage($message) + ->setQuoteMessage($message) + ->setQuoteMessageIndex('qty'); return $result; } else { if ($stockItem->getQty() - $summaryQty < 0) { diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml index 4d90b2159d852..065d9c1878849 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml @@ -14,11 +14,6 @@ </argument> </arguments> </type> - <type name="Magento\CatalogInventory\Model\Spi\StockStateProviderInterface"> - <arguments> - <argument name="qtyCheckApplicable" xsi:type="boolean">false</argument> - </arguments> - </type> <type name="Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider"> <arguments> <argument name="addFieldStrategies" xsi:type="array"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index dbbeb4e252ef7..b9a25dd9c8c24 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -157,7 +157,12 @@ <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> <waitForPageLoad stepKey="waitForShippingMethodLoad"/> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> - <waitForPageLoad stepKey="waitForSuccess"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + <waitForPageLoad stepKey="waitForError"/> + <!-- Check that error remains --> + <actionGroup ref="AssertAdminItemOrderedErrorActionGroup" stepKey="assertProductErrorRemains"> + <argument name="productName" value="$createConfigChildProduct2.name$"/> + <argument name="messageType" value="error"/> + <argument name="message" value="This product is out of stock."/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php index d62497d6b47e9..18c1431f61104 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php @@ -14,6 +14,9 @@ */ class Messages extends \Magento\Framework\View\Element\Messages { + + private const ITEMS_GRID = 'items_grid'; + /** * Preparing global layout * @@ -22,6 +25,24 @@ class Messages extends \Magento\Framework\View\Element\Messages protected function _prepareLayout() { $this->addMessages($this->messageManager->getMessages(true)); + $itemsBlock = $this->getLayout()->getBlock(self::ITEMS_GRID); + if (!$itemsBlock) { + return; + } + $items = $itemsBlock->getItems(); + foreach ($items as $item) { + if ($item->getHasError()) { + $messageCollection = $this->getMessageCollection(); + foreach ($messageCollection->getItems() as $blockMessage) { + if ($item->getMessage(true) === $blockMessage->getText()) { + /* Remove duplicated messages.*/ + $messageCollection->deleteMessageByIdentifier($blockMessage->getIdentifier()); + } + } + $this->setMessages($messageCollection); + } + } + parent::_prepareLayout(); } } diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml index ca74eca88308a..159ba0c2b8e06 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml @@ -72,7 +72,7 @@ <!-- Check that error remains --> <actionGroup ref="AssertAdminItemOrderedErrorActionGroup" stepKey="assertProductErrorRemains"> <argument name="productName" value="$simpleProduct.name$"/> - <argument name="messageType" value="notice"/> + <argument name="messageType" value="error"/> </actionGroup> </test> </tests> From 80cb06e283a030ac3872ca46c4be17104dd87e83 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 26 Mar 2020 09:40:43 +0100 Subject: [PATCH 575/734] =?UTF-8?q?Extract=20the=20problematic=20test=20to?= =?UTF-8?q?=20separate=20file=20`(=E2=95=AF=C2=B0=E2=96=A1=C2=B0)=E2=95=AF?= =?UTF-8?q?=EF=B8=B5=20=E2=94=BB=E2=94=81=E2=94=BB`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Api/ProductRepositoryInterfaceTest.php | 91 --------- .../ProductVideoExternalSourceTest.php | 174 ++++++++++++++++++ 2 files changed, 174 insertions(+), 91 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 44f8dc5498cb8..2512de3537f28 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -381,97 +381,6 @@ public function testCreate($product) $this->deleteProduct($product[ProductInterface::SKU]); } - /** - * Media gallery entries with external videos - * - * @return array - */ - public function externalVideoDataProvider(): array - { - return [ - 'youtube-external-video' => [ - [ - 'media_type' => 'external-video', - 'disabled' => false, - 'label' => 'Test Video Created', - 'types' => [], - 'position' => 1, - 'content' => $this->getVideoThumbnailStub(), - 'extension_attributes' => [ - 'video_content' => [ - 'media_type' => 'external-video', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/', - 'video_title' => 'Video title', - 'video_description' => 'Video description', - 'video_metadata' => 'Video meta', - ], - ], - ] - ], - 'vimeo-external-video' => [ - [ - 'media_type' => 'external-video', - 'disabled' => false, - 'label' => 'Test Video Updated', - 'types' => [], - 'position' => 1, - 'content' => $this->getVideoThumbnailStub(), - 'extension_attributes' => [ - 'video_content' => [ - 'media_type' => 'external-video', - 'video_provider' => 'vimeo', - 'video_url' => 'https://www.vimeo.com/', - 'video_title' => 'Video title', - 'video_description' => 'Video description', - 'video_metadata' => 'Video meta', - ], - ], - ] - ] - ]; - } - - /** - * Returns the array of data for Video thumbnail - * - * @return array|string[] - */ - private function getVideoThumbnailStub(): array - { - return [ - 'type' => 'image/png', - 'name' => 'thumbnail.png', - 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' - . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', - ]; - } - - /** - * Test create/ update product with external video media gallery entry - * - * @dataProvider externalVideoDataProvider - * @param array $mediaGalleryData - */ - public function testCreateWithExternalVideo(array $mediaGalleryData) - { - $simpleProductBaseData = $this->getSimpleProductData( - [ - ProductInterface::NAME => 'Product With Ext. Video', - ProductInterface::SKU => 'prod-with-ext-video' - ] - ); - - $simpleProductBaseData['media_gallery_entries'] = [$mediaGalleryData]; - - $response = $this->saveProduct($simpleProductBaseData); - $this->assertEquals( - $simpleProductBaseData['media_gallery_entries'][0]['extension_attributes'], - $response["media_gallery_entries"][0]["extension_attributes"] - ); - } - /** * @param array $fixtureProduct * diff --git a/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php new file mode 100644 index 0000000000000..33ccf6a8854c5 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php @@ -0,0 +1,174 @@ +<?php +declare(strict_types=1); + +namespace Magento\ProductVideo; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Test for \Magento\ProductVideo feature + * + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductVideoExternalSourceTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + + /** + * Media gallery entries with external videos + * + * @return array + */ + public function externalVideoDataProvider(): array + { + return [ + 'youtube-external-video' => [ + [ + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Created', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', + ], + ], + ] + ], + 'vimeo-external-video' => [ + [ + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Updated', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'vimeo', + 'video_url' => 'https://www.vimeo.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', + ], + ], + ] + ] + ]; + } + + /** + * Returns the array of data for Video thumbnail + * + * @return array|string[] + */ + private function getVideoThumbnailStub(): array + { + return [ + 'type' => 'image/png', + 'name' => 'thumbnail.png', + 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' + . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + ]; + } + + /** + * Test create/ update product with external video media gallery entry + * + * @dataProvider externalVideoDataProvider + * @param array $mediaGalleryData + */ + public function testCreateWithExternalVideo(array $mediaGalleryData) + { + $simpleProductBaseData = $this->getSimpleProductData( + [ + ProductInterface::NAME => 'Product With Ext. Video', + ProductInterface::SKU => 'prod-with-ext-video' + ] + ); + + $simpleProductBaseData['media_gallery_entries'] = [$mediaGalleryData]; + + $response = $this->saveProduct($simpleProductBaseData); + $this->assertEquals( + $simpleProductBaseData['media_gallery_entries'][0]['extension_attributes'], + $response["media_gallery_entries"][0]["extension_attributes"] + ); + } + + /** + * Get Simple Product Data + * + * @param array $productData + * @return array + */ + protected function getSimpleProductData($productData = []) + { + return [ + ProductInterface::SKU => isset($productData[ProductInterface::SKU]) + ? $productData[ProductInterface::SKU] : uniqid('sku-', true), + ProductInterface::NAME => isset($productData[ProductInterface::NAME]) + ? $productData[ProductInterface::NAME] : uniqid('sku-', true), + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::PRICE => 3.62, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + 'custom_attributes' => [ + ['attribute_code' => 'cost', 'value' => ''], + ['attribute_code' => 'description', 'value' => 'Description'], + ] + ]; + } + + /** + * Save Product + * + * @param $product + * @param string|null $storeCode + * @param string|null $token + * @return mixed + */ + protected function saveProduct($product, $storeCode = null, ?string $token = null) + { + if (isset($product['custom_attributes'])) { + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' + && !is_array($attribute['value']) + ) { + $attribute['value'] = [""]; + } + } + } + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } + $requestData = ['product' => $product]; + + return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode); + } +} From 55b8b0b0542d2b130d9c2a81964426cca2a93eef Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 26 Mar 2020 10:46:52 +0200 Subject: [PATCH 576/734] MC-25010: [ElasticSearch] Products does not show in category page after cron:run --- .../Catalog/Model/Indexer/Category/Product/Action/RowsTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php index 00a4c5b230055..c3aced62eecd3 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php @@ -93,7 +93,6 @@ private function getInstalledSearchEngine(): string /** * @magentoDataFixture Magento/Catalog/_files/category_tree_with_products.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @return void From 682097c379ad0e7a699fbf37eb179551bc2ff81e Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Thu, 26 Mar 2020 10:51:05 +0200 Subject: [PATCH 577/734] MC-32243: [Magento Cloud] - "Invalid Form Key. Please refresh the page." not being translated --- .../Controller/Account/LoginPostTest.php | 23 ++++++++++++ .../Customer/Controller/AccountTest.php | 13 +++++++ .../Magento/Framework/App/FrontController.php | 36 +++++++++++++++---- .../App/Test/Unit/FrontControllerTest.php | 32 ++++++++++++++++- 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php index 80502833cb2d7..10476eb01f306 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php @@ -11,6 +11,7 @@ use Magento\Customer\Model\Url; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Phrase; use Magento\Framework\Url\EncoderInterface; use Magento\TestFramework\TestCase\AbstractController; @@ -151,6 +152,28 @@ public function testLoginWithRedirectToDashboard(): void $this->assertRedirect($this->stringContains('customer/account/')); } + /** + * @magentoConfigFixture current_store customer/startup/redirect_dashboard 1 + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testNoFormKeyLoginPostAction(): void + { + $this->prepareRequest('customer@example.com', 'password'); + $this->getRequest()->setPostValue('form_key', null); + $this->getRequest()->setParam(Url::REFERER_QUERY_PARAM_NAME, $this->urlEncoder->encode('test_redirect')); + $this->dispatch('customer/account/loginPost'); + $this->assertFalse($this->session->isLoggedIn()); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSessionMessages( + $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]), + MessageInterface::TYPE_ERROR + ); + } + /** * Prepare request * diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index c5fdd050bb46b..7b0a4823f701e 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -16,6 +16,7 @@ use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Phrase; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Store\Model\StoreManager; @@ -227,6 +228,10 @@ public function testNoFormKeyCreatePostAction() $this->assertNull($this->getCustomerByEmail('test1@email.com')); $this->assertRedirect($this->stringEndsWith('customer/account/create/')); + $this->assertSessionMessages( + $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]), + MessageInterface::TYPE_ERROR + ); } /** @@ -612,6 +617,10 @@ public function testWrongConfirmationEditPostAction() * * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php * @return void + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException */ public function testRegisterCustomerWithEmailConfirmation(): void { @@ -725,6 +734,10 @@ public function testConfirmationEmailWithSpecialCharacters(): void * @magentoConfigFixture current_store customer/captcha/enable 0 * * @return void + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\State\InputMismatchException */ public function testResetPasswordWhenEmailChanged(): void { diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 2e6ce65970b97..d72c548be4fba 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -5,14 +5,15 @@ */ namespace Magento\Framework\App; +use Magento\Framework\App\Action\AbstractAction; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Request\ValidatorInterface as RequestValidator; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Message\ManagerInterface as MessageManager; -use Magento\Framework\App\Action\AbstractAction; use Psr\Log\LoggerInterface; -use Magento\Framework\App\Request\Http as HttpRequest; /** * Front controller responsible for dispatching application requests @@ -51,19 +52,33 @@ class FrontController implements FrontControllerInterface */ private $validatedRequest = false; + /** + * @var State + */ + private $appState; + + /** + * @var AreaList + */ + private $areaList; + /** * @param RouterListInterface $routerList * @param ResponseInterface $response * @param RequestValidator|null $requestValidator * @param MessageManager|null $messageManager * @param LoggerInterface|null $logger + * @param State $appState + * @param AreaList $areaList */ public function __construct( RouterListInterface $routerList, ResponseInterface $response, ?RequestValidator $requestValidator = null, ?MessageManager $messageManager = null, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, + ?State $appState = null, + ?AreaList $areaList = null ) { $this->_routerList = $routerList; $this->response = $response; @@ -73,6 +88,10 @@ public function __construct( ?? ObjectManager::getInstance()->get(MessageManager::class); $this->logger = $logger ?? ObjectManager::getInstance()->get(LoggerInterface::class); + $this->appState = $appState + ?? ObjectManager::getInstance()->get(State::class); + $this->areaList = $areaList + ?? ObjectManager::getInstance()->get(AreaList::class); } /** @@ -81,6 +100,7 @@ public function __construct( * @param RequestInterface|HttpRequest $request * @return ResponseInterface|ResultInterface * @throws \LogicException + * @throws LocalizedException */ public function dispatch(RequestInterface $request) { @@ -120,9 +140,10 @@ public function dispatch(RequestInterface $request) * * @param HttpRequest $request * @param ActionInterface $actionInstance - * @throws NotFoundException - * * @return ResponseInterface|ResultInterface + * @throws LocalizedException + * + * @throws NotFoundException */ private function processRequest( HttpRequest $request, @@ -147,6 +168,9 @@ private function processRequest( ["exception" => $exception] ); $result = $exception->getReplaceResult(); + $area = $this->areaList->getArea($this->appState->getAreaCode()); + $area->load(Area::PART_DESIGN); + $area->load(Area::PART_TRANSLATE); if ($messages = $exception->getMessages()) { foreach ($messages as $message) { $this->messages->addErrorMessage($message); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php index e088bb92c8782..727e27080569c 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php @@ -6,8 +6,12 @@ namespace Magento\Framework\App\Test\Unit; +use Magento\Framework\App\Area; +use Magento\Framework\App\AreaInterface; +use Magento\Framework\App\AreaList; use Magento\Framework\App\Request\InvalidRequestException; use Magento\Framework\App\Request\ValidatorInterface; +use Magento\Framework\App\State; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Message\ManagerInterface as MessageManager; use Psr\Log\LoggerInterface; @@ -57,6 +61,21 @@ class FrontControllerTest extends \PHPUnit\Framework\TestCase */ private $logger; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|AreaList + */ + private $areaListMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|State + */ + private $appStateMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|AreaInterface + */ + private $areaMock; + protected function setUp() { $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -70,12 +89,19 @@ protected function setUp() $this->requestValidator = $this->createMock(ValidatorInterface::class); $this->messages = $this->createMock(MessageManager::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->appStateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + $this->areaListMock = $this->createMock(AreaList::class); + $this->areaMock = $this->createMock(AreaInterface::class); $this->model = new \Magento\Framework\App\FrontController( $this->routerList, $this->response, $this->requestValidator, $this->messages, - $this->logger + $this->logger, + $this->appStateMock, + $this->areaListMock ); } @@ -114,6 +140,10 @@ public function testAddingValidationFailureMessageToDebugLog() $exceptionMessage = 'exception_message'; $exception = new InvalidRequestException($exceptionMessage); + $this->appStateMock->expects($this->any())->method('getAreaCode')->willReturn('frontend'); + $this->areaMock->expects($this->at(0))->method('load')->with(Area::PART_DESIGN)->willReturnSelf(); + $this->areaMock->expects($this->at(1))->method('load')->with(Area::PART_TRANSLATE)->willReturnSelf(); + $this->areaListMock->expects($this->any())->method('getArea')->will($this->returnValue($this->areaMock)); $this->routerList->expects($this->any()) ->method('valid') ->will($this->returnValue(true)); From 77482975bcaa29b27289e7390b95d6b772067212 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Thu, 26 Mar 2020 11:08:00 +0200 Subject: [PATCH 578/734] MC-32339: MFTF Test- Customer Review Grid Filter Not working --- .../AdminFilterCustomerReviewActionGroup.xml | 21 +++++ .../Test/AdminReviewsByProductsReportTest.xml | 2 +- .../StoreFrontReviewByCustomerReportTest.xml | 76 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml create mode 100644 app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml new file mode 100644 index 0000000000000..971ec2dae2072 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterCustomerReviewActionGroup"> + <arguments> + <argument name="reviewCount" type="string" defaultValue="0"/> + </arguments> + <!--Sort Review Column in Grid --> + <waitForPageLoad stepKey="waitForGridToAppear"/> + <fillField userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridCustomer_filter_review_cnt}}" stepKey="searchReview"/> + <click selector="{{AdminCreateNewReviewSection.CustomerSearchButton}}" stepKey="startSearch"/> + <waitForPageLoad stepKey="waitForResults"/> + <see userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="assertReviewColumn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml index 050f30d6ca65f..a93e89059dbf1 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml @@ -15,7 +15,7 @@ <title value="Admin Reports Review by Products"/> <description value="Review By Products Grid Filters"/> <severity value="AVERAGE"/> - <testCaseId value="MC-32083"/> + <testCaseId value="MC-32333"/> </annotations> <before> <!--Login--> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml new file mode 100644 index 0000000000000..85660a3a9f1db --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml @@ -0,0 +1,76 @@ +<?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="StoreFrontReviewByCustomerReportTest"> + <annotations> + <features value="Review"/> + <stories value="Review By Customers"/> + <title value="StoreFront Reports Review By Customers"/> + <description value="Review By Customer Grid Filters"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-32340"/> + </annotations> + <before> + <!--Login--> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!--Create product and Category--> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="createProduct1" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData stepKey="createProduct2" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!-- Delete reviews --> + <actionGroup ref="AdminOpenReviewsPageActionGroup" stepKey="openAllReviewsPage"/> + <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteCustomerReview"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearNickNameReviewFilters"/> + <!-- Delete customer --> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> + <argument name="customerEmail" value="CustomerEntityOne.email"/> + </actionGroup> + <!--delete Category and Products --> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Logout--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Go to frontend and make a user account and login with it --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + <!-- Go to the product view page --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> + <argument name="productUrl" value="$$createProduct1.custom_attributes[url_key]$$"/> + </actionGroup> + <!-- Click on reviews and add reviews with current user --> + <actionGroup ref="StorefrontAddProductReviewActionGroup" stepKey="addReview"/> + <!-- Go to Pending reviews page and clear filters --> + <actionGroup ref="AdminOpenPendingReviewsPageActionGroup" stepKey="openReviewsPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <!-- Moderate first product reviews: change review status from pending to approved, save --> + <actionGroup ref="AdminOpenReviewByUserNicknameActionGroup" stepKey="openFirstCustomerReviews"/> + <actionGroup ref="AdminChangeReviewStatusActionGroup" stepKey="changeFirstReviewStatus"/> + <actionGroup ref="AdminSaveReviewActionGroup" stepKey="saveModeratedFirstReview"/> + <!-- Navigate to Reports > Reviews >By Customers --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByCustomersPage"> + <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuReportsReviewsByCustomers.dataUiId}}"/> + </actionGroup> + <!--Sort Review Column --> + <grabTextFrom selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="grabCustomerReviewQuantity"/> + <actionGroup ref="AdminFilterCustomerReviewActionGroup" stepKey="navigateToCustomerReportsReview"> + <argument name="reviewCount" value="$grabCustomerReviewQuantity"/> + </actionGroup> + </test> +</tests> From 8269b84b7a9ab831b6596d2331a1f1cc9b31b4d9 Mon Sep 17 00:00:00 2001 From: vagrant <eino.keskitalo@vaimo.com> Date: Thu, 26 Mar 2020 10:29:45 +0100 Subject: [PATCH 579/734] Expect email notification to send, and simplify setup a bit. --- .../Sitemap/Test/Unit/Model/ObserverTest.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php index 09f5418bbd762..543d9598553cc 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php @@ -64,18 +64,19 @@ class ObserverTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->getMock(); - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->getMock(); + $this->objectManagerMock = $this->createMock( + \Magento\Framework\ObjectManagerInterface::class + ); + $this->scopeConfigMock = $this->createMock( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ); $this->collectionFactoryMock = $this->getMockBuilder( \Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->sitemapCollectionMock = $this->createPartialMock( - \Magento\Sitemap\Model\ResourceModel\Sitemap\Collection::class, - ['getIterator'] + $this->sitemapCollectionMock = $this->createMock( + \Magento\Sitemap\Model\ResourceModel\Sitemap\Collection::class ); $this->sitemapMock = $this->createPartialMock( \Magento\Sitemap\Model\Sitemap::class, @@ -130,6 +131,10 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail() ) ->willReturn('error-recipient@example.com'); + $this->emailNotificationMock->expects($this->once()) + ->method('sendErrors') + ->with(['Sitemap Exception']); + $this->observer->scheduledGenerateSitemaps(); } } From c9ac37e99ce522c6991702c2691420baf255c9e1 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 26 Mar 2020 11:44:26 +0200 Subject: [PATCH 580/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...WithSeveralAttributesPrependMediaTest.xml} | 22 ++++++++++++++----- ...hVisualSwatchAttributePrependMediaTest.xml | 22 ++++++++++++++----- .../Test/Mftf/Data/ProductVideoData.xml | 5 +++++ 3 files changed, 38 insertions(+), 11 deletions(-) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Test/{StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml => StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml} (97%) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml similarity index 97% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index 61f5f9779b207..f1c7e1a6fc344 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontGalleryConfigurableProductWithSeveralAttributesTest"> + <test name="StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest"> <annotations> <features value="ConfigurableProduct"/> <stories value="Prepend variation media on storefront"/> @@ -94,9 +94,12 @@ <argument name="image" value="Magento3"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{TestImageNew.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> <!-- Load media for configurable product variation option1-option1--> @@ -110,9 +113,12 @@ <argument name="image" value="TestImageNew"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1Option1"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{placeholderSmallImage.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1Option1"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1Option1"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option1"/> <!-- Load media for configurable product variation option1-option2--> @@ -120,16 +126,22 @@ <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeFirstOption.option[store_labels][0][label]$-$swatchAttributeSecondOption.option[store_labels][0][label]$"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addFirstVideoToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{Magento3.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertFirstVideoAddedToConfigurableProductVariationOption1Option2"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertFirstVideoAddedToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1Option2"> <argument name="image" value="MagentoLogo"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{placeholderThumbnailImage.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption1Option2"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option2"/> <!-- Load media for configurable product variation option2-option2--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index daf200da74e34..14b3be714dc68 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -22,7 +22,6 @@ </annotations> <before> - <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> <!--Create 1 configurable product --> <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> <!-- Create product swatch attribute with 2 variations --> @@ -65,9 +64,12 @@ <argument name="image" value="Magento3"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{TestImageNew.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> <!-- Load media for configurable product variation option1 --> @@ -81,9 +83,12 @@ <argument name="image" value="TestImageNew"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{placeholderSmallImage.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1"/> <!-- Load media for configurable product variation option3 --> @@ -91,16 +96,22 @@ <argument name="productSku" value="$createConfigurableProduct.sku$-$swatchAttributeThirdOption.option[store_labels][0][label]$"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{Magento3.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption3"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption3"> <argument name="image" value="TestImageAdobe"/> </actionGroup> <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> <argument name="image" value="{{MagentoLogo.file}}"/> </actionGroup> - <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption3"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption3"/> <!-- Reindex invalidated indices after product attribute has been created --> @@ -108,7 +119,6 @@ </before> <after> - <createData entity="DefaultProductVideoConfig" stepKey="resetStoreDefaultVideoConfig"/> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> <argument name="product" value="$createConfigurableProduct$"/> </actionGroup> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml index 5bc4ad86e0f06..2b111b882394c 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml @@ -13,4 +13,9 @@ <data key="videoTitle">Arctic Monkeys - Do I Wanna Know? (Official Video)</data> <data key="videoShortTitle">Arctic Monkeys</data> </entity> + <entity name="VimeoProductVideo" type="product_video"> + <data key="videoUrl">https://vimeo.com/76979871</data> + <data key="videoTitle">The New Vimeo Player (You Know, For Videos)</data> + <data key="videoShortTitle">The New Vimeo Player</data> + </entity> </entities> From 8685514536812d414c206eba13447c7bd8749f4b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 26 Mar 2020 11:54:21 +0200 Subject: [PATCH 581/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...ConfigurableProductWithSeveralAttributesPrependMediaTest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index f1c7e1a6fc344..0b3a279e2d615 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -22,7 +22,6 @@ </annotations> <before> - <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setupYoutubeApiKey"/> <!--Create 1 configurable product with 2 variations--> <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> <!--Create product drop down attribute--> @@ -158,7 +157,6 @@ </before> <after> - <createData entity="DefaultProductVideoConfig" stepKey="resetStoreDefaultVideoConfig"/> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> <argument name="product" value="$createConfigurableProduct$"/> </actionGroup> From e7d4299c21c4bdf2b80e13b288d631fdc4d7bdc1 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 26 Mar 2020 13:07:33 +0200 Subject: [PATCH 582/734] MC-32577: Fix ActionGroup For MFTF Test --- .../Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml index 2bd1ee0f7d809..95d78777ed922 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml @@ -17,6 +17,8 @@ <argument name="shippingMethod" defaultValue="" type="string"/> </arguments> + <waitForLoadingMaskToDisappear stepKey="waitForCartTotalsLoaded"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.orderTotal}}" stepKey="waitForCartGrandTotalVisible"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="waitProceedToCheckout"/> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> From 36738ecd4c8a7ad1f5b445c96d1bcd1289f65cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 26 Mar 2020 12:17:21 +0100 Subject: [PATCH 583/734] Fix #6310 - restore weight value check --- .../Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php index 4b1b684ea2f8c..70b2948501d2d 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php @@ -84,6 +84,10 @@ public function __construct( */ public function getElementHtml() { + if ($this->getForm()->getDataObject()->getTypeInstance()->hasWeight()) { + $this->weightSwitcher->setValue(WeightResolver::HAS_WEIGHT); + } + if ($this->getDisabled()) { $this->weightSwitcher->setDisabled($this->getDisabled()); } From ee783ac71321949cab64a5b7155f385df1403982 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 26 Mar 2020 15:07:34 +0200 Subject: [PATCH 584/734] MC-31296: Remove Signifyd code from Magento core --- app/code/Magento/Backend/README.md | 1 - .../InContext/Minicart/SmartButton.php | 2 + .../express/in-context/component.phtml | 11 +- .../Api/CaseCreationServiceInterface.php | 29 -- .../Signifyd/Api/CaseManagementInterface.php | 36 -- .../Signifyd/Api/CaseRepositoryInterface.php | 60 --- .../Signifyd/Api/Data/CaseInterface.php | 235 --------- .../Api/Data/CaseSearchResultsInterface.php | 34 -- .../GuaranteeCancelingServiceInterface.php | 27 -- .../Api/GuaranteeCreationServiceInterface.php | 27 -- .../Signifyd/Block/Adminhtml/CaseInfo.php | 120 ----- .../System/Config/Field/WebhookUrl.php | 60 --- .../Adminhtml/System/Config/Fieldset/Info.php | 31 -- .../Magento/Signifyd/Block/Fingerprint.php | 92 ---- .../Signifyd/Controller/Webhooks/Handler.php | 157 ------ app/code/Magento/Signifyd/LICENSE.txt | 48 -- app/code/Magento/Signifyd/LICENSE_AFL.txt | 48 -- .../Magento/Signifyd/Model/CaseEntity.php | 249 ---------- .../Magento/Signifyd/Model/CaseManagement.php | 93 ---- .../Magento/Signifyd/Model/CaseRepository.php | 134 ------ .../Signifyd/Model/CaseSearchResults.php | 18 - .../Model/CaseServices/CreationService.php | 101 ---- .../CaseServices/StubUpdatingService.php | 23 - .../Model/CaseServices/UpdatingService.php | 119 ----- .../CaseServices/UpdatingServiceFactory.php | 78 --- .../CaseServices/UpdatingServiceInterface.php | 23 - .../Signifyd/Model/CommentsHistoryUpdater.php | 65 --- app/code/Magento/Signifyd/Model/Config.php | 101 ---- .../Magento/Signifyd/Model/CustomerOrders.php | 173 ------- .../Guarantee/CancelGuaranteeAbility.php | 84 ---- .../Model/Guarantee/CancelingService.php | 94 ---- .../Guarantee/CreateGuaranteeAbility.php | 124 ----- .../Model/Guarantee/CreationService.php | 97 ---- .../Model/MessageGenerators/CaseRescore.php | 51 -- .../MessageGenerators/GeneratorException.php | 19 - .../MessageGenerators/GeneratorFactory.php | 112 ----- .../MessageGenerators/GeneratorInterface.php | 21 - .../MessageGenerators/PatternGenerator.php | 56 --- .../Signifyd/Model/OrderStateService.php | 119 ----- .../PaymentMethodMapper.php | 40 -- .../XmlToArrayConfigConverter.php | 88 ---- .../Model/PaymentVerificationFactory.php | 109 ----- .../Model/PredefinedVerificationCode.php | 37 -- .../QuoteSession/Adminhtml/BackendSession.php | 40 -- .../Model/QuoteSession/FrontendSession.php | 39 -- .../QuoteSession/QuoteSessionInterface.php | 19 - .../Model/ResourceModel/CaseEntity.php | 22 - .../ResourceModel/CaseEntity/Collection.php | 24 - .../NotSyncedOrderIdListProvider.php | 54 --- .../Model/SalesOrderGrid/OrderGridUpdater.php | 55 --- .../SignifydGateway/ApiCallException.php | 39 -- .../Model/SignifydGateway/ApiClient.php | 49 -- .../Client/HttpClientFactory.php | 146 ------ .../SignifydGateway/Client/RequestBuilder.php | 63 --- .../SignifydGateway/Client/RequestSender.php | 74 --- .../Client/ResponseHandler.php | 124 ----- .../SignifydGateway/Debugger/BlackHole.php | 33 -- .../Debugger/DebuggerFactory.php | 58 --- .../Debugger/DebuggerInterface.php | 35 -- .../Model/SignifydGateway/Debugger/Log.php | 146 ------ .../Model/SignifydGateway/Gateway.php | 225 --------- .../SignifydGateway/GatewayException.php | 14 - .../Request/AddressBuilder.php | 46 -- .../SignifydGateway/Request/CardBuilder.php | 56 --- .../Request/ClientVersionBuilder.php | 50 -- .../Request/CreateCaseBuilder.php | 131 ----- .../Request/CreateCaseBuilderInterface.php | 22 - .../Request/PurchaseBuilder.php | 227 --------- .../Request/RecipientBuilder.php | 55 --- .../SignifydGateway/Request/SellerBuilder.php | 136 ------ .../Request/UserAccountBuilder.php | 100 ---- .../Response/WebhookMessage.php | 64 --- .../Response/WebhookMessageReader.php | 65 --- .../Response/WebhookRequest.php | 58 --- .../Response/WebhookRequestValidator.php | 113 ----- .../Signifyd/Model/SignifydOrderSessionId.php | 39 -- .../Magento/Signifyd/Observer/PlaceOrder.php | 112 ----- .../Magento/Signifyd/Plugin/OrderPlugin.php | 52 -- .../Magento/Signifyd/Plugin/PaymentPlugin.php | 67 --- app/code/Magento/Signifyd/README.md | 78 --- .../Magento/Signifyd/Test/Mftf/LICENSE.txt | 48 -- .../Signifyd/Test/Mftf/LICENSE_AFL.txt | 48 -- .../Mftf/Page/AdminFraudProtectionPage.xml | 14 - app/code/Magento/Signifyd/Test/Mftf/README.md | 3 - .../AdminSignifydConfigurationSection.xml | 16 - ...gnifydConfigDependentOnActiveFieldTest.xml | 34 -- .../Unit/Block/Adminhtml/CaseInfoTest.php | 150 ------ .../Unit/Controller/Webhooks/HandlerTest.php | 374 --------------- .../UpdatingServiceFactoryTest.php | 167 ------- .../CaseServices/UpdatingServiceTest.php | 316 ------------ .../Unit/Model/CommentsHistoryUpdaterTest.php | 176 ------- .../Test/Unit/Model/CustomerOrdersTest.php | 267 ----------- .../Guarantee/CancelGuaranteeAbilityTest.php | 154 ------ .../Model/Guarantee/CancelingServiceTest.php | 215 --------- .../Guarantee/CreateGuaranteeAbilityTest.php | 260 ---------- .../Model/Guarantee/CreationServiceTest.php | 253 ---------- .../MessageGenerators/CaseRescoreTest.php | 148 ------ .../GeneratorFactoryTest.php | 99 ---- .../PatternGeneratorTest.php | 83 ---- .../Test/Unit/Model/OrderStateServiceTest.php | 204 -------- .../XmlToArrayConfigConverterTest.php | 76 --- .../_files/expected_array.php | 12 - .../_files/signifyd_payment_mapping.xml | 32 -- .../Model/PaymentVerificationFactoryTest.php | 222 --------- .../SalesOrderGrid/OrderGridUpdaterTest.php | 72 --- .../Client/HttpClientFactoryTest.php | 131 ----- .../Client/ResponseHandlerTest.php | 182 ------- .../Model/SignifydGateway/GatewayTest.php | 448 ------------------ .../Response/WebhookMessageReaderTest.php | 114 ----- .../Response/WebhookRequestValidatorTest.php | 231 --------- .../Unit/Model/SignifydOrderSessionIdTest.php | 60 --- .../Test/Unit/Observer/PlaceOrderTest.php | 284 ----------- .../Listing/Column/Guarantee/Options.php | 56 --- app/code/Magento/Signifyd/composer.json | 35 -- app/code/Magento/Signifyd/etc/acl.xml | 22 - .../Magento/Signifyd/etc/adminhtml/di.xml | 10 - .../Magento/Signifyd/etc/adminhtml/routes.xml | 14 - .../Magento/Signifyd/etc/adminhtml/system.xml | 77 --- app/code/Magento/Signifyd/etc/config.xml | 19 - app/code/Magento/Signifyd/etc/db_schema.xml | 43 -- .../Signifyd/etc/db_schema_whitelist.json | 28 -- app/code/Magento/Signifyd/etc/di.xml | 105 ---- app/code/Magento/Signifyd/etc/events.xml | 18 - app/code/Magento/Signifyd/etc/frontend/di.xml | 10 - .../Magento/Signifyd/etc/frontend/routes.xml | 14 - app/code/Magento/Signifyd/etc/module.xml | 20 - .../Signifyd/etc/signifyd_payment_mapping.xml | 82 ---- .../Signifyd/etc/signifyd_payment_mapping.xsd | 34 -- app/code/Magento/Signifyd/i18n/en_US.csv | 46 -- app/code/Magento/Signifyd/registration.php | 9 - .../adminhtml/layout/sales_order_view.xml | 14 - .../view/adminhtml/templates/case_info.phtml | 31 -- .../ui_component/sales_order_grid.xml | 20 - .../view/adminhtml/web/images/logo.png | Bin 5218 -> 0 bytes .../view/adminhtml/web/js/request-send.js | 24 - .../frontend/layout/checkout_cart_index.xml | 14 - .../frontend/layout/checkout_index_index.xml | 14 - .../view/frontend/templates/fingerprint.phtml | 16 - .../web/css/source/_module.less | 7 - .../web/css/source/module/_config.less | 25 - .../web/css/source/module/_order.less | 23 - composer.json | 1 - composer.lock | 8 +- dev/tests/functional/credentials.xml.dist | 2 - .../etc/repository_replacer_payments.xml | 7 - .../Test/Block/Adminhtml/Order/Grid.php | 33 -- .../Adminhtml/Order/View/FraudProtection.php | 31 -- .../Test/Block/SignifydConsole/CaseInfo.php | 245 ---------- .../Test/Block/SignifydConsole/CaseSearch.php | 119 ----- .../Block/SignifydConsole/SignifydLogin.php | 64 --- .../Test/Block/SignifydConsole/Webhooks.php | 216 --------- ...tingSignifydGuaranteeInCommentsHistory.php | 73 --- .../Test/Constraint/AssertCaseInfoOnAdmin.php | 85 ---- .../AssertCaseInfoOnSignifydConsole.php | 214 --------- .../AssertSignifydCaseInCommentsHistory.php | 56 --- .../AssertSignifydCaseInOrdersGrid.php | 50 -- ...gnifydGuaranteeCancelInCommentsHistory.php | 57 --- .../Signifyd/Test/Fixture/SignifydAccount.xml | 17 - .../Signifyd/Test/Fixture/SignifydAddress.xml | 15 - .../Fixture/SignifydAddress/Firstname.php | 46 -- .../Signifyd/Test/Fixture/SignifydData.xml | 21 - .../Test/Page/Adminhtml/OrdersGrid.xml | 12 - .../Test/Page/Adminhtml/SalesOrderView.xml | 12 - .../Page/SignifydConsole/SignifydCases.xml | 13 - .../Page/SignifydConsole/SignifydLogin.xml | 12 - .../SignifydConsole/SignifydNotifications.xml | 12 - .../Signifyd/Test/Repository/Address.xml | 22 - .../Signifyd/Test/Repository/ConfigData.xml | 45 -- .../Signifyd/Test/Repository/Customer.xml | 25 - .../Test/Repository/SignifydAccount.xml | 15 - .../Signifyd/Test/Repository/SignifydData.xml | 33 -- ...ymentWithSignifydGuaranteeDeclinedTest.php | 63 --- ...ymentWithSignifydGuaranteeDeclinedTest.xml | 38 -- ...ateSignifydGuaranteeAndCancelOrderTest.php | 62 --- ...ateSignifydGuaranteeAndCancelOrderTest.xml | 67 --- ...ymentWithSignifydGuaranteeDeclinedTest.php | 63 --- ...ymentWithSignifydGuaranteeDeclinedTest.xml | 39 -- .../Test/TestStep/OpenOrderGridStep.php | 173 ------- .../Test/TestStep/SignifydCancelOrderStep.php | 105 ---- .../TestStep/SignifydCreateCustomerStep.php | 77 --- .../SignifydFillShippingAddressStep.php | 70 --- .../Test/TestStep/SignifydLoginStep.php | 68 --- .../Test/TestStep/SignifydObserveCaseStep.php | 165 ------- .../SignifydSetWebhookHandlersStep.php | 64 --- .../TestStep/UnholdAndCancelOrderStep.php | 84 ---- .../app/Magento/Signifyd/Test/etc/di.xml | 34 -- .../Magento/Signifyd/Test/etc/testcase.xml | 58 --- .../testFromCreateProject/composer.lock | 40 -- .../_files/testSkeleton/composer.lock | 40 -- .../Signifyd/Block/Adminhtml/CaseInfoTest.php | 123 ----- .../Signifyd/Block/FingerprintTest.php | 63 --- .../Controller/Webhooks/HandlerTest.php | 135 ------ .../Signifyd/Model/CaseManagementTest.php | 83 ---- .../Signifyd/Model/CaseRepositoryTest.php | 148 ------ .../CaseServices/CreationServiceTest.php | 245 ---------- .../CaseServices/UpdatingServiceTest.php | 186 -------- .../Model/Guarantee/CancelingServiceTest.php | 158 ------ .../Model/Guarantee/CreationServiceTest.php | 155 ------ .../Request/CreateCaseBuilderTest.php | 291 ------------ .../Signifyd/Observer/PlaceOrderTest.php | 218 --------- .../Signifyd/Plugin/CancelOrderTest.php | 114 ----- .../Signifyd/Plugin/DenyPaymentTest.php | 209 -------- .../Magento/Signifyd/_files/approved_case.php | 38 -- .../Magento/Signifyd/_files/case.php | 42 -- .../Magento/Signifyd/_files/customer.php | 38 -- .../Magento/Signifyd/_files/declined_case.php | 38 -- .../Signifyd/_files/multiple_cases.php | 27 -- ..._with_customer_and_two_simple_products.php | 119 ----- .../order_with_guest_and_virtual_product.php | 68 --- .../Magento/Signifyd/_files/store.php | 33 -- .../Magento/Signifyd/_files/webhook_body.json | 1 - .../Signifyd/_files/website_configuration.php | 67 --- .../_files/website_configuration_rollback.php | 44 -- .../Modular/_files/skip_blocks_ce.php | 3 +- .../_files/skip_template_blocks_ce.php | 1 + .../Signifyd/frontend/js/Fingerprint.test.js | 47 -- .../_files/blacklist/exception_hierarchy.txt | 2 - .../Legacy/_files/obsolete_namespaces.php | 1 + 218 files changed, 16 insertions(+), 17251 deletions(-) delete mode 100644 app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/CaseManagementInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/Data/CaseInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php delete mode 100644 app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php delete mode 100644 app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php delete mode 100644 app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php delete mode 100644 app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php delete mode 100644 app/code/Magento/Signifyd/Block/Fingerprint.php delete mode 100644 app/code/Magento/Signifyd/Controller/Webhooks/Handler.php delete mode 100644 app/code/Magento/Signifyd/LICENSE.txt delete mode 100644 app/code/Magento/Signifyd/LICENSE_AFL.txt delete mode 100644 app/code/Magento/Signifyd/Model/CaseEntity.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseManagement.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseRepository.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseSearchResults.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseServices/CreationService.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php delete mode 100644 app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php delete mode 100644 app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php delete mode 100644 app/code/Magento/Signifyd/Model/Config.php delete mode 100644 app/code/Magento/Signifyd/Model/CustomerOrders.php delete mode 100644 app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php delete mode 100644 app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php delete mode 100644 app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php delete mode 100644 app/code/Magento/Signifyd/Model/Guarantee/CreationService.php delete mode 100644 app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php delete mode 100644 app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php delete mode 100644 app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php delete mode 100644 app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php delete mode 100644 app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php delete mode 100644 app/code/Magento/Signifyd/Model/OrderStateService.php delete mode 100644 app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php delete mode 100644 app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php delete mode 100644 app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php delete mode 100644 app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php delete mode 100644 app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php delete mode 100644 app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php delete mode 100644 app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php delete mode 100644 app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php delete mode 100644 app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php delete mode 100644 app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php delete mode 100644 app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php delete mode 100644 app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php delete mode 100644 app/code/Magento/Signifyd/Observer/PlaceOrder.php delete mode 100644 app/code/Magento/Signifyd/Plugin/OrderPlugin.php delete mode 100644 app/code/Magento/Signifyd/Plugin/PaymentPlugin.php delete mode 100644 app/code/Magento/Signifyd/README.md delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/README.md delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php delete mode 100644 app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php delete mode 100644 app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php delete mode 100644 app/code/Magento/Signifyd/composer.json delete mode 100644 app/code/Magento/Signifyd/etc/acl.xml delete mode 100644 app/code/Magento/Signifyd/etc/adminhtml/di.xml delete mode 100644 app/code/Magento/Signifyd/etc/adminhtml/routes.xml delete mode 100644 app/code/Magento/Signifyd/etc/adminhtml/system.xml delete mode 100644 app/code/Magento/Signifyd/etc/config.xml delete mode 100644 app/code/Magento/Signifyd/etc/db_schema.xml delete mode 100644 app/code/Magento/Signifyd/etc/db_schema_whitelist.json delete mode 100644 app/code/Magento/Signifyd/etc/di.xml delete mode 100644 app/code/Magento/Signifyd/etc/events.xml delete mode 100644 app/code/Magento/Signifyd/etc/frontend/di.xml delete mode 100644 app/code/Magento/Signifyd/etc/frontend/routes.xml delete mode 100644 app/code/Magento/Signifyd/etc/module.xml delete mode 100644 app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml delete mode 100644 app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd delete mode 100644 app/code/Magento/Signifyd/i18n/en_US.csv delete mode 100644 app/code/Magento/Signifyd/registration.php delete mode 100644 app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml delete mode 100644 app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml delete mode 100644 app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml delete mode 100644 app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png delete mode 100644 app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js delete mode 100644 app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml delete mode 100644 app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml delete mode 100644 app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml delete mode 100644 app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less delete mode 100644 app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less delete mode 100644 app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php delete mode 100644 dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js diff --git a/app/code/Magento/Backend/README.md b/app/code/Magento/Backend/README.md index 205051809328a..f70dc9f676236 100644 --- a/app/code/Magento/Backend/README.md +++ b/app/code/Magento/Backend/README.md @@ -16,7 +16,6 @@ Before disabling or uninstalling this module, note that the following modules de - Magento_ReleaseNotification - Magento_Search - Magento_Security -- Magento_Signifyd - Magento_Swatches - Magento_Ui - Magento_User diff --git a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php index c6a17fa5efb9f..a4732a8b4c579 100644 --- a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php +++ b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php @@ -27,6 +27,8 @@ class SmartButton extends Template implements ShortcutInterface { private const ALIAS_ELEMENT_INDEX = 'alias'; + const PAYPAL_BUTTON_ID = 'paypal-express-in-context-checkout-main'; + /** * @var Config */ diff --git a/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml b/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml index ba98f5caeb15a..c102b21830de8 100644 --- a/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml +++ b/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -use Magento\Paypal\Block\Express\InContext\Minicart\Button; +use Magento\Paypal\Block\Express\InContext\Minicart\SmartButton; /** @var \Magento\Paypal\Block\Express\InContext\Component $block */ $configuration = [ - 'id' => Button::PAYPAL_BUTTON_ID, + 'id' => SmartButton::PAYPAL_BUTTON_ID, 'path' => $block->getUrl( 'paypal/express/gettoken', [ @@ -21,17 +21,18 @@ $configuration = [ 'locale' => $block->getLocale(), 'environment' => $block->getEnvironment(), 'button' => [ - Button::PAYPAL_BUTTON_ID, + SmartButton::PAYPAL_BUTTON_ID, ] ] ]; ?> -<div style="display: none;" id="<?= /* @noEscape */ Button::PAYPAL_BUTTON_ID ?>"></div> +<div style="display: none;" id="<?= /* @noEscape */ SmartButton::PAYPAL_BUTTON_ID ?>"></div> <script type="text/x-magento-init"> { "*": { - "Magento_Paypal/js/in-context/express-checkout": <?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($configuration) ?> + "Magento_Paypal/js/in-context/express-checkout": + <?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($configuration) ?> } } </script> diff --git a/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php b/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php deleted file mode 100644 index f7611660b93a1..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd case creation interface - * - * Interface of service for new Signifyd case creation and registering it on Magento side. - * Implementation should send request to Signifyd API and create new entity in Magento. - * - * @api - * @since 100.2.0 - */ -interface CaseCreationServiceInterface -{ - /** - * Create new case for order with specified id. - * - * @param int $orderId - * @return bool - * @throws \Magento\Framework\Exception\NotFoundException If order does not exists - * @throws \Magento\Framework\Exception\AlreadyExistsException If case for $orderId already exists - * @since 100.2.0 - */ - public function createForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/CaseManagementInterface.php b/app/code/Magento/Signifyd/Api/CaseManagementInterface.php deleted file mode 100644 index 69075a308cb96..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseManagementInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd management interface - * Allows to performs operations with Signifyd cases. - * - * @api - * @since 100.2.0 - */ -interface CaseManagementInterface -{ - /** - * Creates new Case entity linked to order id. - * - * @param int $orderId - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @throws \Magento\Framework\Exception\NotFoundException If order does not exists - * @throws \Magento\Framework\Exception\AlreadyExistsException If case for $orderId already exists - * @since 100.2.0 - */ - public function create($orderId); - - /** - * Gets Case entity associated with order id. - * - * @param int $orderId - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - * @since 100.2.0 - */ - public function getByOrderId($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php b/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php deleted file mode 100644 index 4f148a082b300..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd Case repository interface - * - * @api - * @since 100.2.0 - */ -interface CaseRepositoryInterface -{ - /** - * Saves case entity. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface $case - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @since 100.2.0 - */ - public function save(\Magento\Signifyd\Api\Data\CaseInterface $case); - - /** - * Gets case entity by order id. - * - * @param int $id - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @since 100.2.0 - */ - public function getById($id); - - /** - * Gets entity by Signifyd case id. - * - * @param int $caseId - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - * @since 100.2.0 - */ - public function getByCaseId($caseId); - - /** - * Deletes case entity. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface $case - * @return bool - * @since 100.2.0 - */ - public function delete(\Magento\Signifyd\Api\Data\CaseInterface $case); - - /** - * Gets list of case entities. - * - * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria - * @return \Magento\Signifyd\Api\Data\CaseSearchResultsInterface - * @since 100.2.0 - */ - public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria); -} diff --git a/app/code/Magento/Signifyd/Api/Data/CaseInterface.php b/app/code/Magento/Signifyd/Api/Data/CaseInterface.php deleted file mode 100644 index eff46b972fc02..0000000000000 --- a/app/code/Magento/Signifyd/Api/Data/CaseInterface.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api\Data; - -use Magento\Signifyd\Model\SignifydGateway\Gateway; - -/** - * Signifyd Case entity interface - * - * @api - * @see https://www.signifyd.com/docs/api/#/reference/cases/retrieve-a-case/get-a-case - * @since 100.2.0 - */ -interface CaseInterface -{ - /**#@+ - * Constants for case available statuses - */ - const STATUS_OPEN = Gateway::STATUS_OPEN; - const STATUS_PENDING = 'PENDING'; - const STATUS_PROCESSING = Gateway::STATUS_PROCESSING; - const STATUS_FLAGGED = Gateway::STATUS_FLAGGED; - const STATUS_DISMISSED = Gateway::STATUS_DISMISSED; - /**#@-*/ - - /**#@+ - * Constants for guarantee available statuses - */ - const GUARANTEE_APPROVED = Gateway::GUARANTEE_APPROVED; - const GUARANTEE_DECLINED = Gateway::GUARANTEE_DECLINED; - const GUARANTEE_PENDING = Gateway::GUARANTEE_PENDING; - const GUARANTEE_CANCELED = Gateway::GUARANTEE_CANCELED; - const GUARANTEE_IN_REVIEW = Gateway::GUARANTEE_IN_REVIEW; - const GUARANTEE_UNREQUESTED = Gateway::GUARANTEE_UNREQUESTED; - /**#@-*/ - - /**#@+ - * Constants for case available review dispositions - */ - const DISPOSITION_GOOD = Gateway::DISPOSITION_GOOD; - const DISPOSITION_FRAUDULENT = Gateway::DISPOSITION_FRAUDULENT; - const DISPOSITION_UNSET = Gateway::DISPOSITION_UNSET; - /**#@-*/ - - /** - * Returns local case entity identifier. - * - * @return int - * @since 100.2.0 - */ - public function getEntityId(); - - /** - * Sets local case entity id. - * - * @param int $id - * @return $this - * @since 100.2.0 - */ - public function setEntityId($id); - - /** - * Returns Signifyd case identifier. - * - * @return int - * @since 100.2.0 - */ - public function getCaseId(); - - /** - * Sets Signifyd case id. - * - * @param int $id - * @return $this - * @since 100.2.0 - */ - public function setCaseId($id); - - /** - * Returns value, which indicates if a guarantee can be requested for a case. - * Returns null if state of guarantee eligible does not set yet. - * - * @return boolean|null - * @since 100.2.0 - */ - public function isGuaranteeEligible(); - - /** - * Sets value-indicator about guarantee availability for a case. - * - * @param bool $guaranteeEligible - * @return $this - * @since 100.2.0 - */ - public function setGuaranteeEligible($guaranteeEligible); - - /** - * Returns decision state of the guarantee. - * - * @return string - * @since 100.2.0 - */ - public function getGuaranteeDisposition(); - - /** - * Sets decision state of the guarantee. - * - * @param string $disposition - * @return $this - * @since 100.2.0 - */ - public function setGuaranteeDisposition($disposition); - - /** - * Returns case status. - * - * @return string - * @since 100.2.0 - */ - public function getStatus(); - - /** - * Sets case status. - * - * @param string $status - * @return $this - * @since 100.2.0 - */ - public function setStatus($status); - - /** - * Returns value, which indicates the likelihood that the order is fraud. - * - * @return int - * @since 100.2.0 - */ - public function getScore(); - - /** - * Sets risk level value. - * - * @param int $score - * @return $this - * @since 100.2.0 - */ - public function setScore($score); - - /** - * Get order id for a case. - * - * @return int - * @since 100.2.0 - */ - public function getOrderId(); - - /** - * Sets order id for a case. - * - * @param int $orderId - * @return $this - * @since 100.2.0 - */ - public function setOrderId($orderId); - - /** - * Returns data about a team associated with a case. - * - * @return array - * @since 100.2.0 - */ - public function getAssociatedTeam(); - - /** - * Sets team data associated with a case. - * - * @param array $team - * @return $this - * @since 100.2.0 - */ - public function setAssociatedTeam(array $team); - - /** - * Returns disposition of an agent's opinion after reviewing the case. - * - * @return string - * @since 100.2.0 - */ - public function getReviewDisposition(); - - /** - * Sets case disposition. - * - * @param string $disposition - * @return $this - * @since 100.2.0 - */ - public function setReviewDisposition($disposition); - - /** - * Returns creation datetime for a case. - * - * @return string - * @since 100.2.0 - */ - public function getCreatedAt(); - - /** - * Sets creation datetime for a case. - * - * @param string $datetime in DATE_ATOM format - * @return $this - * @since 100.2.0 - */ - public function setCreatedAt($datetime); - - /** - * Returns updating datetime for a case. - * - * @return string - * @since 100.2.0 - */ - public function getUpdatedAt(); - - /** - * Sets updating datetime for a case. - * - * @param string $datetime in DATE_ATOM format - * @return $this - * @since 100.2.0 - */ - public function setUpdatedAt($datetime); -} diff --git a/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php b/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php deleted file mode 100644 index 78d37841cff69..0000000000000 --- a/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api\Data; - -use Magento\Framework\Api\SearchResultsInterface; - -/** - * Retrieve and set list of case entities. - * - * @api - * @since 100.2.0 - */ -interface CaseSearchResultsInterface extends SearchResultsInterface -{ - /** - * Gets collection of case entities. - * - * @return \Magento\Signifyd\Api\Data\CaseInterface[] - * @since 100.2.0 - */ - public function getItems(); - - /** - * Sets collection of case entities. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface[] $items - * @return $this - * @since 100.2.0 - */ - public function setItems(array $items); -} diff --git a/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php b/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php deleted file mode 100644 index 41f4753d036a7..0000000000000 --- a/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd guarantee canceling interface. - * - * Interface allows to submit request to cancel previously created guarantee. - * Implementation should send request to Signifyd API and update existing case entity with guarantee information. - * - * @api - * @since 100.2.0 - */ -interface GuaranteeCancelingServiceInterface -{ - /** - * Cancels Signifyd guarantee for an order. - * - * @param int $orderId - * @return bool - * @since 100.2.0 - */ - public function cancelForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php b/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php deleted file mode 100644 index b4502ea861acd..0000000000000 --- a/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd guarantee creation interface - * - * Interface allows submit previously created Signifyd case for a guaranty. - * Implementation should send request to Signifyd API and update existing case entity with guarantee infromation. - * - * @api - * @since 100.2.0 - */ -interface GuaranteeCreationServiceInterface -{ - /** - * Request Signifyd guarantee for order - * - * @param int $orderId - * @return bool - * @since 100.2.0 - */ - public function createForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php b/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php deleted file mode 100644 index 87d09e392e091..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml; - -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Get Signifyd Case Info - * - * @api - * @since 100.2.0 - */ -class CaseInfo extends Template -{ - /** - * @var CaseInterface - */ - private $caseEntity = false; - - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @param Context $context - * @param CaseManagement $caseManagement - * @param array $data - */ - public function __construct( - Context $context, - CaseManagement $caseManagement, - array $data = [] - ) { - $this->caseManagement = $caseManagement; - - parent::__construct($context, $data); - } - - /** - * Gets case entity associated with order id. - * - * @return CaseInterface|null - */ - private function getCaseEntity() - { - if ($this->caseEntity === false) { - $this->caseEntity = $this->caseManagement->getByOrderId( - $this->getOrderId() - ); - } - - return $this->caseEntity; - } - - /** - * Default getter for case properties - * - * @param mixed $defaultValue - * @param callable $callback - * @return mixed - */ - private function getCaseProperty($defaultValue, callable $callback) - { - return $this->isEmptyCase() ? $defaultValue : call_user_func($callback); - } - - /** - * Checks if case is exists for order - * - * @return bool - * @since 100.2.0 - */ - public function isEmptyCase() - { - return $this->getCaseEntity() === null; - } - - /** - * Gets case guarantee disposition status. - * - * @return string - * @since 100.2.0 - */ - public function getCaseGuaranteeDisposition() - { - return $this->getCaseProperty('', function () { - $guaranteeStatusMap = [ - CaseInterface::GUARANTEE_APPROVED => __('Approved'), - CaseInterface::GUARANTEE_DECLINED => __('Declined'), - CaseInterface::GUARANTEE_PENDING => __('Pending'), - CaseInterface::GUARANTEE_CANCELED => __('Canceled'), - CaseInterface::GUARANTEE_IN_REVIEW => __('In Review'), - CaseInterface::GUARANTEE_UNREQUESTED => __('Unrequested') - ]; - - $status = isset($guaranteeStatusMap[$this->getCaseEntity()->getGuaranteeDisposition()]) ? - $guaranteeStatusMap[$this->getCaseEntity()->getGuaranteeDisposition()] : - ''; - - return $status; - }); - } - - /** - * Retrieves current order Id. - * - * @return integer - */ - private function getOrderId() - { - return (int) $this->getRequest()->getParam('order_id'); - } -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php b/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php deleted file mode 100644 index 7964d6b1af397..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml\System\Config\Field; - -use Magento\Framework\Data\Form\Element\AbstractElement; -use Magento\Config\Block\System\Config\Form\Field; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; - -/** - * Input field transformed to text node with link to store Signifyd webhooks controller. - */ -class WebhookUrl extends Field -{ - /** - * @inheritdoc - */ - protected function _getElementHtml(AbstractElement $element) - { - $url = ''; - $originalData = $element->getOriginalData(); - if (!empty($originalData['handler_url'])) { - $url = $this->getStoreUrl(); - $url .= $originalData['handler_url']; - } - - return '<p class="webhook-url">' . $this->escapeHtml($url) . '</p>'; - } - - /** - * @inheritdoc - */ - protected function _isInheritCheckboxRequired(AbstractElement $element) - { - return false; - } - - /** - * Return base store URL. - * - * @return string - */ - private function getStoreUrl() - { - $website = $this->_storeManager->getWebsite($this->getRequest()->getParam('website')); - - $isSecure = $this->_scopeConfig->isSetFlag( - Store::XML_PATH_SECURE_IN_FRONTEND, - ScopeInterface::SCOPE_WEBSITE, - $website->getCode() - ); - - $configPath = $isSecure ? Store::XML_PATH_SECURE_BASE_LINK_URL : Store::XML_PATH_UNSECURE_BASE_LINK_URL; - - return $this->_scopeConfig->getValue($configPath, ScopeInterface::SCOPE_WEBSITE, $website->getCode()); - } -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php b/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php deleted file mode 100644 index c18c3dc596e21..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset; - -use Magento\Config\Block\System\Config\Form\Fieldset; - -/** - * Fieldset renderer with url attached to comment. - */ -class Info extends Fieldset -{ - /** - * @inheritdoc - */ - protected function _getHeaderCommentHtml($element) - { - $groupConfig = $element->getGroup(); - - if (!empty($groupConfig['more_url']) && !empty($element->getComment())) { - $comment = $element->getComment(); - $comment .= '<p><a href="' . $this->escapeUrl($groupConfig['more_url']) . '" target="_blank">' . - $this->escapeHtml(__('Learn more')) . '</a></p>'; - $element->setComment($comment); - } - - return parent::_getHeaderCommentHtml($element); - } -} diff --git a/app/code/Magento/Signifyd/Block/Fingerprint.php b/app/code/Magento/Signifyd/Block/Fingerprint.php deleted file mode 100644 index f43bffce1fc1a..0000000000000 --- a/app/code/Magento/Signifyd/Block/Fingerprint.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block; - -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; - -/** - * Provides data for Signifyd device fingerprinting script. - * - * Signifyd’s device fingerprinting solution uniquely tracks and identifies devices - * used to transact on your site, increasing your protection from fraud. - * - * @api - * @see https://www.signifyd.com/docs/api/#/reference/device-fingerprint/create-a-case - * @since 100.2.0 - */ -class Fingerprint extends Template -{ - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var Config - */ - private $config; - - /** - * @var QuoteSessionInterface - */ - private $quoteSession; - - /** - * @var string - * @since 100.2.0 - */ - protected $_template = 'Magento_Signifyd::fingerprint.phtml'; - - /** - * @param Context $context - * @param Config $config - * @param SignifydOrderSessionId $signifydOrderSessionId - * @param QuoteSessionInterface $quoteSession - * @param array $data - */ - public function __construct( - Context $context, - Config $config, - SignifydOrderSessionId $signifydOrderSessionId, - QuoteSessionInterface $quoteSession, - array $data = [] - ) { - parent::__construct($context, $data); - $this->signifydOrderSessionId = $signifydOrderSessionId; - $this->config = $config; - $this->quoteSession = $quoteSession; - } - - /** - * Returns a unique Signifyd order session id. - * - * @return string - * @since 100.2.0 - */ - public function getSignifydOrderSessionId() - { - $quoteId = $this->quoteSession->getQuote()->getId(); - - return $this->signifydOrderSessionId->get($quoteId); - } - - /** - * Checks if module is enabled. - * - * @return boolean - * @since 100.2.0 - */ - public function isModuleActive() - { - $storeId = $this->quoteSession->getQuote()->getStoreId(); - - return $this->config->isActive($storeId); - } -} diff --git a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php b/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php deleted file mode 100644 index 2dee31f4048b9..0000000000000 --- a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Controller\Webhooks; - -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use Psr\Log\LoggerInterface; - -/** - * Responsible for handling webhook posts from Signifyd service. - * - * @see https://www.signifyd.com/docs/api/#/reference/webhooks/ - */ -class Handler extends Action implements \Magento\Framework\App\CsrfAwareActionInterface -{ - /** - * Event topic of test webhook request. - * - * @var string - */ - private static $eventTopicTest = 'cases/test'; - - /** - * @var WebhookRequest - */ - private $webhookRequest; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var WebhookMessageReader - */ - private $webhookMessageReader; - - /** - * @var UpdatingServiceFactory - */ - private $caseUpdatingServiceFactory; - - /** - * @var WebhookRequestValidator - */ - private $webhookRequestValidator; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var Config - */ - private $config; - - /** - * @param Context $context - * @param WebhookRequest $webhookRequest - * @param LoggerInterface $logger - * @param WebhookMessageReader $webhookMessageReader - * @param UpdatingServiceFactory $caseUpdatingServiceFactory - * @param WebhookRequestValidator $webhookRequestValidator - * @param CaseRepositoryInterface $caseRepository - * @param Config $config - */ - public function __construct( - Context $context, - WebhookRequest $webhookRequest, - LoggerInterface $logger, - WebhookMessageReader $webhookMessageReader, - UpdatingServiceFactory $caseUpdatingServiceFactory, - WebhookRequestValidator $webhookRequestValidator, - CaseRepositoryInterface $caseRepository, - Config $config - ) { - parent::__construct($context); - $this->webhookRequest = $webhookRequest; - $this->logger = $logger; - $this->webhookMessageReader = $webhookMessageReader; - $this->caseUpdatingServiceFactory = $caseUpdatingServiceFactory; - $this->webhookRequestValidator = $webhookRequestValidator; - $this->caseRepository = $caseRepository; - $this->config = $config; - } - - /** - * Processes webhook request data and updates case entity - * - * @return void - */ - public function execute() - { - if ($this->config->isDebugModeEnabled()) { - $this->logger->debug($this->webhookRequest->getEventTopic() . '|' . $this->webhookRequest->getBody()); - } - - if (!$this->webhookRequestValidator->validate($this->webhookRequest)) { - $this->_redirect('noroute'); - return; - } - - $webhookMessage = $this->webhookMessageReader->read($this->webhookRequest); - if ($webhookMessage->getEventTopic() === self::$eventTopicTest) { - return; - } - - $data = $webhookMessage->getData(); - if (empty($data['caseId'])) { - $this->_redirect('noroute'); - return; - } - - $case = $this->caseRepository->getByCaseId($data['caseId']); - if ($case === null) { - $this->_redirect('noroute'); - return; - } - - $caseUpdatingService = $this->caseUpdatingServiceFactory->create($webhookMessage->getEventTopic()); - try { - $caseUpdatingService->update($case, $data); - } catch (LocalizedException $e) { - $this->getResponse()->setHttpResponseCode(400); - $this->logger->critical($e); - } - } - - /** - * @inheritDoc - */ - public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException - { - return null; - } - - /** - * @inheritDoc - */ - public function validateForCsrf(RequestInterface $request): ?bool - { - return true; - } -} diff --git a/app/code/Magento/Signifyd/LICENSE.txt b/app/code/Magento/Signifyd/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Signifyd/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Signifyd/LICENSE_AFL.txt b/app/code/Magento/Signifyd/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Signifyd/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Signifyd/Model/CaseEntity.php b/app/code/Magento/Signifyd/Model/CaseEntity.php deleted file mode 100644 index c11c72db79f16..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseEntity.php +++ /dev/null @@ -1,249 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Data\Collection\AbstractDb; -use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\Context; -use Magento\Framework\Model\ResourceModel\AbstractResource; -use Magento\Framework\Registry; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Implementation of Signifyd Case interface. - */ -class CaseEntity extends AbstractModel implements CaseInterface -{ - /** - * @var string - */ - protected $_eventPrefix = 'signifyd_case'; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * CaseEntity constructor. - * - * @param Context $context - * @param Registry $registry - * @param SerializerInterface $serializer - * @param array $data - * @param AbstractResource|null $resource - * @param AbstractDb|null $resourceCollection - */ - public function __construct( - Context $context, - Registry $registry, - SerializerInterface $serializer, - array $data = [], - AbstractResource $resource = null, - AbstractDb $resourceCollection = null - ) { - $this->serializer = $serializer; - parent::__construct($context, $registry, $resource, $resourceCollection, $data); - } - - /** - * @inheritdoc - */ - protected function _construct() - { - $this->_init(ResourceModel\CaseEntity::class); - } - - /** - * @inheritdoc - */ - public function getEntityId() - { - return (int) $this->getData('entity_id'); - } - - /** - * @inheritdoc - */ - public function setEntityId($id) - { - $this->setData('entity_id', (int) $id); - return $this; - } - - /** - * @inheritdoc - */ - public function getCaseId() - { - return (int) $this->getData('case_id'); - } - - /** - * @inheritdoc - */ - public function setCaseId($id) - { - $this->setData('case_id', (int) $id); - return $this; - } - - /** - * @inheritdoc - */ - public function isGuaranteeEligible() - { - $value = $this->getData('guarantee_eligible'); - return ($value === null) ? $value : (bool) $value; - } - - /** - * @inheritdoc - */ - public function setGuaranteeEligible($guaranteeEligible) - { - $this->setData('guarantee_eligible', $guaranteeEligible); - return $this; - } - - /** - * @inheritdoc - */ - public function getGuaranteeDisposition() - { - return (string) $this->getData('guarantee_disposition'); - } - - /** - * @inheritdoc - */ - public function setGuaranteeDisposition($disposition) - { - $this->setData('guarantee_disposition', (string) $disposition); - return $this; - } - - /** - * @inheritdoc - */ - public function getStatus() - { - return (string) $this->getData('status'); - } - - /** - * @inheritdoc - */ - public function setStatus($status) - { - $this->setData('status', (string) $status); - return $this; - } - - /** - * @inheritdoc - */ - public function getScore() - { - return (int) $this->getData('score'); - } - - /** - * @inheritdoc - */ - public function setScore($score) - { - $this->setData('score', (int) $score); - return $this; - } - - /** - * @inheritdoc - */ - public function getOrderId() - { - return (int) $this->getData('order_id'); - } - - /** - * @inheritdoc - */ - public function setOrderId($orderId) - { - $this->setData('order_id', (int) $orderId); - return $this; - } - - /** - * @inheritdoc - */ - public function getAssociatedTeam() - { - $teamData = $this->getData('associated_team'); - return empty($teamData) ? [] : $this->serializer->unserialize($teamData); - } - - /** - * @inheritdoc - */ - public function setAssociatedTeam(array $team) - { - $this->setData('associated_team', $this->serializer->serialize($team)); - return $this; - } - - /** - * @inheritdoc - */ - public function getReviewDisposition() - { - return (string) $this->getData('review_disposition'); - } - - /** - * @inheritdoc - */ - public function setReviewDisposition($disposition) - { - $this->setData('review_disposition', (string) $disposition); - return $this; - } - - /** - * @inheritdoc - */ - public function getCreatedAt() - { - return $this->getData('created_at'); - } - - /** - * @inheritdoc - */ - public function setCreatedAt($datetime) - { - $this->setData('created_at', $datetime); - return $this; - } - - /** - * @inheritdoc - */ - public function getUpdatedAt() - { - return $this->getData('updated_at'); - } - - /** - * @inheritdoc - */ - public function setUpdatedAt($datetime) - { - $this->setData('updated_at', $datetime); - return $this; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseManagement.php b/app/code/Magento/Signifyd/Model/CaseManagement.php deleted file mode 100644 index 1913f1e7a17b3..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseManagement.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\DB\Adapter\DuplicateException; - -/** - * - * Default case management implementation - */ -class CaseManagement implements CaseManagementInterface -{ - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var CaseInterfaceFactory - */ - private $caseFactory; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * CaseManagement constructor. - * @param CaseRepositoryInterface $caseRepository - * @param CaseInterfaceFactory $caseFactory - * @param FilterBuilder $filterBuilder - * @param SearchCriteriaBuilder $searchCriteriaBuilder - */ - public function __construct( - CaseRepositoryInterface $caseRepository, - CaseInterfaceFactory $caseFactory, - FilterBuilder $filterBuilder, - SearchCriteriaBuilder $searchCriteriaBuilder - ) { - $this->caseRepository = $caseRepository; - $this->caseFactory = $caseFactory; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterBuilder = $filterBuilder; - } - - /** - * @inheritdoc - */ - public function create($orderId) - { - /** @var \Magento\Signifyd\Api\Data\CaseInterface $case */ - $case = $this->caseFactory->create(); - $case->setOrderId($orderId) - ->setStatus(CaseInterface::STATUS_PENDING) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_PENDING); - try { - return $this->caseRepository->save($case); - } catch (DuplicateException $e) { - throw new AlreadyExistsException(__('This order already has associated case entity'), $e); - } - } - - /** - * @inheritdoc - */ - public function getByOrderId($orderId) - { - $filters = [ - $this->filterBuilder->setField('order_id') - ->setValue($orderId) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $items = $this->caseRepository->getList($searchCriteria)->getItems(); - return !empty($items) ? array_pop($items) : null; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseRepository.php b/app/code/Magento/Signifyd/Model/CaseRepository.php deleted file mode 100644 index ea3ea3e67aafd..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseRepository.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterface; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterfaceFactory; -use Magento\Signifyd\Model\ResourceModel\CaseEntity as CaseResourceModel; -use Magento\Signifyd\Model\ResourceModel\CaseEntity\Collection; -use Magento\Signifyd\Model\ResourceModel\CaseEntity\CollectionFactory; - -/** - * Repository for Case interface - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CaseRepository implements CaseRepositoryInterface -{ - /** - * @var CollectionProcessorInterface - */ - private $collectionProcessor; - - /** - * @var CollectionFactory - */ - private $collectionFactory; - - /** - * @var CaseSearchResultsInterfaceFactory - */ - private $searchResultsFactory; - - /** - * @var CaseInterfaceFactory - */ - private $caseFactory; - - /** - * @var CaseResourceModel - */ - private $resourceModel; - - /** - * CaseRepository constructor. - * @param CollectionProcessorInterface $collectionProcessor - * @param CollectionFactory $collectionFactory - * @param CaseSearchResultsInterfaceFactory $searchResultsFactory - * @param CaseInterfaceFactory $caseFactory - * @param CaseResourceModel $resourceModel - */ - public function __construct( - CollectionProcessorInterface $collectionProcessor, - CollectionFactory $collectionFactory, - CaseSearchResultsInterfaceFactory $searchResultsFactory, - CaseInterfaceFactory $caseFactory, - CaseResourceModel $resourceModel - ) { - $this->collectionProcessor = $collectionProcessor; - $this->collectionFactory = $collectionFactory; - $this->searchResultsFactory = $searchResultsFactory; - $this->caseFactory = $caseFactory; - $this->resourceModel = $resourceModel; - } - - /** - * @inheritdoc - */ - public function save(CaseInterface $case) - { - /** @var CaseEntity $case */ - $this->resourceModel->save($case); - - return $case; - } - - /** - * @inheritdoc - */ - public function getById($id) - { - /** @var CaseEntity $case */ - $case = $this->caseFactory->create(); - $this->resourceModel->load($case, $id); - - return $case; - } - - /** - * @inheritdoc - */ - public function getByCaseId($caseId) - { - /** @var CaseEntity $case */ - $case = $this->caseFactory->create(); - $this->resourceModel->load($case, $caseId, 'case_id'); - - return $case->getEntityId() ? $case : null; - } - - /** - * @inheritdoc - */ - public function delete(CaseInterface $case) - { - $this->resourceModel->delete($case); - - return true; - } - - /** - * @inheritdoc - */ - public function getList(SearchCriteriaInterface $searchCriteria) - { - /** @var Collection $collection */ - $collection = $this->collectionFactory->create(); - $this->collectionProcessor->process($searchCriteria, $collection); - - /** @var CaseSearchResultsInterface $searchResults */ - $searchResults = $this->searchResultsFactory->create(); - $searchResults->setSearchCriteria($searchCriteria); - $searchResults->setItems($collection->getItems()); - - return $searchResults; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseSearchResults.php b/app/code/Magento/Signifyd/Model/CaseSearchResults.php deleted file mode 100644 index ff1ab8839f6cd..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseSearchResults.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchResults; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterface; - -/** - * Service Data Object with Case entities search results. - */ -class CaseSearchResults extends SearchResults implements CaseSearchResultsInterface -{ -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php b/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php deleted file mode 100644 index 8413838bd7d5f..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Case Creation Service - * - * Creates new Case entity and register it at Signifyd - */ -class CreationService implements CaseCreationServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var Gateway; - */ - private $signifydGateway; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var OrderGridUpdater - */ - private $orderGridUpdater; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * CreationService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param Gateway $signifydGateway - * @param LoggerInterface $logger - * @param CaseRepositoryInterface $caseRepository - * @param OrderGridUpdater $orderGridUpdater - * @param OrderStateService $orderStateService - */ - public function __construct( - CaseManagementInterface $caseManagement, - Gateway $signifydGateway, - LoggerInterface $logger, - CaseRepositoryInterface $caseRepository, - OrderGridUpdater $orderGridUpdater, - OrderStateService $orderStateService - ) { - $this->caseManagement = $caseManagement; - $this->signifydGateway = $signifydGateway; - $this->logger = $logger; - $this->caseRepository = $caseRepository; - $this->orderGridUpdater = $orderGridUpdater; - $this->orderStateService = $orderStateService; - } - - /** - * {@inheritdoc} - */ - public function createForOrder($orderId) - { - $case = $this->caseManagement->create($orderId); - $this->orderGridUpdater->update($orderId); - - try { - $caseId = $this->signifydGateway->createCase($orderId); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return true; - } - - $case->setCaseId($caseId); - $this->caseRepository->save($case); - $this->orderStateService->updateByCase($case); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php b/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php deleted file mode 100644 index 295d7f13fb0ac..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Stub implementation for case updating service interface and might be used - * for test Signifyd webhooks - */ -class StubUpdatingService implements UpdatingServiceInterface -{ - /** - * @inheritdoc - */ - public function update(CaseInterface $case, array $data) - { - // just stub method and doesn't contain any logic - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php deleted file mode 100644 index 168ab67f8cf50..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\Api\SimpleDataObjectConverter; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; - -/** - * Performs Signifyd case entity updating operations. - */ -class UpdatingService implements UpdatingServiceInterface -{ - /** - * @var GeneratorInterface - */ - private $messageGenerator; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var CommentsHistoryUpdater - */ - private $commentsHistoryUpdater; - - /** - * @var \Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater - */ - private $orderGridUpdater; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * UpdatingService constructor. - * - * @param GeneratorInterface $messageGenerator - * @param CaseRepositoryInterface $caseRepository - * @param CommentsHistoryUpdater $commentsHistoryUpdater - * @param \Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater $orderGridUpdater - * @param OrderStateService $orderStateService - */ - public function __construct( - GeneratorInterface $messageGenerator, - CaseRepositoryInterface $caseRepository, - CommentsHistoryUpdater $commentsHistoryUpdater, - OrderGridUpdater $orderGridUpdater, - OrderStateService $orderStateService - ) { - $this->messageGenerator = $messageGenerator; - $this->caseRepository = $caseRepository; - $this->commentsHistoryUpdater = $commentsHistoryUpdater; - $this->orderGridUpdater = $orderGridUpdater; - $this->orderStateService = $orderStateService; - } - - /** - * Updates Signifyd Case entity by received data. - * - * @param CaseInterface $case - * @param array $data - * @return void - * @throws LocalizedException - */ - public function update(CaseInterface $case, array $data) - { - if (empty($case->getEntityId()) || empty($case->getCaseId())) { - throw new LocalizedException(__('The case entity should not be empty.')); - } - - try { - $previousDisposition = $case->getGuaranteeDisposition(); - $this->setCaseData($case, $data); - $orderHistoryComment = $this->messageGenerator->generate($data); - $case = $this->caseRepository->save($case); - $this->orderGridUpdater->update($case->getOrderId()); - $this->commentsHistoryUpdater->addComment($case, $orderHistoryComment); - if ($case->getGuaranteeDisposition() !== $previousDisposition) { - $this->orderStateService->updateByCase($case); - } - } catch (\Exception $e) { - throw new LocalizedException(__('Cannot update Case entity.'), $e); - } - } - - /** - * Sets data to case entity. - * - * @param CaseInterface $case - * @param array $data - * @return void - */ - private function setCaseData(CaseInterface $case, array $data) - { - // list of keys which should not be replaced, like order id - $notResolvedKeys = [ - 'orderId' - ]; - foreach ($data as $key => $value) { - $methodName = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($key); - if (!in_array($key, $notResolvedKeys) && method_exists($case, $methodName)) { - call_user_func([$case, $methodName], $value); - } - } - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php deleted file mode 100644 index 5415044b5edc4..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\Signifyd\Model\Config; - -/** - * Creates instance of case updating service configured with specific message generator. - * The message generator initialization depends on specified type (like, case creation, re-scoring, review and - * guarantee completion). - */ -class UpdatingServiceFactory -{ - /** - * Type of testing Signifyd case - * @var string - */ - private static $caseTest = 'cases/test'; - - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var GeneratorFactory - */ - private $generatorFactory; - - /** - * @var Config - */ - private $config; - - /** - * UpdatingServiceFactory constructor. - * - * @param ObjectManagerInterface $objectManager - * @param GeneratorFactory $generatorFactory - * @param Config $config - */ - public function __construct( - ObjectManagerInterface $objectManager, - GeneratorFactory $generatorFactory, - Config $config - ) { - $this->objectManager = $objectManager; - $this->generatorFactory = $generatorFactory; - $this->config = $config; - } - - /** - * Creates instance of service updating case. - * As param retrieves type of message generator. - * - * @param string $type - * @return UpdatingServiceInterface - * @throws \InvalidArgumentException - */ - public function create($type) - { - if (!$this->config->isActive() || $type === self::$caseTest) { - return $this->objectManager->create(StubUpdatingService::class); - } - - $messageGenerator = $this->generatorFactory->create($type); - $service = $this->objectManager->create(UpdatingService::class, [ - 'messageGenerator' => $messageGenerator - ]); - - return $service; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php deleted file mode 100644 index daa7b40bfd674..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Common abstraction to perform updating operations with Signifyd case entity. - */ -interface UpdatingServiceInterface -{ - /** - * Updates Signifyd Case entity by received data. - * - * @param CaseInterface $case - * @param array $data - * @return void - */ - public function update(CaseInterface $case, array $data); -} diff --git a/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php b/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php deleted file mode 100644 index f473d157efa4a..0000000000000 --- a/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Phrase; -use Magento\Sales\Api\OrderStatusHistoryRepositoryInterface; -use Magento\Sales\Model\Order\Status\HistoryFactory; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Updates case order comments history. - */ -class CommentsHistoryUpdater -{ - /** - * @var HistoryFactory - */ - private $historyFactory; - - /** - * @var OrderStatusHistoryRepositoryInterface - */ - private $historyRepository; - - /** - * CommentsHistoryUpdater constructor. - * - * @param HistoryFactory $historyFactory - * @param OrderStatusHistoryRepositoryInterface $historyRepository - */ - public function __construct( - HistoryFactory $historyFactory, - OrderStatusHistoryRepositoryInterface $historyRepository - ) { - $this->historyFactory = $historyFactory; - $this->historyRepository = $historyRepository; - } - - /** - * Adds comment to case related order. - * Throws an exception if cannot save history comment. - * - * @param CaseInterface $case - * @param Phrase $message - * @param string $status - * @return void - */ - public function addComment(CaseInterface $case, Phrase $message, $status = '') - { - if (!$message->getText()) { - return; - } - - /** @var \Magento\Sales\Api\Data\OrderStatusHistoryInterface $history */ - $history = $this->historyFactory->create(); - $history->setParentId($case->getOrderId()) - ->setComment($message) - ->setEntityName('order') - ->setStatus($status); - $this->historyRepository->save($history); - } -} diff --git a/app/code/Magento/Signifyd/Model/Config.php b/app/code/Magento/Signifyd/Model/Config.php deleted file mode 100644 index 15d3608bd38c4..0000000000000 --- a/app/code/Magento/Signifyd/Model/Config.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Store\Model\ScopeInterface; - -/** - * Signifyd integration configuration. - * - * Class is a proxy service for retrieving configuration settings. - */ -class Config -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * Config constructor. - * - * @param ScopeConfigInterface $scopeConfig - */ - public function __construct(ScopeConfigInterface $scopeConfig) - { - $this->scopeConfig = $scopeConfig; - } - - /** - * If this config option set to false no Signifyd integration should be available - * (only possibility to configure Signifyd setting in admin) - * - * @param int|null $storeId - * @return bool - */ - public function isActive($storeId = null): bool - { - $enabled = $this->scopeConfig->isSetFlag( - 'fraud_protection/signifyd/active', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $enabled; - } - - /** - * Signifyd API Key used for authentication. - * - * @see https://www.signifyd.com/docs/api/#/introduction/authentication - * @see https://app.signifyd.com/settings - * - * @param int|null $storeId - * @return string - */ - public function getApiKey($storeId = null): string - { - $apiKey = $this->scopeConfig->getValue( - 'fraud_protection/signifyd/api_key', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $apiKey; - } - - /** - * Base URL to Signifyd REST API. - * Usually equals to https://api.signifyd.com/v2 and should not be changed - * - * @param int|null $storeId - * @return string - */ - public function getApiUrl($storeId = null): string - { - $apiUrl = $this->scopeConfig->getValue( - 'fraud_protection/signifyd/api_url', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $apiUrl; - } - - /** - * If is "true" extra information about interaction with Signifyd API are written to debug.log file - * - * @param int|null $storeId - * @return bool - */ - public function isDebugModeEnabled($storeId = null): bool - { - $debugModeEnabled = $this->scopeConfig->isSetFlag( - 'fraud_protection/signifyd/debug', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $debugModeEnabled; - } -} diff --git a/app/code/Magento/Signifyd/Model/CustomerOrders.php b/app/code/Magento/Signifyd/Model/CustomerOrders.php deleted file mode 100644 index c326cf06424c0..0000000000000 --- a/app/code/Magento/Signifyd/Model/CustomerOrders.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Exception; -use Magento\Directory\Model\Currency; -use Magento\Directory\Model\CurrencyFactory; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Psr\Log\LoggerInterface; - -/** - * Provides information about customer orders. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CustomerOrders -{ - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var CurrencyFactory - */ - private $currencyFactory; - - /** - * @var array - */ - private $currencies = []; - - /** - * @var string - */ - private static $usdCurrencyCode = 'USD'; - - /** - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param FilterBuilder $filterBuilder - * @param OrderRepositoryInterface $orderRepository - * @param CurrencyFactory $currencyFactory - * @param LoggerInterface $logger - */ - public function __construct( - SearchCriteriaBuilder $searchCriteriaBuilder, - FilterBuilder $filterBuilder, - OrderRepositoryInterface $orderRepository, - CurrencyFactory $currencyFactory, - LoggerInterface $logger - ) { - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterBuilder = $filterBuilder; - $this->orderRepository = $orderRepository; - $this->currencyFactory = $currencyFactory; - $this->logger = $logger; - } - - /** - * Returns aggregated customer orders count and total amount in USD. - * - * Returned array contains next keys: - * aggregateOrderCount - total count of orders placed by this account since it was created, including the current - * aggregateOrderDollars - total amount spent by this account since it was created, including the current order - * - * @param int $customerId - * @return array - */ - public function getAggregatedOrdersInfo($customerId) - { - $result = [ - 'aggregateOrderCount' => null, - 'aggregateOrderDollars' => null - ]; - - $customerOrders = $this->getCustomerOrders($customerId); - if (!empty($customerOrders)) { - try { - $orderTotalDollars = 0.0; - foreach ($customerOrders as $order) { - $orderTotalDollars += $this->getUsdOrderTotal( - $order->getBaseGrandTotal(), - $order->getBaseCurrencyCode() - ); - } - $result = [ - 'aggregateOrderCount' => count($customerOrders), - 'aggregateOrderDollars' => $orderTotalDollars - ]; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - } - } - - return $result; - } - - /** - * Returns customer orders. - * - * @param int $customerId - * @return OrderInterface[] - */ - private function getCustomerOrders($customerId) - { - $filters = [ - $this->filterBuilder->setField(OrderInterface::CUSTOMER_ID)->setValue($customerId)->create() - ]; - $this->searchCriteriaBuilder->addFilters($filters); - $searchCriteria = $this->searchCriteriaBuilder->create(); - $searchResults = $this->orderRepository->getList($searchCriteria); - - return $searchResults->getItems(); - } - - /** - * Returns amount in USD. - * - * @param float $amount - * @param string $currency - * @return float - */ - private function getUsdOrderTotal($amount, $currency) - { - if ($currency === self::$usdCurrencyCode) { - return $amount; - } - - $operationCurrency = $this->getCurrencyByCode($currency); - - return $operationCurrency->convert($amount, self::$usdCurrencyCode); - } - - /** - * Returns currency by currency code. - * - * @param string|null $currencyCode - * @return Currency - */ - private function getCurrencyByCode($currencyCode) - { - if (isset($this->currencies[$currencyCode])) { - return $this->currencies[$currencyCode]; - } - - /** @var Currency $currency */ - $currency = $this->currencyFactory->create(); - $this->currencies[$currencyCode] = $currency->load($currencyCode); - - return $this->currencies[$currencyCode]; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php b/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php deleted file mode 100644 index 65bede6f0cea5..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Checks if is possible to cancel Guarantee for order. - */ -class CancelGuaranteeAbility -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @param CaseManagement $caseManagement - * @param OrderRepositoryInterface $orderRepository - */ - public function __construct( - CaseManagement $caseManagement, - OrderRepositoryInterface $orderRepository - ) { - $this->caseManagement = $caseManagement; - $this->orderRepository = $orderRepository; - } - - /** - * Checks if it is possible to create Guarantee for order and case. - * - * @param int $orderId - * @return bool - */ - public function isAvailable($orderId) - { - $case = $this->caseManagement->getByOrderId($orderId); - if ($case === null) { - return false; - } - - if (in_array($case->getGuaranteeDisposition(), [null, $case::GUARANTEE_CANCELED])) { - return false; - } - - $order = $this->getOrder($orderId); - if (null === $order) { - return false; - } - - return true; - } - - /** - * Returns order by id - * - * @param int $orderId - * @return OrderInterface|null - */ - private function getOrder($orderId) - { - try { - $order = $this->orderRepository->get($orderId); - } catch (InputException $e) { - return null; - } catch (NoSuchEntityException $e) { - return null; - } - - return $order; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php b/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php deleted file mode 100644 index b30efac8c2190..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Sends request to Signifyd to cancel guarantee and updates case entity. - */ -class CancelingService implements GuaranteeCancelingServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory - */ - private $serviceFactory; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var CancelGuaranteeAbility - */ - private $cancelGuaranteeAbility; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * CancelingService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param UpdatingServiceFactory $serviceFactory - * @param Gateway $gateway - * @param CancelGuaranteeAbility $cancelGuaranteeAbility - * @param LoggerInterface $logger - */ - public function __construct( - CaseManagementInterface $caseManagement, - UpdatingServiceFactory $serviceFactory, - Gateway $gateway, - CancelGuaranteeAbility $cancelGuaranteeAbility, - LoggerInterface $logger - ) { - $this->caseManagement = $caseManagement; - $this->serviceFactory = $serviceFactory; - $this->gateway = $gateway; - $this->cancelGuaranteeAbility = $cancelGuaranteeAbility; - $this->logger = $logger; - } - - /** - * @inheritdoc - */ - public function cancelForOrder($orderId) - { - if (!$this->cancelGuaranteeAbility->isAvailable($orderId)) { - return false; - } - - $caseEntity = $this->caseManagement->getByOrderId($orderId); - - try { - $disposition = $this->gateway->cancelGuarantee($caseEntity->getCaseId()); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return false; - } - - $updatingService = $this->serviceFactory->create('guarantees/cancel'); - $data = [ - 'guaranteeDisposition' => $disposition - ]; - $updatingService->update($caseEntity, $data); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php b/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php deleted file mode 100644 index 15addba3ec4fd..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Checks if is possible to create Guarantee for order. - */ -class CreateGuaranteeAbility -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * Eligible count of days from the order creation date to submit a case for Guarantee. - * - * @var int - */ - private static $guarantyEligibleDays = 7; - - /** - * @param CaseManagement $caseManagement - * @param OrderRepositoryInterface $orderRepository - * @param DateTimeFactory $dateTimeFactory - */ - public function __construct( - CaseManagement $caseManagement, - OrderRepositoryInterface $orderRepository, - DateTimeFactory $dateTimeFactory - ) { - $this->caseManagement = $caseManagement; - $this->orderRepository = $orderRepository; - $this->dateTimeFactory = $dateTimeFactory; - } - - /** - * Checks if it is possible to create Guarantee for order and case. - * - * @param int $orderId - * @return bool - */ - public function isAvailable($orderId) - { - $case = $this->caseManagement->getByOrderId($orderId); - if (null === $case) { - return false; - } - - if ($case->isGuaranteeEligible() === false) { - return false; - } - - $order = $this->getOrder($orderId); - if (null === $order) { - return false; - } - - if (in_array($order->getState(), [Order::STATE_CANCELED, Order::STATE_CLOSED])) { - return false; - } - - if ($this->isOrderOlderThen(static::$guarantyEligibleDays, $order)) { - return false; - } - - return true; - } - - /** - * Checks if Guarantee submit is applicable for order. - * - * @param OrderInterface $order - * @param int $days number of days from the order creation date to submit a case for Guarantee. - * @return bool - */ - private function isOrderOlderThen($days, OrderInterface $order) - { - $orderCreateDate = $this->dateTimeFactory->create($order->getCreatedAt(), new \DateTimeZone('UTC')); - $currentDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - - return $orderCreateDate->diff($currentDate)->days >= $days; - } - - /** - * Returns order by id - * - * @param int $orderId - * @return OrderInterface|null - */ - private function getOrder($orderId) - { - try { - $order = $this->orderRepository->get($orderId); - } catch (InputException $e) { - return null; - } catch (NoSuchEntityException $e) { - return null; - } - - return $order; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php b/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php deleted file mode 100644 index 4080aee453f18..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\Exception\NotFoundException; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\GuaranteeCreationServiceInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Register guarantee at Signifyd and updates case entity - */ -class CreationService implements GuaranteeCreationServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory - */ - private $caseUpdatingServiceFactory; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var CreateGuaranteeAbility - */ - private $createGuaranteeAbility; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * CreationService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param UpdatingServiceFactory $caseUpdatingServiceFactory - * @param Gateway $gateway - * @param CreateGuaranteeAbility $createGuaranteeAbility - * @param LoggerInterface $logger - */ - public function __construct( - CaseManagementInterface $caseManagement, - UpdatingServiceFactory $caseUpdatingServiceFactory, - Gateway $gateway, - CreateGuaranteeAbility $createGuaranteeAbility, - LoggerInterface $logger - ) { - $this->caseManagement = $caseManagement; - $this->caseUpdatingServiceFactory = $caseUpdatingServiceFactory; - $this->gateway = $gateway; - $this->createGuaranteeAbility = $createGuaranteeAbility; - $this->logger = $logger; - } - - /** - * @inheritdoc - */ - public function createForOrder($orderId) - { - if (!$this->createGuaranteeAbility->isAvailable($orderId)) { - return false; - } - - $caseEntity = $this->caseManagement->getByOrderId($orderId); - - try { - $disposition = $this->gateway->submitCaseForGuarantee($caseEntity->getCaseId()); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return false; - } - - $updatingService = $this->caseUpdatingServiceFactory->create('guarantees/creation'); - $data = [ - 'caseId' => $caseEntity->getCaseId(), - 'guaranteeDisposition' => $disposition - ]; - $updatingService->update($caseEntity, $data); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php b/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php deleted file mode 100644 index d0e89854e3909..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Signifyd\Api\CaseRepositoryInterface; - -/** - * Generates message based on previous and current Case scores. - */ -class CaseRescore implements GeneratorInterface -{ - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * CaseRescore constructor. - * - * @param CaseRepositoryInterface $caseRepository - */ - public function __construct(CaseRepositoryInterface $caseRepository) - { - $this->caseRepository = $caseRepository; - } - - /** - * @inheritdoc - */ - public function generate(array $data) - { - if (empty($data['caseId'])) { - throw new GeneratorException(__('The "%1" should not be empty.', 'caseId')); - } - - $caseEntity = $this->caseRepository->getByCaseId($data['caseId']); - - if ($caseEntity === null) { - throw new GeneratorException(__('Case entity not found.')); - } - - return __( - 'Case Update: New score for the order is %1. Previous score was %2.', - !empty($data['score']) ? $data['score'] : 0, - $caseEntity->getScore() - ); - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php deleted file mode 100644 index 103cb9fc1e2d3..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Framework\Exception\LocalizedException; - -/** - * Common exception for Signifyd message generators. - * - * @api - * @since 100.2.0 - */ -class GeneratorException extends LocalizedException -{ - -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php deleted file mode 100644 index 66e02a3803950..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\MessageGeneratorInterface; - -/** - * Creates instance of message generator based on received type of message. - */ -class GeneratorFactory -{ - /** - * Type of message for Signifyd case creation. - * @var string - */ - private static $caseCreation = 'cases/creation'; - - /** - * Type of message for Signifyd case re-scoring. - * @var string - */ - private static $caseRescore = 'cases/rescore'; - - /** - * Type of message for Signifyd case reviewing - * @var string - */ - private static $caseReview = 'cases/review'; - - /** - * Type of message of Signifyd guarantee completion - * @var string - */ - private static $guaranteeCompletion = 'guarantees/completion'; - - /** - * Type of message of Signifyd guarantee creation - * @var string - */ - private static $guaranteeCreation = 'guarantees/creation'; - - /** - * Type of message of Signifyd guarantee canceling - * @var string - */ - private static $guaranteeCancel = 'guarantees/cancel'; - - /** - * UpdatingServiceFactory constructor. - * - * @param ObjectManagerInterface $objectManager - */ - public function __construct(ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * Creates instance of message generator. - * Throws exception if type of message generator does not have implementations. - * - * @param string $type - * @return GeneratorInterface - * @throws \InvalidArgumentException - */ - public function create($type) - { - $className = PatternGenerator::class; - switch ($type) { - case self::$caseCreation: - $classConfig = [ - 'template' => 'Signifyd Case %1 has been created for order.', - 'requiredParams' => ['caseId'] - ]; - break; - case self::$caseRescore: - $classConfig = []; - $className = CaseRescore::class; - break; - case self::$caseReview: - $classConfig = [ - 'template' => 'Case Update: Case Review was completed. Review Deposition is %1.', - 'requiredParams' => ['reviewDisposition'] - ]; - break; - case self::$guaranteeCompletion: - $classConfig = [ - 'template' => 'Case Update: Guarantee Disposition is %1.', - 'requiredParams' => ['guaranteeDisposition'] - ]; - break; - case self::$guaranteeCreation: - $classConfig = [ - 'template' => 'Case Update: Case is submitted for guarantee.' - ]; - break; - case self::$guaranteeCancel: - $classConfig = [ - 'template' => 'Case Update: Case guarantee has been cancelled.' - ]; - break; - default: - throw new \InvalidArgumentException('Specified message type does not supported.'); - } - - return $this->objectManager->create($className, $classConfig); - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php deleted file mode 100644 index 385cbe35f05ac..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -/** - * Represents common abstraction for Signifyd Case/Guarantee messages. - * Each interface implementation might use Case/Guarantee data to generate specific message. - */ -interface GeneratorInterface -{ - /** - * Creates new localized message based on Signifyd Case/Guarantee data. - * @param array $data - * @return \Magento\Framework\Phrase - * @throws GeneratorException - */ - public function generate(array $data); -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php b/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php deleted file mode 100644 index 8501934bebbc5..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -/** - * Common implementation of message generator. - * Takes a message template (placeholders for localization also can be used) and list - * of required params, which should persist in input data. - * - * If template contains placeholders, when required params should be specified in the same order as - * placeholders, for example: - * Message is 'Case Update: New score for the order is %1. Previous score was %2.', then the required params order - * should be ['new_score', 'prev_score']. - */ -class PatternGenerator implements GeneratorInterface -{ - /** - * @var string - */ - private $template; - - /** - * @var array - */ - private $requiredParams; - - /** - * PatternGenerator constructor. - * - * @param string $template - * @param array $requiredParams - */ - public function __construct($template, array $requiredParams = []) - { - $this->template = $template; - $this->requiredParams = $requiredParams; - } - - /** - * @inheritdoc - */ - public function generate(array $data) - { - $placeholders = []; - foreach ($this->requiredParams as $param) { - if (empty($data[$param])) { - throw new GeneratorException(__('The "%1" should not be empty.', $param)); - } - $placeholders[] = $data[$param]; - } - return __($this->template, ...$placeholders); - } -} diff --git a/app/code/Magento/Signifyd/Model/OrderStateService.php b/app/code/Magento/Signifyd/Model/OrderStateService.php deleted file mode 100644 index 2b3f0e155981e..0000000000000 --- a/app/code/Magento/Signifyd/Model/OrderStateService.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\OrderFactory; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Updates order state. - */ -class OrderStateService -{ - /** - * @var OrderFactory - */ - private $orderFactory; - - /** - * @var OrderManagementInterface - */ - private $orderManagement; - - /** - * @var CommentsHistoryUpdater - */ - private $commentsHistoryUpdater; - - /** - * @param OrderFactory $orderFactory - * @param OrderManagementInterface $orderManagement - * @param CommentsHistoryUpdater $commentsHistoryUpdater - */ - public function __construct( - OrderFactory $orderFactory, - OrderManagementInterface $orderManagement, - CommentsHistoryUpdater $commentsHistoryUpdater - ) { - $this->orderFactory = $orderFactory; - $this->orderManagement = $orderManagement; - $this->commentsHistoryUpdater = $commentsHistoryUpdater; - } - - /** - * Updates order state depending on case guarantee disposition status. - * - * @param CaseInterface $case - * @return void - */ - public function updateByCase(CaseInterface $case) - { - $orderId = $case->getOrderId(); - - switch ($case->getGuaranteeDisposition()) { - case CaseInterface::GUARANTEE_APPROVED: - $this->unHold($orderId); - break; - case CaseInterface::GUARANTEE_DECLINED: - $this->hold($orderId); - break; - case CaseInterface::GUARANTEE_PENDING: - if ($this->hold($orderId)) { - $this->commentsHistoryUpdater->addComment( - $case, - __('Awaiting the Signifyd guarantee disposition.'), - Order::STATE_HOLDED - ); - } - break; - } - } - - /** - * Tries to unhold the order. - * - * @param int $orderId - * @return bool - */ - private function unHold($orderId) - { - $order = $this->getOrder($orderId); - if ($order->canUnhold()) { - return $this->orderManagement->unHold($orderId); - } - - return false; - } - - /** - * Tries to hold the order. - * - * @param int $orderId - * @return bool - */ - private function hold($orderId) - { - $order = $this->getOrder($orderId); - if ($order->canHold()) { - return $this->orderManagement->hold($orderId); - } - - return false; - } - - /** - * Returns the order. - * - * @param int $orderId - * @return Order - */ - private function getOrder($orderId) - { - return $this->orderFactory->create()->load($orderId); - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php b/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php deleted file mode 100644 index cdf9041510b45..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\PaymentMethodMapper; - -use Magento\Framework\Config\Data; - -/** - * Load and cache configuration data. - */ -class PaymentMethodMapper -{ - /** - * @var Data - */ - private $paymentMethodMapping; - - /** - * PaymentMapper constructor. - * - * @param Data $paymentMapping - */ - public function __construct(Data $paymentMapping) - { - $this->paymentMethodMapping = $paymentMapping; - } - - /** - * Gets the Sygnifyd payment method by the order's payment method. - * - * @param string $paymentMethod - * @return string - */ - public function getSignifydPaymentMethodCode($paymentMethod) - { - return $this->paymentMethodMapping->get($paymentMethod, ''); - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php b/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php deleted file mode 100644 index 5c3f23bb92729..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\PaymentMethodMapper; - -use Magento\Framework\Config\Dom\ValidationSchemaException; - -/** - * Converts XML config file to payment methods mapping. - */ -class XmlToArrayConfigConverter implements \Magento\Framework\Config\ConverterInterface -{ - /** - * Node type wrapper for magento and signifyd payment codes - * - * @var string - */ - private static $paymentMethodNodeType = 'payment_method'; - - /** - * Node type for payment methods code - * - * @var string - */ - private static $magentoCodeNodeType = 'magento_code'; - - /** - * Node type for Sygnifyd payment methods code - * - * @var string - */ - private static $signifydCodeNodeType = 'signifyd_code'; - - /** - * @inheritdoc - */ - public function convert($source) - { - $paymentMethods = $source->getElementsByTagName(self::$paymentMethodNodeType); - $paymentsList = []; - foreach ($paymentMethods as $paymentMethod) { - $paymentsList += $this->getPaymentMethodMapping($paymentMethod); - } - - return $paymentsList; - } - - /** - * Adds a payment method as key and a Sygnifyd payment method as value - * in the payment list array - * - * @param \DOMElement $payment - * @return array - * @throws ValidationSchemaException - */ - private function getPaymentMethodMapping(\DOMElement $payment) - { - $paymentMethodCode = $this->readSubnodeValue($payment, self::$magentoCodeNodeType); - $signifyPaymentMethodCode = $this->readSubnodeValue($payment, self::$signifydCodeNodeType); - - return [$paymentMethodCode => $signifyPaymentMethodCode]; - } - - /** - * Reads node value by node type - * - * @param \DOMElement $element - * @param string $subNodeType - * @return mixed - * @throws ValidationSchemaException - */ - private function readSubnodeValue(\DOMElement $element, $subNodeType) - { - $domList = $element->getElementsByTagName($subNodeType); - if (empty($domList[0])) { - throw new ValidationSchemaException(__('Only single entrance of "%1" node is required.', $subNodeType)); - } - - $subNodeValue = trim($domList[0]->nodeValue); - if (!$subNodeValue) { - throw new ValidationSchemaException(__('Not empty value for "%1" node is required.', $subNodeType)); - } - - return $subNodeValue; - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php b/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php deleted file mode 100644 index 5be5ccbc5e55a..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Payment\Gateway\ConfigInterface; -use Magento\Framework\Exception\ConfigurationMismatchException; - -/** - * Creates verification service for provided payment method, or PaymentVerificationInterface::class - * if payment method does not support AVS, CVV verifications. - */ -class PaymentVerificationFactory -{ - /** - * @var ConfigInterface - */ - private $config; - - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var PaymentVerificationInterface - */ - private $avsDefaultAdapter; - - /** - * @var PaymentVerificationInterface - */ - private $cvvDefaultAdapter; - - /** - * @param ObjectManagerInterface $objectManager - * @param ConfigInterface|Config $config - * @param PaymentVerificationInterface $avsDefaultAdapter - * @param PaymentVerificationInterface $cvvDefaultAdapter - */ - public function __construct( - ObjectManagerInterface $objectManager, - ConfigInterface $config, - PaymentVerificationInterface $avsDefaultAdapter, - PaymentVerificationInterface $cvvDefaultAdapter - ) { - $this->config = $config; - $this->objectManager = $objectManager; - $this->avsDefaultAdapter = $avsDefaultAdapter; - $this->cvvDefaultAdapter = $cvvDefaultAdapter; - } - - /** - * Creates instance of CVV code verification. - * Exception will be thrown if CVV mapper does not implement PaymentVerificationInterface. - * - * @param string $paymentCode - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException - */ - public function createPaymentCvv($paymentCode) - { - return $this->create($this->cvvDefaultAdapter, $paymentCode, 'cvv_ems_adapter'); - } - - /** - * Creates instance of AVS code verification. - * Exception will be thrown if AVS mapper does not implement PaymentVerificationInterface. - * - * @param string $paymentCode - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException - */ - public function createPaymentAvs($paymentCode) - { - return $this->create($this->avsDefaultAdapter, $paymentCode, 'avs_ems_adapter'); - } - - /** - * Creates instance of PaymentVerificationInterface. - * Default implementation will be returned if payment method does not implement PaymentVerificationInterface. - * - * @param PaymentVerificationInterface $defaultAdapter - * @param string $paymentCode - * @param string $configKey - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException If payment verification instance - * does not implement PaymentVerificationInterface. - */ - private function create(PaymentVerificationInterface $defaultAdapter, $paymentCode, $configKey) - { - $this->config->setMethodCode($paymentCode); - $verificationClass = $this->config->getValue($configKey); - if ($verificationClass === null) { - return $defaultAdapter; - } - $mapper = $this->objectManager->create($verificationClass); - if (!$mapper instanceof PaymentVerificationInterface) { - throw new ConfigurationMismatchException( - __('%1 must implement %2', $verificationClass, PaymentVerificationInterface::class) - ); - } - return $mapper; - } -} diff --git a/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php b/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php deleted file mode 100644 index 618d74b2a52e9..0000000000000 --- a/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Sales\Api\Data\OrderPaymentInterface; - -/** - * Default implementation of payment verification interface. - * The default code value can be configured via DI. - */ -class PredefinedVerificationCode implements PaymentVerificationInterface -{ - /** - * @var string - */ - private $code; - - /** - * @param string $code - */ - public function __construct($code = '') - { - $this->code = $code; - } - - /** - * @inheritdoc - */ - public function getCode(OrderPaymentInterface $orderPayment) - { - return $this->code; - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php b/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php deleted file mode 100644 index 9be02719545c7..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession\Adminhtml; - -use Magento\Backend\Model\Session\Quote as BackendQuoteSession; -use Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface; - -/** - * Implementation of QuoteSessionInterface for Magento backend checkout. - */ -class BackendSession implements QuoteSessionInterface -{ - /** - * @var BackendQuoteSession - */ - private $backendQuoteSession; - - /** - * BackendSession constructor. - * - * Class uses backend session for retrieving quote. - * - * @param BackendQuoteSession $backendQuoteSession - */ - public function __construct(BackendQuoteSession $backendQuoteSession) - { - $this->backendQuoteSession = $backendQuoteSession; - } - - /** - * @inheritdoc - */ - public function getQuote() - { - return $this->backendQuoteSession->getQuote(); - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php b/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php deleted file mode 100644 index 44c226ae4a47e..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession; - -use Magento\Checkout\Model\Session as CheckoutSession; - -/** - * Implementation of QuoteSessionInterface for Magento frontend checkout. - */ -class FrontendSession implements QuoteSessionInterface -{ - /** - * @var CheckoutSession - */ - private $checkoutSession; - - /** - * FrontendSession constructor. - * - * Class uses checkout session for retrieving quote. - * - * @param CheckoutSession $checkoutSession - */ - public function __construct(CheckoutSession $checkoutSession) - { - $this->checkoutSession = $checkoutSession; - } - - /** - * @inheritdoc - */ - public function getQuote() - { - return $this->checkoutSession->getQuote(); - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php b/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php deleted file mode 100644 index 14958ac65a6ee..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession; - -/** - * Interface QuoteSessionInterface - */ -interface QuoteSessionInterface -{ - /** - * Returns quote from session. - * - * @return \Magento\Quote\Api\Data\CartInterface - */ - public function getQuote(); -} diff --git a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php b/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php deleted file mode 100644 index 9c6ffde4504b9..0000000000000 --- a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\ResourceModel; - -use Magento\Framework\Model\ResourceModel\Db\AbstractDb; - -/** - * Implementation of case resource model - */ -class CaseEntity extends AbstractDb -{ - /** - * @inheritdoc - */ - protected function _construct() - { - $this->_init('signifyd_case', 'entity_id'); - } -} diff --git a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php b/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php deleted file mode 100644 index 92e233dd42dbc..0000000000000 --- a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\ResourceModel\CaseEntity; - -use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; -use Magento\Signifyd\Model\CaseEntity; -use Magento\Signifyd\Model\ResourceModel\CaseEntity as CaseResourceModel; - -/** - * Collection of case entities - */ -class Collection extends AbstractCollection -{ - /** - * @inheritdoc - */ - public function _construct() - { - $this->_init(CaseEntity::class, CaseResourceModel::class); - } -} diff --git a/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php b/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php deleted file mode 100644 index 360225ae37b7b..0000000000000 --- a/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SalesOrderGrid; - -use Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface; -use Magento\Signifyd\Model\ResourceModel; -use Magento\Signifyd\Model\ResourceModel\CaseEntity; - -/** - * Provides order ids list which Signifyd Case guaranty status were changed - */ -class NotSyncedOrderIdListProvider implements NotSyncedDataProviderInterface -{ - /** - * @var ResourceModel\CaseEntity - */ - private $caseEntity; - - /** - * @param ResourceModel\CaseEntity $caseEntity - */ - public function __construct( - CaseEntity $caseEntity - ) { - $this->caseEntity = $caseEntity; - } - - /** - * @inheritdoc - */ - public function getIds($mainTableName, $gridTableName) - { - $connection = $this->caseEntity->getConnection(); - $select = $connection->select() - ->from($this->caseEntity->getMainTable(), ['order_id']) - ->joinLeft( - [$gridTableName => $connection->getTableName($gridTableName)], - sprintf( - '%s.%s = %s.%s', - $this->caseEntity->getMainTable(), - 'order_id', - $gridTableName, - 'entity_id' - ), - [] - ) - ->where('guarantee_disposition != signifyd_guarantee_status'); - - return $connection->fetchAll($select, [], \Zend_Db::FETCH_COLUMN); - } -} diff --git a/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php b/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php deleted file mode 100644 index fff42b300be58..0000000000000 --- a/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SalesOrderGrid; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\ResourceModel\GridInterface; - -/** - * Perfoms sales order grid updating operations. - * - * Serves order grid updates in both synchronous and asynchronous modes. - */ -class OrderGridUpdater -{ - /** - * @var ScopeConfigInterface - */ - private $globalConfig; - - /** - * @var GridInterface - */ - private $entityGrid; - - /** - * @param GridInterface $entityGrid - * @param ScopeConfigInterface $globalConfig - */ - public function __construct( - GridInterface $entityGrid, - ScopeConfigInterface $globalConfig - ) { - $this->globalConfig = $globalConfig; - $this->entityGrid = $entityGrid; - } - - /** - * Handles synchronous updating order entity in grid. - * - * Works only if asynchronous grid indexing is disabled - * in global settings. - * - * @param int $orderId - * @return void - */ - public function update($orderId) - { - if (!$this->globalConfig->getValue('dev/grid/async_indexing')) { - $this->entityGrid->refresh($orderId); - } - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php deleted file mode 100644 index 73338c8ea4d62..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -/** - * Exception of interaction with Signifyd API - */ -class ApiCallException extends GatewayException -{ - /** - * @var string - */ - private $requestData; - - /** - * ApiCallException constructor. - * @param string $message - * @param int $code - * @param \Exception|null $previous - * @param string $requestData in JSON format - */ - public function __construct($message = '', $code = 0, \Exception $previous = null, $requestData = '') - { - $this->requestData = $requestData; - parent::__construct($message, $code, $previous); - } - - /** - * Gets request data for unsuccessful request in JSON format - * @return string - */ - public function getRequestData() - { - return $this->requestData; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php deleted file mode 100644 index 2d6d57a510ae3..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -use Magento\Signifyd\Model\SignifydGateway\Client\RequestBuilder; - -/** - * Encapsulates Signifyd API protocol. - */ -class ApiClient -{ - /** - * @var RequestBuilder - */ - private $requestBuilder; - - /** - * ApiClient constructor. - * - * @param RequestBuilder $requestBuilder - */ - public function __construct( - RequestBuilder $requestBuilder - ) { - $this->requestBuilder = $requestBuilder; - } - - /** - * Perform call to Signifyd API. - * - * Method returns associative array that corresponds to successful result. - * Current implementation do not expose details in case of failure. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return array - */ - public function makeApiCall($url, $method, array $params = [], $storeId = null): array - { - $result = $this->requestBuilder->doRequest($url, $method, $params, $storeId); - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php deleted file mode 100644 index 2a9b933b98b5d..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Json\EncoderInterface; -use Magento\Signifyd\Model\Config; - -/** - * Class HttpClientFactory - * Creates and configures HTTP client for RequestBuilder - */ -class HttpClientFactory -{ - /** - * Specifies basic HTTP access authentication Header. - * - * @var string - */ - private static $authorizationType = 'Authorization'; - - /** - * JSON HTTP Content-Type Header. - * - * @var string - */ - private static $jsonDataType = 'application/json'; - - /** - * @var string - */ - private static $urlSeparator = '/'; - - /** - * @var Config - */ - private $config; - - /** - * @var ZendClientFactory - */ - private $clientFactory; - - /** - * @var EncoderInterface - */ - private $dataEncoder; - - /** - * HttpClientCreator constructor. - * - * @param Config $config - * @param ZendClientFactory $clientFactory - * @param EncoderInterface $dataEncoder - */ - public function __construct( - Config $config, - ZendClientFactory $clientFactory, - EncoderInterface $dataEncoder - ) { - $this->config = $config; - $this->clientFactory = $clientFactory; - $this->dataEncoder = $dataEncoder; - } - - /** - * Creates and configures HTTP client. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return ZendClient - */ - public function create($url, $method, array $params = [], $storeId = null): ZendClient - { - $apiKey = $this->getApiKey($storeId); - $apiUrl = $this->buildFullApiUrl($url, $storeId); - - $client = $this->createNewClient(); - $client->setHeaders( - self::$authorizationType, - sprintf('Basic %s', base64_encode($apiKey)) - ); - if (!empty($params)) { - $encodedData = $this->dataEncoder->encode($params); - $client->setRawData($encodedData, self::$jsonDataType); - } - $client->setMethod($method); - $client->setUri($apiUrl); - - return $client; - } - - /** - * @return ZendClient - */ - private function createNewClient() - { - return $this->clientFactory->create(); - } - - /** - * Signifyd API key for merchant account. - * - * @see https://www.signifyd.com/docs/api/#/introduction/authentication - * @param int|null $storeId - * @return string - */ - private function getApiKey($storeId): string - { - return $this->config->getApiKey($storeId); - } - - /** - * Full URL for Singifyd API based on relative URL. - * - * @param string $url - * @param int|null $storeId - * @return string - */ - private function buildFullApiUrl($url, $storeId): string - { - $baseApiUrl = $this->getBaseApiUrl($storeId); - $fullUrl = $baseApiUrl . self::$urlSeparator . ltrim($url, self::$urlSeparator); - - return $fullUrl; - } - - /** - * Base Sigifyd API URL without trailing slash. - * - * @param int|null $storeId - * @return string - */ - private function getBaseApiUrl($storeId): string - { - $baseApiUrl = $this->config->getApiUrl($storeId); - - return rtrim($baseApiUrl, self::$urlSeparator); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php deleted file mode 100644 index ee079a74d345f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -/** - * Class RequestBuilder - * Creates HTTP client, sends request to Signifyd and handles response - */ -class RequestBuilder -{ - /** - * @var HttpClientFactory - */ - private $clientCreator; - - /** - * @var RequestSender - */ - private $requestSender; - - /** - * @var ResponseHandler - */ - private $responseHandler; - - /** - * RequestBuilder constructor. - * - * @param HttpClientFactory $clientCreator - * @param RequestSender $requestSender - * @param ResponseHandler $responseHandler - */ - public function __construct( - HttpClientFactory $clientCreator, - RequestSender $requestSender, - ResponseHandler $responseHandler - ) { - $this->clientCreator = $clientCreator; - $this->requestSender = $requestSender; - $this->responseHandler = $responseHandler; - } - - /** - * Creates HTTP client for API call. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return array - */ - public function doRequest($url, $method, array $params = [], $storeId = null): array - { - $client = $this->clientCreator->create($url, $method, $params, $storeId); - $response = $this->requestSender->send($client, $storeId); - $result = $this->responseHandler->handle($response); - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php deleted file mode 100644 index a63331e055c1c..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Debugger\DebuggerFactory; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\HTTP\ZendClient; - -/** - * Class RequestSender - * Gets HTTP client end sends request to Signifyd API - */ -class RequestSender -{ - /** - * @var DebuggerFactory - */ - private $debuggerFactory; - - /** - * RequestSender constructor. - * - * @param DebuggerFactory $debuggerFactory - */ - public function __construct( - DebuggerFactory $debuggerFactory - ) { - $this->debuggerFactory = $debuggerFactory; - } - - /** - * Sends HTTP request to Signifyd API with configured client. - * - * Each request/response pair is handled by debugger. - * If debug mode for Signifyd integration enabled in configuration - * debug information is recorded to debug.log. - * - * @param ZendClient $client - * @param int|null $storeId - * @return \Zend_Http_Response - * @throws ApiCallException - */ - public function send(ZendClient $client, $storeId = null): \Zend_Http_Response - { - try { - $response = $client->request(); - - $this->debuggerFactory->create($storeId)->success( - $client->getUri(true), - $client->getLastRequest(), - $response->getStatus() . ' ' . $response->getMessage(), - $response->getBody() - ); - - return $response; - } catch (\Exception $e) { - $this->debuggerFactory->create($storeId)->failure( - $client->getUri(true), - $client->getLastRequest(), - $e - ); - - throw new ApiCallException( - 'Unable to process Signifyd API: ' . $e->getMessage(), - $e->getCode(), - $e, - $client->getLastRequest() - ); - } - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php deleted file mode 100644 index 614e59b7c29a2..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\Json\DecoderInterface; - -/** - * Class ResponseHandler - */ -class ResponseHandler -{ - /** - * Successful HTTP response codes. - * - * @var array - */ - private static $successResponseCodes = [200, 201, 204]; - - /** - * Current servers PHP version id. - */ - private static $phpVersionId = PHP_VERSION_ID; - - /** - * Failure HTTP response codes with messages. - * - * @var array - */ - private static $failureResponses = [ - 400 => 'Bad Request - The request could not be parsed. Response: %s', - 401 => 'Unauthorized - user is not logged in, could not be authenticated. Response: %s', - 403 => 'Forbidden - Cannot access resource. Response: %s', - 404 => 'Not Found - resource does not exist. Response: %s', - 409 => 'Conflict - with state of the resource on server. Can occur with (too rapid) PUT requests. Response: %s', - 500 => 'Server error. Response: %s' - ]; - - /** - * Unexpected Signifyd API response message. - * - * @var string - */ - private static $unexpectedResponse = 'Unexpected Signifyd API response code "%s" with content "%s".'; - - /** - * @var DecoderInterface - */ - private $dataDecoder; - - /** - * ResponseHandler constructor. - * - * @param DecoderInterface $dataDecoder - */ - public function __construct( - DecoderInterface $dataDecoder - ) { - $this->dataDecoder = $dataDecoder; - } - - /** - * Reads result of successful operation and throws exception in case of any failure. - * - * @param \Zend_Http_Response $response - * @return array - * @throws ApiCallException - */ - public function handle(\Zend_Http_Response $response) - { - $responseCode = $response->getStatus(); - - if (!in_array($responseCode, self::$successResponseCodes)) { - $errorMessage = $this->buildApiCallFailureMessage($response); - throw new ApiCallException($errorMessage); - } - - $responseBody = (string)$response->getBody(); - - if (self::$phpVersionId < 70000 && empty($responseBody)) { - /* - * Only since PHP 7.0 empty string treated as JSON syntax error - * http://php.net/manual/en/function.json-decode.php - */ - throw new ApiCallException('Response is not valid JSON: Decoding failed: Syntax error'); - } - - try { - $decodedResponseBody = $this->dataDecoder->decode($responseBody); - } catch (\Exception $e) { - throw new ApiCallException( - 'Response is not valid JSON: ' . $e->getMessage(), - $e->getCode(), - $e - ); - } - - return $decodedResponseBody; - } - - /** - * Error message for request rejected by Signify. - * - * @param \Zend_Http_Response $response - * @return string - */ - private function buildApiCallFailureMessage(\Zend_Http_Response $response) - { - $responseBody = $response->getBody(); - - if (key_exists($response->getStatus(), self::$failureResponses)) { - return sprintf(self::$failureResponses[$response->getStatus()], $responseBody); - } - - return sprintf( - self::$unexpectedResponse, - $response->getStatus(), - $responseBody - ); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php deleted file mode 100644 index 7057313b5e415..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Exception; - -/** - * This debugger ignores any information. - * Optimal production environment. - */ -class BlackHole implements DebuggerInterface -{ - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody) - { - // ignore - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function failure($requestUrl, $requestData, Exception $exception) - { - // ignore - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php deleted file mode 100644 index 1e61a313899cc..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\Config; - -/** - * Factory produces debugger based on runtime configuration. - * - * Configuration may be changed by - * - config.xml - * - at Admin panel (Stores > Configuration > Sales > Fraud Detection > Signifyd > Debug) - */ -class DebuggerFactory -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Config - */ - private $config; - - /** - * DebuggerFactory constructor. - * - * @param ObjectManagerInterface $objectManager - * @param Config $config - */ - public function __construct( - ObjectManagerInterface $objectManager, - Config $config - ) { - $this->objectManager = $objectManager; - $this->config = $config; - } - - /** - * Create debugger instance - * - * @param int|null $storeId - * @return DebuggerInterface - */ - public function create($storeId = null): DebuggerInterface - { - if (!$this->config->isDebugModeEnabled($storeId)) { - return $this->objectManager->get(BlackHole::class); - } - - return $this->objectManager->get(Log::class); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php deleted file mode 100644 index f4a2f9cc56a8f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Exception; - -/** - * Interface for debugging interaction with Signifyd API - */ -interface DebuggerInterface -{ - /** - * Register debug information about accepted request to Signifyd API - * - * @param string $requestUrl - * @param string $requestData - * @param string $responseStatus - * @param string $responseBody - * @return void - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody); - - /** - * Register debug information about failed request to Signifyd API - * - * @param string $requestUrl - * @param string $requestData - * @param Exception $exception - * @return mixed - */ - public function failure($requestUrl, $requestData, Exception $exception); -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php deleted file mode 100644 index 4b7258180f53e..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Psr\Log\LoggerInterface; -use Exception; - -/** - * Debugger writes information about request, response and possible exception to standard system log. - */ -class Log implements DebuggerInterface -{ - /** - * @var LoggerInterface - */ - private $logger; - - /** - * Log constructor. - * - * @param LoggerInterface $logger - */ - public function __construct(LoggerInterface $logger) - { - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody) - { - $requestInfo = $this->buildRequestInfo($requestUrl, $requestData); - $responseInfo = $this->buildResponseInfo($responseStatus, $responseBody); - - $info = $requestInfo - . $responseInfo; - - $this->writeToLog($info); - } - - /** - * {@inheritdoc} - */ - public function failure($requestUrl, $requestData, Exception $exception) - { - $requestInfo = $this->buildRequestInfo($requestUrl, $requestData); - $exceptionInfo = $this->buildExceptionInfo($exception); - - $info = $requestInfo - . $exceptionInfo; - - $this->writeToLog($info); - } - - /** - * Build string with request URL and body - * - * @param string $requestUrl - * @param string $requestData - * @return string - */ - private function buildRequestInfo($requestUrl, $requestData) - { - $infoContent = $this->buildInfoSection('URL', $requestUrl) - . $this->buildInfoSection('Body', $requestData); - - $info = $this->buildInfoSection('Request', $infoContent); - return $info; - } - - /** - * Build string with response status code and body - * - * @param string $responseStatus - * @param string $responseBody - * @return string - */ - private function buildResponseInfo($responseStatus, $responseBody) - { - $infoContent = $this->buildInfoSection('Status', $responseStatus) - . $this->buildInfoSection('Body', $responseBody); - - $info = $this->buildInfoSection('Response', $infoContent); - return $info; - } - - /** - * Build string with exception information - * - * @param Exception $exception - * @return string - */ - private function buildExceptionInfo(Exception $exception) - { - $infoContent = (string)$exception; - $info = $this->buildInfoSection('Exception', $infoContent); - return $info; - } - - /** - * Write debug information to log file (var/log/debug.log by default) - * - * @param string $info - * @return void - */ - private function writeToLog($info) - { - $logMessage = $this->buildInfoSection('Signifyd API integration debug info', $info); - $this->logger->debug($logMessage); - } - - /** - * Build unified debug section string - * - * @param string $title - * @param string $content - * @return string - */ - private function buildInfoSection($title, $content) - { - $formattedInfo = $title . ":\n" - . $this->addIndent($content) . "\n"; - return $formattedInfo; - } - - /** - * Add indent to each line in content - * - * @param string $content - * @param string $indent - * @return string - */ - private function addIndent($content, $indent = ' ') - { - $contentLines = explode("\n", $content); - $contentLinesWithIndent = array_map(function ($line) use ($indent) { - return $indent . $line; - }, $contentLines); - $content = implode("\n", $contentLinesWithIndent); - return $content; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php deleted file mode 100644 index 9f7a053c58724..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php +++ /dev/null @@ -1,225 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface; - -/** - * Signifyd Gateway. - * - * Encapsulates interaction with Signifyd API. - */ -class Gateway -{ - /**#@+ - * Constants for case available statuses - */ - const STATUS_OPEN = 'OPEN'; - const STATUS_PROCESSING = 'PROCESSING'; - const STATUS_FLAGGED = 'FLAGGED'; - const STATUS_DISMISSED = 'DISMISSED'; - /**#@-*/ - - /**#@+ - * Constants for guarantee available statuses - * @see https://www.signifyd.com/resources/manual/signifyd-guarantee/signifyd-guarantee/ - */ - const GUARANTEE_APPROVED = 'APPROVED'; - const GUARANTEE_DECLINED = 'DECLINED'; - const GUARANTEE_PENDING = 'PENDING'; - const GUARANTEE_CANCELED = 'CANCELED'; - const GUARANTEE_IN_REVIEW = 'IN_REVIEW'; - const GUARANTEE_UNREQUESTED = 'UNREQUESTED'; - /**#@-*/ - - /**#@+ - * Constants for case available review dispositions - */ - const DISPOSITION_GOOD = 'GOOD'; - const DISPOSITION_FRAUDULENT = 'FRAUDULENT'; - const DISPOSITION_UNSET = 'UNSET'; - - /** - * @var CreateCaseBuilderInterface - */ - private $createCaseBuilder; - - /** - * @var ApiClient - */ - private $apiClient; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * Gateway constructor. - * - * @param CreateCaseBuilderInterface $createCaseBuilder - * @param ApiClient $apiClient - * @param OrderRepositoryInterface $orderRepository - * @param CaseRepositoryInterface $caseRepository - */ - public function __construct( - CreateCaseBuilderInterface $createCaseBuilder, - ApiClient $apiClient, - OrderRepositoryInterface $orderRepository, - CaseRepositoryInterface $caseRepository - ) { - $this->createCaseBuilder = $createCaseBuilder; - $this->apiClient = $apiClient; - $this->orderRepository = $orderRepository; - $this->caseRepository = $caseRepository; - } - - /** - * Returns id of created case (investigation) on Signifyd service - * @see https://www.signifyd.com/docs/api/#/reference/cases/create-a-case - * - * @param int $orderId - * @return int Signifyd case (investigation) identifier - * @throws GatewayException - */ - public function createCase($orderId) - { - $caseParams = $this->createCaseBuilder->build($orderId); - $storeId = $this->getStoreIdFromOrder($orderId); - - $caseCreationResult = $this->apiClient->makeApiCall( - '/cases', - 'POST', - $caseParams, - $storeId - ); - - if (!isset($caseCreationResult['investigationId'])) { - throw new GatewayException('Expected field "investigationId" missed.'); - } - - return (int)$caseCreationResult['investigationId']; - } - - /** - * Returns guaranty decision result - * @see https://www.signifyd.com/docs/api/#/reference/guarantees/submit-a-case-for-guarantee - * - * @param int $signifydCaseId - * @return string - * @throws GatewayException - */ - public function submitCaseForGuarantee($signifydCaseId) - { - $storeId = $this->getStoreIdFromCase($signifydCaseId); - $guaranteeCreationResult = $this->apiClient->makeApiCall( - '/guarantees', - 'POST', - [ - 'caseId' => $signifydCaseId, - ], - $storeId - ); - - $disposition = $this->processDispositionResult($guaranteeCreationResult); - return $disposition; - } - - /** - * Sends request to cancel guarantee and returns disposition. - * - * @see https://www.signifyd.com/docs/api/#/reference/guarantees/submit-a-case-for-guarantee/cancel-guarantee - * @param int $caseId - * @return string - * @throws GatewayException - */ - public function cancelGuarantee($caseId) - { - $storeId = $this->getStoreIdFromCase($caseId); - $result = $this->apiClient->makeApiCall( - '/cases/' . $caseId . '/guarantee', - 'PUT', - [ - 'guaranteeDisposition' => self::GUARANTEE_CANCELED - ], - $storeId - ); - - $disposition = $this->processDispositionResult($result); - if ($disposition !== self::GUARANTEE_CANCELED) { - throw new GatewayException("API returned unexpected disposition: $disposition."); - } - - return $disposition; - } - - /** - * Processes result from Signifyd API. - * Throws the GatewayException is result does not contain guarantee disposition in response or - * disposition has unknown status. - * - * @param array $result - * @return string - * @throws GatewayException - */ - private function processDispositionResult(array $result) - { - if (!isset($result['disposition'])) { - throw new GatewayException('Expected field "disposition" missed.'); - } - - $disposition = strtoupper($result['disposition']); - - if (!in_array($disposition, [ - self::GUARANTEE_APPROVED, - self::GUARANTEE_DECLINED, - self::GUARANTEE_PENDING, - self::GUARANTEE_CANCELED, - self::GUARANTEE_IN_REVIEW, - self::GUARANTEE_UNREQUESTED - ])) { - throw new GatewayException( - sprintf('API returns unknown guaranty disposition "%s".', $disposition) - ); - } - - return $disposition; - } - - /** - * Returns store id by case. - * - * @param int $caseId - * @return int|null - */ - private function getStoreIdFromCase(int $caseId) - { - $case = $this->caseRepository->getByCaseId($caseId); - $orderId = $case->getOrderId(); - - return $this->getStoreIdFromOrder($orderId); - } - - /** - * Returns store id from order. - * - * @param int $orderId - * @return int|null - */ - private function getStoreIdFromOrder(int $orderId) - { - $order = $this->orderRepository->get($orderId); - - return $order->getStoreId(); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php b/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php deleted file mode 100644 index 666217f8ccc85..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -/** - * Exception of interaction with Signifyd API - */ -class GatewayException extends \Exception -{ - -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php deleted file mode 100644 index 482f243f6f05d..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Api\Data\OrderAddressInterface; - -/** - * Prepare address data - */ -class AddressBuilder -{ - /** - * Returns address data params based on OrderAddressInterface - * - * @param OrderAddressInterface $address - * @return array - */ - public function build(OrderAddressInterface $address) - { - return [ - 'streetAddress' => $this->getStreetLine(1, $address->getStreet()), - 'unit' => $this->getStreetLine(2, $address->getStreet()), - 'city' => $address->getCity(), - 'provinceCode' => $address->getRegionCode(), - 'postalCode' => $address->getPostcode(), - 'countryCode' => $address->getCountryId() - ]; - } - - /** - * Get street line by number - * - * @param int $number - * @param string[]|null $street - * @return string - */ - private function getStreetLine($number, $street) - { - $lines = is_array($street) ? $street : []; - - return $lines[$number - 1] ?? ''; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php deleted file mode 100644 index 5e3a1a83e7aeb..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; - -/** - * Prepare data related to the card that was used for the purchase and its cardholder. - */ -class CardBuilder -{ - /** - * @var AddressBuilder - */ - private $addressBuilder; - - /** - * @param AddressBuilder $addressBuilder - */ - public function __construct( - AddressBuilder $addressBuilder - ) { - $this->addressBuilder = $addressBuilder; - } - - /** - * Returns card data params based on payment and billing address info - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - $address = $order->getBillingAddress(); - if ($address === null) { - return $result; - } - - $payment = $order->getPayment(); - $result = [ - 'card' => [ - 'cardHolderName' => $address->getFirstname() . ' ' . $address->getLastname(), - 'last4' => $payment->getCcLast4(), - 'expiryMonth' => $payment->getCcExpMonth(), - 'expiryYear' => $payment->getCcExpYear(), - 'billingAddress' => $this->addressBuilder->build($address) - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php deleted file mode 100644 index 8db06473b96d8..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\App\ProductMetadataInterface; - -/** - * Provides platform name, edition and version info - */ -class ClientVersionBuilder -{ - /** - * @var string - */ - private static $clientVersion = '1.0'; - - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - - /** - * @param ProductMetadataInterface $productMetadata - */ - public function __construct( - ProductMetadataInterface $productMetadata - ) { - $this->productMetadata = $productMetadata; - } - - /** - * Returns version info - * - * @return array - */ - public function build() - { - return [ - 'platformAndClient' => [ - 'storePlatform' => $this->productMetadata->getName() . ' ' . $this->productMetadata->getEdition(), - 'storePlatformVersion' => $this->productMetadata->getVersion(), - 'signifydClientApp' => $this->productMetadata->getName(), - 'signifydClientAppVersion' => self::$clientVersion, - ] - ]; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php deleted file mode 100644 index 3e41003d47842..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\OrderFactory; - -/** - * Signifyd case creation request builder. - * - * Handles the conversion from Magento Order to Signifyd Case. - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateCaseBuilder implements CreateCaseBuilderInterface -{ - /** - * @var OrderFactory - */ - private $orderFactory; - - /** - * @var PurchaseBuilder - */ - private $purchaseBuilder; - - /** - * @var CardBuilder - */ - private $cardBuilder; - - /** - * @var RecipientBuilder - */ - private $recipientBuilder; - - /** - * @var SellerBuilder - */ - private $sellerBuilder; - - /** - * @var ClientVersionBuilder - */ - private $clientVersionBuilder; - - /** - * @var UserAccountBuilder - */ - private $userAccountBuilder; - - /** - * @param OrderFactory $orderFactory - * @param PurchaseBuilder $purchaseBuilder - * @param CardBuilder $cardBuilder - * @param RecipientBuilder $recipientBuilder - * @param SellerBuilder $sellerBuilder - * @param ClientVersionBuilder $clientVersionBuilder - * @param UserAccountBuilder $userAccountBuilder - */ - public function __construct( - OrderFactory $orderFactory, - PurchaseBuilder $purchaseBuilder, - CardBuilder $cardBuilder, - RecipientBuilder $recipientBuilder, - SellerBuilder $sellerBuilder, - ClientVersionBuilder $clientVersionBuilder, - UserAccountBuilder $userAccountBuilder - ) { - $this->orderFactory = $orderFactory; - $this->purchaseBuilder = $purchaseBuilder; - $this->cardBuilder = $cardBuilder; - $this->recipientBuilder = $recipientBuilder; - $this->sellerBuilder = $sellerBuilder; - $this->clientVersionBuilder = $clientVersionBuilder; - $this->userAccountBuilder = $userAccountBuilder; - } - - /** - * @inheritdoc - */ - public function build($orderId) - { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->orderFactory->create()->load($orderId); - - return $this->removeEmptyValues( - array_merge( - $this->purchaseBuilder->build($order), - $this->cardBuilder->build($order), - $this->recipientBuilder->build($order), - $this->userAccountBuilder->build($order), - $this->sellerBuilder->build($order), - $this->clientVersionBuilder->build() - ) - ); - } - - /** - * Remove empty and null values. - * - * @param array $data - * @return array - */ - private function removeEmptyValues($data) - { - foreach ($data as $key => $value) { - if (is_array($value)) { - $data[$key] = $this->removeEmptyValues($data[$key]); - } - - if ($this->isEmpty($data[$key])) { - unset($data[$key]); - } - } - - return $data; - } - - /** - * Empty values are null, empty string and empty array. - * - * @param mixed $value - * @return bool - */ - private function isEmpty($value) - { - return $value === null || (is_array($value) && empty($value)); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php deleted file mode 100644 index 56662f69e7c5a..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -/** - * Collects information about order and build array with parameters required by Signifyd API - * - * @see https://www.signifyd.com/docs/api/#/reference/cases/create-a-case - */ -interface CreateCaseBuilderInterface -{ - /** - * Returns params for Case creation request - * - * @param int $orderId - * @return array - */ - public function build($orderId); -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php deleted file mode 100644 index 5e544e4b4048e..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php +++ /dev/null @@ -1,227 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\App\Area; -use Magento\Framework\Config\ScopeInterface; -use Magento\Framework\Exception\ConfigurationMismatchException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderPaymentInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper; -use Magento\Signifyd\Model\PaymentVerificationFactory; -use Magento\Signifyd\Model\SignifydOrderSessionId; - -/** - * Prepare data related to purchase event represented in case creation request. - */ -class PurchaseBuilder -{ - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var ScopeInterface - */ - private $scope; - - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var PaymentVerificationFactory - */ - private $paymentVerificationFactory; - - /** - * @var PaymentMethodMapper - */ - private $paymentMethodMapper; - - /** - * PurchaseBuilder constructor. - * - * @param DateTimeFactory $dateTimeFactory - * @param ScopeInterface $scope - * @param SignifydOrderSessionId $signifydOrderSessionId - * @param PaymentVerificationFactory $paymentVerificationFactory - * @param PaymentMethodMapper $paymentMethodMapper - */ - public function __construct( - DateTimeFactory $dateTimeFactory, - ScopeInterface $scope, - SignifydOrderSessionId $signifydOrderSessionId, - PaymentVerificationFactory $paymentVerificationFactory, - PaymentMethodMapper $paymentMethodMapper - ) { - $this->dateTimeFactory = $dateTimeFactory; - $this->scope = $scope; - $this->signifydOrderSessionId = $signifydOrderSessionId; - $this->paymentVerificationFactory = $paymentVerificationFactory; - $this->paymentMethodMapper = $paymentMethodMapper; - } - - /** - * Returns purchase data params - * - * @param Order $order - * @return array - * @throws ConfigurationMismatchException - */ - public function build(Order $order) - { - $orderPayment = $order->getPayment(); - $createdAt = $this->dateTimeFactory->create( - $order->getCreatedAt(), - new \DateTimeZone('UTC') - ); - - $result = [ - 'purchase' => [ - 'orderSessionId' => $this->signifydOrderSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => $createdAt->format(\DateTime::ATOM), - 'paymentGateway' => $this->getPaymentGateway($orderPayment->getMethod()), - 'transactionId' => $orderPayment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => $this->getAvsCode($orderPayment), - 'cvvResponseCode' => $this->getCvvCode($orderPayment), - 'orderChannel' => $this->getOrderChannel(), - 'totalPrice' => $order->getGrandTotal(), - 'paymentMethod' => $this->paymentMethodMapper - ->getSignifydPaymentMethodCode($orderPayment->getMethod()) - ], - ]; - - $shippingDescription = $order->getShippingDescription(); - if ($shippingDescription !== null) { - $result['purchase']['shipments'] = [ - [ - 'shipper' => $this->getShipper($order->getShippingDescription()), - 'shippingMethod' => $this->getShippingMethod($order->getShippingDescription()), - 'shippingPrice' => $order->getShippingAmount() - ] - ]; - } - - $products = $this->getProducts($order); - if (!empty($products)) { - $result['purchase']['products'] = $products; - } - - return $result; - } - - /** - * Returns the products purchased in the transaction. - * - * @param Order $order - * @return array - */ - private function getProducts(Order $order) - { - $result = []; - foreach ($order->getAllItems() as $orderItem) { - $result[] = [ - 'itemId' => $orderItem->getSku(), - 'itemName' => $orderItem->getName(), - 'itemPrice' => $orderItem->getPrice(), - 'itemQuantity' => (int)$orderItem->getQtyOrdered(), - 'itemUrl' => $orderItem->getProduct()->getProductUrl(), - 'itemWeight' => $orderItem->getProduct()->getWeight() - ]; - } - - return $result; - } - - /** - * Returns the name of the shipper - * - * @param string $shippingDescription - * @return string - */ - private function getShipper($shippingDescription) - { - $result = explode(' - ', $shippingDescription, 2); - - return count($result) == 2 ? $result[0] : ''; - } - - /** - * Returns the type of the shipment method used - * - * @param string $shippingDescription - * @return string - */ - private function getShippingMethod($shippingDescription) - { - $result = explode(' - ', $shippingDescription, 2); - - return count($result) == 2 ? $result[1] : ''; - } - - /** - * Returns the gateway that processed the transaction. For PayPal orders should be paypal_account. - * - * @param string $gatewayCode - * @return string - */ - private function getPaymentGateway($gatewayCode) - { - $payPalCodeList = [ - 'paypal_express', - 'braintree_paypal', - 'payflowpro', - 'payflow_express', - 'payflow_link', - 'payflow_advanced', - 'hosted_pro', - ]; - return in_array($gatewayCode, $payPalCodeList) ? 'paypal_account' : $gatewayCode; - } - - /** - * Returns WEB for web-orders, PHONE for orders created by Admin - * - * @return string - */ - private function getOrderChannel() - { - return $this->scope->getCurrentScope() === Area::AREA_ADMINHTML ? 'PHONE' : 'WEB'; - } - - /** - * Gets AVS code for order payment method. - * - * @param OrderPaymentInterface $orderPayment - * @return string - * @throws ConfigurationMismatchException - */ - private function getAvsCode(OrderPaymentInterface $orderPayment) - { - $avsAdapter = $this->paymentVerificationFactory->createPaymentAvs($orderPayment->getMethod()); - return $avsAdapter->getCode($orderPayment); - } - - /** - * Gets CVV code for order payment method. - * - * @param OrderPaymentInterface $orderPayment - * @return string - * @throws ConfigurationMismatchException - */ - private function getCvvCode(OrderPaymentInterface $orderPayment) - { - $cvvAdapter = $this->paymentVerificationFactory->createPaymentCvv($orderPayment->getMethod()); - return $cvvAdapter->getCode($orderPayment); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php deleted file mode 100644 index d9d26c8943b88..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; - -/** - * Prepare data related to person or organization receiving the items purchased - */ -class RecipientBuilder -{ - /** - * @var AddressBuilder - */ - private $addressBuilder; - - /** - * @param AddressBuilder $addressBuilder - */ - public function __construct( - AddressBuilder $addressBuilder - ) { - $this->addressBuilder = $addressBuilder; - } - - /** - * Returns recipient data params based on shipping address - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - $address = $order->getShippingAddress(); - if ($address === null) { - return $result; - } - - $result = [ - 'recipient' => [ - 'fullName' => $address->getName(), - 'confirmationEmail' => $address->getEmail(), - 'confirmationPhone' => $address->getTelephone(), - 'organization' => $address->getCompany(), - 'deliveryAddress' => $this->addressBuilder->build($address) - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php deleted file mode 100644 index b2cf0401b247f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Directory\Model\RegionFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Shipment; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\Information; -use Magento\Store\Model\ScopeInterface; - -/** - * Prepare data related to the seller of the product. - * - * This information is optional unless you are operating a marketplace, - * listing goods on behalf of multiple sellers who each hold a seller account registered with your site. - */ -class SellerBuilder -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var RegionFactory - */ - private $regionFactory; - - /** - * @var array - */ - private $regionCodes = []; - - /** - * @param ScopeConfigInterface $scopeConfig - * @param RegionFactory $regionFactory - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - RegionFactory $regionFactory - ) { - $this->scopeConfig = $scopeConfig; - $this->regionFactory = $regionFactory; - } - - /** - * Returns seller data params - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $store = $order->getStore(); - - return [ - 'seller' => [ - 'name' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_NAME, $store), - 'domain' => $this->getPublicDomain($store), - 'shipFromAddress' => [ - 'streetAddress' => $this->getConfigValue(Shipment::XML_PATH_STORE_ADDRESS1, $store), - 'unit' => $this->getConfigValue(Shipment::XML_PATH_STORE_ADDRESS2, $store), - 'city' => $this->getConfigValue(Shipment::XML_PATH_STORE_CITY, $store), - 'provinceCode' => $this->getRegionCodeById( - $this->getConfigValue(Shipment::XML_PATH_STORE_REGION_ID, $store) - ), - 'postalCode' => $this->getConfigValue(Shipment::XML_PATH_STORE_ZIP, $store), - 'countryCode' => $this->getConfigValue(Shipment::XML_PATH_STORE_COUNTRY_ID, $store), - ], - 'corporateAddress' => [ - 'streetAddress' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_STREET_LINE1, $store), - 'unit' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_STREET_LINE2, $store), - 'city' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_CITY, $store), - 'provinceCode' => $this->getRegionCodeById( - $this->getConfigValue(Information::XML_PATH_STORE_INFO_REGION_CODE, $store) - ), - 'postalCode' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_POSTCODE, $store), - 'countryCode' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_COUNTRY_CODE, $store), - ] - ] - ]; - } - - /** - * Returns region code by id - * - * @param int $regionId - * @return string - */ - private function getRegionCodeById($regionId) - { - if (!isset($this->regionCodes[$regionId])) { - $this->regionCodes[$regionId] = $this->regionFactory->create()->load($regionId)->getCode(); - } - - return $this->regionCodes[$regionId]; - } - - /** - * Returns value from config - * - * @param string $value - * @param StoreInterface $store - * @return mixed - */ - private function getConfigValue($value, StoreInterface $store) - { - return $this->scopeConfig->getValue( - $value, - ScopeInterface::SCOPE_STORE, - $store - ); - } - - /** - * Returns public domain name - * - * @param StoreInterface $store - * @return string|null null if no DNS records corresponding to a current host found - */ - private function getPublicDomain(StoreInterface $store) - { - $baseUrl = $store->getBaseUrl(); - $domain = parse_url($baseUrl, PHP_URL_HOST); - if (\function_exists('checkdnsrr') && false === \checkdnsrr($domain)) { - return null; - } - - return $domain; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php deleted file mode 100644 index 0da49b85da869..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CustomerOrders; - -/** - * Prepares details based on registered user account info - */ -class UserAccountBuilder -{ - /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface - */ - private $customerRepository; - - /** - * @var \Magento\Framework\Intl\DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var CustomerOrders - */ - private $customerOrders; - - /** - * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository - * @param CustomerOrders $customerOrders - * @param \Magento\Framework\Intl\DateTimeFactory $dateTimeFactory - */ - public function __construct( - \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, - CustomerOrders $customerOrders, - \Magento\Framework\Intl\DateTimeFactory $dateTimeFactory - ) { - $this->customerRepository = $customerRepository; - $this->dateTimeFactory = $dateTimeFactory; - $this->customerOrders = $customerOrders; - } - - /** - * Returns user account data params. - * Only for registered customers. - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - - $customerId = $order->getCustomerId(); - if (null === $customerId) { - return $result; - } - - $customer = $this->customerRepository->getById($customerId); - $result = [ - 'userAccount' => [ - 'email' => $customer->getEmail(), - 'username' => $customer->getEmail(), - 'phone' => $order->getBillingAddress()->getTelephone(), - 'accountNumber' => $customerId, - 'createdDate' => $this->formatDate($customer->getCreatedAt()), - 'lastUpdateDate' => $this->formatDate($customer->getUpdatedAt()) - ] - ]; - - $ordersInfo = $this->customerOrders->getAggregatedOrdersInfo($customerId); - if (isset($ordersInfo['aggregateOrderCount'])) { - $result['userAccount']['aggregateOrderCount'] = $ordersInfo['aggregateOrderCount']; - } - if (isset($ordersInfo['aggregateOrderDollars'])) { - $result['userAccount']['aggregateOrderDollars'] = $ordersInfo['aggregateOrderDollars']; - } - - return $result; - } - - /** - * Returns date formatted according to ISO8601. - * - * @param string $date - * @return string - */ - private function formatDate($date) - { - $result = $this->dateTimeFactory->create( - $date, - new \DateTimeZone('UTC') - ); - - return $result->format(\DateTime::ATOM); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php deleted file mode 100644 index f77db737473c0..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -/** - * Webhooks are messages sent by SIGNIFYD via HTTP POST to a url you configure on your - * Notifications page in the SIGNIFYD settings. - * - * WebhookMessage messages are sent when certain events occur in the life of an investigation. - * They allow your application to receive pushed updates about a case, rather than poll SIGNIFYD for status changes. - * - * @see https://www.signifyd.com/docs/api/#/reference/webhooks - */ -class WebhookMessage -{ - /** - * Decoded webhook request body. - * - * @var array - */ - private $data; - - /** - * Event topic identifier. - * - * @var string - */ - private $eventTopic; - - /** - * @param array $data - * @param string $eventTopic - */ - public function __construct( - array $data, - $eventTopic - ) { - $this->data = $data; - $this->eventTopic = $eventTopic; - } - - /** - * Returns decoded webhook request body. - * - * @return array - */ - public function getData() - { - return $this->data; - } - - /** - * Returns event topic identifier. - * - * @return string - */ - public function getEventTopic() - { - return $this->eventTopic; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php deleted file mode 100644 index 50389102359b1..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; - -/** - * Reads request and produces webhook message data object based on request params. - */ -class WebhookMessageReader -{ - /** - * @var DecoderInterface - */ - private $dataDecoder; - - /** - * @var WebhookMessageFactory - */ - private $webhookMessageFactory; - - /** - * @param DecoderInterface $decoder - * @param WebhookMessageFactory $webhookMessageFactory - */ - public function __construct( - DecoderInterface $decoder, - WebhookMessageFactory $webhookMessageFactory - ) { - $this->dataDecoder = $decoder; - $this->webhookMessageFactory = $webhookMessageFactory; - } - - /** - * Returns webhook message data object. - * - * @param WebhookRequest $request - * @return WebhookMessage - * @throws \InvalidArgumentException - */ - public function read(WebhookRequest $request) - { - try { - $decodedData = $this->dataDecoder->decode($request->getBody()); - } catch (\Exception $e) { - throw new \InvalidArgumentException( - 'Webhook request body is not valid JSON: ' . $e->getMessage(), - $e->getCode(), - $e - ); - } - - $webhookMessage = $this->webhookMessageFactory->create( - [ - 'data' => $decodedData, - 'eventTopic' => $request->getEventTopic() - ] - ); - - return $webhookMessage; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php deleted file mode 100644 index 214ccf0eeb70f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\App\Request\Http; - -/** - * Reads Signifyd webhook request data. - */ -class WebhookRequest -{ - /** - * @var Http - */ - private $request; - - /** - * @param Http $request - */ - public function __construct( - Http $request - ) { - $this->request = $request; - } - - /** - * Returns Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message. - * - * @return string - */ - public function getHash() - { - return (string)$this->request->getHeader('X-SIGNIFYD-SEC-HMAC-SHA256'); - } - - /** - * Returns event topic identifier. - * - * @return string - */ - public function getEventTopic() - { - return (string)$this->request->getHeader('X-SIGNIFYD-TOPIC'); - } - - /** - * Returns raw data from the request body. - * - * @return string - */ - public function getBody() - { - return (string)$this->request->getContent(); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php deleted file mode 100644 index 274ef2f854684..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\Config; - -/** - * Validates webhook request. - * - */ -class WebhookRequestValidator -{ - /** - * Allowed topic identifiers which will be sent in the X-SIGNIFYD-TOPIC header of the webhook. - * - * @var array - */ - private $allowedTopicValues = [ - 'cases/creation', - 'cases/rescore', - 'cases/review', - 'guarantees/completion', - 'cases/test' - ]; - - /** - * @var Config - */ - private $config; - - /** - * @var DecoderInterface - */ - private $decoder; - - /** - * @param Config $config - * @param DecoderInterface $decoder - */ - public function __construct( - Config $config, - DecoderInterface $decoder - ) { - $this->config = $config; - $this->decoder = $decoder; - } - - /** - * Validates webhook request. - * - * @param WebhookRequest $webhookRequest - * @return bool - */ - public function validate(WebhookRequest $webhookRequest) - { - $body = $webhookRequest->getBody(); - $eventTopic = $webhookRequest->getEventTopic(); - $hash = $webhookRequest->getHash(); - - return $this->isValidTopic($eventTopic) - && $this->isValidBody($body) - && $this->isValidHash($eventTopic, $body, $hash); - } - - /** - * Checks if value of topic identifier is in allowed list - * - * @param string $topic topic identifier. - * @return bool - */ - private function isValidTopic($topic) - { - return in_array($topic, $this->allowedTopicValues); - } - - /** - * Verifies a webhook request body is valid JSON and not empty. - * - * @param string $body - * @return bool - */ - private function isValidBody($body) - { - try { - $decodedBody = $this->decoder->decode($body); - } catch (\Exception $e) { - return false; - } - - return !empty($decodedBody); - } - - /** - * Verifies a webhook request has in fact come from SIGNIFYD. - * - * @param string $eventTopic - * @param string $body - * @param string $hash - * @return bool - */ - private function isValidHash($eventTopic, $body, $hash) - { - // In the case that this is a webhook test, the encoding ABCDE is allowed - $apiKey = $eventTopic == 'cases/test' ? 'ABCDE' : $this->config->getApiKey(); - $actualHash = base64_encode(hash_hmac('sha256', $body, $apiKey, true)); - - return $hash === $actualHash; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php b/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php deleted file mode 100644 index 52746bc4ec6c5..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\DataObject\IdentityGeneratorInterface; - -/** - * Encapsulates generation of uuid by quote id. - */ -class SignifydOrderSessionId -{ - /** - * @var IdentityGeneratorInterface - */ - private $identityGenerator; - - /** - * @param IdentityGeneratorInterface $identityGenerator - */ - public function __construct( - IdentityGeneratorInterface $identityGenerator - ) { - $this->identityGenerator = $identityGenerator; - } - - /** - * Returns unique identifier through generation uuid by quote id. - * - * @param int $quoteId - * @return string - */ - public function get($quoteId) - { - return $this->identityGenerator->generateIdForData($quoteId); - } -} diff --git a/app/code/Magento/Signifyd/Observer/PlaceOrder.php b/app/code/Magento/Signifyd/Observer/PlaceOrder.php deleted file mode 100644 index 7c451a129cccd..0000000000000 --- a/app/code/Magento/Signifyd/Observer/PlaceOrder.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Observer; - -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Model\Config; -use Psr\Log\LoggerInterface; - -/** - * Observer should be triggered when new order is created and placed. - * If Signifyd integration enabled in configuration then new case will be created. - */ -class PlaceOrder implements ObserverInterface -{ - /** - * @var Config - */ - private $signifydIntegrationConfig; - - /** - * @var CaseCreationServiceInterface - */ - private $caseCreationService; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @param Config $signifydIntegrationConfig - * @param CaseCreationServiceInterface $caseCreationService - * @param LoggerInterface $logger - */ - public function __construct( - Config $signifydIntegrationConfig, - CaseCreationServiceInterface $caseCreationService, - LoggerInterface $logger - ) { - $this->signifydIntegrationConfig = $signifydIntegrationConfig; - $this->caseCreationService = $caseCreationService; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function execute(Observer $observer) - { - $orders = $this->extractOrders( - $observer->getEvent() - ); - - if (null === $orders) { - return; - } - - foreach ($orders as $order) { - $storeId = $order->getStoreId(); - if ($this->signifydIntegrationConfig->isActive($storeId)) { - $this->createCaseForOrder($order); - } - } - } - - /** - * Creates Signifyd case for single order with online payment method. - * - * @param OrderInterface $order - * @return void - */ - private function createCaseForOrder($order) - { - $orderId = $order->getEntityId(); - if (null === $orderId - || $order->getPayment()->getMethodInstance()->isOffline() - || $order->getState() === Order::STATE_PENDING_PAYMENT) { - return; - } - - try { - $this->caseCreationService->createForOrder($orderId); - } catch (AlreadyExistsException $e) { - $this->logger->error($e->getMessage()); - } - } - - /** - * Returns Orders entity list from Event data container - * - * @param Event $event - * @return OrderInterface[]|null - */ - private function extractOrders(Event $event) - { - $order = $event->getData('order'); - if (null !== $order) { - return [$order]; - } - - return $event->getData('orders'); - } -} diff --git a/app/code/Magento/Signifyd/Plugin/OrderPlugin.php b/app/code/Magento/Signifyd/Plugin/OrderPlugin.php deleted file mode 100644 index 663409d0eb824..0000000000000 --- a/app/code/Magento/Signifyd/Plugin/OrderPlugin.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; - -/** - * Plugin for Magento\Sales\Model\Order. - * - * @see Order - */ -class OrderPlugin -{ - /** - * @var GuaranteeCancelingServiceInterface - */ - private $guaranteeCancelingService; - - /** - * @param GuaranteeCancelingServiceInterface $guaranteeCancelingService - */ - public function __construct( - GuaranteeCancelingServiceInterface $guaranteeCancelingService - ) { - $this->guaranteeCancelingService = $guaranteeCancelingService; - } - - /** - * Performs Signifyd guarantee cancel operation after order canceling - * if cancel order operation was successful. - * - * @see Order::cancel - * @param Order $order - * @param OrderInterface $result - * @return OrderInterface - */ - public function afterCancel(Order $order, $result) - { - if ($order->isCanceled()) { - $this->guaranteeCancelingService->cancelForOrder( - $order->getEntityId() - ); - } - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php b/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php deleted file mode 100644 index 17cf4d7e7dbe9..0000000000000 --- a/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Payment\Model\InfoInterface; -use Magento\Payment\Model\MethodInterface; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; - -/** - * Plugin for Magento\Payment\Model\MethodInterface. - * - * @see MethodInterface - */ -class PaymentPlugin -{ - /** - * @var GuaranteeCancelingServiceInterface - */ - private $guaranteeCancelingService; - - /** - * @param GuaranteeCancelingServiceInterface $guaranteeCancelingService - */ - public function __construct( - GuaranteeCancelingServiceInterface $guaranteeCancelingService - ) { - $this->guaranteeCancelingService = $guaranteeCancelingService; - } - - /** - * Performs Signifyd guarantee cancel operation after payment denying. - * - * @see MethodInterface::denyPayment - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param MethodInterface $subject - * @param MethodInterface|bool $result - * @param InfoInterface $payment - * @return bool|MethodInterface - */ - public function afterDenyPayment(MethodInterface $subject, $result, InfoInterface $payment) - { - if ($this->isPaymentDenied($payment, $result)) { - $this->guaranteeCancelingService->cancelForOrder($payment->getParentId()); - } - - return $result; - } - - /** - * Checks if deny payment operation was successful. - * - * Result not false check for payment methods using AbstractMethod. - * Transaction is closed check for payment methods using Gateway. - * - * @param InfoInterface $payment - * @param MethodInterface $result - * @return bool - */ - private function isPaymentDenied($payment, $result) - { - return $result !== false || $payment->getIsTransactionClosed(); - } -} diff --git a/app/code/Magento/Signifyd/README.md b/app/code/Magento/Signifyd/README.md deleted file mode 100644 index 8048716bed444..0000000000000 --- a/app/code/Magento/Signifyd/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Magento_Signifyd module - -## Overview - -The Magento_Signifyd module provides integration with the [Signifyd](https://www.signifyd.com/) fraud protection system. The integration is based on the Signifyd API; see the [Signifyd API docs](https://www.signifyd.com/docs/api/#/introduction/) for technical details. - -The module implementation allows to: - - - create a [Signifyd case](https://www.signifyd.com/docs/api/#/reference/cases) for a placed order - - automatically receive a [Signifyd guarantee](https://www.signifyd.com/docs/api/#/reference/guarantees) for a created case - - automatically cancel a guarantee when the order is canceled - -## Extensibility - -The Magento_Signifyd module does not add own Events, Layouts, and UI Components as extension points. - -### Public API - -The following interfaces (marked with the `@api` annotation) provide methods that allow to: - -`Magento\Signifyd\Api\Data\CaseInterface` (common abstraction for the Signifyd case entity): - -- set or retrieve all case data fields - -`Magento\Signifyd\Api\CaseManagementInterface`: - -- create a new case entity -- retrieve the existing case entity for a specified order - -`Magento\Signifyd\Api\CaseCreationServiceInterface`: - -- create a case entity for a specified order -- send a request through the Signifyd API to create a new case - -`Magento\Signifyd\Api\CaseRepositoryInterface`: - -- describe methods to work with a case entity - -`Magento\Signifyd\Api\GuaranteeCreationServiceInterface`: - -- send a request through the Signifyd API to create a new case guarantee - -`Magento\Signifyd\Api\GuaranteeCancelingServiceInterface`: -- send a request through the Signifyd API to cancel the Signifyd case guarantee - -`Magento\Signifyd\Api\Data\CaseSearchResultsInterface`: - -- might be used by `Magento\Signifyd\Api\CaseRepositoryInterface` to retrieve a list of case entities by specific conditions - -For information about a public API in Magento 2, see [Public interfaces & APIs](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/api-concepts.html). - -## Additional information - -### Webhooks - -To update the entity data for a case or guarantee, the Magento_Signifyd module uses the [Signifyd Webhooks](https://www.signifyd.com/docs/api/#/reference/webhooks) mechanism. - -The newly created case entities have the `PENDING` status for a case and a guarantee. After receiving Webhook, both statuses are changed to appropriate Signifyd statuses. - -### Debug mode - -The Debug Mode may be enabled in the module configuration. This logs the communication data between the Magento_Signifyd module and the Signifyd service in this file: - - var/log/debug.log - -### Backward incompatible changes - -The Magento_Signifyd module does not introduce backward incompatible changes. - -You can track [backward incompatible changes in patch releases](https://devdocs.magento.com/guides/v2.3/release-notes/backward-incompatible-changes/reference.html). - -### Processing supplementary payment information - -To improve the accuracy of Signifyd's transaction estimation, you may perform these operations (links lead to the Magento Developer Documentation Portal): - -- [Provide custom AVS/CVV mapping](https://devdocs.magento.com/guides/v2.3/payments-integrations/signifyd/signifyd.html#provide-avscvv-response-codes) - -- [Retrieve payment method for a placed order](https://devdocs.magento.com/guides/v2.3/payments-integrations/signifyd/signifyd.html#retrieve-payment-method-for-a-placed-order) diff --git a/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt b/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml deleted file mode 100644 index 07b58b8594843..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminFraudProtectionPage" url="admin/system_config/edit/section/fraud_protection/" area="admin" module="Magento_Signifyd"> - <section name="AdminSignifydConfigurationSection"/> - </page> -</pages> diff --git a/app/code/Magento/Signifyd/Test/Mftf/README.md b/app/code/Magento/Signifyd/Test/Mftf/README.md deleted file mode 100644 index 9391d7b314ea5..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Signifyd Functional Tests - -The Functional Test Module for **Magento Signifyd** module. diff --git a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml deleted file mode 100644 index 618e9d520dd87..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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="AdminSignifydConfigurationSection"> - <element name="head" type="button" selector="#fraud_protection_signifyd_config-head"/> - <element name="enabled" type="input" selector="#fraud_protection_signifyd_config_active"/> - <element name="url" type="text" selector="#fraud_protection_signifyd_config_api_url"/> - </section> -</sections> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml deleted file mode 100644 index e3275d4097c63..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?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="AdminSignifydConfigDependentOnActiveFieldTest"> - <annotations> - <features value="Signifyd"/> - <stories value="Signify ID Settings"/> - <title value="Signifyd config dependent on active field" /> - <description value="Signifyd system configs dependent by Enable this Solution field."/> - <severity value="MINOR"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <magentoCLI command="config:set fraud_protection/signifyd/active 1" stepKey="enableSignifyd"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <magentoCLI command="config:set fraud_protection/signifyd/active 0" stepKey="disableSignifyd"/> - </after> - - <amOnPage url="{{AdminFraudProtectionPage.url}}" stepKey="openFraudProtectionPagePage" /> - <conditionalClick dependentSelector="{{AdminSignifydConfigurationSection.enabled}}" visible="false" selector="{{AdminSignifydConfigurationSection.head}}" stepKey="openCollapsibleBlock"/> - <seeInField selector="{{AdminSignifydConfigurationSection.url}}" userInput="https://api.signifyd.com/v2/" stepKey="seeApiUrlField"/> - <selectOption selector="{{AdminSignifydConfigurationSection.enabled}}" userInput="0" stepKey="disableSignifydOption"/> - <dontSeeElement selector="{{AdminSignifydConfigurationSection.url}}" stepKey="dontSeeApiUrlField"/> - </test> -</tests> diff --git a/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php b/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php deleted file mode 100644 index 164cd8018fb69..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Block\Adminhtml; - -use Magento\Framework\App\RequestInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Block\Adminhtml\CaseInfo; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Config; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Tests for Signifyd block information. - * - * Class CaseInfoTest - */ -class CaseInfoTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var CaseInfo - */ - private $caseInfo; - - /** - * @var Context|MockObject - */ - private $context; - - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var CaseManagement|MockObject - */ - private $caseManagement; - - /** - * @var RequestInterface|MockObject - */ - private $request; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->context = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder(RequestInterface::class) - ->getMockForAbstractClass(); - - $this->context->expects(self::once()) - ->method('getRequest') - ->willReturn($this->request); - - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getScore']) - ->getMockForAbstractClass(); - - $this->caseInfo = $objectManager->getObject(CaseInfo::class, [ - 'context' => $this->context, - 'config' => $this->config, - 'caseManagement' => $this->caseManagement - ]); - } - - /** - * Checks label according to Signifyd Guarantee Disposition. - * - * @param string $guaranteeDisposition - * @param string $expectedLabel - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseGuaranteeDisposition() - * @dataProvider getGuaranteeLabelDataProvider - */ - public function testGetGuaranteeDisposition($guaranteeDisposition, $expectedLabel) - { - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->willReturn($this->caseEntity); - - $this->caseEntity->expects(self::atLeastOnce()) - ->method('getGuaranteeDisposition') - ->willReturn($guaranteeDisposition); - - self::assertEquals( - $expectedLabel, - $this->caseInfo->getCaseGuaranteeDisposition() - ); - } - - /** - * Case Guarantee Disposition and corresponding label data provider. - * - * @return array - */ - public function getGuaranteeLabelDataProvider() - { - return [ - [CaseInterface::GUARANTEE_APPROVED, __('Approved')], - [CaseInterface::GUARANTEE_DECLINED, __('Declined')], - [CaseInterface::GUARANTEE_PENDING, __('Pending')], - [CaseInterface::GUARANTEE_CANCELED, __('Canceled')], - [CaseInterface::GUARANTEE_IN_REVIEW, __('In Review')], - [CaseInterface::GUARANTEE_UNREQUESTED, __('Unrequested')], - ['Unregistered', ''] - ]; - } - - /** - * Checks case property getter with empty case. - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseProperty - */ - public function testCasePropertyWithEmptyCase() - { - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->willReturn(null); - - self::assertEquals( - '', - $this->caseInfo->getCaseGuaranteeDisposition() - ); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php b/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php deleted file mode 100644 index 8b98be338b973..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php +++ /dev/null @@ -1,374 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Controller\Webhooks; - -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Response\Http as ResponseHttp; -use Magento\Framework\App\Response\RedirectInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Controller\Webhooks\Handler; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessage; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Class IndexTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class HandlerTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Handler - */ - private $controller; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var RedirectInterface|MockObject - */ - private $redirect; - - /** - * @var ResponseHttp|MockObject - */ - private $response; - - /** - * @var Context|MockObject - */ - private $context; - - /** - * @var WebhookRequest|MockObject - */ - private $webhookRequest; - - /** - * @var WebhookMessageReader|MockObject - */ - private $webhookMessageReader; - - /** - * @var WebhookRequestValidator|MockObject - */ - private $webhookRequestValidator; - - /** - * @var UpdatingServiceFactory|MockObject - */ - private $caseUpdatingServiceFactory; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->context = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookMessageReader = $this->getMockBuilder(WebhookMessageReader::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookRequestValidator = $this->getMockBuilder(WebhookRequestValidator::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseUpdatingServiceFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->response = $this->getMockBuilder(ResponseHttp::class) - ->disableOriginalConstructor() - ->getMock(); - $this->context->expects($this->once()) - ->method('getResponse') - ->willReturn($this->response); - $this->redirect = $this->getMockBuilder(RedirectInterface::class) - ->getMockForAbstractClass(); - $this->context->expects($this->once()) - ->method('getRedirect') - ->willReturn($this->redirect); - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByCaseId']) - ->getMockForAbstractClass(); - - $config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isDebugModeEnabled', 'getByCaseId']) - ->getMock(); - $config->expects(self::any()) - ->method('getByCaseId') - ->willReturn(false); - - $this->controller = new Handler( - $this->context, - $this->webhookRequest, - $this->logger, - $this->webhookMessageReader, - $this->caseUpdatingServiceFactory, - $this->webhookRequestValidator, - $this->caseRepository, - $config - ); - } - - /** - * Successful case - */ - public function testExecuteSuccessfully() - { - $eventTopic = 'cases/creation'; - $caseId = 1; - $data = ['score' => 200, 'caseId' => $caseId]; - - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->exactly(2)) - ->method('getEventTopic') - ->willReturn($eventTopic); - $webhookMessage->expects($this->once()) - ->method('getData') - ->willReturn($data); - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn($caseEntity); - - $caseUpdatingService = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - $caseUpdatingService->expects($this->once()) - ->method('update') - ->with($caseEntity, $data); - - $this->caseUpdatingServiceFactory->expects($this->once()) - ->method('create') - ->with($eventTopic) - ->willReturn($caseUpdatingService); - - $this->controller->execute(); - } - - /** - * Case when there is exception while updating case - */ - public function testExecuteCaseUpdatingServiceException() - { - $eventTopic = 'cases/creation'; - $caseId = 1; - $data = ['score' => 200, 'caseId' => $caseId]; - - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->exactly(2)) - ->method('getEventTopic') - ->willReturn($eventTopic); - $webhookMessage->expects($this->once()) - ->method('getData') - ->willReturn($data); - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn($caseEntity); - - $caseUpdatingService = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - $caseUpdatingService->expects($this->once()) - ->method('update') - ->with($caseEntity, $data) - ->willThrowException(new LocalizedException(__('Error'))); - - $this->caseUpdatingServiceFactory->expects($this->once()) - ->method('create') - ->with($eventTopic) - ->willReturn($caseUpdatingService); - - $this->response->expects($this->once()) - ->method('setHttpResponseCode') - ->with(400); - $this->logger->expects($this->once()) - ->method('critical'); - - $this->controller->execute(); - } - - /** - * Case when webhook request validation fails - */ - public function testExecuteRequestValidationFails() - { - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(false); - $this->redirect->expects($this->once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - $this->webhookMessageReader->expects($this->never()) - ->method('read'); - $this->caseUpdatingServiceFactory->expects($this->never()) - ->method('create'); - - $this->controller->execute(); - } - - /** - * Case when webhook request has test event topic. - */ - public function testExecuteWithTestEventTopic() - { - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - $this->redirect->expects($this->never()) - ->method('redirect'); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->once()) - ->method('getEventTopic') - ->willReturn('cases/test'); - $webhookMessage->expects($this->never()) - ->method('getData'); - - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->caseUpdatingServiceFactory->expects($this->never()) - ->method('create'); - - $this->controller->execute(); - } - - /** - * Checks a test case when received input data does not contain Signifyd case id. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - */ - public function testExecuteWithMissedCaseId() - { - $this->webhookRequestValidator->expects(self::once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->once()) - ->method('getEventTopic') - ->willReturn('cases/creation'); - $webhookMessage->expects(self::once()) - ->method('getData') - ->willReturn([ - 'orderId' => '1000101' - ]); - - $this->webhookMessageReader->expects(self::once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->redirect->expects(self::once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - - $this->controller->execute(); - } - - /** - * Checks a case when Signifyd case entity not found. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - */ - public function testExecuteWithNotFoundCaseEntity() - { - $caseId = 123; - - $this->webhookRequestValidator->expects(self::once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->setMethods(['getData']) - ->getMock(); - $webhookMessage->expects(self::once()) - ->method('getData') - ->willReturn([ - 'orderId' => '1000101', - 'caseId' => $caseId - ]); - - $this->webhookMessageReader->expects(self::once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn(null); - - $this->redirect->expects(self::once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - - $this->controller->execute(); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php deleted file mode 100644 index f0184c032b550..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\CaseServices; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\CaseServices\StubUpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests for case updating service factory. - */ -class UpdatingServiceFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var UpdatingServiceFactory - */ - private $factory; - - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @var GeneratorFactory|MockObject - */ - private $generatorFactory; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isActive']) - ->getMock(); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMockForAbstractClass(); - - $this->generatorFactory = $this->getMockBuilder(GeneratorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $objectManager = new ObjectManager($this); - $this->factory = $objectManager->getObject(UpdatingServiceFactory::class, [ - 'objectManager' => $this->fakeObjectManager, - 'generatorFactory' => $this->generatorFactory, - 'config' => $this->config - ]); - } - - /** - * Checks type of instance for updating service if Signifyd is not enabled. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreateWithInactiveConfig() - { - $type = 'cases/creation'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(false); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(StubUpdatingService::class) - ->willReturn(new StubUpdatingService()); - - $instance = $this->factory->create($type); - static::assertInstanceOf(StubUpdatingService::class, $instance); - } - - /** - * Checks type of instance for updating service if test type is received. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreateWithTestType() - { - $type = 'cases/test'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(StubUpdatingService::class) - ->willReturn(new StubUpdatingService()); - - $instance = $this->factory->create($type); - static::assertInstanceOf(StubUpdatingService::class, $instance); - } - - /** - * Checks exception type and message for unknown case type. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Specified message type does not supported. - */ - public function testCreateWithException() - { - $type = 'cases/unknown'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $this->generatorFactory->expects(self::once()) - ->method('create') - ->with($type) - ->willThrowException(new \InvalidArgumentException('Specified message type does not supported.')); - - $this->factory->create($type); - } - - /** - * Checks if factory creates correct instance of case updating service. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreate() - { - $type = 'case/creation'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $messageGenerator = $this->getMockBuilder(GeneratorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->generatorFactory->expects(self::once()) - ->method('create') - ->with($type) - ->willReturn($messageGenerator); - - $service = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(UpdatingService::class, ['messageGenerator' => $messageGenerator]) - ->willReturn($service); - - $result = $this->factory->create($type); - static::assertInstanceOf(UpdatingService::class, $result); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php deleted file mode 100644 index 6eb7e5c37d5fc..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php +++ /dev/null @@ -1,316 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\CaseServices; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\MessageGenerators\GeneratorException; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests with different negative and positive scenarios for case updating service. - */ -class UpdatingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var UpdatingService - */ - private $service; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var GeneratorInterface|MockObject - */ - private $messageGenerator; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @var CommentsHistoryUpdater|MockObject - */ - private $commentsHistoryUpdater; - - /** - * @var OrderGridUpdater|MockObject - */ - private $orderGridUpdater; - - /** - * @var OrderStateService|MockObject - */ - private $orderStateService; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->messageGenerator = $this->getMockBuilder(GeneratorInterface::class) - ->disableOriginalConstructor() - ->setMethods(['generate']) - ->getMock(); - - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByCaseId']) - ->getMockForAbstractClass(); - - $this->commentsHistoryUpdater = $this->getMockBuilder(CommentsHistoryUpdater::class) - ->disableOriginalConstructor() - ->setMethods(['addComment']) - ->getMock(); - - $this->orderGridUpdater = $this->getMockBuilder(OrderGridUpdater::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderStateService = $this->getMockBuilder(OrderStateService::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->getObject(UpdatingService::class, [ - 'messageGenerator' => $this->messageGenerator, - 'caseRepository' => $this->caseRepository, - 'commentsHistoryUpdater' => $this->commentsHistoryUpdater, - 'orderGridUpdater' => $this->orderGridUpdater, - 'orderStateService' => $this->orderStateService - ]); - } - - /** - * Checks a test case when Signifyd case is empty entity. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The case entity should not be empty. - */ - public function testUpdateWithEmptyCaseEntity() - { - $data = []; - $caseEntity = $this->withCaseEntity(null, 123, $data); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when Signifyd case id is not specified for a case entity. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The case entity should not be empty. - */ - public function testUpdateWithEmptyCaseId() - { - $data = [ - 'caseId' => 123 - ]; - $caseEntity = $this->withCaseEntity(1, null, $data); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks as test case when service cannot save Signifyd case entity - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithFailedCaseSaving() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'status' => CaseInterface::STATUS_OPEN, - 'orderId' => '10000012', - 'score' => 500 - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->willThrowException(new \Exception('Something wrong.')); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks as test case when message generator throws an exception - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithExceptionFromMessageGenerator() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::never()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntity); - - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willThrowException(new GeneratorException(__('Cannot generate message.'))); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when comments history updater throws an exception. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithFailedCommentSaving() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => 1 - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntity); - - $this->orderGridUpdater->expects(self::once()) - ->method('update') - ->with($data['orderId']); - - $message = __('Message is generated.'); - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willReturn($message); - - $this->commentsHistoryUpdater->expects(self::once()) - ->method('addComment') - ->with($caseEntity, $message) - ->willThrowException(new \Exception('Something wrong')); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when Signifyd case entity is successfully updated and message stored in comments history. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - */ - public function testUpdate() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => 1 - ]; - - $caseEntity = $this->withCaseEntity(21, $caseId, $data); - - $caseEntitySaved = clone $caseEntity; - $caseEntitySaved->expects(self::once()) - ->method('getGuaranteeDisposition') - ->willReturn('APPROVED'); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntitySaved); - - $message = __('Message is generated.'); - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willReturn($message); - - $this->orderGridUpdater->expects(self::once()) - ->method('update') - ->with($data['orderId']); - - $this->commentsHistoryUpdater->expects(self::once()) - ->method('addComment') - ->with($caseEntitySaved, $message); - - $this->orderStateService->expects(self::once()) - ->method('updateByCase') - ->with($caseEntitySaved); - - $this->service->update($caseEntity, $data); - } - - /** - * Create mock for case entity with common scenarios. - * - * @param $caseEntityId - * @param $caseId - * @param array $data - * @return CaseInterface|MockObject - */ - private function withCaseEntity($caseEntityId, $caseId, array $data = []) - { - /** @var CaseInterface|MockObject $caseEntity */ - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getEntityId', 'getCaseId', 'getOrderId', - 'setCaseId', 'setStatus', 'setOrderId', 'setScore' - ]) - ->getMockForAbstractClass(); - - $caseEntity->expects(self::any()) - ->method('getEntityId') - ->willReturn($caseEntityId); - $caseEntity->expects(self::any()) - ->method('getCaseId') - ->willReturn($caseId); - - foreach ($data as $property => $value) { - $method = 'set' . ucfirst($property); - if ($property === 'orderId') { - $caseEntity->expects(self::never()) - ->method($method); - } - $caseEntity->expects(self::any()) - ->method($method) - ->with(self::equalTo($value)) - ->willReturnSelf(); - - $method = 'get' . ucfirst($property); - $caseEntity->expects(self::any()) - ->method($method) - ->willReturn($value); - } - - return $caseEntity; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php deleted file mode 100644 index 5cbb3d8d93cdd..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Model\Order\Status\HistoryFactory; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Sales\Api\OrderStatusHistoryRepositoryInterface; - -/** - * Contains tests for comments history updater class. - */ -class CommentsHistoryUpdaterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 123; - - /** - * @var string - */ - private static $message = 'Case is created.'; - - /** - * @var string - */ - private static $status = 'On Hold'; - - /** - * @var CommentsHistoryUpdater - */ - private $updater; - - /** - * @var HistoryFactory|MockObject - */ - private $historyFactory; - - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var OrderStatusHistoryInterface|MockObject - */ - private $historyEntity; - - /** - * @var OrderStatusHistoryRepositoryInterface|MockObject - */ - private $historyRepository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->historyFactory = $this->getMockBuilder(HistoryFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create', 'save']) - ->getMock(); - - $this->historyRepository = $this->getMockBuilder(OrderStatusHistoryRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getOrderId']) - ->getMockForAbstractClass(); - - $this->initCommentMock(); - - $this->updater = $objectManager->getObject(CommentsHistoryUpdater::class, [ - 'historyFactory' => $this->historyFactory, - 'historyRepository' => $this->historyRepository - ]); - } - - /** - * Checks a test case when updater throws an exception while saving history comment. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - * @expectedException \Exception - */ - public function testAddCommentWithException() - { - $this->caseEntity->expects(self::once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->historyEntity->method('setStatus') - ->with('') - ->willReturnSelf(); - $this->historyRepository->expects(self::once()) - ->method('save') - ->with($this->historyEntity) - ->willThrowException(new \Exception('Cannot save comment message.')); - - $this->updater->addComment($this->caseEntity, __(self::$message)); - } - - /** - * Checks a test case when updater successfully saves history comment. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - */ - public function testAddComment() - { - $this->caseEntity->expects(self::once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->historyEntity->method('setStatus') - ->with(self::$status) - ->willReturnSelf(); - $this->historyRepository->expects(self::once()) - ->method('save') - ->with($this->historyEntity) - ->willReturnSelf(); - - $this->updater->addComment($this->caseEntity, __(self::$message), self::$status); - } - - /** - * Checks a test when message does not specified. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - */ - public function testAddCommentWithoutMessage() - { - $this->caseEntity->expects(self::never()) - ->method('getOrderId'); - - $this->historyFactory->expects(self::never()) - ->method('save'); - - $phrase = ''; - $this->updater->addComment($this->caseEntity, __($phrase)); - } - - /** - * Creates mock object for history entity. - * - * @return void - */ - private function initCommentMock() - { - $this->historyEntity = $this->getMockBuilder(OrderStatusHistoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setParentId', 'setComment', 'setEntityName', 'save']) - ->getMockForAbstractClass(); - - $this->historyFactory->method('create') - ->willReturn($this->historyEntity); - - $this->historyEntity->method('setParentId') - ->with(self::$orderId) - ->willReturnSelf(); - $this->historyEntity->method('setComment') - ->with(self::$message) - ->willReturnSelf(); - $this->historyEntity->method('setEntityName') - ->with('order') - ->willReturnSelf(); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php deleted file mode 100644 index 02d3b4b9ad7a7..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Directory\Model\Currency; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Sales\Api\Data\OrderSearchResultInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CustomerOrders; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Sales\Api\OrderRepositoryInterface; -use Psr\Log\LoggerInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CustomerOrdersTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $customerId = 1; - - /** - * Order amount in EUR - * @var int - */ - private static $eurAmount = 100; - - /** - * Order amount in UAH - * @var int - */ - private static $uahAmount = 270; - - /** - * Order amount in USD - * @var int - */ - private static $usdAmount = 50; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var FilterBuilder|MockObject - */ - private $filterBuilder; - - /** - * @var Currency|MockObject - */ - private $eurCurrency; - - /** - * @var Currency|MockObject - */ - private $uahCurrency; - - /** - * @var CustomerOrders - */ - private $model; - - /** - * @var OrderRepositoryInterface|MockObject - */ - private $orderRepository; - - /** - * @var SearchCriteriaBuilder|MockObject - */ - private $searchCriteriaBuilder; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = $this->objectManager->getObject(CustomerOrders::class, [ - 'filterBuilder' => $this->filterBuilder, - 'orderRepository' => $this->orderRepository, - 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, - 'logger' => $this->logger - ]); - - $this->initCurrencies(); - $this->initOrderRepository(); - - $this->objectManager->setBackwardCompatibleProperty( - $this->model, - 'currencies', - ['EUR' => $this->eurCurrency, 'UAH' => $this->uahCurrency] - ); - } - - /** - * @covers \Magento\Signifyd\Model\CustomerOrders::getAggregatedOrdersInfo() - */ - public function testGetCountAndTotalAmount() - { - $this->eurCurrency->expects($this->once()) - ->method('convert') - ->with(self::$eurAmount, 'USD') - ->willReturn(109); - - $this->uahCurrency->expects($this->once()) - ->method('convert') - ->with(self::$uahAmount, 'USD') - ->willReturn(10.35); - - $actual = $this->model->getAggregatedOrdersInfo(self::$customerId); - - static::assertEquals(3, $actual['aggregateOrderCount']); - static::assertEquals(169.35, $actual['aggregateOrderDollars']); - } - - /** - * Test case when required currency rate is absent and exception is thrown - * @covers \Magento\Signifyd\Model\CustomerOrders::getAggregatedOrdersInfo() - */ - public function testGetCountAndTotalAmountNegative() - { - $this->eurCurrency->expects($this->once()) - ->method('convert') - ->with(self::$eurAmount, 'USD') - ->willReturn(109); - - $this->uahCurrency->expects($this->once()) - ->method('convert') - ->with(self::$uahAmount, 'USD') - ->willThrowException(new \Exception()); - - $this->logger->expects($this->once()) - ->method('error'); - - $actual = $this->model->getAggregatedOrdersInfo(self::$customerId); - - $this->assertNull($actual['aggregateOrderCount']); - $this->assertNull($actual['aggregateOrderDollars']); - } - - /** - * Populate order repository with mocked orders - */ - private function initOrderRepository() - { - $this->filterBuilder->expects($this->once()) - ->method('setField') - ->willReturnSelf(); - $this->filterBuilder->expects($this->once()) - ->method('setValue') - ->willReturnSelf(); - $filter = $this->getMockBuilder(\Magento\Framework\Api\Filter::class) - ->disableOriginalConstructor() - ->getMock(); - $this->filterBuilder->expects($this->once()) - ->method('create') - ->willReturn($filter); - - $searchCriteria = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteria::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchCriteriaBuilder->expects($this->once()) - ->method('create') - ->willReturn($searchCriteria); - - $orderSearchResult = $this->getMockBuilder(OrderSearchResultInterface::class) - ->getMockForAbstractClass(); - $orderSearchResult->expects($this->once()) - ->method('getItems') - ->willReturn($this->getOrders()); - $this->orderRepository->expects($this->once()) - ->method('getList') - ->willReturn($orderSearchResult); - } - - /** - * Creates mocks for currencies - * @return void - */ - private function initCurrencies() - { - $this->eurCurrency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['convert']) - ->getMock(); - - $this->uahCurrency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['convert']) - ->getMock(); - } - - /** - * Get list of mocked orders with different currencies - * @return array - */ - private function getOrders() - { - $eurOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $eurOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$eurAmount); - $eurOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('EUR'); - - $uahOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $uahOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$uahAmount); - $uahOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('UAH'); - - $usdOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $usdOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$usdAmount); - $usdOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('USD'); - - return [$usdOrder, $eurOrder, $uahOrder]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php deleted file mode 100644 index f7b4e473a0ec8..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseEntity; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Guarantee\CancelGuaranteeAbility; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CancelGuaranteeAbilityTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderRepository; - - /** - * @var CaseManagement|\PHPUnit_Framework_MockObject_MockObject - */ - private $caseManagement; - - /** - * @var CancelGuaranteeAbility - */ - private $cancelGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->cancelGuaranteeAbility = new CancelGuaranteeAbility( - $this->caseManagement, - $this->orderRepository - ); - } - - /** - * Success test for Cancel Guarantee Request button - */ - public function testIsAvailableSuccess() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_APPROVED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertTrue($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Case entity doesn't exist for order - */ - public function testIsAvailableWithNullCase() - { - $orderId = 123; - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn(null); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Guarantee Disposition has Canceled states. - */ - public function testIsAvailableWithCanceledGuarantee() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_CANCELED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when order does not exist. - */ - public function testIsAvailableWithNullOrder() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_APPROVED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willThrowException(new NoSuchEntityException()); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php deleted file mode 100644 index f8f1d4a4522c9..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php +++ /dev/null @@ -1,215 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\StubUpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Guarantee\CancelGuaranteeAbility; -use Magento\Signifyd\Model\Guarantee\CancelingService; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains test cases for Signifyd guarantee canceling service. - */ -class CancelingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 23; - - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var CancelingService - */ - private $service; - - /** - * @var CaseManagementInterface|MockObject - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory|MockObject - */ - private $updatingFactory; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var CancelGuaranteeAbility|MockObject - */ - private $guaranteeAbility; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->caseManagement = $this->getMockBuilder(CaseManagementInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByOrderId']) - ->getMockForAbstractClass(); - - $this->updatingFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['cancelGuarantee']) - ->getMock(); - - $this->guaranteeAbility = $this->getMockBuilder(CancelGuaranteeAbility::class) - ->disableOriginalConstructor() - ->setMethods(['isAvailable']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['error']) - ->getMockForAbstractClass(); - - $this->service = new CancelingService( - $this->caseManagement, - $this->updatingFactory, - $this->gateway, - $this->guaranteeAbility, - $this->logger - ); - } - - /** - * Checks a test case, when validation for a guarantee is failed. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrderWithUnavailableDisposition() - { - $this->guaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo(self::$orderId)) - ->willReturn(false); - - $this->caseManagement->expects(self::never()) - ->method('getByOrderId'); - - $this->gateway->expects(self::never()) - ->method('cancelGuarantee'); - - $this->logger->expects(self::never()) - ->method('error'); - - $this->updatingFactory->expects(self::never()) - ->method('create'); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to Signifyd API fails. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrderWithFailedRequest() - { - $this->withCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willThrowException(new GatewayException('Something wrong.')); - - $this->logger->expects(self::once()) - ->method('error') - ->with(self::equalTo('Something wrong.')); - - $this->updatingFactory->expects(self::never()) - ->method('create'); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to Signifyd successfully processed and case entity has been updated. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrder() - { - $case = $this->withCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willReturn(CaseInterface::GUARANTEE_CANCELED); - - $this->logger->expects(self::never()) - ->method('error'); - - $service = $this->getMockBuilder(StubUpdatingService::class) - ->setMethods(['update']) - ->getMock(); - $this->updatingFactory->expects(self::once()) - ->method('create') - ->willReturn($service); - - $service->expects(self::once()) - ->method('update') - ->with(self::equalTo($case), self::equalTo(['guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED])); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertTrue($result); - } - - /** - * Gets mock for a case entity. - * - * @return CaseInterface|MockObject - */ - private function withCaseEntity() - { - $this->guaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo(self::$orderId)) - ->willReturn(true); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getCaseId']) - ->getMockForAbstractClass(); - - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->with(self::equalTo(self::$orderId)) - ->willReturn($caseEntity); - - $caseEntity->expects(self::once()) - ->method('getCaseId') - ->willReturn(self::$caseId); - return $caseEntity; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php deleted file mode 100644 index 6b7a6112a932e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php +++ /dev/null @@ -1,260 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Guarantee\CreateGuaranteeAbility; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateGuaranteeAbilityTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderRepository; - - /** - * @var CaseManagement|\PHPUnit_Framework_MockObject_MockObject - */ - private $caseManagement; - - /** - * @var CreateGuaranteeAbility - */ - private $createGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->dateTimeFactory = new DateTimeFactory(); - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->createGuaranteeAbility = new CreateGuaranteeAbility( - $this->caseManagement, - $this->orderRepository, - $this->dateTimeFactory - ); - } - - public function testIsAvailableSuccess() - { - $orderId = 123; - $orderCreatedAt = $this->getDateAgo(6); - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn(Order::STATE_COMPLETE); - $order->expects($this->once()) - ->method('getCreatedAt') - ->willReturn($orderCreatedAt); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertTrue($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Case entity doesn't exist for order - */ - public function testIsAvailableWithNullCase() - { - $orderId = 123; - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn(null); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when GuaranteeEligible for Case is false - */ - public function testIsAvailableWithGuarantyEligibleFalse() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(false); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when GuaranteeEligible for Case is false - */ - public function testIsAvailableWithNullOrder() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willThrowException(new NoSuchEntityException()); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when order has Canceled Or Closed states. - * - * @param string $state - * @dataProvider isAvailableWithCanceledOrderDataProvider - */ - public function testIsAvailableWithCanceledOrder($state) - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn($state); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * @return array - */ - public function isAvailableWithCanceledOrderDataProvider() - { - return [ - [Order::STATE_CANCELED], [Order::STATE_CLOSED] - ]; - } - - public function testIsAvailableWithOldOrder() - { - $orderId = 123; - $orderCreatedAt = $this->getDateAgo(8); - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn(Order::STATE_COMPLETE); - $order->expects($this->once()) - ->method('getCreatedAt') - ->willReturn($orderCreatedAt); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Returns date N days ago - * - * @param int $days number of days that will be deducted from the current date - * @return string - */ - private function getDateAgo($days) - { - $createdAtTime = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - $createdAtTime->sub(new \DateInterval('P' . $days . 'D')); - - return $createdAtTime->format('Y-m-d h:i:s'); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php deleted file mode 100644 index db64b38375fe1..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php +++ /dev/null @@ -1,253 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceInterface; -use Magento\Signifyd\Model\Guarantee\CreateGuaranteeAbility; -use Magento\Signifyd\Model\Guarantee\CreationService; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use \PHPUnit\Framework\TestCase as TestCase; -use Psr\Log\LoggerInterface; - -class CreationServiceTest extends TestCase -{ - /** - * @var CreationService|MockObject - */ - private $service; - - /** - * @var CaseManagementInterface|MockObject - */ - private $caseManagement; - - /** - * @var UpdatingServiceInterface|MockObject - */ - private $caseUpdatingService; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CreateGuaranteeAbility|MockObject - */ - private $createGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->caseManagement = $this->getMockBuilder(CaseManagementInterface::class) - ->getMockForAbstractClass(); - - $caseUpdatingServiceFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseUpdatingService = $this->getMockBuilder(UpdatingServiceInterface::class) - ->getMockForAbstractClass(); - $caseUpdatingServiceFactory - ->method('create') - ->willReturn($this->caseUpdatingService); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->createGuaranteeAbility = $this->getMockBuilder(CreateGuaranteeAbility::class) - ->disableOriginalConstructor() - ->setMethods(['isAvailable']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->service = new CreationService( - $this->caseManagement, - $caseUpdatingServiceFactory, - $this->gateway, - $this->createGuaranteeAbility, - $this->logger - ); - } - - /** - * Checks a test case, when guarantee ability checker does not allow to submit case for a guarantee. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - */ - public function testCreateForOrderWithNotEligibleCase() - { - $orderId = 1; - - $this->createGuaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with($orderId) - ->willReturn(false); - - $this->caseManagement->expects(self::never()) - ->method('getByOrderId'); - - $this->gateway->expects(self::never()) - ->method('submitCaseForGuarantee'); - - $result = $this->service->createForOrder($orderId); - self::assertFalse($result); - } - - public function testCreateForOrderWitCase() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - - $this->gateway - ->expects($this->once()) - ->method('submitCaseForGuarantee'); - - $this->service->createForOrder($dummyOrderId); - } - - public function testCreateForOrderWithGatewayFailure() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGatewayFailureMessage = 'Everything fails sometimes'; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewayFailure($dummyGatewayFailureMessage); - - $this->logger - ->expects($this->once()) - ->method('error') - ->with($this->equalTo($dummyGatewayFailureMessage)); - $this->caseUpdatingService - ->expects($this->never()) - ->method('update'); - - $result = $this->service->createForOrder($dummyOrderId); - $this->assertEquals( - false, - $result, - 'Service should return false in case of gateway failure' - ); - } - - public function testCreateForOrderWithGatewaySuccess() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGuaranteeDisposition = 'foo'; - $caseEntity = $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewaySuccess($dummyGuaranteeDisposition); - - $this->caseUpdatingService - ->expects($this->once()) - ->method('update') - ->with($caseEntity, $this->equalTo([ - 'caseId' => $dummyCaseId, - 'guaranteeDisposition' => $dummyGuaranteeDisposition, - ])); - - $this->service->createForOrder($dummyOrderId); - } - - public function testCreateForOrderWithCaseUpdate() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGuaranteeDisposition = 'foo'; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewaySuccess($dummyGuaranteeDisposition); - - $result = $this->service->createForOrder($dummyOrderId); - $this->assertEquals( - true, - $result, - 'Service should return true in case if case update service is called' - ); - } - - /** - * @param $orderId - * @param array $caseData - * @return MockObject - */ - private function withCaseEntityExistsForOrderId($orderId, array $caseData = []) - { - $this->createGuaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo($orderId)) - ->willReturn(true); - - $dummyCaseEntity = $this->getMockBuilder(CaseInterface::class) - ->getMockForAbstractClass(); - foreach ($caseData as $caseProperty => $casePropertyValue) { - $dummyCaseEntity - ->method('get' . ucfirst($caseProperty)) - ->willReturn($casePropertyValue); - } - - $this->caseManagement - ->method('getByOrderId') - ->with($this->equalTo($orderId)) - ->willReturn($dummyCaseEntity); - - return $dummyCaseEntity; - } - - /** - * @param $failureMessage - */ - private function withGatewayFailure($failureMessage) - { - $this->gateway - ->method('submitCaseForGuarantee') - ->willThrowException(new GatewayException($failureMessage)); - } - - /** - * @param $gatewayResult - */ - private function withGatewaySuccess($gatewayResult) - { - $this->gateway - ->method('submitCaseForGuarantee') - ->willReturn($gatewayResult); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php deleted file mode 100644 index ba14036cd68d0..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\MessageGenerators\CaseRescore; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Tests for Signifyd CaseRescore message generator. - * - * Class CaseRescoreTest - */ -class CaseRescoreTest extends \PHPUnit\Framework\TestCase -{ - private static $data = [ - 'caseId' => 100, - 'score' => 200 - ]; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @var CaseRescore|MockObject - */ - private $caseRescore; - - /** - * @var CaseInterface|MockObject - */ - private $case; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManager = new ObjectManager($this); - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - } - - /** - * Data array without required attribute caseId. - * - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage The "caseId" should not be empty - */ - public function testGenerateEmptyCaseIdException() - { - $this->caseRescore->generate([]); - } - - /** - * Case entity was not found in DB. - * - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage Case entity not found. - */ - public function testGenerateNotFoundException() - { - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn(null); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $this->caseRescore->generate(self::$data); - } - - /** - * Generate case message with not empty previous score. - */ - public function testGenerateWithPreviousScore() - { - $this->case->expects($this->once()) - ->method('getScore') - ->willReturn(self::$data['score']); - - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn($this->case); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $phrase = __( - 'Case Update: New score for the order is %1. Previous score was %2.', - self::$data['score'], - self::$data['score'] - ); - - $message = $this->caseRescore->generate(self::$data); - - $this->assertEquals($phrase, $message); - } - - /** - * Generate case message with empty previous score. - */ - public function testGenerateWithoutPreviousScore() - { - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn($this->case); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $phrase = __( - 'Case Update: New score for the order is %1. Previous score was %2.', - self::$data['score'], - null - ); - - $message = $this->caseRescore->generate(self::$data); - - $this->assertEquals($phrase, $message); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php deleted file mode 100644 index 50f87df3b694f..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\MessageGenerators\PatternGenerator; -use Magento\Signifyd\Model\MessageGenerators\CaseRescore; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests for messages generators factory. - */ -class GeneratorFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var GeneratorFactory - */ - private $factory; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMockForAbstractClass(); - - $this->factory = $objectManager->getObject(GeneratorFactory::class, [ - 'objectManager' => $this->fakeObjectManager - ]); - } - - /** - * Checks if factory returns correct instance of message generator. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\GeneratorFactory::create - * @param string $type - * @param string $className - * @dataProvider typeDataProvider - */ - public function testCreate($type, $className) - { - $generator = $this->getMockBuilder($className) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with($className) - ->willReturn($generator); - - $instance = $this->factory->create($type); - self::assertInstanceOf($className, $instance); - } - - /** - * Get list of available messages generators types and equal class names. - * - * @return array - */ - public function typeDataProvider() - { - return [ - ['cases/creation', PatternGenerator::class], - ['cases/review', PatternGenerator::class], - ['cases/rescore', CaseRescore::class], - ['guarantees/completion', PatternGenerator::class], - ['guarantees/creation', PatternGenerator::class], - ['guarantees/cancel', PatternGenerator::class], - ]; - } - - /** - * Checks correct exception message for unknown type of message generator. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\GeneratorFactory::create - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Specified message type does not supported. - */ - public function testCreateWithException() - { - $type = 'cases/unknown'; - $this->factory->create($type); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php deleted file mode 100644 index 9d5f71f657a1e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Signifyd\Model\MessageGenerators\PatternGenerator; - -/** - * Contains tests for different variations like empty data, wrong required arguments, or bad placeholders. - */ -class PatternGeneratorTest extends \PHPUnit\Framework\TestCase -{ - /** - * Checks an exception if generators does not receives required data. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\PatternGenerator::generate - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage The "caseId" should not be empty. - */ - public function testGenerateThrowsException() - { - $data = []; - $generator = new PatternGenerator('Signifyd Case %1 has been created for order.', ['caseId']); - $generator->generate($data); - } - - /** - * Checks cases with different template placeholders and input data. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\PatternGenerator::generate - * @param string $template - * @param array $requiredFields - * @param string $expected - * @dataProvider messageDataProvider - */ - public function testGenerate($template, array $requiredFields, $expected) - { - $data = [ - 'caseId' => 123, - 'reviewDisposition' => 'Good', - 'guaranteeDisposition' => 'Approved', - 'score' => 500, - 'case_score' => 300 - ]; - - $generator = new PatternGenerator($template, $requiredFields); - $actual = $generator->generate($data); - self::assertEquals($expected, $actual); - } - - /** - * Get list of variations with message templates, required fields and expected generated messages. - * - * @return array - */ - public function messageDataProvider() - { - return [ - [ - 'Signifyd Case %1 has been created for order.', - ['caseId'], - 'Signifyd Case 123 has been created for order.' - ], - [ - 'Case Update: Case Review was completed. Review Deposition is %1.', - ['reviewDisposition'], - 'Case Update: Case Review was completed. Review Deposition is Good.' - ], - [ - 'Case Update: New score for the order is %1. Previous score was %2.', - ['score', 'case_score'], - 'Case Update: New score for the order is 500. Previous score was 300.' - ], - [ - 'Case Update: Case is submitted for guarantee.', - [], - 'Case Update: Case is submitted for guarantee.' - ], - ]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php deleted file mode 100644 index 3a567a79891f8..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php +++ /dev/null @@ -1,204 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\OrderFactory; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\OrderStateService; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -class OrderStateServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 123; - - /** - * @var OrderFactory|MockObject - */ - private $orderFactory; - - /** - * @var OrderManagementInterface|MockObject - */ - private $orderManagement; - - /** - * @var CommentsHistoryUpdater|MockObject - */ - private $commentsHistoryUpdater; - - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var Order|MockObject - */ - private $order; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->orderManagement = $this->getMockBuilder(OrderManagementInterface::class) - ->getMockForAbstractClass(); - - $this->commentsHistoryUpdater = $this->getMockBuilder(CommentsHistoryUpdater::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderFactory = $this->getMockBuilder(OrderFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->order = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); - $this->order->expects($this->once()) - ->method('load') - ->willReturnSelf(); - - $this->orderFactory->expects($this->once()) - ->method('create') - ->willReturn($this->order); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->caseEntity->expects($this->once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->orderStateService = new OrderStateService( - $this->orderFactory, - $this->orderManagement, - $this->commentsHistoryUpdater - ); - } - - /** - * Tests update order state flow when case guarantee disposition is PENDING. - * - * @param bool $canHold - * @param bool $hold - * @param int $addCommentCall - * @dataProvider updateByCaseWithGuaranteePendingDataProvider - */ - public function testUpdateByCaseWithGuaranteePending($canHold, $hold, $addCommentCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_PENDING); - $this->order->expects($this->any()) - ->method('canHold') - ->willReturn($canHold); - $this->orderManagement->expects($this->any()) - ->method('hold') - ->willReturn($hold); - $this->commentsHistoryUpdater->expects($this->exactly($addCommentCall)) - ->method('addComment') - ->with( - $this->caseEntity, - __('Awaiting the Signifyd guarantee disposition.'), - Order::STATE_HOLDED - ); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteePendingDataProvider() - { - return [ - ['canHold' => true, 'hold' => true, 'addCommentCall' => 1], - ['canHold' => false, 'hold' => true, 'addCommentCall' => 0], - ['canHold' => true, 'hold' => false, 'addCommentCall' => 0], - ]; - } - - /** - * Tests update order state flow when case guarantee disposition is APPROVED. - * - * @param bool $canUnhold - * @param int $unholdCall - * @dataProvider updateByCaseWithGuaranteeApprovedDataProvider - */ - public function testUpdateByCaseWithGuaranteeApproved($canUnhold, $unholdCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_APPROVED); - $this->order->expects($this->any()) - ->method('canUnhold') - ->willReturn($canUnhold); - $this->orderManagement->expects($this->exactly($unholdCall)) - ->method('unHold'); - $this->commentsHistoryUpdater->expects($this->never()) - ->method('addComment'); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteeApprovedDataProvider() - { - return [ - ['canUnhold' => true, 'unholdCall' => 1], - ['canUnhold' => false, 'unholdCall' => 0] - ]; - } - - /** - * Tests update order state flow when case guarantee disposition is DECLINED. - * - * @param bool $canHold - * @param int $holdCall - * @dataProvider updateByCaseWithGuaranteeDeclinedDataProvider - */ - public function testUpdateByCaseWithGuaranteeDeclined($canHold, $holdCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_DECLINED); - $this->order->expects($this->any()) - ->method('canHold') - ->willReturn($canHold); - $this->orderManagement->expects($this->exactly($holdCall)) - ->method('hold'); - $this->commentsHistoryUpdater->expects($this->never()) - ->method('addComment'); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteeDeclinedDataProvider() - { - return [ - ['canHold' => true, 'holdCall' => 1], - ['canHold' => false, 'holdCall' => 0] - ]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php deleted file mode 100644 index 319229e326c4b..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\PaymentMethodMapper; - -use Magento\Signifyd\Model\PaymentMethodMapper\XmlToArrayConfigConverter; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\Config\Dom\ValidationSchemaException; - -class XmlToArrayConfigConverterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var XmlToArrayConfigConverter - */ - private $converter; - - /** - * @var string - */ - private $filePath; - - public function setUp() - { - $this->filePath = realpath(__DIR__) . '/_files/'; - - $objectManagerHelper = new ObjectManager($this); - $this->converter = $objectManagerHelper->getObject( - XmlToArrayConfigConverter::class - ); - } - - public function testConvert() - { - $testDom = $this->filePath . 'signifyd_payment_mapping.xml'; - $dom = new \DOMDocument(); - $dom->load($testDom); - $mapping = $this->converter->convert($dom); - $expectedArray = include $this->filePath . 'expected_array.php'; - - $this->assertEquals($expectedArray, $mapping); - } - - /** - * @expectedException \Magento\Framework\Config\Dom\ValidationSchemaException - * @expectedExceptionMessage Only single entrance of "magento_code" node is required. - */ - public function testConvertEmptyPaymentMethodException() - { - $dom = new \DOMDocument(); - $element = $dom->createElement('payment_method'); - $subelement = $dom->createElement('signifyd_code', 'test'); - $element->appendChild($subelement); - $dom->appendChild($element); - - $this->converter->convert($dom); - } - - /** - * @expectedException \Magento\Framework\Config\Dom\ValidationSchemaException - * @expectedExceptionMessage Not empty value for "signifyd_code" node is required. - */ - public function testConvertEmptySygnifydPaymentMethodException() - { - $dom = new \DOMDocument(); - $element = $dom->createElement('payment_method'); - $subelement = $dom->createElement('magento_code', 'test'); - $subelement2 = $dom->createElement('signifyd_code', ''); - $element->appendChild($subelement); - $element->appendChild($subelement2); - $dom->appendChild($element); - - $this->converter->convert($dom); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php deleted file mode 100644 index f5d3436ae6b7b..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -return [ - 'payment_method_1' => 'PAYMENT_CARD', - 'payment_method_2' => 'PAYPAL_ACCOUNT', - 'payment_method_3' => 'CHECK', - 'payment_method_4' => 'CASH', - 'payment_method_5' => 'FREE' -]; diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml deleted file mode 100644 index f70763e22c418..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Signifyd:etc/payment_mapping.xsd"> - <payment_method_list> - <payment_method> - <magento_code>payment_method_1</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_2</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_3</magento_code> - <signifyd_code>CHECK</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_4</magento_code> - <signifyd_code>CASH</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_5</magento_code> - <signifyd_code>FREE</signifyd_code> - </payment_method> - </payment_method_list> -</config> diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php deleted file mode 100644 index b0f9239d43bfa..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Signifyd\Model\PaymentVerificationFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Payment\Gateway\ConfigInterface; - -class PaymentVerificationFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var PaymentVerificationFactory - */ - private $factory; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @var ConfigInterface|MockObject - */ - private $config; - - /** - * @var PaymentVerificationInterface|MockObject - */ - private $avsDefaultAdapter; - - /** - * @var PaymentVerificationInterface|MockObject - */ - private $cvvDefaultAdapter; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->config = $this->getMockBuilder(ConfigInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->avsDefaultAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->cvvDefaultAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->factory = $this->objectManager->getObject(PaymentVerificationFactory::class, [ - 'objectManager' => $this->fakeObjectManager, - 'config' => $this->config, - 'avsDefaultAdapter' => $this->avsDefaultAdapter, - 'cvvDefaultAdapter' => $this->cvvDefaultAdapter - ]); - } - - /** - * Checks a test case when factory creates CVV mapper for provided payment method. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - */ - public function testCreatePaymentCvv() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(PaymentVerificationInterface::class); - - /** @var PaymentVerificationInterface|MockObject $cvvAdapter */ - $cvvAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(PaymentVerificationInterface::class)) - ->willReturn($cvvAdapter); - - $mapper = $this->factory->createPaymentCvv($paymentMethodCode); - self::assertInstanceOf(PaymentVerificationInterface::class, $mapper); - } - - /** - * Checks a test case, when provided payment method does not have cvv mapper. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - */ - public function testCreateDefaultCvvMapper() - { - $paymentMethodCode = 'non_exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(null); - - $this->fakeObjectManager->expects(self::never()) - ->method('create'); - - $mapper = $this->factory->createPaymentCvv($paymentMethodCode); - self::assertSame($this->cvvDefaultAdapter, $mapper); - } - - /** - * Checks a test case, when mapper implementation does not corresponding to PaymentVerificationInterface. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - * @expectedException \Magento\Framework\Exception\ConfigurationMismatchException - * @expectedExceptionMessage stdClass must implement Magento\Payment\Api\PaymentVerificationInterface - */ - public function testCreateWithUnsupportedImplementation() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(\stdClass::class); - - $cvvAdapter = new \stdClass(); - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(\stdClass::class)) - ->willReturn($cvvAdapter); - - $this->factory->createPaymentCvv($paymentMethodCode); - } - - /** - * Checks a test case when factory creates AVS mapper for provided payment method. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentAvs - */ - public function testCreatePaymentAvs() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('avs_ems_adapter') - ->willReturn(PaymentVerificationInterface::class); - - $avsAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(PaymentVerificationInterface::class)) - ->willReturn($avsAdapter); - - $mapper = $this->factory->createPaymentAvs($paymentMethodCode); - self::assertInstanceOf(PaymentVerificationInterface::class, $mapper); - } - - /** - * Checks a test case when provided payment method does not support - */ - public function testCreateDefaultAvsMapper() - { - $paymentMethodCode = 'non_exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('avs_ems_adapter') - ->willReturn(null); - - $this->fakeObjectManager->expects(self::never()) - ->method('create'); - - $mapper = $this->factory->createPaymentAvs($paymentMethodCode); - self::assertSame($this->avsDefaultAdapter, $mapper); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php deleted file mode 100644 index 885c9f018a488..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SalesOrderGrid; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\ResourceModel\GridInterface; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -class OrderGridUpdaterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var GridInterface|MockObject - */ - private $orderGrid; - - /** - * @var ScopeConfigInterface|MockObject - */ - private $globalConfig; - - /** - * @var OrderGridUpdater - */ - private $model; - - /** - * Sets up testing class and dependency mocks. - */ - protected function setUp() - { - $this->orderGrid = $this->getMockBuilder(GridInterface::class) - ->getMockForAbstractClass(); - $this->globalConfig = $this->getMockBuilder(ScopeConfigInterface::class) - ->getMockForAbstractClass(); - - $this->model = new OrderGridUpdater($this->orderGrid, $this->globalConfig); - } - - public function testUpdateInSyncMode() - { - $orderId = 1; - - $this->globalConfig->expects($this->once()) - ->method('getValue') - ->with('dev/grid/async_indexing', 'default', null) - ->willReturn(false); - $this->orderGrid->expects($this->once()) - ->method('refresh') - ->with($orderId); - - $this->model->update($orderId); - } - - public function testUpdateInAsyncMode() - { - $orderId = 1; - - $this->globalConfig->expects($this->once()) - ->method('getValue') - ->with('dev/grid/async_indexing', 'default', null) - ->willReturn(true); - $this->orderGrid->expects($this->never()) - ->method('refresh') - ->with($orderId); - - $this->model->update($orderId); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php deleted file mode 100644 index 4aefd63355773..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Client\HttpClientFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\Config; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Json\EncoderInterface; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; - -class HttpClientFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private static $dummy = 'dummy'; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Config - */ - private $config; - - /** - * @var ZendClientFactory|MockObject - */ - private $clientFactory; - - /** - * @var ZendClient|MockObject - */ - private $client; - - /** - * @var EncoderInterface|MockObject - */ - private $dataEncoder; - - /** - * @var ZendClient|MockObject - */ - private $httpClient; - - public function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->client = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['setHeaders', 'setMethod', 'setUri', 'setRawData']) - ->getMock(); - - $this->clientFactory = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->clientFactory->expects($this->once()) - ->method('create') - ->willReturn($this->client); - - $this->dataEncoder = $this->getMockBuilder(EncoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->httpClient = $this->objectManager->getObject(HttpClientFactory::class, [ - 'config' => $this->config, - 'clientFactory' => $this->clientFactory, - 'dataEncoder' => $this->dataEncoder - ]); - } - - public function testCreateHttpClient() - { - $this->config->expects($this->once()) - ->method('getApiKey') - ->willReturn('testKey'); - - $this->config->expects($this->once()) - ->method('getApiUrl') - ->willReturn('testUrl'); - - $client = $this->httpClient->create('url', 'method'); - - $this->assertInstanceOf(ZendClient::class, $client); - } - - public function testCreateWithParams() - { - $param = ['id' => 1]; - $storeId = 1; - $json = '{"id":1}'; - - $this->config->expects($this->once()) - ->method('getApiKey') - ->with($storeId) - ->willReturn('testKey'); - - $this->config->expects($this->once()) - ->method('getApiUrl') - ->with($storeId) - ->willReturn(self::$dummy); - - $this->dataEncoder->expects($this->once()) - ->method('encode') - ->with($this->equalTo($param)) - ->willReturn($json); - - $this->client->expects($this->once()) - ->method('setRawData') - ->with($this->equalTo($json), 'application/json') - ->willReturnSelf(); - - $client = $this->httpClient->create('url', 'method', $param, $storeId); - - $this->assertInstanceOf(ZendClient::class, $client); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php deleted file mode 100644 index 1ee55d7ad150c..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php +++ /dev/null @@ -1,182 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Client\ResponseHandler; -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Zend_Http_Response as Response; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; - -class ResponseHandlerTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private static $errorMessage = 'Some error'; - - /** - * @var string - */ - private static $testJson = '{"id": 1}'; - - /** - * @var int - */ - private static $successfulCode = 200; - - /** - * @var int - */ - private static $phpVersionId = 50000; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ResponseHandler|MockObject - */ - private $responseHandler; - - /** - * @var DecoderInterface|MockObject - */ - private $dataDecoder; - - /** - * @var Response|MockObject - */ - private $response; - - public function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->dataDecoder = $this->getMockBuilder(DecoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->response = $this->getMockBuilder(Response::class) - ->disableOriginalConstructor() - ->setMethods(['getStatus', 'getBody']) - ->getMock(); - - $this->responseHandler = $this->objectManager->getObject(ResponseHandler::class, [ - 'dataDecoder' => $this->dataDecoder - ]); - } - - /** - * @dataProvider errorsProvider - */ - public function testHandleFailureMessage($code, $message) - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn($code); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(self::$errorMessage); - - try { - $this->responseHandler->handle($this->response); - } catch (ApiCallException $e) { - $this->assertEquals($e->getMessage(), sprintf($message, self::$errorMessage)); - } - } - - /** - * @return array - */ - public function errorsProvider() - { - return [ - [400, 'Bad Request - The request could not be parsed. Response: %s'], - [401, 'Unauthorized - user is not logged in, could not be authenticated. Response: %s'], - [403, 'Forbidden - Cannot access resource. Response: %s'], - [404, 'Not Found - resource does not exist. Response: %s'], - [ - 409, - 'Conflict - with state of the resource on server. Can occur with (too rapid) PUT requests. Response: %s' - ], - [500, 'Server error. Response: %s'] - ]; - } - - /** - * @expectedException \Magento\Signifyd\Model\SignifydGateway\ApiCallException - * @expectedExceptionMessage Response is not valid JSON: Decoding failed: Syntax error - */ - public function testHandleEmptyJsonException() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(''); - - $r = new \ReflectionObject($this->responseHandler); - $prop = $r->getProperty('phpVersionId'); - $prop->setAccessible(true); - $prop->setValue(self::$phpVersionId); - - $this->responseHandler->handle($this->response); - } - - /** - * @expectedException \Magento\Signifyd\Model\SignifydGateway\ApiCallException - * @expectedExceptionMessage Response is not valid JSON: Some error - */ - public function testHandleInvalidJson() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn('param'); - - $this->dataDecoder = $this->getMockBuilder(DecoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->dataDecoder->expects($this->once()) - ->method('decode') - ->with('param') - ->willThrowException(new \Exception(self::$errorMessage, 30)); - - $this->responseHandler = $this->objectManager->getObject(ResponseHandler::class, [ - 'dataDecoder' => $this->dataDecoder - ]); - - $this->responseHandler->handle($this->response); - } - - public function testHandle() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(self::$testJson); - - $this->dataDecoder->expects($this->once()) - ->method('decode') - ->willReturn(json_decode(self::$testJson, 1)); - - $decodedResponseBody = $this->responseHandler->handle($this->response); - $this->assertEquals($decodedResponseBody, ['id' => 1]); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php deleted file mode 100644 index ba82ff4619ad3..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php +++ /dev/null @@ -1,448 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway; - -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; - -class GatewayTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CreateCaseBuilderInterface|MockObject - */ - private $createCaseBuilder; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var OrderRepositoryInterface|MockObject - */ - private $orderRepository; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - public function setUp() - { - $this->createCaseBuilder = $this->getMockBuilder(CreateCaseBuilderInterface::class) - ->getMockForAbstractClass(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseRepository= $this->getMockBuilder(CaseRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->gateway = new Gateway( - $this->createCaseBuilder, - $this->apiClient, - $this->orderRepository, - $this->caseRepository - ); - } - - public function testCreateCaseForSpecifiedOrder() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $this->createCaseBuilder - ->expects($this->atLeastOnce()) - ->method('build') - ->with($this->equalTo($dummyOrderId)) - ->willReturn([]); - - $result = $this->gateway->createCase($dummyOrderId); - $this->assertEquals(42, $result); - } - - public function testCreateCaseCallsValidApiMethod() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - - $this->apiClient - ->expects($this->atLeastOnce()) - ->method('makeApiCall') - ->with( - $this->equalTo('/cases'), - $this->equalTo('POST'), - $this->isType('array'), - $this->equalTo($dummyStoreId) - ) - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $result = $this->gateway->createCase($dummyOrderId); - $this->assertEquals(42, $result); - } - - public function testCreateCaseNormalFlow() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $returnedInvestigationId = $this->gateway->createCase($dummyOrderId); - $this->assertEquals( - $dummySignifydInvestigationId, - $returnedInvestigationId, - 'Method must return value specified in "investigationId" response parameter' - ); - } - - public function testCreateCaseWithFailedApiCall() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $apiCallFailureMessage = 'Api call failed'; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willThrowException(new ApiCallException($apiCallFailureMessage)); - - $this->expectException(GatewayException::class); - $this->expectExceptionMessage($apiCallFailureMessage); - $this->gateway->createCase($dummyOrderId); - } - - public function testCreateCaseWithMissedResponseRequiredData() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'someOtherParameter' => 'foo', - ]); - - $this->expectException(GatewayException::class); - $this->gateway->createCase($dummyOrderId); - } - - public function testCreateCaseWithAdditionalResponseData() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId, - 'someOtherParameter' => 'foo', - ]); - - $returnedInvestigationId = $this->gateway->createCase($dummyOrderId); - $this->assertEquals( - $dummySignifydInvestigationId, - $returnedInvestigationId, - 'Method must return value specified in "investigationId" response parameter and ignore any other parameters' - ); - } - - public function testSubmitCaseForGuaranteeCallsValidApiMethod() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyDisposition = 'APPROVED'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->expects($this->atLeastOnce()) - ->method('makeApiCall') - ->with( - $this->equalTo('/guarantees'), - $this->equalTo('POST'), - $this->equalTo([ - 'caseId' => $dummySygnifydCaseId - ]), - $this->equalTo($dummyStoreId) - )->willReturn([ - 'disposition' => $dummyDisposition - ]); - - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('APPROVED', $result); - } - - public function testSubmitCaseForGuaranteeWithFailedApiCall() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $apiCallFailureMessage = 'Api call failed'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willThrowException(new ApiCallException($apiCallFailureMessage)); - - $this->expectException(GatewayException::class); - $this->expectExceptionMessage($apiCallFailureMessage); - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('Api call failed', $result); - } - - public function testSubmitCaseForGuaranteeReturnsDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyDisposition = 'APPROVED'; - $dummyGuaranteeId = 123; - $dummyRereviewCount = 0; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'guaranteeId' => $dummyGuaranteeId, - 'disposition' => $dummyDisposition, - 'rereviewCount' => $dummyRereviewCount, - ]); - - $actualDisposition = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals( - $dummyDisposition, - $actualDisposition, - 'Method must return guarantee disposition retrieved in Signifyd API response as a result' - ); - } - - public function testSubmitCaseForGuaranteeWithMissedDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyGuaranteeId = 123; - $dummyRereviewCount = 0; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'guaranteeId' => $dummyGuaranteeId, - 'rereviewCount' => $dummyRereviewCount, - ]); - - $this->expectException(GatewayException::class); - $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - } - - public function testSubmitCaseForGuaranteeWithUnexpectedDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyUnexpectedDisposition = 'UNEXPECTED'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'disposition' => $dummyUnexpectedDisposition, - ]); - - $this->expectException(GatewayException::class); - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('UNEXPECTED', $result); - } - - /** - * @dataProvider supportedGuaranteeDispositionsProvider - */ - public function testSubmitCaseForGuaranteeWithExpectedDisposition($dummyExpectedDisposition) - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'disposition' => $dummyExpectedDisposition, - ]); - - try { - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals($dummyExpectedDisposition, $result); - } catch (GatewayException $e) { - $this->fail(sprintf( - 'Expected disposition "%s" was not accepted with message "%s"', - $dummyExpectedDisposition, - $e->getMessage() - )); - } - } - - /** - * Checks a test case when guarantee for a case is successfully canceled - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee - */ - public function testCancelGuarantee() - { - $caseId = 123; - $dummyStoreId = 1; - - $this->withCaseEntity($caseId, $dummyStoreId); - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - '/cases/' . $caseId . '/guarantee', - 'PUT', - ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED], - $dummyStoreId - ) - ->willReturn( - ['disposition' => Gateway::GUARANTEE_CANCELED] - ); - - $result = $this->gateway->cancelGuarantee($caseId); - self::assertEquals(Gateway::GUARANTEE_CANCELED, $result); - } - - /** - * Checks a case when API request returns unexpected guarantee disposition. - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee - * @expectedException \Magento\Signifyd\Model\SignifydGateway\GatewayException - * @expectedExceptionMessage API returned unexpected disposition: DECLINED. - */ - public function testCancelGuaranteeWithUnexpectedDisposition() - { - $caseId = 123; - $dummyStoreId = 1; - - $this->withCaseEntity($caseId, $dummyStoreId); - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - '/cases/' . $caseId . '/guarantee', - 'PUT', - ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED], - $dummyStoreId - ) - ->willReturn(['disposition' => Gateway::GUARANTEE_DECLINED]); - - $result = $this->gateway->cancelGuarantee($caseId); - $this->assertEquals(Gateway::GUARANTEE_CANCELED, $result); - } - - /** - * @return array - */ - public function supportedGuaranteeDispositionsProvider() - { - return [ - 'APPROVED' => ['APPROVED'], - 'DECLINED' => ['DECLINED'], - 'PENDING' => ['PENDING'], - 'CANCELED' => ['CANCELED'], - 'IN_REVIEW' => ['IN_REVIEW'], - 'UNREQUESTED' => ['UNREQUESTED'], - ]; - } - - /** - * Specifies order entity mock execution. - * - * @param int $orderId - * @param int $storeId - * @return void - */ - private function withOrderEntity(int $orderId, int $storeId): void - { - $orderEntity = $this->getMockBuilder(OrderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $orderEntity->method('getStoreId') - ->willReturn($storeId); - $this->orderRepository->method('get') - ->with($orderId) - ->willReturn($orderEntity); - } - - /** - * Specifies case entity mock execution. - * - * @param int $caseId - * @param int $storeId - * @return void - */ - private function withCaseEntity(int $caseId, int $storeId): void - { - $orderId = 1; - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $caseEntity->method('getOrderId') - ->willReturn($orderId); - $this->caseRepository->method('getByCaseId') - ->with($caseId) - ->willReturn($caseEntity); - - $this->withOrderEntity($orderId, $storeId); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php deleted file mode 100644 index 0dfdf4980fb58..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessage; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageFactory; - -/** - * Class WebhookMessageReaderTest - */ -class WebhookMessageReaderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var WebhookMessageReader - */ - private $model; - - /** - * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $decoder; - - /** - * @var WebhookMessageFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $webhookMessageFactory; - - /** - * @var WebhookRequest|\PHPUnit_Framework_MockObject_MockObject - */ - private $webhookRequest; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->decoder = $this->getMockBuilder(DecoderInterface::class) - ->getMockForAbstractClass(); - - $this->webhookMessageFactory = $this->getMockBuilder(WebhookMessageFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new WebhookMessageReader( - $this->decoder, - $this->webhookMessageFactory - ); - } - - /** - * Tests successful reading webhook message from request. - * - */ - public function testReadSuccess() - { - $rawBody = 'body'; - $topic = 'topic'; - $decodedData = ['status' => "DISMISSED", 'orderId' => '19418']; - - $this->webhookRequest->expects($this->once()) - ->method('getBody') - ->willReturn($rawBody); - $this->webhookRequest->expects($this->once()) - ->method('getEventTopic') - ->willReturn('topic'); - $this->decoder->expects($this->once()) - ->method('decode') - ->with($rawBody) - ->willReturn($decodedData); - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookMessageFactory->expects($this->once()) - ->method('create') - ->with( - [ - 'data' => $decodedData, - 'eventTopic' => $topic - ] - ) - ->willReturn($webhookMessage); - - $this->assertEquals( - $webhookMessage, - $this->model->read($this->webhookRequest) - ); - } - - /** - * Tests reading failure webhook message from request. - * - * @expectedException \InvalidArgumentException - */ - public function testReadFail() - { - $this->decoder->expects($this->once()) - ->method('decode') - ->willThrowException(new \Exception('Error')); - - $this->model->read($this->webhookRequest); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php deleted file mode 100644 index 5ae6b95a77548..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; - -/** - * Class WebhookRequestValidatorTest - */ -class WebhookRequestValidatorTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var WebhookRequestValidator - */ - private $model; - - /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject - */ - private $config; - - /** - * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $decoder; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->decoder = $this->getMockBuilder(DecoderInterface::class) - ->getMockForAbstractClass(); - - $this->model = new WebhookRequestValidator( - $this->config, - $this->decoder - ); - } - - /** - * Tests successful cases. - * - * @param string $body - * @param string $topic - * @param string $hash - * @param int$callConfigCount - * @dataProvider validateSuccessDataProvider - */ - public function testValidateSuccess($body, $topic, $hash, $callConfigCount) - { - $this->config->expects($this->exactly($callConfigCount)) - ->method('getApiKey') - ->willReturn('GpFZZnxGgIxuI8BazSm3v6eGK'); - - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(['status' => "DISMISSED", 'orderId' => '19418']); - - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->assertTrue( - $this->model->validate($webhookRequest) - ); - } - - /** - * @case 1. All data are correct, event topic has real value - * @case 2. All data are correct, event topic has test value - * @return array - */ - public function validateSuccessDataProvider() - { - return [ - 1 => [ - 'body' => '{ status: "DISMISSED", orderId: "19418" }', - 'topic' => 'cases/creation', - 'hash' => 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q=', - 'callConfigCount' => 1 - ], - 2 => [ - 'body' => '{ status: "DISMISSED", orderId: "19418" }', - 'topic' => 'cases/test', - 'hash' => '6npAahliNbzYo/Qi4+g+JeqPhLFgg19sIbuxDLmvobw=', - 'callConfigCount' => 0 - ] - ]; - } - - /** - * Case with wrong event topic - * - * @param string $topic - * @dataProvider validationTopicFailsDataProvider - */ - public function testValidationTopicFails($topic) - { - $body = '{ status: "DISMISSED", orderId: "19418" }'; - $hash = 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q='; - - $this->config->expects($this->never()) - ->method('getApiKey'); - - $this->decoder->expects($this->never()) - ->method('decode'); - - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook event topic value validation fails' - ); - } - - /** - * @return array - */ - public function validationTopicFailsDataProvider() - { - return [ - ['wrong topic' => 'bla-bla-topic'], - ['empty topic' => ''] - ]; - } - - /** - * Case with wrong webhook request body - * - * @param string $body - * @dataProvider validationBodyFailsDataProvider - */ - public function testValidationBodyFails($body) - { - $topic = 'cases/creation'; - $hash = 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q='; - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->config->expects($this->never()) - ->method('getApiKey'); - - if (empty($body)) { - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(''); - } else { - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willThrowException(new \Exception('Error')); - } - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook request body validation fails' - ); - } - - /** - * @return array - */ - public function validationBodyFailsDataProvider() - { - return [ - ['Empty request body' => ''], - ['Bad request body' => '{ bad data}'] - ]; - } - - /** - * Case with wrong hash - */ - public function testValidationHashFails() - { - $topic = 'cases/creation'; - $body = '{ status: "DISMISSED", orderId: "19418" }'; - $hash = 'wrong hash'; - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->config->expects($this->once()) - ->method('getApiKey') - ->willReturn('GpFZZnxGgIxuI8BazSm3v6eGK'); - - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(['status' => "DISMISSED", 'orderId' => '19418']); - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook hash validation fails' - ); - } - - /** - * Returns mocked WebhookRequest - * - * @param string $body - * @param string $topic - * @param string $hash - * @return WebhookRequest|\PHPUnit_Framework_MockObject_MockObject - */ - private function createWebhookRequest($body, $topic, $hash) - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->once()) - ->method('getBody') - ->willReturn($body); - $webhookRequest->expects($this->once()) - ->method('getEventTopic') - ->willReturn($topic); - $webhookRequest->expects($this->once()) - ->method('getHash') - ->willReturn($hash); - - return $webhookRequest; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php deleted file mode 100644 index 9d3061f240c21..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Framework\DataObject\IdentityGeneratorInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class SignifydOrderSessionIdTest tests that SignifydOrderSessionId class dependencies - * follow the contracts. - */ -class SignifydOrderSessionIdTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var IdentityGeneratorInterface|MockObject - */ - private $identityGenerator; - - /** - * Sets up testing class and dependency mocks. - */ - protected function setUp() - { - $this->identityGenerator = $this->getMockBuilder(IdentityGeneratorInterface::class) - ->getMockForAbstractClass(); - - $this->signifydOrderSessionId = new SignifydOrderSessionId($this->identityGenerator); - } - - /** - * Tests method by passing quoteId parameter - * - * @covers \Magento\Signifyd\Model\SignifydOrderSessionId::get - */ - public function testGetByQuoteId() - { - $quoteId = 1; - $signifydOrderSessionId = 'asdfzxcv'; - - $this->identityGenerator->expects(self::once()) - ->method('generateIdForData') - ->with($quoteId) - ->willReturn($signifydOrderSessionId); - - $this->assertEquals( - $signifydOrderSessionId, - $this->signifydOrderSessionId->get($quoteId) - ); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php deleted file mode 100644 index d63831b1d4a8e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php +++ /dev/null @@ -1,284 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Observer; - -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Payment\Model\MethodInterface; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Observer\PlaceOrder; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -class PlaceOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var CaseCreationServiceInterface|MockObject - */ - private $creationService; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var Observer|MockObject - */ - private $observer; - - /** - * @var Event|MockObject - */ - private $event; - - /** - * @var OrderInterface|MockObject - */ - private $orderEntity; - - /** - * @var PlaceOrder - */ - private $placeOrder; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isActive']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->creationService = $this->getMockBuilder(CaseCreationServiceInterface::class) - ->disableOriginalConstructor() - ->setMethods(['createForOrder']) - ->getMock(); - - $this->observer = $this->getMockBuilder(Observer::class) - ->disableOriginalConstructor() - ->setMethods(['getEvent']) - ->getMock(); - - $this->event = $this->getMockBuilder(Event::class) - ->disableOriginalConstructor() - ->setMethods(['getData']) - ->getMock(); - - $this->placeOrder = new PlaceOrder( - $this->config, - $this->creationService, - $this->logger - ); - } - - /** - * Checks a test case when Signifyd module is disabled. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithDisabledModule() - { - $orderId = 1; - $storeId = 2; - $this->withActiveSignifydIntegration(false, $storeId); - $this->withOrderEntity($orderId, $storeId); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when the observer event returns empty an order entity. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithoutOrder() - { - $this->withActiveSignifydIntegration(true); - $this->withOrderEntity(null, null); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when the order placed with offline payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithOfflinePayment() - { - $orderId = 1; - $storeId = 2; - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(false); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when case creation service fails. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithFailedCaseCreation() - { - $orderId = 1; - $storeId = 2; - $exceptionMessage = __('Case with the same order id already exists.'); - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(true); - - $this->creationService->method('createForOrder') - ->with(self::equalTo($orderId)) - ->willThrowException(new AlreadyExistsException($exceptionMessage)); - - $this->logger->method('error') - ->with(self::equalTo($exceptionMessage)); - - $result = $this->placeOrder->execute($this->observer); - $this->assertNull($result); - } - - /** - * Checks a test case when observer successfully calls case creation service. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecute() - { - $orderId = 1; - $storeId = 2; - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(true); - - $this->creationService - ->method('createForOrder') - ->with(self::equalTo($orderId)); - - $this->logger->expects(self::never()) - ->method('error'); - - $this->placeOrder->execute($this->observer); - } - - public function testExecuteWithOrderPendingPayment() - { - $orderId = 1; - $storeId = 2; - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->orderEntity->method('getState') - ->willReturn(Order::STATE_PENDING_PAYMENT); - $this->withAvailablePaymentMethod(true); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Specifies order entity mock execution. - * - * @param int|null $orderId - * @param int|null $storeId - * @return void - */ - private function withOrderEntity($orderId, $storeId): void - { - $this->orderEntity = $this->getMockBuilder(OrderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderEntity->method('getEntityId') - ->willReturn($orderId); - $this->orderEntity->method('getStoreId') - ->willReturn($storeId); - - $this->observer->method('getEvent') - ->willReturn($this->event); - - $this->event->method('getData') - ->with('order') - ->willReturn($this->orderEntity); - } - - /** - * Specifies config mock execution. - * - * @param bool $isActive - * @param int|null $storeId - * @return void - */ - private function withActiveSignifydIntegration(bool $isActive, $storeId = null): void - { - $this->config->method('isActive') - ->with($storeId) - ->willReturn($isActive); - } - - /** - * Specifies payment method mock execution. - * - * @param bool $isAvailable - * @return void - */ - private function withAvailablePaymentMethod($isAvailable) - { - /** @var MethodInterface|MockObject $paymentMethod */ - $paymentMethod = $this->getMockBuilder(MethodInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - /** - * The code depends on implementation but not interface - * because order payment implements two interfaces - */ - /** @var Payment|MockObject $orderPayment */ - $orderPayment = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->getMock(); - $this->orderEntity->method('getPayment') - ->willReturn($orderPayment); - - $orderPayment->method('getMethodInstance') - ->willReturn($paymentMethod); - - $paymentMethod->method('isOffline') - ->willReturn(!$isAvailable); - } -} diff --git a/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php b/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php deleted file mode 100644 index 1e6234a8e27a9..0000000000000 --- a/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Ui\Component\Listing\Column\Guarantee; - -use Magento\Framework\Escaper; -use Magento\Framework\Data\OptionSourceInterface; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Class Options - */ -class Options implements OptionSourceInterface -{ - /** - * @var Escaper - */ - private $escaper; - - /** - * Constructor - * - * @param Escaper $escaper - */ - public function __construct(Escaper $escaper) - { - $this->escaper = $escaper; - } - - /** - * {@inheritdoc} - */ - public function toOptionArray() - { - return [ - [ - 'value' => CaseInterface::GUARANTEE_DECLINED, - 'label' => $this->escaper->escapeHtml(__('Declined')) - ], - [ - 'value' => CaseInterface::GUARANTEE_APPROVED, - 'label' => $this->escaper->escapeHtml(__('Approved')) - ], - [ - 'value' => CaseInterface::GUARANTEE_CANCELED, - 'label' => $this->escaper->escapeHtml(__('Canceled')) - ], - [ - 'value' => CaseInterface::GUARANTEE_PENDING, - 'label' => $this->escaper->escapeHtml(__('Pending')) - ] - ]; - } -} diff --git a/app/code/Magento/Signifyd/composer.json b/app/code/Magento/Signifyd/composer.json deleted file mode 100644 index c0214e9a2813c..0000000000000 --- a/app/code/Magento/Signifyd/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/module-signifyd", - "description": "Submitting Case Entry to Signifyd on Order Creation", - "config": { - "sort-packages": true - }, - "require": { - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-directory": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-store": "*", - "php": "~7.1.3||~7.2.0||~7.3.0" - }, - "suggest": { - "magento/module-config": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - } -} diff --git a/app/code/Magento/Signifyd/etc/acl.xml b/app/code/Magento/Signifyd/etc/acl.xml deleted file mode 100644 index 32f0493fbcad9..0000000000000 --- a/app/code/Magento/Signifyd/etc/acl.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> - <acl> - <resources> - <resource id="Magento_Backend::admin"> - <resource id="Magento_Backend::stores"> - <resource id="Magento_Backend::stores_settings"> - <resource id="Magento_Config::config"> - <resource id="Magento_Sales::fraud_protection" title="Fraud Protection Section" translate="title" /> - </resource> - </resource> - </resource> - </resource> - </resources> - </acl> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/di.xml b/app/code/Magento/Signifyd/etc/adminhtml/di.xml deleted file mode 100644 index c771d67216d43..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/di.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface" type="Magento\Signifyd\Model\QuoteSession\Adminhtml\BackendSession" /> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/routes.xml b/app/code/Magento/Signifyd/etc/adminhtml/routes.xml deleted file mode 100644 index c078ab3c8c4c1..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - <router id="admin"> - <route id="signifyd" frontName="signifyd"> - <module name="Magento_Signifyd" before="Magento_Backend" /> - </route> - </router> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml deleted file mode 100644 index 182a67e4e1f35..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="fraud_protection" translate="label" type="text" sortOrder="410" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Fraud Protection</label> - <tab>sales</tab> - <resource>Magento_Sales::fraud_protection</resource> - <group id="signifyd" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> - <fieldset_css>signifyd-logo-header</fieldset_css> - <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> - <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset\Info</frontend_model> - <fieldset_css>signifyd-about-header</fieldset_css> - <label><![CDATA[Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.]]></label> - <comment><![CDATA[Signifyd automatically reviews your orders for fraud, telling you in seconds which orders to ship, and which to reject. - We back our approvals with 100% chargeback protection, reimbursing you the full order amount plus fees should you ever receive a fraudulent chargeback. - <p>Benefits:<ul> - <li>Grow your business without fear of fraud</li> - <li>Accept more orders and maximize your revenue</li> - <li>Automate order review and shift fraud off your plate</li></ul></p>]]> - </comment> - <more_url>https://www.signifyd.com/magento-guaranteed-fraud-protection</more_url> - </group> - <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> - <fieldset_css>signifyd-about-header</fieldset_css> - <label>Configuration</label> - <comment><![CDATA[<a href="https://www.signifyd.com/resources/manual/magento-2/signifyd-on-magento-integration-guide/" target="_blank">View our setup guide</a> for step-by-step instructions on how to integrate Signifyd with Magento.<br />For support contact <a href="mailto:support@signifyd.com">support@signifyd.com</a>.]]> - </comment> - <field id="active" translate="label" type="select" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>fraud_protection/signifyd/active</config_path> - </field> - <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>API Key</label> - <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console.]]></comment> - <config_path>fraud_protection/signifyd/api_key</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>API URL</label> - <config_path>fraud_protection/signifyd/api_url</config_path> - <comment>Don’t change unless asked to do so.</comment> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Debug</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>fraud_protection/signifyd/debug</config_path> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Webhook URL</label> - <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> - <attribute type="handler_url">signifyd/webhooks/handler</attribute> - <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Field\WebhookUrl</frontend_model> - <depends> - <field id="active">1</field> - </depends> - </field> - </group> - </group> - </section> - </system> -</config> diff --git a/app/code/Magento/Signifyd/etc/config.xml b/app/code/Magento/Signifyd/etc/config.xml deleted file mode 100644 index 804342a14bb08..0000000000000 --- a/app/code/Magento/Signifyd/etc/config.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> - <default> - <fraud_protection> - <signifyd> - <active>0</active> - <api_url>https://api.signifyd.com/v2/</api_url> - <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> - <debug>0</debug> - </signifyd> - </fraud_protection> - </default> -</config> diff --git a/app/code/Magento/Signifyd/etc/db_schema.xml b/app/code/Magento/Signifyd/etc/db_schema.xml deleted file mode 100644 index 8d47321c38b29..0000000000000 --- a/app/code/Magento/Signifyd/etc/db_schema.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> - <table name="signifyd_case" resource="sales" engine="innodb" comment="signifyd_case"> - <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true" - comment="Entity_id"/> - <column xsi:type="int" name="order_id" padding="10" unsigned="true" nullable="true" identity="false" - comment="Order_id"/> - <column xsi:type="int" name="case_id" padding="10" unsigned="true" nullable="true" identity="false" - comment="Case_id"/> - <column xsi:type="boolean" name="guarantee_eligible" nullable="true" comment="Guarantee_eligible"/> - <column xsi:type="varchar" name="guarantee_disposition" nullable="true" length="32" default="PENDING" - comment="Guarantee_disposition"/> - <column xsi:type="varchar" name="status" nullable="true" length="32" default="PENDING" comment="Status"/> - <column xsi:type="int" name="score" padding="10" unsigned="true" nullable="true" identity="false" - comment="Score"/> - <column xsi:type="text" name="associated_team" nullable="true" comment="Associated_team"/> - <column xsi:type="varchar" name="review_disposition" nullable="true" length="32" comment="Review_disposition"/> - <column xsi:type="timestamp" name="created_at" on_update="false" nullable="true" comment="Created_at"/> - <column xsi:type="timestamp" name="updated_at" on_update="false" nullable="true" comment="Updated_at"/> - <constraint xsi:type="primary" referenceId="PRIMARY"> - <column name="entity_id"/> - </constraint> - <constraint xsi:type="foreign" referenceId="SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID" table="signifyd_case" - column="order_id" referenceTable="sales_order" referenceColumn="entity_id" onDelete="SET NULL"/> - <constraint xsi:type="unique" referenceId="SIGNIFYD_CASE_ORDER_ID"> - <column name="order_id"/> - </constraint> - <constraint xsi:type="unique" referenceId="SIGNIFYD_CASE_CASE_ID"> - <column name="case_id"/> - </constraint> - </table> - <table name="sales_order_grid" resource="sales" comment="Sales Flat Order Grid"> - <column xsi:type="varchar" name="signifyd_guarantee_status" nullable="true" length="32" - comment="Signifyd Guarantee Disposition Status"/> - </table> -</schema> diff --git a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json b/app/code/Magento/Signifyd/etc/db_schema_whitelist.json deleted file mode 100644 index 69d164b23d9e7..0000000000000 --- a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "signifyd_case": { - "column": { - "entity_id": true, - "order_id": true, - "case_id": true, - "guarantee_eligible": true, - "guarantee_disposition": true, - "status": true, - "score": true, - "associated_team": true, - "review_disposition": true, - "created_at": true, - "updated_at": true - }, - "constraint": { - "PRIMARY": true, - "SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SIGNIFYD_CASE_ORDER_ID": true, - "SIGNIFYD_CASE_CASE_ID": true - } - }, - "sales_order_grid": { - "column": { - "signifyd_guarantee_status": true - } - } -} \ No newline at end of file diff --git a/app/code/Magento/Signifyd/etc/di.xml b/app/code/Magento/Signifyd/etc/di.xml deleted file mode 100644 index e82e8f84b3584..0000000000000 --- a/app/code/Magento/Signifyd/etc/di.xml +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Api\Data\CaseInterface" type="Magento\Signifyd\Model\CaseEntity" /> - <preference for="Magento\Signifyd\Api\CaseRepositoryInterface" type="Magento\Signifyd\Model\CaseRepository" /> - <preference for="Magento\Signifyd\Api\CaseManagementInterface" type="Magento\Signifyd\Model\CaseManagement" /> - <preference for="Magento\Signifyd\Api\Data\CaseSearchResultsInterface" type="Magento\Signifyd\Model\CaseSearchResults" /> - <preference for="Magento\Signifyd\Api\CaseCreationServiceInterface" type="Magento\Signifyd\Model\CaseServices\CreationService" /> - <preference for="Magento\Signifyd\Api\GuaranteeCreationServiceInterface" type="Magento\Signifyd\Model\Guarantee\CreationService" /> - <preference for="Magento\Signifyd\Api\GuaranteeCancelingServiceInterface" type="Magento\Signifyd\Model\Guarantee\CancelingService" /> - <preference for="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface" type="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder" /> - - <virtualType name="SignifydAvsDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> - <virtualType name="SignifydCvvDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> - - <type name="Magento\Signifyd\Model\PaymentVerificationFactory"> - <arguments> - <argument name="config" xsi:type="object">Magento\Payment\Gateway\Config\Config</argument> - <argument name="avsDefaultAdapter" xsi:type="object">SignifydAvsDefaultMapper</argument> - <argument name="cvvDefaultAdapter" xsi:type="object">SignifydCvvDefaultMapper</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater"> - <arguments> - <argument name="entityGrid" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Grid</argument> - </arguments> - </type> - <virtualType name="SignifydIdListProvider" type="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider"> - <arguments> - <argument name="providers" xsi:type="array"> - <item name="signifyd" xsi:type="string">Magento\Signifyd\Model\SalesOrderGrid\NotSyncedOrderIdListProvider</item> - </argument> - </arguments> - </virtualType> - <virtualType name="Magento\Sales\Model\ResourceModel\Order\Grid"> - <arguments> - <argument name="idListProvider" xsi:type="object">SignifydIdListProvider</argument> - <argument name="joins" xsi:type="array"> - <item name="signifyd_case" xsi:type="array"> - <item name="table" xsi:type="string">signifyd_case</item> - <item name="origin_column" xsi:type="string">entity_id</item> - <item name="target_column" xsi:type="string">order_id</item> - </item> - </argument> - <argument name="columns" xsi:type="array"> - <item name="signifyd_guarantee_status" xsi:type="string">signifyd_case.guarantee_disposition</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Signifyd\Model\ResourceModel\CaseEntity"> - <arguments> - <argument name="connectionName" xsi:type="string">sales</argument> - </arguments> - </type> - <virtualType name="PaymentMapperSchemaLocator" type="Magento\Framework\Config\GenericSchemaLocator"> - <arguments> - <argument name="moduleName" xsi:type="string">Magento_Signifyd</argument> - <argument name="schema" xsi:type="string">signifyd_payment_mapping.xsd</argument> - </arguments> - </virtualType> - <virtualType name="PaymentMapperConfigReader" type="Magento\Framework\Config\Reader\Filesystem"> - <arguments> - <argument name="converter" xsi:type="object">Magento\Signifyd\Model\PaymentMethodMapper\XmlToArrayConfigConverter</argument> - <argument name="schemaLocator" xsi:type="object">PaymentMapperSchemaLocator</argument> - <argument name="fileName" xsi:type="string">signifyd_payment_mapping.xml</argument> - <argument name="idAttributes" xsi:type="array"> - <item name="/config/payment_method_list/payment_method" xsi:type="string">name</item> - </argument> - </arguments> - </virtualType> - <virtualType name="PaymentMethodConfigData" type="Magento\Framework\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">PaymentMapperConfigReader</argument> - <argument name="cacheId" xsi:type="string">signifyd_payment_list_cache</argument> - </arguments> - </virtualType> - <type name="Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper"> - <arguments> - <argument name="paymentMapping" xsi:type="object">PaymentMethodConfigData</argument> - </arguments> - </type> - <type name="Magento\Config\Model\Config\TypePool"> - <arguments> - <argument name="sensitive" xsi:type="array"> - <item name="fraud_protection/signifyd/api_key" xsi:type="string">1</item> - <item name="fraud_protection/signifyd/api_url" xsi:type="string">1</item> - </argument> - <argument name="environment" xsi:type="array"> - <item name="fraud_protection/signifyd/api_key" xsi:type="string">1</item> - <item name="fraud_protection/signifyd/api_url" xsi:type="string">1</item> - </argument> - </arguments> - </type> - <type name="Magento\Sales\Model\Order"> - <plugin name="cancelGuaranteeAfterOrderCancel" type="Magento\Signifyd\Plugin\OrderPlugin" /> - </type> - <type name="Magento\Payment\Model\MethodInterface"> - <plugin name="cancelGuaranteeAfterPaymentDeny" type="Magento\Signifyd\Plugin\PaymentPlugin" /> - </type> -</config> diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml deleted file mode 100644 index d44665f9fb97b..0000000000000 --- a/app/code/Magento/Signifyd/etc/events.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="checkout_submit_all_after"> - <observer name="signifyd_place_order_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> - <event name="paypal_checkout_success"> - <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> - <event name="checkout_onepage_controller_success_action"> - <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> -</config> diff --git a/app/code/Magento/Signifyd/etc/frontend/di.xml b/app/code/Magento/Signifyd/etc/frontend/di.xml deleted file mode 100644 index 08a690d1a9930..0000000000000 --- a/app/code/Magento/Signifyd/etc/frontend/di.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface" type="Magento\Signifyd\Model\QuoteSession\FrontendSession" /> -</config> diff --git a/app/code/Magento/Signifyd/etc/frontend/routes.xml b/app/code/Magento/Signifyd/etc/frontend/routes.xml deleted file mode 100644 index 5803f59d8624b..0000000000000 --- a/app/code/Magento/Signifyd/etc/frontend/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - <router id="standard"> - <route id="signifyd" frontName="signifyd"> - <module name="Magento_Signifyd" /> - </route> - </router> -</config> diff --git a/app/code/Magento/Signifyd/etc/module.xml b/app/code/Magento/Signifyd/etc/module.xml deleted file mode 100644 index 264f295e8c528..0000000000000 --- a/app/code/Magento/Signifyd/etc/module.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Signifyd" > - <sequence> - <module name="Magento_Sales" /> - <module name="Magento_Store" /> - <module name="Magento_Customer" /> - <module name="Magento_Directory" /> - <module name="Magento_Checkout" /> - <module name="Magento_Backend" /> - <module name="Magento_Payment" /> - </sequence> - </module> -</config> diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml deleted file mode 100644 index 9ff952d04925d..0000000000000 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - /** - * Custom payment method might adds a block in payment_method_list e.g. - * <payment_method name="custom_payment_method"> - * <magento_code>custom_payment_method</magento_code> - * <signifyd_code>PAYMENT_CARD</signifyd_code> - * </payment_method> - * Appropriate value for the <signifyd_code> tag from Signifyd documentation: - * @see https://www.signifyd.com/docs/api/#/reference/webhooks/manage-single-webhooks/create-a-case - * Create a Case -> Request -> ATTRIBUTES -> paymentMethod - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Signifyd:etc/signifyd_payment_mapping.xsd"> - <payment_method_list> - <payment_method name="braintree"> - <magento_code>braintree</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="braintree_paypal"> - <magento_code>braintree_paypal</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="paypal_express"> - <magento_code>paypal_express</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="paypal_express_bml"> - <magento_code>paypal_express_bml</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflow_express"> - <magento_code>payflow_express</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflow_express_bml"> - <magento_code>payflow_express_bml</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflowpro"> - <magento_code>payflowpro</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="payflow_link"> - <magento_code>payflow_link</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="payflow_advanced"> - <magento_code>payflow_advanced</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="hosted_pro"> - <magento_code>hosted_pro</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="authorizenet_directpost"> - <magento_code>authorizenet_directpost</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="worldpay"> - <magento_code>worldpay</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="eway"> - <magento_code>eway</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="cybersource"> - <magento_code>cybersource</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="free"> - <magento_code>free</magento_code> - <signifyd_code>FREE</signifyd_code> - </payment_method> - </payment_method_list> -</config> diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd deleted file mode 100644 index bb3b3036d0c9c..0000000000000 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="1" maxOccurs="unbounded" name="payment_method_list" type="payment_method_list"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:complexType name="payment_method_list"> - <xs:sequence> - <xs:element minOccurs="1" maxOccurs="unbounded" name="payment_method" type="payment_method"/> - </xs:sequence> - </xs:complexType> - <xs:complexType name="payment_method"> - <xs:sequence> - <xs:element minOccurs="1" name="magento_code"/> - <xs:element minOccurs="1" name="signifyd_code"/> - </xs:sequence> - <xs:attribute name="name" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> - Element's unique identifier. - </xs:documentation> - </xs:annotation> - </xs:attribute> - </xs:complexType> -</xs:schema> diff --git a/app/code/Magento/Signifyd/i18n/en_US.csv b/app/code/Magento/Signifyd/i18n/en_US.csv deleted file mode 100644 index 40772188dac9e..0000000000000 --- a/app/code/Magento/Signifyd/i18n/en_US.csv +++ /dev/null @@ -1,46 +0,0 @@ -OPEN,Open -PROCESSING,Processing -FLAGGED,Flagged -DISMISSED,Dismissed -HELD,Held -GOOD,Good -FRAUDULENT,Fraudulent -UNSET,Unset -NULL,Unset -APPROVED,Approved -DECLINED,Declined -PENDING,Pending -CANCELED,Canceled -IN_REVIEW,"In review" -Approved,Approved -Declined,Declined -Pending,Pending -Canceled,Canceled -"In Review","In Review" -Unrequested,Unrequested -"Learn more","Learn more" -"This order already has associated case entity","This order already has associated case entity" -"The case entity should not be empty.","The case entity should not be empty." -"Cannot update Case entity.","Cannot update Case entity." -"The ""%1"" should not be empty.","The ""%1"" should not be empty." -"Case entity not found.","Case entity not found." -"Case Update: New score for the order is %1. Previous score was %2.","Case Update: New score for the order is %1. Previous score was %2." -"Awaiting the Signifyd guarantee disposition.","Awaiting the Signifyd guarantee disposition." -"Only single entrance of ""%1"" node is required.","Only single entrance of ""%1"" node is required." -"Not empty value for ""%1"" node is required.","Not empty value for ""%1"" node is required." -"%1 must implement %2","%1 must implement %2" -Error,Error -"Cannot generate message.","Cannot generate message." -"Message is generated.","Message is generated." -"Case with the same order id already exists.","Case with the same order id already exists." -"Fraud Protection Information","Fraud Protection Information" -"Signifyd Guarantee Decision","Signifyd Guarantee Decision" -"Fraud Protection Section","Fraud Protection Section" -"Fraud Protection","Fraud Protection" -"Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.","Protect your store from fraud with Guaranteed Fraud Protection by Signifyd." -Configuration,Configuration -"Enable this Solution","Enable this Solution" -"API Key","API Key" -"API URL","API URL" -Debug,Debug -"Webhook URL","Webhook URL" diff --git a/app/code/Magento/Signifyd/registration.php b/app/code/Magento/Signifyd/registration.php deleted file mode 100644 index e7fa9cfd2f9b3..0000000000000 --- a/app/code/Magento/Signifyd/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Framework\Component\ComponentRegistrar; - -ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Signifyd', __DIR__); diff --git a/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml deleted file mode 100644 index ad62b2c6e01cf..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="order_additional_info"> - <block class="Magento\Signifyd\Block\Adminhtml\CaseInfo" name="order_case_info" template="Magento_Signifyd::case_info.phtml"/> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml b/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml deleted file mode 100644 index 07f1b2c2e4ae6..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -?> -<?php /** @var $block Magento\Signifyd\Block\Adminhtml\CaseInfo */ ?> -<?php -if ($block->isEmptyCase()) { - return ''; -} -?> -<section class="admin__page-section order-case-info"> - <div class="admin__page-section-title"> - <span class="title"><?= $block->escapeHtml(__('Fraud Protection Information')) ?></span> - </div> - <div class="admin__page-section-content"> - <div class="admin__page-section-item case-information"> - <div class="admin__table-wrapper"> - <table class="admin__table-secondary order-case-table"> - <tbody> - <tr> - <th class="col-guarantee-disposition"><?= $block->escapeHtml(__('Signifyd Guarantee Decision')) ?></th> - <td class="col-guarantee-disposition"><?= $block->escapeHtml($block->getCaseGuaranteeDisposition()) ?></td> - </tr> - </tbody> - </table> - </div> - </div> - </div> -</section> diff --git a/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml b/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml deleted file mode 100644 index 91053d617f31f..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ /** - ~ * Copyright © Magento, Inc. All rights reserved. - ~ * See COPYING.txt for license details. - ~ */ - --> -<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> - <columns name="sales_order_columns"> - <column name="signifyd_guarantee_status" component="Magento_Ui/js/grid/columns/select"> - <settings> - <filter>select</filter> - <options class="Magento\Signifyd\Ui\Component\Listing\Column\Guarantee\Options"/> - <visible>true</visible> - <dataType>select</dataType> - <label translate="true">Signifyd Guarantee Decision</label> - </settings> - </column> - </columns> -</listing> diff --git a/app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png b/app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png deleted file mode 100644 index 7c6645e7c6c93e6ee1a2a5d14ad1b7e95c07ff2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5218 zcmaJ_c|25Y`xk|geG7>(b~0udV`l7(HCu?3Vwkbc7-q)4WX+(mRzk8bk*%^k$QoI) z6bd1`l!QE_=r?-nd7nRipZ9#uxzBy>^Zj1e_qy)uI)9vaOLIdu79kcoIyyFEBeWIm z{(^R$VP>R_8|C8kw3{GB|02bj=t>E|kZ^Q5SfVoyXiUJk;jC~NY>;m+PMwa9VZ+1b zBITl)DawUN0Ar3Y;6Q>8jZH_Vt`+Emaq+@YfX+BK4{r_7MpG*Y=z-M$*{PU8%zX54 z?jA<LB%F1yxs6M(mkSaL($WN~2cl>O2sjD`7)Zc-lTm>hpkH=TwE5ApA_({kLh;f7 z{YTVAGfSWzk%R-PfRz<oAW$d}4hKVFDsVXTEKmsoRZ@h|PPhUThEjo}lvIJg4-m~8 z3G0foLL2<{MSIc!xl<@UC`HA9fB<lSGMGqmQ-mUs$Ris{N(wZD0y)T=f(cacCX4^E zfX0zsNFF{E5282l$Rfs>=tt22(Ukrt1cHy5**}K8$-fgtOPOLI#zzqfhA0vUM{)f^ zlPOlX|H}A((PWz-ADp5Uj!g6;xzP5*Rs0W`mb?G$=noL<@(<3(kA(jfC)Py~hsP0c z-V`!T6!e!OnsSr{$%D3m7(ANj;&=4c7_9;F^Yg%>RM1Kg1X2lxRMs^>>q4POC`<>T zf`p)zp(+rB9^%N^k%4~`X)9nXjSEBT=oui8$_8kNJ`}1CN5FNVNIfJJse({eRY5}k zaE-mm6pXhE?#Ss8*W<rjrT@uA>5*_43Xx<(B;x;+iZLGVN2ZXDmVtWKaG;DC#>K;% zLL|G(9_{6CwK$T8KMrd^A`*bVHUs7HFAcu}$3m2$F0NQ*1%xX`Ndbd{K@|`<XPAPs zimJ0R%mt3Yz>uKdWbD7P_9u7<6hW&~RTxB72?9qzp$H`<9RpPr2ogaXAUdibMVcC{ z3yR`F!Q+0HsScj<dxrNoDistS<LySvi2@eqit)o!K$>I^H*XKuAgsFLQ4!)jXz*XN z|1cH*W$)MWzpC~RlGX}G(|>x9_V7;^<Gg9DOQQ8?sNX|bIyz2(F<QqaaPpG_Ae48B z?^;M~b`}r)qBi(>RD$qLU>wkB1n91R3&eYZ>p4UF<b$`JERXo)dDW8(WbA6>K)?hR zP*-U#Gd)PiSeJHu#>2DB{OdQ?pGi7OJ?<Is=JpC|ofUPhRi1U&*lXQw5j6Yu@%@D- zLUaXO?;Bg9=W5oi#cIO1&;k`s0q(BzLus6BsaJeBsCpFLRZbjL_ul#wW@Tr_{O%T) zyk2IzIxUXxpM$tSY;cHu)_p7Hd3LlG`V^Y!c#c>G=X2^~sx>u<ODnJy)gU->?{1P9 zd-PA;9+BwPXd@j7U<c<I6@-S*%XhJNJuj!Wt8MSEv^Z^zPYg>Q(qB=Yj_alR#4v1% zis;>BMLTr%uW)V{hi@bbwDn6;*HdS!v{{$7E#;f`_9HiX!l-h)3;NCFq<4VOqz~^v zoQeR{7@u!}=*pux!dWVP_>2@$Z8hxvZMHb&InI+kuDbdJfwSLy=nE+_$6LDL1IIF< zSN#uI&nFleI;`WGCcGI&JlCDd;2nE;A;97mw!XwGb`mdmsEt&t7ICeFI!%2^b+|Wc z2mEV$D_DyyCjHAUb@LNlsPQrRc`_EpZ*#C|_u#r%TzJI5yU#xM_;~jFFI_PqB!NyF zxsevCeRJrgZtY?*{x77m8<Fp#_Lz=K6`WuYJCP$NAXyC-wnHrYQR`!O#HpuZc20A8 z*<{NjH;9~5oaJ18z=h{wnb}W8az(=0=Ho*!pOZO&RcDXEE|t{Z;XyfZJ42j>IX3M$ zrc!q%r{yJ8Qhb6)-~qG#;~~~fQu;qQYtWIt$qqx`n>Xfm$sMG4(S?*PVcmD1OMK}J zOTTkzfowA$C3AaH+J3Zu*Dk4v9xEm@vz++UzEW;8Xcdn!DRQ_tnD(uOh5l4xwZjm* zRl0&)_d6MTQQ5VdRWk4O=ifHie%Us*!vn!Xx1g=#ejlzic}N#_-O&yrWL$Bz&gbsL zi41}qXY8X>s%lX}-HzYbC`J$C)-t6=qSfbQ?oF?XiRxpO?)Pp&Le)5~ovLb|>$FJ5 zsISfKI{fW<FH|#Dq&p)nea9YjCroWgPuc}Jsv=OhqO+x*U!TDA`HK(1Gu%!dlez<n zZ|9M`pYNnj0&B$VAgO))2qI}sjq79)Z=#I3p|ka~8-=yPtRrXMrwJZBd)vBKK%siO zrFhpdou9)n2e1D88x|g|cTx;7^QF>$unqsl0hXL6T=zU{6ZSf~KuNXaR7l3uy}OK^ z$|6`#NfX9-zgzec$yUJ(sp)|y(pD&^B1u-EPpDycb<N8pma<`g-Jwmv6MZF^Q2FgC z5}bQ?r((1CBzI4p&BY-%xkWS239-vq`iSqB@-ME$@3W=gAE(Q{7paeIZi}z7sA=-< zEbe--cJO}K9Sfh>zy(xsbw%Yo3A2%;bgrWBO0hoE9+hX0H*8_uIkO<iey<$iI~M#U z^Vwohu_uV_-p6P;&Zlqt_tfTn#rF5IBCO|sDv6dW6s8YpTvSTiVDd>d3sxQK(e!Ev zoV(K-E*!_A)z7=Enkex#A)5%&@UqI>I5^Ym!R>F#SK)54l~eV$|1)%^yR!ZRP--;l ze3|W~jWyY`RofV065s8sc<gyO5zkWq4Ve`QYd{isd0}?&<D0!Lov3%xJ&Bi%S%)hO zxRXEeu*NJN6aBWhL~V-MAvjPrB~ftgYfI$8+s`Gm((h+pLsCs;b|mtyhL@*F_`Gx$ z9bpsf6AMmOypUZLUl1XbDY=vTLF6Ikkof}Uv(K^;`>;@oxex^|clR9I;u*kn*tzR( zepdAf_wGw6PMKWGdegDuUnLQDKW|(+)V@F(*^X05@;4-dFDWG#!$I9?$Bt!Zy+VVp z^GzyLk+?;u&A5QJ=ob}df^OGX+VTLz{$lhnH?qOs`J$TNx^ZaA8Y8Fgte?h$s_(@I zluN8!Ud;Q#g%7exeUijp7g-qiO(B~{{98k5O?2Z`4*smGo^?T~)uCbX8UyjePtw0* zA&hKejVAtzB0`K~cYK(~&aGJ7-&U$J0gW+tb!zd5i{Yxc#vPL<TwAIgm~%=1Q3^yd z`@)^mQ3Rlux`45aHB(Yg!Nj*8TL^^x=!A#KtzzTai(|QKBM&4-%Ow}@c5E5DqulaM zAi>&!XWI;Wtmf8v-soE4<I|lZ+xiWAZKA+Lef>HepS}_jslM8D8-0y;yo|&zT`}Z| zW8gn&rV&j5^4?i=jf{)<p>7~vudV4_b~AzjsgYf%YosqTq6wV%F+^2%c(gjLtNPF| zqR~Pb(D~#Ie!Ts$U_twMZd#(qVDO2b4^F&XxuWT=uMfa16_w8Ra!AO^#8|4mJk_yp z-LbX6jhkNY2~6Sk6sBIi1`?{uj0_FBeYnU&SQYG5tG#Brjg;~Bg4wnWZtQFKA{o&7 zFyyd+pk$HKJMru(;bn<RH%`97mg?*?yuq=X8}DL$_WpthI)lpFy?2AY-!+!gvbpfx zO<vH5`*rhUmM^7In<d8McXhY7KEbql`j&nqS2Jbq9>}I_caSJR##ear$a2f6t@3=a zR*=x3ZI^b6Yy$U&MYAbY#S<F?c=mGrSUE>YB)GVj>umOuM_;PtB{$^TFVr3~Z+-;I zP*-mc9@krVm^}UWCo8sq-Ts(_K7`xGJyBgrqZ-_{n2(uu-F3^6wzi+&qin_2i<o82 z_Z6^l(kP~D9_u~XS=`|#DtmRGf|d>EDCb95n5dq>p_O5DL*P@!3V2InJ{*tpx9@2C zls_mKZAEtaKEPHTw8g)g;&W_}R|>ayp=+>>FgpfUNjHLWNhJw!U12<(RD|IL_!@A< zTjrZ=b9%LJ7mB{jX^r0#a$t+~ME57m-RX03&pI>oHuB-&mt2Nn=(D#IA<I2zy+@`+ zZl?piA9$RLw`n+t25R|wo)@?oCF}fkBxUI}+LlSW-`7$F&?fNtE8s;6xQ?kw`<ih? zE$6_g+y*`KTiq#T$`>7>*T>gCyH|h(Gg6t-DpB;x`=ZA*3?sf?m3Eq*`{>kPb9nmM zfW-{w*kJGlXY3M{vFS57`~{n{yUDG}h>hCd_S7(Or-^ip+%iJ<hf=nPNf&<cZcwo} zW3PkEQ~%dDR$DoDOR1@KbRx|a!)UJyeB4u`s0?KYL(Zh~vH4D~?60rlKDm}a&dJy| z_l^`5b%Ev<4$)add?KBF*MaL@_upJM0NaAgiq}4oUrl{_Pi1*Nz<NPUUaP~w^A;>o zB{;Z&S3zzF-A4Va)uy~#ap`(|cdhrPWHRp+K-ucIv;u`e;8cW(Wp_Ii+`+ir6Uwq3 z7tV6}*4J1=e^W$w9q2Id?%P*<u#lNWb}xb@^($+K*|1^}r?8)9(2AS^2RL>x)#kxF zKAt|iSA0TL0nerrnc+Y%dD|)uP`gr$8zpFH(j}OsaGqH^C7$teq(;8#_0A7|2g$iR z$-2ez3JPA|Cf^O+%+!7JG%u3cTy%%EQ1~Xpe1U^sqQ07e3p*r3X7meZSj(*sjNcGU zQZv`LS5irJ16PCgH$N~L=y1(wtD!Nh!v%`N8RzpJRm#;~DfbI@GDB*FD6igF2fFRQ zD6nuGtD!Tf45!<YcM$`mZ84hL^db^W4N5EF9pi%Km{;%9EbSKq?n4uGa;U*x;nI1M zUvoNLhM#P{yi0wwFwM~Xp(SGrdFJL*{z~&3rCKuz{2$yrnp+v^vqQ4E$KTLC_p|f~ z`2_u<0bbTcr_-;y0-=OaN7yY0Y{9I)ee%GiEyHJS+(&O(ZIm!+IynJ<@GKd@meAT# zp5J9mY<|shSn`m4U{XDBe^NdXv(R-D;ytZrDQLxXe*Ns=1i8dFLmbopRR!Yl!WhEW zxXcFYYl@T^u%6{nsUz@<eC4jBvp*1)J}o3I$#gvMb(TY{LE>QH%vf&OYq_-tg;J(# zAGsYp^8>EzKZ&qvY9gO`l$-7>B0Sd32r@^Xa$f*VAmHW^PnisLOkUX=F{|2_8i;yV z!x>gT_II%EP^MUTw>I#en5yoUSFdZGdKHLmv8m|Nij%QXYwjh!_{_i9i&?eiV7G#^ z<Q6z_<%M$2?^oxmsKk~OdVh8at=*bRbk54do0o&hn*6H`T6`l<HaDN!$=5!yNz8og zp<eN_0g$SeLslI|{s??)qH%KRr)5&e<vzL`BQ55W1{)Et9tya(IV?Uk{y3-VF02x( z17)wb{9qSqUELZLHk)3dcnFXkK^Q-com6$7Sw7bw5h)ZW@KR}Kmngziu1t)PfZa-7 zo-Wc^DIHCAuWLBG=KbkAq-OU#mAOXy!6Hj(`-}5#<b!VR{0QzK?%+*n9V+OsPjI<Y zFDOiE1@YkExQGNF=c+hkQ%vhf|7FK3uQnsFjS@`LMOOl_kV1c%82`T*M-VWD<mM~F zX*1?9%vDc=F^iAkBKr~-v~L4$7UaeIHtYs*jNVmNYb-aUkFx_M8IK2)6<$(kUnuLe z!vjj>W;)KHDle@nj5!(!#9Wz@tz~IU(i1}JF155q%XQXV$WSeXq)9OW#cQ0Ol$Ppd zH~<>!8ttv}?oQgQzD9DCHn_{3T9X7cbAO}Htjo0KITMzy`{6j&|D=XZ<~dY|aXkaK zCwmxIW`LKP?9-e}+QM52&=W$^fcws5_piD2Ie&o;)uB#B_ED3^Q+ZQoPUqISXRZX^ zz76m1{2R;pRr6R2K;{|Ilu~x_`DPhGl0zuuaVcxVeVtgk;E~x+7{^SlysD-fgm$LT zw1gLX<U2aTOD$pPpw#-j${T99Qq>Rknh`4-&U~Al#8K2Ew%P<W@iTpqWe4WkVSeQo zPIJdBJsaS;``Bujf<+o;GTY8qintHA$R%00Snnh*LvM7<N<+Xt&#XG8u9NcL0Mtz( zDB&0G&?u|;H*?H;@K;RYhi$xbsSUy&t#p511H{Cev}lE_ZtC#c_C(m)ar6@>*n@2% zx0I^oD_|2gIn^Z_7j~TY#6KJho=_gJaE$I&ENUpyfUW=7kv!pkT9UD)JQ``_eSNjf zXi)EPwg|lxZo``I5qXzS@?CmB`kU*~jhe^(2QQvby_$9I!OuJ<FPjudIaelE$-T`^ zxxb6XSG?M`<;$n}vPJdfgG*v($~7zJ9jNmoh5~;Hfq(%UNo78TK)(47FIcKe&%<Vm zfZ#@Wy#z}?NzpN0QSRxobexs?+42-HOa0FrL1Wc2-#tE^HGD9w-5M5D8j$O(Hm9y; z&lSIWpYb3il65n^@1zvjp)WV)fYEl}rr2@xlCkySXw~`lZymms;oszXHD!l$S+$1T zRHEg)IuW%J$_Ff%Oq_G3NlM&|L7`&saEQG2XhMFsd|YMRmYg`n;%xPV@Z<^luqn}q zmBQ?Pjgg�XR{6;SJx?rQR~alI+~G`a8p+x4$P!Mh-6hh`l-cx#%KxIrM8`)xv>Y b#4);)59=<+A+s!w{`wi~o1-groud8^wIwiR diff --git a/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js b/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js deleted file mode 100644 index f55170336ca03..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'mageUtils', - 'Magento_Ui/js/form/components/button' -], function (utils, Button) { - 'use strict'; - - return Button.extend({ - - /** - * Creates and submits form for Guarantee create/cancel - */ - sendRequest: function () { - utils.submit({ - url: this.requestURL, - data: this.data - }); - } - }); -}); diff --git a/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml b/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml deleted file mode 100644 index 30472ae5e9ae9..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="head.components"> - <block class="Magento\Signifyd\Block\Fingerprint" name="signifyd.fingerprint" /> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml deleted file mode 100644 index 30472ae5e9ae9..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="head.components"> - <block class="Magento\Signifyd\Block\Fingerprint" name="signifyd.fingerprint" /> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml b/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml deleted file mode 100644 index 657043b895f60..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** @var $block Magento\Signifyd\Block\Fingerprint */ -?> -<?php if ($block->isModuleActive()) : ?> - <script - async - id="sig-api" - data-order-session-id="<?= $block->escapeHtml($block->getSignifydOrderSessionId()) ?>" - src="https://cdn-scripts.signifyd.com/api/script-tag.js"> - </script> -<?php endif; ?> diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less deleted file mode 100644 index 3f0875da552a3..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less +++ /dev/null @@ -1,7 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -@import 'module/_order.less'; -@import 'module/_config.less'; diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less deleted file mode 100644 index b66c9c753aa83..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less +++ /dev/null @@ -1,25 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -@image-signifyd-logo-path: '../Magento_Signifyd/images/logo.png'; - -// -// Outer slider of configuration -// -.signifyd-logo-header { - > .entry-edit-head { - > a:after { - content: url(@image-signifyd-logo-path); - } - } - - ul { - margin-left: 5em; - } - - .webhook-url { - word-break: break-all; - } -} diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less deleted file mode 100644 index d0ae877b3f18c..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less +++ /dev/null @@ -1,23 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -// -// Order Case Info -// --------------------------------------------- - -.order-case-table { - &:extend(.abs-order-tables all); - &:extend(.abs-order-tbody-border all); -} - -// -// Layout -// --------------------------------------------- -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - .case-information { - float: left; - #mix-grid .width(6,12); - } -} diff --git a/composer.json b/composer.json index 50e8db204f051..708ef7e4c15cb 100644 --- a/composer.json +++ b/composer.json @@ -241,7 +241,6 @@ "magento/module-send-friend": "*", "magento/module-send-friend-graph-ql": "*", "magento/module-shipping": "*", - "magento/module-signifyd": "*", "magento/module-sitemap": "*", "magento/module-store": "*", "magento/module-store-graph-ql": "*", diff --git a/composer.lock b/composer.lock index f6894e3c31d09..3b92c901aac8e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc4c877a835fbea7420d8477ade2fe4f", + "content-hash": "9e13134f3e209140ec6c43092c189da9", "packages": [ { "name": "braintree/braintree_php", @@ -9962,12 +9962,12 @@ }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "^3.4|^4.0|^5.0" + "symfony/expression-language": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -9994,7 +9994,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-02-13T19:40:01+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "symfony/mime", diff --git a/dev/tests/functional/credentials.xml.dist b/dev/tests/functional/credentials.xml.dist index 01e3a35be9a2d..89ea0f23ef30e 100644 --- a/dev/tests/functional/credentials.xml.dist +++ b/dev/tests/functional/credentials.xml.dist @@ -79,6 +79,4 @@ <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_username" value="" /> <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_password" value="" /> <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_signature" value="" /> - - <field path="fraud_protection/signifyd/api_key" value="" /> </replace> diff --git a/dev/tests/functional/etc/repository_replacer_payments.xml b/dev/tests/functional/etc/repository_replacer_payments.xml index a0ecca61eb372..2b20e7220f537 100644 --- a/dev/tests/functional/etc/repository_replacer_payments.xml +++ b/dev/tests/functional/etc/repository_replacer_payments.xml @@ -21,11 +21,4 @@ <field name="password" xsi:type="string">AUTHORIZENET_PASSWORD</field> </dataset> </repository> - - <repository class="Magento\Signifyd\Test\Repository\SignifydAccount"> - <dataset name="sandbox_default"> - <field name="email" xsi:type="string">SIGNIFYD_EMAIL</field> - <field name="password" xsi:type="string">SIGNIFYD_PASSWORD</field> - </dataset> - </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php deleted file mode 100644 index 54f580f48dcf4..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\Adminhtml\Order; - -use Magento\Ui\Test\Block\Adminhtml\DataGrid; - -/** - * Admin Data Grid for managing "Sales Order" entities. - */ -class Grid extends DataGrid -{ - /** - * Filters array mapping. - * - * @var array - */ - protected $filters = [ - 'id' => [ - 'selector' => '[name="increment_id"]', - ], - 'status' => [ - 'selector' => '[name="status"]', - 'input' => 'select', - ], - 'signifyd_guarantee_status' => [ - 'selector' => '[name="signifyd_guarantee_status"]', - 'input' => 'select' - ] - ]; -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php deleted file mode 100644 index e2b4e6f748977..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\Adminhtml\Order\View; - -use Magento\Mtf\Block\Block; - -/** - * Information about fraud protection on order page. - */ -class FraudProtection extends Block -{ - /** - * Case Guarantee Disposition. - * - * @var string - */ - private $caseGuaranteeDisposition = 'td.col-guarantee-disposition'; - - /** - * Get Case Guarantee Disposition status. - * - * @return string - */ - public function getCaseGuaranteeDisposition() - { - return $this->_rootElement->find($this->caseGuaranteeDisposition)->getText(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php deleted file mode 100644 index 1529755fdc3c9..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Block; - -/** - * Case information block. - */ -class CaseInfo extends Block -{ - /** - * Css selector of "Flag Case As Good" button. - * - * @var string - */ - private $flagCaseAsGoodButton = '[class*="flag-case-good"]'; - - /** - * Css selector of "Flag Case As Bad" button. - * - * @var string - */ - private $flagCaseAsBadButton = '[class*="flag-case-bad"]'; - - /** - * Css selector of guarantee status. - * - * @var string - */ - private $guaranteeDisposition = '[class*="guarantee-status"]'; - - /** - * Css selector of CVV response description. - * - * @var string - */ - private $cvvResponseDescription = '[ng-bind="caseOrderSummary.cvvResponseDescription"]'; - - /** - * Css selector of CVV response code. - * - * @var string - */ - private $cvvResponseCode = '[ng-bind="caseOrderSummary.cvvResponseCode"]'; - - /** - * Css selector of AVS response description. - * - * @var string - */ - private $avsResponseDescription = '[ng-bind="caseOrderSummary.avsResponseDescription"]'; - - /** - * Css selector of AVS response code. - * - * @var string - */ - private $avsResponseCode = '[ng-bind="caseOrderSummary.avsResponseCode"]'; - - /** - * Css selector of displayed case order id. - * - * @var string - */ - private $orderId = '[ng-bind="currentCase.caseIdDisplay"]'; - - /** - * Css selector of displayed order amount. - * - * @var string - */ - private $orderAmount = '[ng-bind*="currentCase.orderTotalAmount"]'; - - /** - * Locator value for order amount currency. - * - * @var string - */ - private $orderAmountCurrency = '[ng-bind*="currentCase.currency"]'; - - /** - * Css selector of displayed card holder name. - * - * @var string - */ - private $cardHolder = '[data-dropdown="peopleLinks0_card_holders"]'; - - /** - * Css selector of displayed billing address. - * - * @var string - */ - private $billingAddress = '[data-dropdown="streetLinks0"]'; - - /** - * Locator value for "No analysis available" block in "Device" container. - * - * @var string - */ - private $noDeviceAnalysisAvailable = '[ng-hide^="caseAnalysis.deviceAnalysis.details.length"]'; - - /** - * Locator value for "Shipping Price" block. - * - * @var string - */ - private $shippingPrice = '[ng-if="shipment.shippingPrice"]'; - - /** - * Check if device data are present. - * - * @return bool - */ - public function isAvailableDeviceData() - { - return !$this->_rootElement->find($this->noDeviceAnalysisAvailable)->isVisible(); - } - - /** - * Returns shipping price. - * - * @return string - */ - public function getShippingPrice() - { - return $this->_rootElement->find($this->shippingPrice)->getText(); - } - - /** - * Flags case as good or bad. - * - * @param string $flagType - * @return void - */ - public function flagCase($flagType) - { - $flagSelector = ($flagType === 'Good') - ? $this->flagCaseAsGoodButton - : $this->flagCaseAsBadButton; - - $this->_rootElement->find($flagSelector)->click(); - } - - /** - * Flags case as bad. - * - * @return void - */ - public function flagCaseAsBad() - { - $this->_rootElement->find($this->flagCaseAsBadButton)->click(); - } - - /** - * Gets guarantee disposition. - * - * @return string - */ - public function getGuaranteeDisposition() - { - return $this->_rootElement->find($this->guaranteeDisposition)->getText(); - } - - /** - * Gets CVV response. - * - * @return string - */ - public function getCvvResponse() - { - return sprintf( - '%s (%s)', - $this->_rootElement->find($this->cvvResponseDescription)->getText(), - $this->_rootElement->find($this->cvvResponseCode)->getText() - ); - } - - /** - * Gets AVS response. - * - * @return string - */ - public function getAvsResponse() - { - return sprintf( - '%s (%s)', - $this->_rootElement->find($this->avsResponseDescription)->getText(), - $this->_rootElement->find($this->avsResponseCode)->getText() - ); - } - - /** - * Gets displayed order id. - * - * @return string - */ - public function getOrderId() - { - return $this->_rootElement->find($this->orderId)->getText(); - } - - /** - * Gets displayed order amount. - * - * @return string - */ - public function getOrderAmount() - { - return $this->_rootElement->find($this->orderAmount)->getText(); - } - - /** - * Returns displayed order amount currency. - * - * @return string - */ - public function getOrderAmountCurrency() - { - return $this->_rootElement->find($this->orderAmountCurrency)->getText(); - } - - /** - * Gets displayed card holder name. - * - * @return string - */ - public function getCardHolder() - { - return $this->_rootElement->find($this->cardHolder)->getText(); - } - - /** - * Gets displayed billing address. - * - * @return string - */ - public function getBillingAddress() - { - return $this->_rootElement->find($this->billingAddress)->getText(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php deleted file mode 100644 index 16621f9a0cd48..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Form; - -/** - * Side block with case search and cases list. - */ -class CaseSearch extends Form -{ - /** - * Css selector of search input. - * - * @var string - */ - private $searchBar = '[id=queueSearchBar]'; - - /** - * Css selector of search submit button. - * - * @var string - */ - private $submitButton = '[type=submit]'; - - /** - * Css selector of searched element in cases list. - * - * @var string - */ - private $selectCaseLink = 'ul[case-list=cases] li[case-list-case=case] a'; - - /** - * Locator for resolving applied filters list. - * - * @var string - */ - private $appliedFilters = '.app-taglist > ul > li > a'; - - /** - * Locator for loading spinner. - * - * @var string - */ - private $spinner = '.cases-loading-spinner'; - - /** - * Fill search input with customer name and submit. - * - * @param string $customerName - * @return void - */ - public function searchCaseByCustomerName($customerName) - { - $this->resetFilters(); - $this->_rootElement->find($this->searchBar)->setValue($customerName); - $this->_rootElement->find($this->submitButton)->click(); - $this->waitLoadingSpinner(); - } - - /** - * Reset applied filters. - * - * @return void - */ - private function resetFilters(): void - { - $filters = $this->_rootElement->getElements($this->appliedFilters); - if (!empty($filters)) { - foreach ($filters as $filter) { - $filter->click(); - $this->waitLoadingSpinner(); - } - } - } - - /** - * Wait until loading spinner disappeared. - * - * @return void - */ - private function waitLoadingSpinner(): void - { - $this->waitForElementNotVisible($this->spinner); - } - - /** - * Checks if any case is visible. - * - * @return bool - */ - public function isAnyCaseVisible(): bool - { - return $this->_rootElement->find($this->selectCaseLink)->isVisible(); - } - - /** - * Select searched case. - * - * @return void - */ - public function selectCase() - { - $this->_rootElement->find($this->selectCaseLink)->click(); - } - - /** - * Waiting of case page loading. - * - * @return void - */ - public function waitForLoading() - { - $this->waitForElementVisible($this->searchBar); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php deleted file mode 100644 index 7705a67360e55..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Form; -use Magento\Mtf\Client\Element\SimpleElement; -use Magento\Mtf\Fixture\FixtureInterface; - -/** - * Signifyd login block. - */ -class SignifydLogin extends Form -{ - /** - * Css selector of Signifyd login button. - * - * @var string - */ - private $loginButton = '[type=submit]'; - - /** - * Locator for admin form notification window. - * - * @var string - */ - private $notificationCloseButton = '.wm-close-button'; - - /** - * @inheritdoc - */ - public function fill(FixtureInterface $fixture, SimpleElement $element = null) - { - $this->closeNotification(); - - return parent::fill($fixture, $element); - } - - /** - * Login to Signifyd. - * - * @return void - */ - public function login() - { - $this->closeNotification(); - $this->_rootElement->find($this->loginButton)->click(); - } - - /** - * Close notification popup. - * - * @return void - */ - private function closeNotification(): void - { - $notification = $this->browser->find($this->notificationCloseButton); - if ($notification->isVisible()) { - $notification->click(); - } - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php deleted file mode 100644 index 424d5681c07c6..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php +++ /dev/null @@ -1,216 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Block; -use Magento\Mtf\Client\ElementInterface; -use Magento\Mtf\Client\Locator; - -/** - * Signifyd webhook addition block. - */ -class Webhooks extends Block -{ - /** - * Map of webhook event select option values on names of events in webhook grid. - * - * @var array - */ - private $webhookEventOptionsMap = [ - 'CASE_CREATION' => 'Case Creation', - 'CASE_REVIEW' => 'Case Review', - 'GUARANTEE_COMPLETION' => 'Guarantee Completion' - ]; - - /** - * XPath selector of webhook element added into grid. - * - * @var string - */ - private $webhookAddedElement = '//table[@id="webhooks"]//tr[./td/span/text()="%s" and ./td/span/text()="%s"]'; - - /** - * Css selector of webhook url input. - * - * @var string - */ - private $webhookUrl = '[id="webhookUrl"]'; - - /** - * XPath selector of test team select option. - * - * @var string - */ - private $webhookTeamOption = './/select[@id="webhookTeams"]//option[text()="%s"]'; - - /** - * Css selector of webhook event select option. - * - * @var string - */ - private $webhookEventOption = 'select[id="webhookEvent"] option[value="%s"]'; - - /** - * Css selector of webhook addition button. - * - * @var string - */ - private $webhookAddButton = '[id="addWebhook"] [type=submit]'; - - /** - * Css selector of delete button in element of webhook grid. - * - * @var string - */ - private $webhookDeleteButton = '[class*="webhook-delete"]'; - - /** - * Css selector of confirming button for deleting webhook. - * - * @var string - */ - private $webhookDeleteConfirmButton = '[class="appriseOuter"] button[value="ok"]'; - - /** - * Creates new set of webhooks, if it not exists. - * - * @param string $team - * @return void - */ - public function create($team) - { - $handlerUrl = $this->getHandlerUrl(); - - foreach ($this->webhookEventOptionsMap as $webhookEventCode => $webhookEventName) { - if ($this->getWebhook($team, $webhookEventName)) { - continue; - } - - $this->addWebhook($handlerUrl, $webhookEventCode, $team); - } - } - - /** - * Deletes set of webhooks. - * - * @param string $team - * @return void - */ - public function cleanup($team) - { - foreach ($this->webhookEventOptionsMap as $webhookEventName) { - if ($webhook = $this->getWebhook($team, $webhookEventName)) { - $this->deleteWebhook($webhook); - } - } - } - - /** - * Gets webhook if exists. - * - * @param string $team - * @param string $webhookEventName - * @return ElementInterface|null - */ - private function getWebhook($team, $webhookEventName) - { - $webhook = $this->_rootElement->find( - sprintf($this->webhookAddedElement, $team, $webhookEventName), - Locator::SELECTOR_XPATH - ); - - return $webhook->isPresent() ? $webhook : null; - } - - /** - * Delete webhook element with confirmation popup. - * - * @param ElementInterface $webhook - * @return void - */ - private function deleteWebhook(ElementInterface $webhook) - { - $webhook->find($this->webhookDeleteButton)->click(); - $this->_rootElement->find($this->webhookDeleteConfirmButton)->click(); - } - - /** - * Sets webhook data and add it. - * - * @param string $handlerUrl - * @param string $webhookEventCode - * @param string $team - * @return void - */ - private function addWebhook( - $handlerUrl, - $webhookEventCode, - $team - ) { - $this->setEvent($webhookEventCode); - $this->setTeam($team); - $this->setUrl($handlerUrl); - $this->submit(); - } - - /** - * Sets appropriate webhook event select option by code. - * - * @param string $webhookEventCode - * @return void - */ - private function setEvent($webhookEventCode) - { - $this->_rootElement->find( - sprintf($this->webhookEventOption, $webhookEventCode) - )->click(); - } - - /** - * Sets test team select option. - * - * @param string $team - * @return void - */ - private function setTeam($team) - { - $this->_rootElement->find( - sprintf($this->webhookTeamOption, $team), - Locator::SELECTOR_XPATH - )->click(); - } - - /** - * Sets webhook handler url input value. - * - * @param string $handlerUrl - * @return void - */ - private function setUrl($handlerUrl) - { - $this->_rootElement->find($this->webhookUrl)->setValue($handlerUrl); - } - - /** - * Add webhook element. - * - * @return void - */ - private function submit() - { - $this->_rootElement->find($this->webhookAddButton)->click(); - } - - /** - * Gets webhook handler url. - * - * @return string - */ - private function getHandlerUrl() - { - return $_ENV['app_frontend_url'] . 'signifyd/webhooks/handler'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php deleted file mode 100644 index f85e77a5c421b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; - -/** - * Assert that comment about awaiting the Signifyd guarantee disposition - * exists in Comments History section on order page in Admin. - */ -class AssertAwaitingSignifydGuaranteeInCommentsHistory extends AbstractConstraint -{ - /** - * Expected history comment. - */ - private $historyComment = 'Awaiting the Signifyd guarantee disposition.'; - - /** - * Expected history status. - */ - private $historyCommentStatus = 'On Hold'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - - $key = array_search( - $this->historyComment, - array_column($orderComments, 'comment') - ); - - \PHPUnit\Framework\Assert::assertNotFalse( - $key, - 'There is no message about awaiting the Signifyd guarantee disposition' . - ' in Comments History section for the order #' . $orderId - ); - - \PHPUnit\Framework\Assert::assertEquals( - $this->historyCommentStatus, - $orderComments[$key]['status'], - 'Message about awaiting the Signifyd guarantee disposition' . - ' doesn\'t have status "'. $this->historyCommentStatus.'"' . - ' in Comments History section for the order #' . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about awaiting the Signifyd guarantee disposition is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php deleted file mode 100644 index 298b957131395..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Signifyd\Test\Fixture\SignifydData; - -/** - * Assert that Order Case Entity is correct on order page in Admin. - */ -class AssertCaseInfoOnAdmin extends AbstractConstraint -{ - /** - * Customized order view page. - * - * @var SalesOrderView - */ - private $orderView; - - /** - * Signifyd data fixture. - * - * @var SignifydData - */ - private $signifydData; - - /** - * Order id. - * - * @var string - */ - private $orderId; - - /** - * Assert that Signifyd Case information is correct in Admin. - * - * @param SalesOrderView $orderView - * @param OrderIndex $salesOrder - * @param SignifydData $signifydData - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $orderView, - OrderIndex $salesOrder, - SignifydData $signifydData, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - $this->orderView = $orderView; - $this->signifydData = $signifydData; - $this->orderId = $orderId; - - $this->checkCaseGuaranteeDisposition(); - } - - /** - * Checks case guarantee disposition is correct. - * - * @return void - */ - private function checkCaseGuaranteeDisposition() - { - \PHPUnit\Framework\Assert::assertEquals( - $this->signifydData->getGuaranteeDisposition(), - $this->orderView->getFraudProtectionBlock()->getCaseGuaranteeDisposition(), - 'Case Guarantee Disposition status is wrong for order #' . $this->orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Signifyd Case information is correct in Admin.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php deleted file mode 100644 index a8a1f735d004c..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Signifyd\Test\Fixture\SignifydAddress; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; - -/** - * Assert that order information is correct on Signifyd case info page. - */ -class AssertCaseInfoOnSignifydConsole extends AbstractConstraint -{ - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * @param SignifydCases $signifydCases - * @param SignifydAddress $billingAddress - * @param SignifydData $signifydData - * @param array $prices - * @param string $orderId - * @param string $customerFullName - * @return void - */ - public function processAssert( - SignifydCases $signifydCases, - SignifydAddress $billingAddress, - SignifydData $signifydData, - array $prices, - $orderId, - $customerFullName - ) { - $this->signifydCases = $signifydCases; - - $this->checkDeviceData(); - $this->checkShippingPrice($signifydData->getShippingPrice()); - $this->checkGuaranteeDisposition($signifydData->getGuaranteeDisposition()); - $cvvResponse = $signifydData->getCvvResponse(); - if (isset($cvvResponse)) { - $this->checkCvvResponse($cvvResponse); - } - $this->checkAvsResponse($signifydData->getAvsResponse()); - $this->checkOrderId($orderId); - $this->checkOrderAmount($prices['grandTotal']); - $this->checkOrderAmountCurrency($prices['grandTotalCurrency']); - $this->checkCardHolder($customerFullName); - $this->checkBillingAddress($billingAddress); - } - - /** - * Checks device data are present. - * - * @return void - */ - private function checkDeviceData() - { - \PHPUnit\Framework\Assert::assertTrue( - $this->signifydCases->getCaseInfoBlock()->isAvailableDeviceData(), - 'Device data are not available on case page in Signifyd console.' - ); - } - - /** - * Checks shipping price is correct. - * - * @param string $shippingPrice - * @return void - */ - private function checkShippingPrice($shippingPrice) - { - \PHPUnit\Framework\Assert::assertContains( - $shippingPrice, - $this->signifydCases->getCaseInfoBlock()->getShippingPrice(), - 'Shipping price is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks guarantee disposition is correct. - * - * @param string $guaranteeDisposition - * @return void - */ - private function checkGuaranteeDisposition($guaranteeDisposition) - { - \PHPUnit\Framework\Assert::assertEquals( - $guaranteeDisposition, - $this->signifydCases->getCaseInfoBlock()->getGuaranteeDisposition(), - 'Guarantee disposition is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks CVV response is correct. - * - * @param string $cvvResponse - * @return void - */ - private function checkCvvResponse($cvvResponse) - { - \PHPUnit\Framework\Assert::assertEquals( - $cvvResponse, - $this->signifydCases->getCaseInfoBlock()->getCvvResponse(), - 'CVV response is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks AVS response is correct. - * - * @param string $avsResponse - * @return void - */ - private function checkAvsResponse($avsResponse) - { - \PHPUnit\Framework\Assert::assertEquals( - $avsResponse, - $this->signifydCases->getCaseInfoBlock()->getAvsResponse(), - 'AVS response is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order id is correct. - * - * @param string $orderId - * @return void - */ - private function checkOrderId($orderId) - { - \PHPUnit\Framework\Assert::assertEquals( - $orderId, - $this->signifydCases->getCaseInfoBlock()->getOrderId(), - 'Order id is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order amount is correct. - * - * @param string $amount - * @return void - */ - private function checkOrderAmount($amount) - { - \PHPUnit\Framework\Assert::assertEquals( - number_format($amount, 2), - $this->signifydCases->getCaseInfoBlock()->getOrderAmount(), - 'Order amount is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order amount currency is correct. - * - * @param string $currency - * @return void - */ - private function checkOrderAmountCurrency($currency) - { - \PHPUnit\Framework\Assert::assertEquals( - $currency, - $this->signifydCases->getCaseInfoBlock()->getOrderAmountCurrency(), - 'Order amount currency is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks card holder is correct. - * - * @param string $customerFullName - * @return void - */ - private function checkCardHolder($customerFullName) - { - \PHPUnit\Framework\Assert::assertEquals( - $customerFullName, - $this->signifydCases->getCaseInfoBlock()->getCardHolder(), - 'Card holder name is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks billing address is correct. - * - * @param SignifydAddress $billingAddress - * @return void - */ - private function checkBillingAddress(SignifydAddress $billingAddress) - { - \PHPUnit\Framework\Assert::assertContains( - $billingAddress->getStreet(), - $this->signifydCases->getCaseInfoBlock()->getBillingAddress(), - 'Billing address is incorrect on case page in Signifyd console.' - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Case information is correct on case page in Signifyd console.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php deleted file mode 100644 index ea919f9410d4d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Assert that comment about created Signifyd Case - * exists in Comments History section on order page in Admin. - */ -class AssertSignifydCaseInCommentsHistory extends AbstractConstraint -{ - /** - * Pattern of message about created Signifyd Case in order. - */ - const CASE_CREATED_PATTERN = '/Signifyd Case (\d)+ has been created for order\./'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - $commentsMessages = array_column($orderComments, 'comment'); - - \PHPUnit\Framework\Assert::assertRegExp( - self::CASE_CREATED_PATTERN, - implode('. ', $commentsMessages), - 'Signifyd case is not created for the order #' . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about Signifyd Case is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php deleted file mode 100644 index bdf3598d4f46a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\Adminhtml\OrdersGrid; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Assert that Signifyd Guarantee Status is present in Orders grid. - */ -class AssertSignifydCaseInOrdersGrid extends AbstractConstraint -{ - /** - * @param string $orderId - * @param OrdersGrid $ordersGrid - * @param SignifydData $signifydData - * @return void - */ - public function processAssert( - $orderId, - OrdersGrid $ordersGrid, - SignifydData $signifydData - ) { - $filter = [ - 'id' => $orderId, - 'signifyd_guarantee_status' => $signifydData->getGuaranteeDisposition() - ]; - - $errorMessage = implode(', ', $filter); - - $ordersGrid->open(); - - \PHPUnit\Framework\Assert::assertTrue( - $ordersGrid->getSignifydOrdersGrid()->isRowVisible(array_filter($filter)), - 'Order with following data \'' . $errorMessage . '\' is absent in orders grid.' - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Signifyd guarantee status is displayed in sales orders grid.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php deleted file mode 100644 index 618d77885736c..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; - -/** - * Assert that comment about created Signifyd Case guarantee - * has been cancelled exists in Comments History section on order page in Admin. - */ -class AssertSignifydGuaranteeCancelInCommentsHistory extends AbstractConstraint -{ - /** - * Signifyd case guarantee cancel message in order view. - */ - private $guaranteeCancelMessage = 'Case Update: Case guarantee has been cancelled.'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - $commentsMessages = array_column($orderComments, 'comment'); - - \PHPUnit\Framework\Assert::assertContains( - $this->guaranteeCancelMessage, - implode('. ', $commentsMessages), - 'There is no message regarding Signifyd guarantee cancel in Comments History section for the order #' - . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about Signifyd guarantee cancel is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml deleted file mode 100644 index 8b1455811d003..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifyd_account" - module="Magento_Signifyd" - type="virtual" - repository_class="Magento\Signifyd\Test\Repository\SignifydAccount" - class="Magento\Signifyd\Test\Fixture\SignifydAccount"> - <field name="email" /> - <field name="password" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml deleted file mode 100644 index 02b5cc8211d89..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.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. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifydAddress" - module="Magento_Signifyd" - class="Magento\Signifyd\Test\Fixture\SignifydAddress" - extends="\Magento\Customer\Test\Fixture\Address"> - <field name="firstname" source="Magento\Signifyd\Test\Fixture\SignifydAddress\Firstname" is_required="1" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php deleted file mode 100644 index 9e4930484eef0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Fixture\SignifydAddress; - -use Magento\Mtf\Fixture\DataSource; - -/** - * Source handler for `firstname` field in shipping address fixture. - */ -class Firstname extends DataSource -{ - /** - * @param string $data - */ - public function __construct($data = '') - { - $this->data = $data; - } - - /** - * Add isolation for `firstname` field. - * - * @param null $key - * @return string - */ - public function getData($key = null) - { - $this->data = str_replace('%signifyd_isolation%', $this->generateIsolation(), $this->data); - - return parent::getData($key); - } - - /** - * Generates character isolation. - * - * @param int $length - * @return string - */ - private function generateIsolation($length = 10) - { - return substr(str_shuffle(str_repeat("abcdefghijklmnopqrstuvwxyz", $length)), 0, $length); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml deleted file mode 100644 index 23ee2cddf434a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifyd_data" - module="Magento_Signifyd" - type="virtual" - repository_class="Magento\Signifyd\Test\Repository\SignifydData" - class="Magento\Signifyd\Test\Fixture\SignifydData"> - <field name="team" /> - <field name="caseFlag" /> - <field name="guaranteeDisposition" /> - <field name="cvvResponse" /> - <field name="avsResponse" /> - <field name="shippingPrice" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml deleted file mode 100644 index 749d91c3ed395..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="ordersGrid" area="Adminhtml" mca="sales/order" module="Magento_Signifyd"> - <block name="signifydOrdersGrid" class="Magento\Signifyd\Test\Block\Adminhtml\Order\Grid" locator="//div[contains(@data-bind, 'sales_order_grid')]" strategy="xpath" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml deleted file mode 100644 index 36d77723dfcee..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SalesOrderView" area="Adminhtml" mca="sales/order/view"> - <block name="fraudProtectionBlock" class="Magento\Signifyd\Test\Block\Adminhtml\Order\View\FraudProtection" locator=".admin__page-section.order-case-info" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml deleted file mode 100644 index cfe889bb8de44..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydCases" area="SignifydConsole" mca="https://app.signifyd.com/cases" module="Magento_Signifyd"> - <block name="caseSearchBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\CaseSearch" locator="[class$=app-sidebar]" strategy="css selector" /> - <block name="caseInfoBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\CaseInfo" locator="[class*=app-wrapper]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml deleted file mode 100644 index 5fe5da6d28013..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydLogin" area="SignifydConsole" mca="https://app.signifyd.com/login" module="Magento_Signifyd"> - <block name="loginBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\SignifydLogin" locator="[id=loginForm]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml deleted file mode 100644 index dd12f14c28800..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydNotifications" area="SignifydConsole" mca="https://app.signifyd.com/settings/notifications" module="Magento_Signifyd"> - <block name="webhooksBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\Webhooks" locator="[id=body]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml deleted file mode 100644 index a534cbc6107fe..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Customer\Test\Repository\Address"> - <dataset name="signifyd_us_shipping_address"> - <field name="firstname" xsi:type="string">John%signifyd_isolation%</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="company" xsi:type="string">Magento</field> - <field name="city" xsi:type="string">Culver City</field> - <field name="street" xsi:type="string">6161 West Centinela Avenue</field> - <field name="telephone" xsi:type="string">555-55-555-55</field> - <field name="country_id" xsi:type="string">United States</field> - <field name="region_id" xsi:type="string">California</field> - <field name="postcode" xsi:type="string">90230</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml deleted file mode 100644 index 2af46f077b304..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Config\Test\Repository\ConfigData"> - <dataset name="signifyd"> - <field name="fraud_protection/signifyd/active" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="fraud_protection/signifyd/config/api_key" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">SIGNIFYD_CONFIG_API_KEY</item> - </field> - <field name="fraud_protection/signifyd/api_url" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://api.signifyd.com/v2/</item> - </field> - <field name="fraud_protection/signifyd/debug" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - </dataset> - <dataset name="signifyd_rollback"> - <field name="fraud_protection/signifyd/active" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">0</item> - </field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml deleted file mode 100644 index b45e3d01b8ec8..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Customer\Test\Repository\Customer"> - <dataset name="signifyd_approve_us_customer"> - <field name="firstname" xsi:type="string">John</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="email" xsi:type="string">testapprove@magento.com</field> - <field name="password" xsi:type="string">123123^q</field> - <field name="password_confirmation" xsi:type="string">123123^q</field> - </dataset> - <dataset name="signifyd_decline_us_customer"> - <field name="firstname" xsi:type="string">John</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="email" xsi:type="string">testdecline@magento.com</field> - <field name="password" xsi:type="string">123123^q</field> - <field name="password_confirmation" xsi:type="string">123123^q</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml deleted file mode 100644 index 4d3dd5229eae3..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Signifyd\Test\Repository\SignifydAccount"> - <dataset name="sandbox_default"> - <field name="email" xsi:type="string">SIGNIFYD_EMAIL</field> - <field name="password" xsi:type="string">SIGNIFYD_PASSWORD</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml deleted file mode 100644 index 02037534b0149..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Signifyd\Test\Repository\SignifydData"> - <dataset name="signifyd_guarantee_approve"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Good</field> - <field name="guaranteeDisposition" xsi:type="string">Approved</field> - <field name="cvvResponse" xsi:type="string">CVV2 Match (M)</field> - <field name="avsResponse" xsi:type="string">Full match (Y)</field> - <field name="shippingPrice" xsi:type="string">USD 5.00</field> - </dataset> - <dataset name="signifyd_guarantee_decline"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Bad</field> - <field name="guaranteeDisposition" xsi:type="string">Declined</field> - <field name="cvvResponse" xsi:type="string">CVV2 Match (M)</field> - <field name="avsResponse" xsi:type="string">Full match (Y)</field> - <field name="shippingPrice" xsi:type="string">USD 5.00</field> - </dataset> - <dataset name="signifyd_guarantee_fraudulent"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Bad</field> - <field name="guaranteeDisposition" xsi:type="string">Declined</field> - <field name="shippingPrice" xsi:type="string">GBP 10.00</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php deleted file mode 100644 index 1dd742c9f7096..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select Hosted Pro method. - * 10. Click 'Continue' button. - * 11. Specify credit card data in Paypal iframe. - * 12. Click 'Pay Now' button. - * 13. Log in to Signifyd account and search for created case. - * 14. Open created case. - * 15. Click "Flag case as bad" button. - * 16. Perform case info assertions. - * 17. Log in to Admin. - * 18. Proceed to order grid. - * 19. Perform Signifyd guarantee status assertions. - * 20. Proceed to order view. - * 21. Perform order status and case info assertions. - * 22. Click Accept Payment button. - * 23. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-65333 - */ -class AcceptPaymentWithSignifydGuaranteeDeclinedTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S2'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml deleted file mode 100644 index 6391081e5e161..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\AcceptPaymentWithSignifydGuaranteeDeclinedTest" - summary="One Page Checkout with Signifyd, PayPal Payments Pro Hosted Solution with Fraud filters triggered."> - <variation name="AcceptPaymentWithSignifydGuaranteeDeclinedWithHostedProVariation1" summary="Accept order placed within PayPal Payments Pro Hosted Solution with Fraud filters triggered and Signifyd Guarantee Declined" ticketId="MAGETWO-65333"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_100_dollar</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">hosted_pro</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">210.00</item> - <item name="grandTotalCurrency" xsi:type="string">GBP</item> - </data> - <data name="creditCardClass" xsi:type="string">credit_card_hostedpro</data> - <data name="creditCard/dataset" xsi:type="string">visa_hosted_pro</data> - <data name="isVaultPresent" xsi:type="boolean">false</data> - <data name="configData" xsi:type="string">merchant_country_gb, hosted_pro, config_base_currency_gb, signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Suspected Fraud</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_fraudulent</data> - <data name="status" xsi:type="string">Processing</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S2</data> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertAcceptPaymentMessageInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php deleted file mode 100644 index 5ca76ff70479f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select payment method. - * 10. Specify credit card data. - * 11. Click 'Place order' button. - * 12. Search for created case. - * 13. Open created case. - * 14. Click "Flag case as good" or "Flag case as bad" button. - * 15. Perform case info assertions. - * 16. Log in to Admin. - * 17. Proceed to order grid. - * 18. Perform Signifyd guarantee status assertions. - * 19. Proceed to order view. - * 20. Perform order status and case info assertions. - * 21. Click Cancel button. - * 22. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-62120, MAGETWO-63221, MAGETWO-64305, MAGETWO-65253 - */ -class CreateSignifydGuaranteeAndCancelOrderTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S1'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml deleted file mode 100644 index 082627fe5821d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\CreateSignifydGuaranteeAndCancelOrderTest" - summary="One Page Checkout with Signifyd and cancel order."> - <variation name="CreateSignifydGuaranteeAndCancelOrderWithBraintreeVariation1" - summary="Cancel order placed within Braintree credit card with Signifyd approved guarantee." - ticketId="MAGETWO-62120,MAGETWO-63221"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="customer/dataset" xsi:type="string">signifyd_approve_us_customer</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">braintree</data> - <data name="paymentForm" xsi:type="string">braintree</data> - <data name="prices/grandTotal" xsi:type="string">15.00</data> - <data name="prices/grandTotalCurrency" xsi:type="string">USD</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="creditCard/data/payment_code" xsi:type="string">braintree</data> - <data name="configData" xsi:type="string">braintree,signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Processing</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_approve</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCanceled" /> - <constraint name="Magento\Sales\Test\Constraint\AssertCancelInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertAwaitingSignifydGuaranteeInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory" /> - </variation> - <variation name="CreateSignifydGuaranteeAndCancelOrderWithBraintreeVariation2" - summary="Cancel order placed within Braintree credit card with Signifyd declined guarantee." - ticketId="MAGETWO-64305, MAGETWO-65253"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">braintree</data> - <data name="paymentForm" xsi:type="string">braintree</data> - <data name="prices/grandTotal" xsi:type="string">15.00</data> - <data name="prices/grandTotalCurrency" xsi:type="string">USD</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="creditCard/data/payment_code" xsi:type="string">braintree</data> - <data name="configData" xsi:type="string">braintree,signifyd</data> - <data name="placeOrderStatus" xsi:type="string">On Hold</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_decline</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCanceled" /> - <constraint name="Magento\Sales\Test\Constraint\AssertCancelInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertAwaitingSignifydGuaranteeInCommentsHistory" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php deleted file mode 100644 index 698861da26018..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select Hosted Pro method. - * 10. Click 'Continue' button. - * 11. Specify credit card data in Paypal iframe. - * 12. Click 'Pay Now' button. - * 13. Log in to Signifyd account and search for created case. - * 14. Open created case. - * 15. Click "Flag case as bad" button. - * 16. Perform case info assertions. - * 17. Log in to Admin. - * 18. Proceed to order grid. - * 19. Perform Signifyd guarantee status assertions. - * 20. Proceed to order view. - * 21. Perform order status and case info assertions. - * 22. Click Deny Payment button. - * 23. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-65332 - */ -class DenyPaymentWithSignifydGuaranteeDeclinedTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S2'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml deleted file mode 100644 index 5496619fe1bf9..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\DenyPaymentWithSignifydGuaranteeDeclinedTest" - summary="One Page Checkout with Signifyd, PayPal Payments Pro Hosted Solution with Fraud filters triggered."> - <variation name="DenyPaymentWithSignifydGuaranteeDeclinedWithHostedProVariation1" summary="Deny order placed within PayPal Payments Pro Hosted Solution with Fraud filters triggered and Signifyd Guarantee Declined" ticketId="MAGETWO-65332"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_100_dollar</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">hosted_pro</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">210.00</item> - <item name="grandTotalCurrency" xsi:type="string">GBP</item> - </data> - <data name="creditCardClass" xsi:type="string">credit_card_hostedpro</data> - <data name="creditCard/dataset" xsi:type="string">visa_hosted_pro</data> - <data name="isVaultPresent" xsi:type="boolean">false</data> - <data name="configData" xsi:type="string">merchant_country_gb, hosted_pro, config_base_currency_gb, signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Suspected Fraud</data> - <data name="status" xsi:type="string">Canceled</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_fraudulent</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S2</data> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertDenyPaymentMessageInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php deleted file mode 100644 index 409dffc8340b7..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect as AssertOrderStatus; -use Magento\Signifyd\Test\Constraint\AssertSignifydCaseInOrdersGrid as AssertOrdersGrid; -use Magento\Signifyd\Test\Constraint\AssertCaseInfoOnAdmin; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\Adminhtml\OrdersGrid; - -/** - * Open order grid step. - */ -class OpenOrderGridStep implements TestStepInterface -{ - /** - * Magento order status assertion. - * - * @var AssertOrderStatus - */ - private $assertOrderStatus; - - /** - * Case information on Magento Admin assertion. - * - * @var AssertCaseInfoOnAdmin - */ - private $assertCaseInfo; - - /** - * Case information on Magento order grid assertion. - * - * @var AssertOrdersGrid - */ - private $assertOrdersGrid; - - /** - * Magento order status. - * - * @var string - */ - private $placeOrderStatus; - - /** - * Magento order id. - * - * @var int - */ - private $orderId; - - /** - * Order View Page. - * - * @var SalesOrderView - */ - private $salesOrderView; - - /** - * Orders grid page. - * - * @var OrdersGrid - */ - private $ordersGrid; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * Orders Page. - * - * @var OrderIndex - */ - private $orderIndex; - - /** - * @param string $placeOrderStatus - * @param int $orderId - * @param OrderIndex $orderIndex - * @param SalesOrderView $salesOrderView - * @param OrdersGrid $ordersGrid - * @param AssertOrderStatus $assertOrderStatus - * @param AssertCaseInfoOnAdmin $assertCaseInfo - * @param AssertOrdersGrid $assertOrdersGrid - * @param SignifydData $signifydData - */ - public function __construct( - $placeOrderStatus, - $orderId, - OrderIndex $orderIndex, - SalesOrderView $salesOrderView, - OrdersGrid $ordersGrid, - AssertOrderStatus $assertOrderStatus, - AssertCaseInfoOnAdmin $assertCaseInfo, - AssertOrdersGrid $assertOrdersGrid, - SignifydData $signifydData - ) { - $this->placeOrderStatus = $placeOrderStatus; - $this->orderId = $orderId; - $this->orderIndex = $orderIndex; - $this->salesOrderView = $salesOrderView; - $this->ordersGrid = $ordersGrid; - $this->assertOrderStatus = $assertOrderStatus; - $this->assertCaseInfo = $assertCaseInfo; - $this->assertOrdersGrid = $assertOrdersGrid; - $this->signifydData = $signifydData; - } - - /** - * Open order. - * - * @return void - */ - public function run() - { - $this->checkOrdersGrid(); - $this->checkCaseInfo(); - $this->checkOrderStatus(); - } - - /** - * Run assert to check Signifyd Case Disposition status in orders grid. - * - * @return void - */ - private function checkOrdersGrid() - { - $this->assertOrdersGrid->processAssert( - $this->orderId, - $this->ordersGrid, - $this->signifydData - ); - } - - /** - * Run assert to check order status is valid. - * - * @return void - */ - private function checkOrderStatus() - { - $this->assertOrderStatus->processAssert( - $this->placeOrderStatus, - $this->orderId, - $this->orderIndex, - $this->salesOrderView - ); - } - - /** - * Run assert to check Signifyd Case information is correct in Admin. - * - * @return void - */ - private function checkCaseInfo() - { - $this->assertCaseInfo->processAssert( - $this->salesOrderView, - $this->orderIndex, - $this->signifydData, - $this->orderId - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php deleted file mode 100644 index 54f4dd75223e0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Sales\Test\TestStep\CancelOrderStep; -use Magento\Sales\Test\TestStep\DenyPaymentStep; -use Magento\Sales\Test\TestStep\UnholdOrderStep; - -/** - * Rollback step for Signifyd scenarios. - */ -class SignifydCancelOrderStep implements TestStepInterface -{ - /** - * Order index page. - * - * @var OrderIndex - */ - private $orderIndex; - - /** - * Order fixture. - * - * @var OrderInjectable - */ - private $order; - - /** - * Order View page. - * - * @var SalesOrderView - */ - private $salesOrderView; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param OrderIndex $orderIndex - * @param OrderInjectable $order - * @param SalesOrderView $salesOrderView - * @param TestStepFactory $testStepFactory - */ - public function __construct( - OrderIndex $orderIndex, - OrderInjectable $order, - SalesOrderView $salesOrderView, - TestStepFactory $testStepFactory - ) { - $this->orderIndex = $orderIndex; - $this->order = $order; - $this->salesOrderView = $salesOrderView; - $this->testStepFactory = $testStepFactory; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->orderIndex->open(); - $this->orderIndex->getSalesOrderGrid() - ->searchAndOpen(['id' => $this->order->getId()]); - - switch ($this->salesOrderView->getOrderInfoBlock()->getOrderStatus()) { - case 'Suspected Fraud': - $this->getStepInstance(DenyPaymentStep::class)->run(); - break; - case 'On Hold': - $this->getStepInstance(UnholdOrderStep::class)->run(); - $this->getStepInstance(CancelOrderStep::class)->run(); - break; - case 'Canceled': - break; - default: - $this->getStepInstance(CancelOrderStep::class)->run(); - } - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['order' => $this->order] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php deleted file mode 100644 index 5fec5988d2c9f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Customer\Test\Fixture\Customer; -use Magento\Customer\Test\TestStep\CreateCustomerStep; -use Magento\Customer\Test\TestStep\DeleteCustomerStep; -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; - -/** - * Customized create customer step with remove cleanup. - */ -class SignifydCreateCustomerStep implements TestStepInterface -{ - /** - * Customer fixture. - * - * @var Customer - */ - private $customer; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param Customer $customer - * @param TestStepFactory $testStepFactory - */ - public function __construct( - Customer $customer, - TestStepFactory $testStepFactory - ) { - $this->customer = $customer; - $this->testStepFactory = $testStepFactory; - } - - /** - * Run step flow. - * - * @return void - */ - public function run() - { - $this->getStepInstance(CreateCustomerStep::class)->run(); - } - - /** - * @return void - */ - public function cleanup() - { - $this->getStepInstance(CreateCustomerStep::class)->cleanup(); - $this->getStepInstance(DeleteCustomerStep::class)->run(); - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['customer' => $this->customer] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php deleted file mode 100644 index 0b1dda1d0a46a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Checkout\Test\TestStep\FillShippingAddressStep; -use Magento\Checkout\Test\Page\CheckoutOnepage; -use Magento\Mtf\Fixture\FixtureFactory; -use Magento\Customer\Test\Fixture\Customer; -use Magento\Mtf\ObjectManager; -use Magento\Signifyd\Test\Fixture\SignifydAddress; - -/** - * Class SignifydFillShippingAddressStep only overrides type of $shippingAddress. - * - * We cannot configure Checkout_FillShippingAddressStep this via di.xml, - * because 'source' handler Firstname, specified in SignifydAddress - * fixture did not apply when the step was called from testcase.xml scenario. - * - * Note. When fixture was called directly in the class constructor, - * source handlers applied correctly. - */ -class SignifydFillShippingAddressStep extends FillShippingAddressStep -{ - /** - * @var SignifydAddress|null - */ - private $signifydAddress; - - /** - * @param CheckoutOnepage $checkoutOnepage - * @param Customer $customer - * @param ObjectManager $objectManager - * @param FixtureFactory $fixtureFactory - * @param SignifydAddress|null $signifydAddress - * @param null $shippingAddressCustomer - */ - public function __construct( - CheckoutOnepage $checkoutOnepage, - Customer $customer, - ObjectManager $objectManager, - FixtureFactory $fixtureFactory, - SignifydAddress $signifydAddress = null, - $shippingAddressCustomer = null - ) { - parent::__construct( - $checkoutOnepage, - $customer, - $objectManager, - $fixtureFactory, - $signifydAddress, - $shippingAddressCustomer - ); - $this->signifydAddress = $signifydAddress; - } - - /** - * @inheritdoc - */ - public function run() - { - parent::run(); - - return [ - 'signifydAddress' => $this->signifydAddress, - ]; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php deleted file mode 100644 index 3dd1b94d6b505..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Signifyd\Test\Fixture\SignifydAccount; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydLogin; - -/** - * Login into Signifyd console step. - */ -class SignifydLoginStep implements TestStepInterface -{ - /** - * Signifyd account fixture. - * - * @var SignifydAccount - */ - private $signifydAccount; - - /** - * Signifyd login page. - * - * @var SignifydLogin - */ - private $signifydLogin; - - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * @param SignifydAccount $signifydAccount - * @param SignifydLogin $signifydLogin - * @param SignifydCases $signifydCases - */ - public function __construct( - SignifydAccount $signifydAccount, - SignifydLogin $signifydLogin, - SignifydCases $signifydCases - ) { - $this->signifydAccount = $signifydAccount; - $this->signifydLogin = $signifydLogin; - $this->signifydCases = $signifydCases; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydLogin->open(); - - if ($this->signifydLogin->getLoginBlock()->isVisible()) { - $this->signifydLogin->getLoginBlock()->fill($this->signifydAccount); - $this->signifydLogin->getLoginBlock()->login(); - } - - $this->signifydCases->getCaseSearchBlock()->waitForLoading(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php deleted file mode 100644 index c00c81fa237e0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php +++ /dev/null @@ -1,165 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Signifyd\Test\Constraint\AssertCaseInfoOnSignifydConsole; -use Magento\Signifyd\Test\Fixture\SignifydAddress; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydNotifications; - -/** - * Observe case information in Signifyd console step. - */ -class SignifydObserveCaseStep implements TestStepInterface -{ - /** - * Case information on Signifyd console assertion. - * - * @var AssertCaseInfoOnSignifydConsole - */ - private $assertCaseInfo; - - /** - * Billing address fixture. - * - * @var SignifydAddress - */ - private $signifydAddress; - - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * Signifyd notifications page. - * - * @var SignifydNotifications - */ - private $signifydNotifications; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * Prices list. - * - * @var array - */ - private $prices; - - /** - * Order fixture. - * - * @var string - */ - private $order; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @var int - */ - private $searchAttempts = 10; - - /** - * @param AssertCaseInfoOnSignifydConsole $assertCaseInfoOnSignifydConsole - * @param SignifydAddress $signifydAddress - * @param SignifydCases $signifydCases - * @param SignifydNotifications $signifydNotifications - * @param SignifydData $signifydData - * @param OrderInjectable $order - * @param TestStepFactory $testStepFactory - * @param array $prices - */ - public function __construct( - AssertCaseInfoOnSignifydConsole $assertCaseInfoOnSignifydConsole, - SignifydAddress $signifydAddress, - SignifydCases $signifydCases, - SignifydNotifications $signifydNotifications, - SignifydData $signifydData, - OrderInjectable $order, - TestStepFactory $testStepFactory, - array $prices - ) { - $this->assertCaseInfo = $assertCaseInfoOnSignifydConsole; - $this->signifydAddress = $signifydAddress; - $this->signifydCases = $signifydCases; - $this->signifydNotifications = $signifydNotifications; - $this->signifydData = $signifydData; - $this->order = $order; - $this->testStepFactory = $testStepFactory; - $this->prices = $prices; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydCases->open(); - // Search case few times because it can appear with delay. - for ($attempts = $this->searchAttempts; $attempts > 0; $attempts--) { - $this->signifydCases->getCaseSearchBlock() - ->searchCaseByCustomerName($this->signifydAddress->getFirstname()); - if ($this->signifydCases->getCaseSearchBlock()->isAnyCaseVisible()) { - break; - } - sleep(3); - } - - $this->signifydCases->getCaseSearchBlock()->selectCase(); - $this->signifydCases->getCaseInfoBlock()->flagCase($this->signifydData->getCaseFlag()); - - $this->assertCaseInfo->processAssert( - $this->signifydCases, - $this->signifydAddress, - $this->signifydData, - $this->prices, - $this->order->getId(), - $this->getCustomerFullName($this->signifydAddress) - ); - } - - /** - * Cancel order if test fails, or in the end of variation. - * - * @return void - */ - public function cleanup() - { - $this->testStepFactory->create( - SignifydCancelOrderStep::class, - ['order' => $this->order] - )->run(); - } - - /** - * Gets customer full name. - * - * @param SignifydAddress $billingAddress - * @return string - */ - private function getCustomerFullName(SignifydAddress $billingAddress) - { - return sprintf('%s %s', $billingAddress->getFirstname(), $billingAddress->getLastname()); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php deleted file mode 100644 index 2b630b8c1dac8..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydNotifications; - -/** - * Set webhook handlers in Signifyd console step. - */ -class SignifydSetWebhookHandlersStep implements TestStepInterface -{ - /** - * Signifyd Notifications page. - * - * @var SignifydNotifications - */ - private $signifydNotifications; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * @param SignifydNotifications $signifydNotifications - * @param SignifydData $signifydData - */ - public function __construct( - SignifydNotifications $signifydNotifications, - SignifydData $signifydData - ) { - $this->signifydNotifications = $signifydNotifications; - $this->signifydData = $signifydData; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydNotifications->open(); - $this->signifydNotifications->getWebhooksBlock() - ->create($this->signifydData->getTeam()); - } - - /** - * Removes webhooks if test fails, or in the end of variation execution. - * - * @return void - */ - public function cleanup() - { - $this->signifydNotifications->open(); - $this->signifydNotifications->getWebhooksBlock() - ->cleanup($this->signifydData->getTeam()); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php deleted file mode 100644 index 0c90b1b76937b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Sales\Test\TestStep\CancelOrderStep; -use Magento\Sales\Test\TestStep\UnholdOrderStep; - -/** - * Unhold and cancel order. - */ -class UnholdAndCancelOrderStep implements TestStepInterface -{ - /** - * Magento order status. - * - * @var string - */ - private $placeOrderStatus; - - /** - * Order fixture. - * - * @var OrderInjectable - */ - private $order; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param string $placeOrderStatus - * @param OrderInjectable $order - * @param TestStepFactory $testStepFactory - */ - public function __construct( - $placeOrderStatus, - OrderInjectable $order, - TestStepFactory $testStepFactory - ) { - $this->placeOrderStatus = $placeOrderStatus; - $this->order = $order; - $this->testStepFactory = $testStepFactory; - } - - /** - * Cancel order step. - * - * If order was held - unhold and then cancel the order. - * - * @return void - */ - public function run() - { - if ($this->placeOrderStatus === 'On Hold') { - $this->getStepInstance(UnholdOrderStep::class)->run(); - } - - $this->getStepInstance(CancelOrderStep::class)->run(); - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['order' => $this->order] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml deleted file mode 100644 index 2ba4b6c97d33f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Signifyd\Test\Constraint\AssertCaseInfoOnAdmin"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertCaseInfoOnSignifydConsole"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInOrdersGrid"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory"> - <arguments> - <argument name="severity" xsi:type="string">S2</argument> - </arguments> - </type> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml deleted file mode 100644 index cfbd2e6ace2b4..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/TestCase/etc/testcase.xsd"> - <scenario name="CreateSignifydGuaranteeAndCancelOrderTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="selectPaymentMethod" /> - <step name="selectPaymentMethod" module="Magento_Checkout" next="placeOrder" /> - <step name="placeOrder" module="Magento_Checkout" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="unholdAndCancelOrder" /> - <step name="unholdAndCancelOrder" module="Magento_Signifyd" /> - </scenario> - <scenario name="AcceptPaymentWithSignifydGuaranteeDeclinedTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="placeOrderWithHostedPro" /> - <step name="placeOrderWithHostedPro" module="Magento_Paypal" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="acceptPayment" /> - <step name="acceptPayment" module="Magento_Sales" /> - </scenario> - <scenario name="DenyPaymentWithSignifydGuaranteeDeclinedTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="placeOrderWithHostedPro" /> - <step name="placeOrderWithHostedPro" module="Magento_Paypal" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="denyPayment" /> - <step name="denyPayment" module="Magento_Sales" /> - </scenario> -</config> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock index e723549288cda..69b6d314ee64d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock @@ -8437,45 +8437,6 @@ ], "description": "N/A" }, - { - "name": "magento/module-signifyd", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-signifyd/magento-module-signifyd-100.3.1.0.zip", - "reference": null, - "shasum": "768442b807e31bacfb5bbf914e155bac3ca88dde" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-customer": "102.0.*", - "magento/module-directory": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "Submitting Case Entry to Signifyd on Order Creation" - }, { "name": "magento/module-sitemap", "version": "100.3.1", @@ -9816,7 +9777,6 @@ "magento/module-send-friend": "100.3.1", "magento/module-send-friend-graph-ql": "100.3.0", "magento/module-shipping": "100.3.1", - "magento/module-signifyd": "100.3.1", "magento/module-sitemap": "100.3.1", "magento/module-store": "101.0.1", "magento/module-store-graph-ql": "100.3.1", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock index 2e2fa561fa4cc..151c2b5eca565 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock @@ -8437,45 +8437,6 @@ ], "description": "N/A" }, - { - "name": "magento/module-signifyd", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-signifyd/magento-module-signifyd-100.3.1.0.zip", - "reference": null, - "shasum": "768442b807e31bacfb5bbf914e155bac3ca88dde" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-customer": "102.0.*", - "magento/module-directory": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "Submitting Case Entry to Signifyd on Order Creation" - }, { "name": "magento/module-sitemap", "version": "100.3.1", @@ -9816,7 +9777,6 @@ "magento/module-send-friend": "100.3.1", "magento/module-send-friend-graph-ql": "100.3.0", "magento/module-shipping": "100.3.1", - "magento/module-signifyd": "100.3.1", "magento/module-sitemap": "100.3.1", "magento/module-store": "101.0.1", "magento/module-store-graph-ql": "100.3.1", diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php deleted file mode 100644 index 7607526b1389e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml; - -use Magento\Framework\App\RequestInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\View\Element\Template\Context; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; - -class CaseInfoTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Order - */ - private $order; - - /** - * @var \Magento\Framework\View\LayoutFactory - */ - protected $layoutFactory; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->order = $this->objectManager->create(Order::class); - $this->layoutFactory = $this->objectManager->get(\Magento\Framework\View\LayoutFactory::class); - } - - /** - * Checks that block has contents when case entity for order is exists - * even if Signifyd module is inactive. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 0 - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoAppArea adminhtml - */ - public function testModuleIsInactive() - { - $this->order->loadByIncrementId('100000001'); - - self::assertNotEmpty($this->getBlock()->toHtml()); - } - - /** - * Checks that block does not give contents - * if there is no case entity created for order. - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseEntity - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @magentoAppArea adminhtml - */ - public function testCaseEntityNotExists() - { - $this->order->loadByIncrementId('100000001'); - - self::assertEmpty($this->getBlock()->toHtml()); - } - - /** - * Checks that: - * - block give contents - * - block contents guarantee decision field - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getScoreClass - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoAppArea adminhtml - */ - public function testCaseEntityExists() - { - $this->order->loadByIncrementId('100000001'); - - $block = $this->getBlock(); - self::assertNotEmpty($block->toHtml()); - self::assertContains((string) $block->getCaseGuaranteeDisposition(), $block->toHtml()); - } - - /** - * Gets block. - * - * @return CaseInfo - */ - private function getBlock() - { - $layout = $this->layoutFactory->create(); - - $layout->addContainer('order_additional_info', 'Container'); - - /** @var CaseInfo $block */ - $block = $layout->addBlock(CaseInfo::class, 'order_case_info', 'order_additional_info'); - $block->setAttribute('context', $this->getContext()); - $block->setTemplate('Magento_Signifyd::case_info.phtml'); - - return $block; - } - - /** - * Creates template context with necessary order id param. - * - * @return Context - */ - private function getContext() - { - /** @var RequestInterface $request */ - $request = $this->objectManager->get(RequestInterface::class); - $request->setParams(['order_id' => $this->order->getEntityId()]); - - return $this->objectManager->create(Context::class, ['request' => $request]); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php deleted file mode 100644 index 1efbb8b6d33e4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block; - -use Magento\Framework\App\Area; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\View\LayoutInterface; -use Magento\TestFramework\Helper\Bootstrap; - -class FingerprintTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - - $this->objectManager = Bootstrap::getObjectManager(); - } - - /** - * Checks if session id attribute is present when the module is enabled. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testSessionIdPresent() - { - self::assertContains('data-order-session-id', $this->getBlockContents()); - } - - /** - * Checks if block is an empty when the module is disabled. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 0 - */ - public function testBlockEmpty() - { - self::assertEmpty($this->getBlockContents()); - } - - /** - * Renders block contents. - * - * @return string - */ - private function getBlockContents() - { - $block = $this->objectManager->get(LayoutInterface::class) - ->createBlock(Fingerprint::class); - - return $block->fetchView($block->getTemplateFile()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php deleted file mode 100644 index 667cb079f9096..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Controller\Webhooks; - -use Magento\TestFramework\TestCase\AbstractController; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; - -/** - * Class tests handling webhook post from Signifyd service. - */ -class HandlerTest extends AbstractController -{ - /** - * @var string - */ - private static $entryPoint = 'signifyd/webhooks/handler'; - - /** - * Tests handling webhook message of cases/rescore type. - * Checks updated case entity and comment in order history. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoConfigFixture current_store fraud_protection/signifyd/api_key ApFZZvxGgIxuP8BazSm3v8eGN - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testExecuteSuccess() - { - $caseId = 123; - $webhookRequest = $this->getWebhookRequest(); - $this->_objectManager->addSharedInstance($webhookRequest, WebhookRequest::class); - - $this->dispatch(self::$entryPoint); - - /** @var CaseRepositoryInterface $caseManagement */ - $caseRepository = $this->_objectManager->get(CaseRepositoryInterface::class); - /** @var CaseInterface $caseEntity */ - $caseEntity = $caseRepository->getByCaseId($caseId); - $orderEntityId = $caseEntity->getOrderId(); - - self::assertNotEmpty($caseEntity); - self::assertEquals('2017-01-06 12:47:03', $caseEntity->getCreatedAt()); - self::assertEquals('2017-01-06 12:47:03', $caseEntity->getUpdatedAt()); - self::assertEquals('Magento', $caseEntity->getAssociatedTeam()['teamName']); - self::assertEquals(true, $caseEntity->isGuaranteeEligible()); - self::assertEquals(CaseInterface::STATUS_OPEN, $caseEntity->getStatus()); - self::assertEquals($orderEntityId, $caseEntity->getOrderId()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseComment); - - self::assertEquals( - "Case Update: New score for the order is 384. Previous score was 553.", - $caseComment->getComment() - ); - - $this->_objectManager->removeSharedInstance(WebhookRequest::class); - } - - /** - * Tests handling webhook message of cases/test type. - * Controller should response with code 200. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testExecuteTestSuccess() - { - $webhookRequest = $this->getTestWebhookRequest(); - $this->_objectManager->addSharedInstance($webhookRequest, WebhookRequest::class); - $this->dispatch(self::$entryPoint); - $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); - $this->_objectManager->removeSharedInstance(WebhookRequest::class); - } - - /** - * Returns mocked WebhookRequest - * - * @return WebhookRequest|\PHPUnit\Framework\MockObject_MockObject - */ - private function getWebhookRequest() - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../../_files/webhook_body.json')); - $webhookRequest->expects($this->any()) - ->method('getEventTopic') - ->willReturn('cases/rescore'); - $webhookRequest->expects($this->any()) - ->method('getHash') - ->willReturn('m/X29RcHWPSCDPgQuSXjnyTfKISJDopcdGbVsRLeqy8='); - - return $webhookRequest; - } - - /** - * Returns mocked test WebhookRequest - * - * @return WebhookRequest|\PHPUnit\Framework\MockObject_MockObject - */ - private function getTestWebhookRequest() - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../../_files/webhook_body.json')); - $webhookRequest->expects($this->any()) - ->method('getEventTopic') - ->willReturn('cases/test'); - $webhookRequest->expects($this->any()) - ->method('getHash') - ->willReturn('wyG0r9mOmv1IqVlN6ZqJ5sgA635yKW6lbSsqlYF2b8U='); - - return $webhookRequest; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php deleted file mode 100644 index 30603baf867ff..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Contains test methods for case management service - */ -class CaseManagementTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setup() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->caseManagement = $this->objectManager->get(CaseManagement::class); - } - - /** - * @covers \Magento\Signifyd\Model\CaseManagement::create - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreate() - { - $order = $this->getOrder(); - $case = $this->caseManagement->create($order->getEntityId()); - - self::assertNotEmpty($case->getEntityId()); - self::assertEquals(CaseInterface::STATUS_PENDING, $case->getStatus()); - self::assertEquals(CaseInterface::GUARANTEE_PENDING, $case->getGuaranteeDisposition()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseManagement::getByOrderId - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testGetByOrderId() - { - $order = $this->getOrder(); - $case = $this->caseManagement->getByOrderId($order->getEntityId()); - - self::assertEquals(CaseInterface::STATUS_PROCESSING, $case->getStatus()); - self::assertEquals(CaseInterface::DISPOSITION_GOOD, $case->getReviewDisposition()); - self::assertEquals('2016-12-12 15:17:17', $case->getCreatedAt()); - self::assertEquals('2016-12-12 19:23:16', $case->getUpdatedAt()); - } - - /** - * Get stored order - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php deleted file mode 100644 index ca98a20b15bec..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Testing case repository - */ -class CaseRepositoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var CaseRepository - */ - private $repository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->filterBuilder = $this->objectManager->get(FilterBuilder::class); - $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $this->repository = $this->objectManager->get(CaseRepository::class); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::delete - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testDelete() - { - $filters = [ - $this->filterBuilder->setField('case_id') - ->setValue(123) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $cases = $this->repository->getList($searchCriteria) - ->getItems(); - - $case = array_pop($cases); - $this->repository->delete($case); - - self::assertEmpty($this->repository->getList($searchCriteria)->getItems()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getById - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testGetById() - { - $filters = [ - $this->filterBuilder->setField('case_id') - ->setValue(123) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $cases = $this->repository->getList($searchCriteria) - ->getItems(); - - $case = array_pop($cases); - - $found = $this->repository->getById($case->getEntityId()); - - self::assertNotEmpty($found->getEntityId()); - self::assertEquals($case->getEntityId(), $found->getEntityId()); - self::assertEquals($case->getOrderId(), $found->getOrderId()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getList - * @magentoDataFixture Magento/Signifyd/_files/multiple_cases.php - */ - public function testGetListDateInterval() - { - $startDateInterval = [ - $this->filterBuilder->setField('created_at') - ->setConditionType('gteq') - ->setValue('2016-12-01 00:00:01') - ->create() - ]; - $endDateInterval = [ - $this->filterBuilder->setField('created_at') - ->setConditionType('lteq') - ->setValue('2016-12-03 23:59:59') - ->create() - ]; - - $this->searchCriteriaBuilder->addFilters($startDateInterval); - $searchCriteria = $this->searchCriteriaBuilder->addFilters($endDateInterval)->create(); - $items = $this->repository->getList($searchCriteria) - ->getItems(); - - self::assertCount(3, $items); - - for ($i = 1; $i < 4; $i ++) { - $current = array_shift($items); - self::assertEquals($i, $current->getCaseId()); - } - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getList - * @magentoDataFixture Magento/Signifyd/_files/multiple_cases.php - */ - public function testGetListStatusProcessing() - { - $filters = [ - $this->filterBuilder->setField('status') - ->setValue(CaseInterface::STATUS_PROCESSING) - ->create() - ]; - - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $items = $this->repository->getList($searchCriteria) - ->getItems(); - - self::assertCount(1, $items); - - $case = array_pop($items); - self::assertEquals(123, $case->getCaseId()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php deleted file mode 100644 index 6cf1e0bfddc9a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\ResourceModel\Order\Grid\Collection; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\Signifyd\Model\SignifydGateway\Client\RequestBuilder; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Class tests interaction with Signifyd Case creation service - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreationServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var OrderInterface - */ - private $order; - - /** - * @var RequestBuilder|MockObject - */ - private $requestBuilder; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CreationService - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->requestBuilder = $this->getMockBuilder(RequestBuilder::class) - ->disableOriginalConstructor() - ->setMethods(['doRequest']) - ->getMock(); - - $apiClient = $this->objectManager->create( - ApiClient::class, - ['requestBuilder' => $this->requestBuilder] - ); - - $gateway = $this->objectManager->create( - Gateway::class, - ['apiClient' => $apiClient] - ); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['error']) - ->getMockForAbstractClass(); - - $this->service = $this->objectManager->create( - CreationService::class, - [ - 'signifydGateway' => $gateway, - 'logger' => $this->logger - ] - ); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrderWithEmptyResponse() - { - $order = $this->getOrder(); - $exceptionMessage = 'Response is not valid JSON: Decoding failed: Syntax error'; - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willThrowException(new ApiCallException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with($exceptionMessage); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrderWithBadResponse() - { - $order = $this->getOrder(); - $responseData = [ - 'messages' => [ - 'Something wrong' - ] - ]; - $exceptionMessage = 'Bad Request - The request could not be parsed. Response: ' . json_encode($responseData); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willThrowException(new ApiCallException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with($exceptionMessage); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateOrderWithEmptyInvestigationId() - { - $order = $this->getOrder(); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willReturn([]); - - $this->logger->expects(self::once()) - ->method('error') - ->with('Expected field "investigationId" missed.'); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrder() - { - $order = $this->getOrder(); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willReturn(['investigationId' => 123123]); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(123123); - $gridGuarantyStatus = $this->getOrderGridGuarantyStatus($caseEntity->getOrderId()); - - self::assertNotEmpty($caseEntity); - self::assertEquals($order->getEntityId(), $caseEntity->getOrderId()); - self::assertEquals( - $gridGuarantyStatus, - $caseEntity->getGuaranteeDisposition(), - 'Signifyd guaranty status in sales_order_grid table does not match case entity guaranty status' - ); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - self::assertEquals(Order::STATE_HOLDED, $order->getState()); - - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $orderHoldComment */ - $orderHoldComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $orderHoldComment); - self::assertEquals("Awaiting the Signifyd guarantee disposition.", $orderHoldComment->getComment()); - } - - /** - * Get stored order - * - * @return OrderInterface - */ - private function getOrder() - { - if ($this->order === null) { - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - $this->order = array_pop($orders); - } - - return $this->order; - } - - /** - * Returns value of signifyd_guarantee_status column from sales order grid - * - * @param int $orderEntityId - * @return string|null - */ - private function getOrderGridGuarantyStatus($orderEntityId) - { - /** @var Collection $orderGridCollection */ - $orderGridCollection = $this->objectManager->get(Collection::class); - - $items = $orderGridCollection->addFilter($orderGridCollection->getIdFieldName(), $orderEntityId) - ->getItems(); - $result = array_pop($items); - - return isset($result['signifyd_guarantee_status']) ? $result['signifyd_guarantee_status'] : null; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php deleted file mode 100644 index 50e510ca072be..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\ResourceModel\Order\Grid\Collection; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Contains tests for case entity updating service. - */ -class UpdatingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var UpdatingService - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - /** @var GeneratorFactory $messageFactory */ - $messageFactory = $this->objectManager->get(GeneratorFactory::class); - $messageGenerator = $messageFactory->create('cases/creation'); - - $this->service = $this->objectManager->create(UpdatingService::class, [ - 'messageGenerator' => $messageGenerator - ]); - } - - /** - * Checks case updating flow and messages in order comments history. - * Also checks that order is unholded when case guarantee disposition is APPROVED. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testUpdate() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'score' => 750, - 'orderId' => '100000001', - 'reviewDisposition' => CaseInterface::DISPOSITION_FRAUDULENT, - 'associatedTeam' => [ - 'teamName' => 'AnyTeam', - 'teamId' => 26, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 2 - ], - 'createdAt' => '2017-01-05T14:23:26-0800', - 'updatedAt' => '2017-01-05T14:44:26-0800', - 'guaranteeDisposition' => CaseInterface::GUARANTEE_APPROVED - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - /** @var CaseInterface $caseEntity */ - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - $caseEntity = $caseRepository->getByCaseId($caseId); - $orderEntityId = $caseEntity->getOrderId(); - $gridGuarantyStatus = $this->getOrderGridGuarantyStatus($orderEntityId); - - self::assertNotEmpty($caseEntity); - self::assertEquals('2017-01-05 22:23:26', $caseEntity->getCreatedAt()); - self::assertEquals(CaseInterface::GUARANTEE_APPROVED, $caseEntity->getGuaranteeDisposition()); - self::assertEquals('AnyTeam', $caseEntity->getAssociatedTeam()['teamName']); - self::assertEquals(true, $caseEntity->isGuaranteeEligible()); - self::assertEquals(CaseInterface::STATUS_PROCESSING, $caseEntity->getStatus()); - self::assertEquals($orderEntityId, $caseEntity->getOrderId()); - self::assertEquals( - $gridGuarantyStatus, - $caseEntity->getGuaranteeDisposition(), - 'Signifyd guaranty status in sales_order_grid table does not match case entity guaranty status' - ); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - self::assertEquals(Order::STATE_PROCESSING, $order->getState()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals("Signifyd Case $caseId has been created for order.", $caseCreationComment->getComment()); - } - - /** - * Checks that order is holded when case guarantee disposition is DECLINED. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - */ - public function testOrderStateAfterDeclinedGuaranteeDisposition() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => '100000001', - 'guaranteeDisposition' => CaseInterface::GUARANTEE_DECLINED - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - - self::assertEquals(Order::STATE_HOLDED, $order->getState()); - } - - /** - * Checks that order doesn't become holded - * when previous case guarantee disposition was DECLINED - * and webhook without guarantee disposition was received. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/declined_case.php - */ - public function testOrderStateAfterWebhookWithoutGuaranteeDisposition() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => '100000001' - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - - self::assertEquals(Order::STATE_PROCESSING, $order->getState()); - } - - /** - * Returns value of signifyd_guarantee_status column from sales order grid - * - * @param int $orderEntityId - * @return string|null - */ - private function getOrderGridGuarantyStatus($orderEntityId) - { - /** @var Collection $orderGridCollection */ - $orderGridCollection = $this->objectManager->get(Collection::class); - - $items = $orderGridCollection->addFilter($orderGridCollection->getIdFieldName(), $orderEntityId) - ->getItems(); - $result = array_pop($items); - - return isset($result['signifyd_guarantee_status']) ? $result['signifyd_guarantee_status'] : null; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php deleted file mode 100644 index 2cc7a9a1f240a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains test cases for canceling Signifyd guarantee flow. - */ -class CancelingServiceTest extends \PHPUnit\Framework\TestCase -{ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CancelingService; - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['cancelGuarantee']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->create(CancelingService::class, [ - 'gateway' => $this->gateway, - 'logger' => $this->logger - ]); - } - - /** - * Checks a test case, when Signifyd guarantee was canceled. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrderWithCanceledGuarantee() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - $caseEntity->setGuaranteeDisposition(CaseInterface::GUARANTEE_CANCELED); - $caseRepository->save($caseEntity); - - $this->gateway->expects(self::never()) - ->method('cancelGuarantee'); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when Signifyd gateway throws an exception. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrderWithFailedRequest() - { - $exceptionMessage = 'Something wrong.'; - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willThrowException(new GatewayException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with(self::equalTo($exceptionMessage)); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to cancel is submitted and case entity is updated successfully. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrder() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willReturn(CaseInterface::GUARANTEE_CANCELED); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertTrue($result); - - $updatedCase = $caseRepository->getByCaseId(self::$caseId); - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $updatedCase->getGuaranteeDisposition()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($updatedCase->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals('Case Update: Case guarantee has been cancelled.', $caseCreationComment->getComment()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php deleted file mode 100644 index 157e3270648b3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php +++ /dev/null @@ -1,155 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains positive and negative test cases for Signifyd case guarantee creation flow. - */ -class CreationServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CreationService - */ - private $service; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - /** @var ObjectManager $objectManager */ - $this->objectManager = Bootstrap::getObjectManager(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['submitCaseForGuarantee']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->create(CreationService::class, [ - 'gateway' => $this->gateway, - 'logger' => $this->logger - ]); - } - - /** - * Checks a test case, when Signifyd case entity cannot be found - * for a specified order. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - */ - public function testCreateWithoutCaseEntity() - { - $orderId = 123; - - $this->gateway->expects(self::never()) - ->method('submitCaseForGuarantee'); - - $result = $this->service->createForOrder($orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request is failing. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testCreateWithFailedRequest() - { - $caseEntity = $this->getCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('submitCaseForGuarantee') - ->willThrowException(new GatewayException('Something wrong')); - - $this->logger->expects(self::once()) - ->method('error') - ->with('Something wrong'); - - $result = $this->service->createForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when case entity is updated successfully. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCreate() - { - $caseEntity = $this->getCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('submitCaseForGuarantee') - ->with($caseEntity->getCaseId()) - ->willReturn(CaseInterface::GUARANTEE_IN_REVIEW); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->createForOrder($caseEntity->getOrderId()); - self::assertTrue($result); - - $updatedCase = $this->getCaseEntity(); - self::assertEquals(CaseInterface::GUARANTEE_IN_REVIEW, $updatedCase->getGuaranteeDisposition()); - self::assertEquals(CaseInterface::STATUS_PROCESSING, $updatedCase->getStatus()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($updatedCase->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals('Case Update: Case is submitted for guarantee.', $caseCreationComment->getComment()); - } - - /** - * Gets case entity. - * - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - */ - private function getCaseEntity() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - return $caseRepository->getByCaseId(123); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php deleted file mode 100644 index 594c065185ac7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php +++ /dev/null @@ -1,291 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\Config\ScopeInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Area; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Framework\ObjectManagerInterface; -use Magento\Sales\Model\Order; -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Framework\App\ProductMetadataInterface; - -/** - * Class CreateCaseBuilderTest - * @magentoAppIsolation enabled - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateCaseBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var CreateCaseBuilder - */ - private $caseBuilder; - - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * Initial setup - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - $this->objectManager = Bootstrap::getObjectManager(); - $this->dateTimeFactory = $this->objectManager->create(DateTimeFactory::class); - $this->caseBuilder = $this->objectManager->create(CreateCaseBuilder::class); - } - - /** - * Test builder on order with customer, simple product, frontend area, - * PayPal gateway, shipping and billing addresses, with two orders - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder::build() - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testCreateCaseBuilderWithFullSetOfData() - { - /** @var Order $order */ - $order = $this->objectManager->create(Order::class); - $order->loadByIncrementId('100000001'); - - $orderItems = $order->getAllItems(); - $payment = $order->getPayment(); - $billingAddress = $order->getBillingAddress(); - $shippingAddress = $order->getShippingAddress(); - - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class); - $customer = $customerRepository->getById($order->getCustomerId()); - - $productMetadata = $this->objectManager->create(ProductMetadataInterface::class); - - /** @var SignifydOrderSessionId $signifydOrderSessionId */ - $signifydOrderSessionId = $this->objectManager->create(SignifydOrderSessionId::class); - - $expected = [ - 'purchase' => [ - 'orderSessionId' => $signifydOrderSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => date('c', strtotime(date('Y-m-d 00:00:55'))), - 'paymentGateway' => 'paypal_account', - 'transactionId' => $payment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => '', - 'cvvResponseCode' => '', - 'orderChannel' => 'WEB', - 'totalPrice' => $order->getGrandTotal(), - 'shipments' => [ - 0 => [ - 'shipper' => 'Flat Rate', - 'shippingMethod' => 'Fixed', - 'shippingPrice' => $order->getShippingAmount() - ] - ], - 'products' => [ - 0 => [ - 'itemId' => $orderItems[0]->getSku(), - 'itemName' => $orderItems[0]->getName(), - 'itemPrice' => $orderItems[0]->getPrice(), - 'itemQuantity' => $orderItems[0]->getQtyOrdered(), - 'itemUrl' => $orderItems[0]->getProduct()->getProductUrl(), - 'itemWeight' => $orderItems[0]->getProduct()->getWeight() - ], - 1 => [ - 'itemId' => $orderItems[1]->getSku(), - 'itemName' => $orderItems[1]->getName(), - 'itemPrice' => $orderItems[1]->getPrice(), - 'itemQuantity' => $orderItems[1]->getQtyOrdered(), - 'itemUrl' => $orderItems[1]->getProduct()->getProductUrl(), - 'itemWeight' => $orderItems[1]->getProduct()->getWeight() - ] - ], - 'paymentMethod' => 'PAYPAL_ACCOUNT' - ], - 'card' => [ - 'cardHolderName' => 'firstname lastname', - 'last4' => $payment->getCcLast4(), - 'expiryMonth' => $payment->getCcExpMonth(), - 'expiryYear' => $payment->getCcExpYear(), - 'billingAddress' => [ - 'streetAddress' => 'street', - 'city' => $billingAddress->getCity(), - 'provinceCode' => $billingAddress->getRegionCode(), - 'postalCode' => $billingAddress->getPostcode(), - 'countryCode' => $billingAddress->getCountryId(), - 'unit' => '' - ] - ], - 'recipient' => [ - 'fullName' => $shippingAddress->getName(), - 'confirmationEmail' => $shippingAddress->getEmail(), - 'confirmationPhone' => $shippingAddress->getTelephone(), - 'deliveryAddress' => [ - 'streetAddress' => '6161 West Centinela Avenue', - 'unit' => 'app. 33', - 'city' => $shippingAddress->getCity(), - 'provinceCode' => $shippingAddress->getRegionCode(), - 'postalCode' => $shippingAddress->getPostcode(), - 'countryCode' => $shippingAddress->getCountryId() - ] - ], - 'userAccount' => [ - 'email' => $customer->getEmail(), - 'username' => $customer->getEmail(), - 'phone' => $order->getBillingAddress()->getTelephone(), - 'accountNumber' => $customer->getId(), - 'createdDate' => $this->formatDate($customer->getCreatedAt()), - 'lastUpdateDate' => $this->formatDate($customer->getUpdatedAt()), - 'aggregateOrderCount' => 2, - 'aggregateOrderDollars' => 150.0 - ], - 'seller' => $this->getSellerData(), - 'platformAndClient' => [ - 'storePlatform' => $productMetadata->getName() . ' ' . $productMetadata->getEdition(), - 'storePlatformVersion' => $productMetadata->getVersion(), - 'signifydClientApp' => $productMetadata->getName(), - 'signifydClientAppVersion' => '1.0' - ] - ]; - - self::assertEquals( - $expected, - $this->caseBuilder->build($order->getEntityId()) - ); - } - - /** - * Test builder on order with guest, virtual product, admin area, - * none PayPal gateway, no shipping address, without credit card data - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder::build() - * @magentoDataFixture Magento/Signifyd/_files/order_with_guest_and_virtual_product.php - */ - public function testCreateCaseBuilderWithVirtualProductAndGuest() - { - /** @var Order $order */ - $order = $this->objectManager->create(Order::class); - $order->loadByIncrementId('100000002'); - - $scope = $this->objectManager->get(ScopeInterface::class); - $scope->setCurrentScope(Area::AREA_ADMINHTML); - - $orderItems = $order->getAllItems(); - $product = $orderItems[0]->getProduct(); - $payment = $order->getPayment(); - $billingAddress = $order->getBillingAddress(); - $productMetadata = $this->objectManager->create(ProductMetadataInterface::class); - - /** @var SignifydOrderSessionId $quoteSessionId */ - $quoteSessionId = $this->objectManager->create(SignifydOrderSessionId::class); - - $expected = [ - 'purchase' => [ - 'orderSessionId' => $quoteSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => '2016-12-12T12:00:55+00:00', - 'paymentGateway' => $payment->getMethod(), - 'transactionId' => $payment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => 'Y', - 'cvvResponseCode' => 'M', - 'orderChannel' => 'PHONE', - 'totalPrice' => $order->getGrandTotal(), - 'products' => [ - 0 => [ - 'itemId' => $orderItems[0]->getSku(), - 'itemName' => $orderItems[0]->getName(), - 'itemPrice' => $orderItems[0]->getPrice(), - 'itemQuantity' => $orderItems[0]->getQtyOrdered(), - 'itemUrl' => $product->getProductUrl() - ], - ], - 'paymentMethod' => 'PAYMENT_CARD' - ], - 'card' => [ - 'cardHolderName' => 'firstname lastname', - 'billingAddress' => [ - 'streetAddress' => 'street', - 'city' => $billingAddress->getCity(), - 'provinceCode' => $billingAddress->getRegionCode(), - 'postalCode' => $billingAddress->getPostcode(), - 'countryCode' => $billingAddress->getCountryId(), - 'unit' => '' - ] - ], - 'seller' => $this->getSellerData(), - 'platformAndClient' => [ - 'storePlatform' => $productMetadata->getName() . ' ' . $productMetadata->getEdition(), - 'storePlatformVersion' => $productMetadata->getVersion(), - 'signifydClientApp' => $productMetadata->getName(), - 'signifydClientAppVersion' => '1.0' - ] - ]; - - self::assertEquals( - $expected, - $this->caseBuilder->build($order->getEntityId()) - ); - } - - /** - * Return seller data according to fixture - * - * @return array - */ - private function getSellerData() - { - return [ - 'name' => 'Sample Store', - 'domain' => 'm2.com', - 'shipFromAddress' => [ - 'streetAddress' => '6161 West Centinela Avenue', - 'unit' => 'app. 111', - 'city' => 'Culver City', - 'provinceCode' => 'AE', - 'postalCode' => '90230', - 'countryCode' => 'US', - ], - 'corporateAddress' => [ - 'streetAddress' => '5th Avenue', - 'unit' => '75', - 'city' => 'New York', - 'provinceCode' => 'MH', - 'postalCode' => '19032', - 'countryCode' => 'US', - ], - ]; - } - - /** - * Format date in ISO8601 - * - * @param string $date - * @return string - */ - private function formatDate($date) - { - $result = $this->dateTimeFactory->create( - $date, - new \DateTimeZone('UTC') - ); - - return $result->format(\DateTime::ATOM); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php deleted file mode 100644 index e547187be5ed7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php +++ /dev/null @@ -1,218 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Observer; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Test for Magento\Signifyd\Observer\PlaceOrderTest class. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PlaceOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseCreationServiceInterface|MockObject - */ - private $creationService; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var PlaceOrder - */ - private $placeOrder; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->creationService = $this->getMockBuilder(CaseCreationServiceInterface::class) - ->disableOriginalConstructor() - ->setMethods(['createForOrder']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->placeOrder = $this->objectManager->create(PlaceOrder::class, [ - 'caseCreationService' => $this->creationService, - 'logger' => $this->logger - ]); - } - - /** - * Checks a case when order placed with offline payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecuteWithOfflinePayment() - { - $order = $this->getOrder('100000005'); - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order] - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Checks a test case when order placed with online payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecute() - { - $order = $this->getOrder('100000001'); - - $this->creationService->expects(self::once()) - ->method('createForOrder') - ->with(self::equalTo($order->getEntityId())); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order], - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Signifyd is enabled for default store. - * Checks a test case when order placed with website where signifyd is disabled. - * - * @return void - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @magentoDataFixture Magento/Signifyd/_files/website_configuration.php - */ - public function testExecuteWithWebsiteConfiguration(): void - { - /** @var StoreRepositoryInterface $storeRepository */ - $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); - $store = $storeRepository->get('test_second_store'); - - /** @var StoreManagerInterface $storeManager */ - $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore($store->getId()); - - $order = $this->getOrder('100000001'); - $order->setStoreId($store->getId()); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order], - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Checks a test case when observer event contains two orders: - * one order with offline payment and one order with online payment. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecuteWithMultipleOrders() - { - $orderWithOnlinePayment = $this->getOrder('100000001'); - $orderWithOfflinePayment = $this->getOrder('100000005'); - - // this service mock should be called only once for the order with online payment method. - $this->creationService->expects(self::once()) - ->method('createForOrder') - ->with(self::equalTo($orderWithOnlinePayment->getEntityId())); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['orders' => [$orderWithOfflinePayment, $orderWithOnlinePayment]] - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Gets stored order. - * - * @param string $incrementId - * @return OrderInterface - */ - private function getOrder($incrementId) - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $incrementId) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - $order = array_pop($orders); - - return $order; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php deleted file mode 100644 index 7c1af95bdb89c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -class CancelOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->setMethods(['makeApiCall']) - ->getMock(); - - $this->objectManager->addSharedInstance($this->apiClient, ApiClient::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ApiClient::class); - } - - /** - * Checks a test case, when order has been cancelled - * and calls plugin to cancel Signifyd case guarantee. - * - * @covers \Magento\Signifyd\Plugin\OrderPlugin::afterCancel - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testAfterCancel() - { - $order = $this->getOrder(); - - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - self::equalTo('/cases/' . self::$caseId . '/guarantee'), - 'PUT', - [ - 'guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED - ] - ) - ->willReturn([ - 'disposition' => CaseInterface::GUARANTEE_CANCELED - ]); - - /** @var OrderManagementInterface $orderService */ - $orderService = $this->objectManager->get(OrderManagementInterface::class); - $orderService->cancel($order->getEntityId()); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $case = $caseRepository->getByCaseId(self::$caseId); - - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $case->getGuaranteeDisposition()); - } - - /** - * Get stored order. - * - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php deleted file mode 100644 index 72da71a630dff..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php +++ /dev/null @@ -1,209 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Registry; -use Magento\Payment\Model\Info as PaymentInfo; -use Magento\Paypal\Model\Api\Nvp; -use Magento\Paypal\Model\Config; -use Magento\Paypal\Model\Express; -use Magento\Paypal\Model\Info; -use Magento\Paypal\Model\Pro; -use Magento\Paypal\Model\ProFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class DenyPaymentTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->setMethods(['makeApiCall']) - ->getMock(); - - $this->registry = $this->objectManager->get(Registry::class); - - $this->objectManager->addSharedInstance($this->apiClient, ApiClient::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ApiClient::class); - } - - /** - * Checks a test case, when payment has been denied - * and calls plugin to cancel Signifyd case guarantee. - * - * @covers \Magento\Signifyd\Plugin\PaymentPlugin::afterDenyPayment - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testAfterDenyPayment() - { - $order = $this->getOrder(); - $this->registry->register('current_order', $order); - - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - self::equalTo('/cases/' . self::$caseId . '/guarantee'), - 'PUT', - [ - 'guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED - ] - ) - ->willReturn([ - 'disposition' => CaseInterface::GUARANTEE_CANCELED - ]); - - /** @var \Magento\Sales\Model\Order\Payment $payment */ - $payment = $order->getPayment(); - $payment->setData('method_instance', $this->getMethodInstance()); - $payment->deny(); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $case = $caseRepository->getByCaseId(self::$caseId); - - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $case->getGuaranteeDisposition()); - } - - /** - * Get stored order. - * - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } - - /** - * Gets payment method instance. - * - * @return Express - */ - private function getMethodInstance() - { - /** @var PaymentInfo $infoInstance */ - $infoInstance = $this->objectManager->get(PaymentInfo::class); - $infoInstance->setAdditionalInformation( - Info::PAYMENT_STATUS_GLOBAL, - Info::PAYMENTSTATUS_PENDING - ); - $infoInstance->setAdditionalInformation( - Info::PENDING_REASON_GLOBAL, - Info::PAYMENTSTATUS_PENDING - ); - - /** @var Express $methodInstance */ - $methodInstance = $this->objectManager->create( - Express::class, - ['proFactory' => $this->getProFactory()] - ); - $methodInstance->setData('info_instance', $infoInstance); - - return $methodInstance; - } - - /** - * Gets Pro factory mock. - * - * @return ProFactory|MockObject - */ - protected function getProFactory() - { - $pro = $this->getMockBuilder(Pro::class) - ->disableOriginalConstructor() - ->setMethods(['getApi', 'setMethod', 'getConfig', '__wakeup', 'reviewPayment']) - ->getMock(); - $nvpClient = $this->getMockBuilder(Nvp::class) - ->disableOriginalConstructor() - ->getMock(); - - $pro->method('getConfig') - ->willReturn($this->getConfig()); - $pro->method('getApi') - ->willReturn($nvpClient); - $pro->method('reviewPayment') - ->willReturn(true); - - $proFactory = $this->getMockBuilder(ProFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $proFactory->method('create') - ->willReturn($pro); - - return $proFactory; - } - - /** - * Gets config mock. - * - * @return Config|MockObject - */ - protected function getConfig() - { - $config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - $config->method('getValue') - ->with('payment_action') - ->willReturn(Config::PAYMENT_ACTION_AUTH); - - return $config; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php deleted file mode 100644 index eaa3622c04e0e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(false) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_APPROVED) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_GOOD) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php deleted file mode 100644 index 4102233f6fb1f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var OrderManagementInterface $orderManagement */ -$orderManagement = $objectManager->create(OrderManagementInterface::class); -$orderManagement->hold($order->getEntityId()); - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(true) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_GOOD) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_PENDING) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php deleted file mode 100644 index 7c5f34cd203fa..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Customer\Model\Customer; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\CustomerRegistry; - -$objectManager = Bootstrap::getObjectManager(); -/** @var CustomerRegistry $customerRegistry */ -$customerRegistry = $objectManager->get(CustomerRegistry::class); -$customer = $objectManager->create(Customer::class); - -/** @var CustomerInterface $customer */ -$customer->setWebsiteId(1) - ->setId(1) - ->setEmail('customer@example.com') - ->setGroupId(1) - ->setStoreId(1) - ->setPrefix('Mr.') - ->setFirstname('John') - ->setMiddlename('A') - ->setLastname('Smith') - ->setSuffix('Esq.') - ->setDefaultBilling(1) - ->setDefaultShipping(1) - ->setTaxvat('12') - ->setGender(0) - ->setCreatedAt('2016-12-12T11:00:00+0000') - ->setUpdatedAt('2016-12-12T11:05:00+0000'); - -$customer->isObjectNew(true); -$customer->save(); - -$customerRegistry->remove($customer->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php deleted file mode 100644 index 041cdb0291d83..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(false) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_DECLINED) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_FRAUDULENT) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php deleted file mode 100644 index 4930906954148..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Signifyd\Api\Data\CaseInterface; - -require __DIR__ . '/case.php'; - -for ($i = 1; $i < 4; $i ++) { - $newOrder = clone $order; - $newOrder->setEntityId(null) - ->setIncrementId($order->getIncrementId() + $i); - - $orderRepository->save($newOrder); - - $newCase = clone $case; - $newCase->setEntityId(null) - ->setCaseId($i) - ->setOrderId($newOrder->getEntityId()) - ->setStatus(CaseInterface::STATUS_OPEN) - ->setCreatedAt('2016-12-0' . $i . 'T15:' . $i . ':17+0000') - ->setUpdatedAt('2016-12-12T0' . $i . ':23:16+0000') - ->setId(null); - - $caseRepository->save($newCase); -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php deleted file mode 100644 index 49a0a2d33e236..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\Helper\Bootstrap; - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; -require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; -require __DIR__ . '/store.php'; - -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; - -$objectManager = Bootstrap::getObjectManager(); - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping') - ->setStreet(['6161 West Centinela Avenue', 'app. 33']) - ->setFirstname('John') - ->setLastname('Doe') - ->setShippingMethod('flatrate_flatrate'); - -$payment = $objectManager->create(Payment::class); -$payment->setMethod('paypal_express') - ->setLastTransId('00001') - ->setCcLast4('1234') - ->setCcExpMonth('01') - ->setCcExpYear('21'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -/** @var Item $orderItem */ -$orderItem2 = $objectManager->create(Item::class); -$orderItem2->setProductId($product->getId()) - ->setSku('simple2') - ->setName('Simple product') - ->setPrice(100) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000001') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt(date('Y-m-d 00:00:55')) - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->addItem($orderItem2) - ->setPayment($payment) - ->setQuoteId(1); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -$orderAmount2 = 50; -$payment2 = $objectManager->create(Payment::class); -$payment2->setMethod('checkmo'); -/** @var Order $order2 */ -$order2 = $objectManager->create(Order::class); -$order2->setIncrementId('100000005') - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt('2016-12-12T12:00:55+0000') - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setGrandTotal($orderAmount2) - ->setBaseGrandTotal($orderAmount2) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->setPayment($payment2) - ->setQuoteId(2); - -$orderRepository->save($order2); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php deleted file mode 100644 index ba0e92687dc17..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Sales\Api\Data\OrderPaymentInterface; - -require __DIR__ . '/../../../Magento/Catalog/_files/product_virtual.php'; -require __DIR__ . '/store.php'; -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; - -$objectManager = Bootstrap::getObjectManager(); - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -/** @var OrderPaymentInterface $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setMethod('braintree') - ->setLastTransId('00001') - ->setAdditionalInformation('avsPostalCodeResponseCode', 'M') - ->setAdditionalInformation('avsStreetAddressResponseCode', 'M') - ->setAdditionalInformation('cvvResponseCode', 'M'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerIsGuest(true) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt('2016-12-12T12:00:55+0000') - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->setPayment($payment) - ->setQuoteId(1); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php deleted file mode 100644 index b814263bdc5ef..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Store\Model\Store; -use Magento\Store\Model\Information; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Sales\Model\Order\Shipment; -use Magento\Framework\App\Config\MutableScopeConfigInterface; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -$store = $objectManager->get(StoreManagerInterface::class)->getStore(); -/** @var MutableScopeConfigInterface $mutableConfig */ -$mutableConfig = $objectManager->get(MutableScopeConfigInterface::class); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_NAME, 'Sample Store', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Store::XML_PATH_UNSECURE_BASE_LINK_URL, 'http://m2.com/', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ADDRESS1, '6161 West Centinela Avenue', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ADDRESS2, 'app. 111', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_CITY, 'Culver City', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_REGION_ID, 10, ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ZIP, '90230', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_COUNTRY_ID, 'US', ScopeInterface::SCOPE_STORE); - -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_STREET_LINE1, '5th Avenue', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_STREET_LINE2, '75', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_CITY, 'New York', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_REGION_CODE, 30, ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_POSTCODE, '19032', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_COUNTRY_CODE, 'US', ScopeInterface::SCOPE_STORE); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json b/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json deleted file mode 100644 index 4308c8bf833ef..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json +++ /dev/null @@ -1 +0,0 @@ -{"investigationId":123,"analysisUrl":"https://signifyd.com/v2/cases/185088720/analysis","entriesUrl":"https://signifyd.com/v2/cases/185088720/entries","notesUrl":"https://signifyd.com/v2/cases/185088720/notes","orderUrl":"https://signifyd.com/v2/cases/185088720/order","currency":"USD","uuid":"368df42c-d25f-44ef-a1d9-92755f743901","createdAt":"2017-01-06T12:47:03+0000","updatedAt":"2017-01-06T12:47:03+0000","status":"OPEN","caseId":123,"score":384,"headline":"John Doe","orderId":"000000003","adjustedScore":385,"orderDate":"2017-01-06T12:46:58+0000","orderAmount":5.85,"orderOutcome":"SUCCESSFUL","associatedTeam":{"teamName":"Magento","teamId":7940},"testInvestigation":true,"reviewDisposition":null} \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php deleted file mode 100644 index e53b0431503e7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Config\Model\Config; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Store\Model\ResourceModel\Store as StoreResourceModel; -use Magento\Store\Model\ResourceModel\Website as WebsiteResourceModel; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Model\Website; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var $website Website */ -$website = $objectManager->create(Website::class); -$website->setData(['code' => 'test_website', 'name' => 'Test Website', 'default_group_id' => '1', 'is_default' => '0']); -$websiteResourceModel = $objectManager->create(WebsiteResourceModel::class); -$websiteResourceModel->save($website); - -$websiteId = $website->getId(); -$store = $objectManager->create(Store::class); -$groupId = Bootstrap::getObjectManager()->get(StoreManagerInterface::class) - ->getWebsite() - ->getDefaultGroupId(); -$store->setCode('test_second_store') - ->setWebsiteId($websiteId) - ->setGroupId($groupId) - ->setName('Test Second Store') - ->setSortOrder(10) - ->setIsActive(1); -$storeResourceModel = $objectManager->create(StoreResourceModel::class); -$storeResourceModel->save($store); - -/* Refresh stores memory cache */ -$objectManager->get(StoreManagerInterface::class)->reinitStores(); - -$processConfigData = function (Config $config, array $data) { - foreach ($data as $key => $value) { - $config->setDataByPath($key, $value); - $config->save(); - } -}; - -// save signifyd configuration for the default scope -$configData = [ - 'fraud_protection/signifyd/active' => '1', -]; -/** @var Config $defConfig */ -$defConfig = $objectManager->create(Config::class); -$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); -$processConfigData($defConfig, $configData); - -// save signifyd website config data -$websiteConfigData = [ - 'fraud_protection/signifyd/active' => '0', -]; -/** @var Config $websiteConfig */ -$websiteConfig = $objectManager->create(Config::class); -$websiteConfig->setScope(ScopeInterface::SCOPE_WEBSITES); -$websiteConfig->setWebsite($websiteId); -$processConfigData($websiteConfig, $websiteConfigData); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php deleted file mode 100644 index 9b731813fea3b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Store\Api\WebsiteRepositoryInterface; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\Website; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -$deleteConfigData = function (WriterInterface $writer, $scope, $scopeId) { - $configData = [ - 'fraud_protection/signifyd/active', - ]; - foreach ($configData as $path) { - $writer->delete($path, $scope, $scopeId); - } -}; - -/** @var WriterInterface $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$deleteConfigData($configWriter, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); - -/** @var WebsiteRepositoryInterface $websiteRepository */ -$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); -$website = $websiteRepository->get('test_website'); -$deleteConfigData($configWriter, ScopeInterface::SCOPE_WEBSITES, $website->getId()); - -$website = $objectManager->create(Website::class); -/** @var $website Website */ -if ($website->load('test_website', 'code')->getId()) { - $website->delete(); -} -$store = $objectManager->create(Store::class); -if ($store->load('test_second_store', 'code')->getId()) { - $store->delete(); -} diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php index 229207aa03899..7a8f04d0160ff 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php @@ -20,5 +20,6 @@ // Fails because of dependence on registry \Magento\Reminder\Block\Adminhtml\Reminder\Edit\Tab\Customers::class, \Magento\LayeredNavigation\Block\Navigation::class, - \Magento\LayeredNavigation\Block\Navigation\State::class + \Magento\LayeredNavigation\Block\Navigation\State::class, + \Magento\Paypal\Block\Express\InContext\Minicart\Button::class, ]; diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php index 91d74b0908078..1edb23738820f 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php @@ -12,4 +12,5 @@ \Magento\Reminder\Block\Adminhtml\Reminder\Edit\Tab\Customers::class, \Magento\LayeredNavigation\Block\Navigation::class, \Magento\LayeredNavigation\Block\Navigation\State::class, + \Magento\Paypal\Block\Express\InContext\Minicart\Button::class, ]; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js deleted file mode 100644 index a9d9fe8d08047..0000000000000 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery' -], function ($) { - 'use strict'; - - /*eslint max-nested-callbacks: ["error", 5]*/ - describe('Signifyd device fingerprint client script', function () { - var originalTimeout; - - beforeEach(function () { - originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; - jasmine.DEFAULT_TIMEOUT_INTERVAL = 12000; - }); - - afterEach(function () { - jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; - }); - - it('SIGNIFYD_GLOBAL object initialization check', function (done) { - var script = document.createElement('script'); - - script.setAttribute('src', 'https://cdn-scripts.signifyd.com/api/script-tag.js'); - script.setAttribute('id', 'sig-api'); - script.setAttribute('type', 'text/javascript'); - script.setAttribute('async', ''); - script.setAttribute('data-order-session-id', 'mage-jasmin-test'); - - $(document.body).append(script); - - setTimeout(function () { - var signifyd = window.SIGNIFYD_GLOBAL; - - expect(signifyd).toBeDefined(); - expect(typeof signifyd).toBe('object'); - expect(signifyd.scriptTagHasLoaded).toBeDefined(); - expect(typeof signifyd.scriptTagHasLoaded).toBe('function'); - expect(signifyd.scriptTagHasLoaded()).toBe(true); - done(); - }, 10000); - }); - }); -}); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt index 8eb3392476400..195c88274ae4a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt @@ -10,6 +10,4 @@ \Magento\Framework\DB\DataConverter\DataConversionException \Magento\Framework\DB\FieldDataConversionException \Magento\Catalog\Model\Product\Image\NotLoadInfoImageException -\Magento\Signifyd\Model\SignifydGateway\GatewayException -\Magento\Signifyd\Model\SignifydGateway\ApiCallException \Magento\Framework\MessageQueue\ConnectionLostException diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php index f2411cdad86de..168ac5cde16cb 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php @@ -201,4 +201,5 @@ ['Magento\MysqlMq\Model\Resource', 'Magento\MysqlMq\Model\ResourceModel'], ['Magento\BulkOperations', 'Magento\AsynchronousOperations'], ['Zend', 'Laminas'], + ['Magento\Signifyd'], ]; From 4583fb91385045b05d8aabc77894d730058f0516 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 26 Mar 2020 15:49:00 +0200 Subject: [PATCH 585/734] MC-32772: addToCart events are not getting tracked in datalayer --- .../frontend/web/js/catalog-add-to-cart.js | 9 +++- .../js/product/view/product-info-resolver.js | 35 ++++++++++++++ .../web/js/product/view/product-info.js | 12 +++++ .../Checkout/view/frontend/web/js/sidebar.js | 7 ++- .../view/frontend/requirejs-config.js | 7 +++ .../web/js/catalog-add-to-cart-mixin.js | 33 +++++++++++++ .../js/product/view/product-info-resolver.js | 47 +++++++++++++++++++ .../frontend/web/js/product-ids-resolver.js | 10 +++- .../Checkout/frontend/js/sidebar.test.js | 4 +- 9 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js create mode 100644 app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js create mode 100644 app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js create mode 100644 app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 382b4ef98532b..7d3e4b3280473 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -8,8 +8,9 @@ define([ 'mage/translate', 'underscore', 'Magento_Catalog/js/product/view/product-ids-resolver', + 'Magento_Catalog/js/product/view/product-info-resolver', 'jquery-ui-modules/widget' -], function ($, $t, _, idsResolver) { +], function ($, $t, _, idsResolver, productInfoResolver) { 'use strict'; $.widget('mage.catalogAddToCart', { @@ -24,7 +25,8 @@ define([ addToCartButtonDisabledClass: 'disabled', addToCartButtonTextWhileAdding: '', addToCartButtonTextAdded: '', - addToCartButtonTextDefault: '' + addToCartButtonTextDefault: '', + productInfoResolver: productInfoResolver }, /** @inheritdoc */ @@ -90,6 +92,7 @@ define([ ajaxSubmit: function (form) { var self = this, productIds = idsResolver(form), + productInfo = self.options.productInfoResolver(form), formData; $(self.options.minicartSelector).trigger('contentLoading'); @@ -119,6 +122,7 @@ define([ $(document).trigger('ajax:addToCart', { 'sku': form.data().productSku, 'productIds': productIds, + 'productInfo': productInfo, 'form': form, 'response': res }); @@ -172,6 +176,7 @@ define([ $(document).trigger('ajax:addToCart:error', { 'sku': form.data().productSku, 'productIds': productIds, + 'productInfo': productInfo, 'form': form, 'response': res }); diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js new file mode 100644 index 0000000000000..f58f0ba2dfa68 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js @@ -0,0 +1,35 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'Magento_Catalog/js/product/view/product-info' +], function (_, productInfo) { + 'use strict'; + + /** + * Returns info about products in form. + * + * @param {jQuery} $form + * @return {Array} + */ + return function ($form) { + var product = _.findWhere($form.serializeArray(), { + name: 'product' + }); + + if (!_.isUndefined(product)) { + productInfo().push( + { + 'id': product.value + } + ); + } + + return _.uniq(productInfo(), function (item) { + return item.id; + }); + }; +}); + diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js new file mode 100644 index 0000000000000..2198b7b8e48b0 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js @@ -0,0 +1,12 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'ko' +], function (ko) { + 'use strict'; + + return ko.observableArray([]); +}); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index 6fc5ef9d2a574..a7ccb217fa102 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -260,7 +260,12 @@ define([ if (!_.isUndefined(productData)) { $(document).trigger('ajax:removeFromCart', { - productIds: [productData['product_id']] + productIds: [productData['product_id']], + productInfo: [ + { + 'id': productData['product_id'] + } + ] }); if (window.location.href.indexOf(this.shoppingCartUrl) === 0) { diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js b/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js index c25096121be5d..b2208686e7fab 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js @@ -8,5 +8,12 @@ var config = { '*': { configurable: 'Magento_ConfigurableProduct/js/configurable' } + }, + config: { + mixins: { + 'Magento_Catalog/js/catalog-add-to-cart': { + 'Magento_ConfigurableProduct/js/catalog-add-to-cart-mixin': true + } + } } }; diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js new file mode 100644 index 0000000000000..88bb2ab75dc1d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js @@ -0,0 +1,33 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'jquery', + 'Magento_ConfigurableProduct/js/product/view/product-info-resolver' +], function (_, $, productInfoResolver) { + 'use strict'; + + return function (widget) { + + $.widget('mage.catalogAddToCart', widget, { + /** + * @param {jQuery} form + */ + ajaxSubmit: function (form) { + var isConfigurable = !!_.find(form.serializeArray(), function (item) { + return item.name.indexOf('super_attribute') !== -1; + }); + + if (isConfigurable) { + this.options.productInfoResolver = productInfoResolver; + } + + return this._super(form); + } + }); + + return $.mage.catalogAddToCart; + }; +}); diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js new file mode 100644 index 0000000000000..679632606c72d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js @@ -0,0 +1,47 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'Magento_Catalog/js/product/view/product-info' +], function (_, productInfo) { + 'use strict'; + + /** + * Returns info about configurable products in form. + * + * @param {jQuery} $form + * @return {Array} + */ + return function ($form) { + var optionValues = [], + product = _.findWhere($form.serializeArray(), { + name: 'product' + }), + productId; + + if (!_.isUndefined(product)) { + productId = product.value; + _.each($form.serializeArray(), function (item) { + if (item.name.indexOf('super_attribute') !== -1) { + optionValues.push(item.value); + } + }); + optionValues.sort(); + productInfo().push( + { + 'id': productId, + 'optionValues': optionValues + } + ); + } + + return _.uniq(productInfo(), function (item) { + var optionValuesStr = item.optionValues ? item.optionValues.join() : ''; + + return item.id + optionValuesStr; + }); + }; +}); + diff --git a/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js b/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js index e6294d8043a50..ea5f841d738a3 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js +++ b/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js @@ -4,8 +4,9 @@ */ define([ 'jquery', - 'Magento_Catalog/js/product/view/product-ids' -], function ($, productIds) { + 'Magento_Catalog/js/product/view/product-ids', + 'Magento_Catalog/js/product/view/product-info' +], function ($, productIds, productInfo) { 'use strict'; /** @@ -18,6 +19,11 @@ define([ return function (config, element) { $(element).find('div[data-product-id]').each(function () { productIds.push($(this).data('productId').toString()); + productInfo.push( + { + 'id': $(this).data('productId').toString() + } + ); }); return productIds(); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js index 7c21a8aae2f26..e9b606eebac3d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js @@ -67,7 +67,9 @@ define([ sidebar._removeItemAfter(elem); expect(mocks['Magento_Customer/js/customer-data'].get).toHaveBeenCalledWith('cart'); expect(jQuery('body').trigger).toHaveBeenCalledWith('ajax:removeFromCart', { - 'productIds': ['5'] + 'productIds': ['5'], 'productInfo': [{ + 'id': '5' + }] }); }); From 40774ddc5990ecb89f72614915d4a1113380ea88 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Thu, 26 Mar 2020 15:56:51 +0200 Subject: [PATCH 586/734] MC-32339: MFTF Test- Customer Review Grid Filter Not working --- .../Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml index 85660a3a9f1db..e0c85f06ec712 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml @@ -33,7 +33,7 @@ <!-- Delete reviews --> <actionGroup ref="AdminOpenReviewsPageActionGroup" stepKey="openAllReviewsPage"/> <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteCustomerReview"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearNickNameReviewFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearNickNameReviewFilters"/> <!-- Delete customer --> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> <argument name="customerEmail" value="CustomerEntityOne.email"/> @@ -57,7 +57,7 @@ <actionGroup ref="StorefrontAddProductReviewActionGroup" stepKey="addReview"/> <!-- Go to Pending reviews page and clear filters --> <actionGroup ref="AdminOpenPendingReviewsPageActionGroup" stepKey="openReviewsPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <!-- Moderate first product reviews: change review status from pending to approved, save --> <actionGroup ref="AdminOpenReviewByUserNicknameActionGroup" stepKey="openFirstCustomerReviews"/> <actionGroup ref="AdminChangeReviewStatusActionGroup" stepKey="changeFirstReviewStatus"/> From db71c464b0c803775c11c4988c47c4f929e32131 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 26 Mar 2020 16:26:20 +0100 Subject: [PATCH 587/734] Add missing Copyright --- .../Magento/ProductVideo/ProductVideoExternalSourceTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php index 33ccf6a8854c5..7c38cf15545c6 100644 --- a/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); namespace Magento\ProductVideo; From f0b3ab87851b2e19b0a695bf229f2260a90494ad Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Thu, 26 Mar 2020 18:04:55 +0200 Subject: [PATCH 588/734] Fix Static test --- app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php index 543d9598553cc..7430213030f96 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php @@ -11,7 +11,7 @@ use Magento\Store\Model\App\Emulation; /** - * Class ObserverTest + * Class for ObserverTest * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ From 162b5095f7d3f1291c6522694706d7ff2ca3ab8f Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 26 Mar 2020 12:11:06 -0500 Subject: [PATCH 589/734] MC-32120: Catalog price rules are not included in CartItemPrices - update composer --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index f6894e3c31d09..937567f5fe64a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc4c877a835fbea7420d8477ade2fe4f", + "content-hash": "5d91717fba26046648157ebf69639b5a", "packages": [ { "name": "braintree/braintree_php", From 5e04fe0757a8d56f991c72dc0c2532cbd290b7fc Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 26 Mar 2020 19:26:31 +0200 Subject: [PATCH 590/734] MC-30588: Adding new non-searchable product attribute invalidates indexes --- .../Model/Indexer/Fulltext/Plugin/AttributeTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php index 176ed2c0e425b..c789183069c10 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php @@ -131,6 +131,8 @@ public function searchableAttributesDataProvider(): array */ public function testCheckIndexStatusAfterUpdateSearchableAttributeToNonSearchable(): void { + $this->indexerProcessor->reindexAll(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); $this->attribute->loadByCode(Product::ENTITY, 'test_catalog_view'); $this->assertFalse($this->attribute->isObjectNew()); $this->attribute->setIsFilterable(false)->save(); From 96c4baf2e0ad294eaca7a89464dab86e7708a6e5 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Thu, 26 Mar 2020 14:24:53 -0500 Subject: [PATCH 591/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Fixed code as per new review comments --- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 2 +- .../QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 2eaee2dd594aa..315e03b2c697c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -57,7 +57,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; - if ($addressInput) { + if (!$customerAddressId && $addressInput) { $addressInput['save_in_address_book'] = isset($billingAddressInput['address']['save_in_address_book']) ? (bool) $billingAddressInput['address']['save_in_address_book'] : true; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 3fb6b8d8ae26e..f73daa715c1df 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -48,12 +48,11 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('You cannot specify multiple shipping addresses.') ); } - $shippingAddressInput = current($shippingAddressesInput) ?? null; + $shippingAddressInput = current($shippingAddressesInput) ?? []; $customerAddressId = $shippingAddressInput['customer_address_id'] ?? null; - if ($shippingAddressInput && !$customerAddressId) { - $shippingAddressInput['address']['save_in_address_book'] = isset($shippingAddressInput['address']['save_in_address_book']) - ? (bool) $shippingAddressInput['address']['save_in_address_book'] : true; + if (!$customerAddressId && !isset($shippingAddressInput['address']['save_in_address_book'])) { + $shippingAddressInput['address']['save_in_address_book'] = true; } $shippingAddress = $this->getShippingAddress->execute($context, $shippingAddressInput); From c0333561e7581db6e2d1f9ca7c300070958430a2 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 26 Mar 2020 16:33:35 -0500 Subject: [PATCH 592/734] MQE-2033: Deliver MFTF 3.0.0 RC1 --- .../LoginAdminWithCredentialsActionGroup.xml} | 6 +- .../LoginAsAdminActionGroup.xml | 13 + .../ActionGroup/_Deprecated_ActionGroup.xml | 56 -- .../LoginActionGroup.xml | 11 + .../LoginAdminWithCredentialsActionGroup.xml | 25 + .../LoginAsAdminActionGroup.xml | 24 + .../LoginAsAnyUserActionGroup.xml | 20 + .../LogoutActionGroup.xml | 11 + .../Page/AdminConfigurationStoresPage.xml | 20 - .../ConfigurationStoresPage.xml | 13 + .../GeneralConfigurationPage.xml | 13 + .../WebConfigurationPage.xml | 13 + .../BraintreeConfigurationPaymentSection.xml | 2 +- .../CaptchaFormsDisplayingTest.xml | 67 ++ .../CaptchaWithDisabledGuestCheckoutTest.xml} | 62 +- ...yLevelByParentCategoryLevelActionGroup.xml | 5 +- ...refrontRecentlyViewedWidgetActionGroup.xml | 5 +- .../AdminCategoryBasicFieldSection.xml | 87 -- .../AdminCategoryBasicFieldSection.xml | 26 + .../CatalogWYSIWYGSection.xml | 46 + .../CategoryContentSection.xml | 15 + .../CategoryDesignSection.xml | 14 + .../CategoryDisplaySettingsSection.xml | 25 + .../AdminCreateProductAttributeSection.xml | 106 --- .../AdvancedAttributePropertiesSection.xml | 26 + .../AttributeDeleteModalSection.xml | 14 + .../AttributeManageSwatchSection.xml | 14 + .../AttributeOptionsSection.xml | 13 + .../AttributePropertiesSection.xml | 33 + .../StorefrontPropertiesSection.xml | 18 + .../WYSIWYGProductAttributeSection.xml | 46 + .../AdminProductAttributeSetSection.xml | 16 +- .../AttributeSetSection.xml | 13 + .../GroupSection.xml | 13 + .../ModifyAttributesSection.xml | 14 + .../UnassignedAttributesSection.xml | 13 + ...AdminProductCustomizableOptionsSection.xml | 24 +- .../AdminProductImportOptionsSection.xml | 19 + .../AdminProductFormAttributeSection.xml | 40 - .../AdminProductFormAttributeSection.xml | 13 + ...ProductFormNewAttributeAdvancedSection.xml | 15 + ...inProductFormNewAttributeNewSetSection.xml | 15 + .../AdminProductFormNewAttributeSection.xml | 21 + ...oductFormNewAttributeStorefrontSection.xml | 15 + .../Mftf/Section/AdminProductFormSection.xml | 225 ----- .../AdminAddRelatedProductsModalSection.xml | 14 + .../AdminProductAttributeSection.xml | 20 + ...AdminProductFormAdvancedPricingSection.xml | 15 + ...oductFormRelatedUpSellCrossSellSection.xml | 15 + .../AdminProductFormSection.xml | 80 ++ .../ProductAttributeWYSIWYGSection.xml | 29 + ...roductDescriptionWYSIWYGToolbarSection.xml | 49 + .../ProductDescriptionWysiwygSection.xml | 14 + .../ProductDesignSection.xml | 15 + .../ProductInWebsitesSection.xml | 16 + ...tShortDescriptionWYSIWYGToolbarSection.xml | 46 + .../ProductShortDescriptionWysiwygSection.xml | 13 + .../ProductWYSIWYGSection.xml | 19 + .../AdminAddUpSellProductsModalSection.xml | 14 + ...ductFormRelatedUpSellCrossSellSection.xml} | 7 +- .../AdminUpdateAttributesSection.xml | 7 +- .../AdminUpdateAttributesWebsiteSection.xml | 14 + ...StorefrontCategorySidebarMobileSection.xml | 13 + .../StorefrontCategorySidebarSection.xml | 15 +- .../AdminApplyTierPriceToProductTest.xml | 79 +- ...iceToProductWithPercentageDiscountTest.xml | 62 ++ ...reateSimpleProductSwitchToVirtualTest.xml} | 25 +- ...CreateVirtualProductSwitchToSimpleTest.xml | 30 + .../Mftf/Test/AdminCreateCategoryTest.xml | 103 --- ...oryFormDisplaySettingsUIValidationTest.xml | 44 + ...goryLayoutFromConfigurationSettingTest.xml | 42 + .../AdminCreateCategoryTest.xml | 39 + ...dminCreateProductDuplicateProductTest.xml} | 37 +- .../AdminCreateProductDuplicateUrlkeyTest.xml | 42 + .../Test/AdminCreateSimpleProductTest.xml | 112 --- ...ductLayoutFromConfigurationSettingTest.xml | 39 + ...inCreateSimpleProductNegativePriceTest.xml | 28 + .../AdminCreateSimpleProductTest.xml | 41 + .../AdminCreateSimpleProductZeroPriceTest.xml | 30 + ...oductAttributesStoreViewScopeMysqlTest.xml | 82 ++ ...ateProductAttributesStoreViewScopeTest.xml | 77 +- ...teProductStatusStoreViewScopeMysqlTest.xml | 151 +++ ...sUpdateProductStatusStoreViewScopeTest.xml | 162 +--- ...roductTypeSwitchingToSimpleProductTest.xml | 46 + ...ypeSwitchingToDownloadableProductTest.xml} | 41 +- .../AdminSimpleProductImagesTest.xml | 167 ++++ .../AdminSimpleProductRemoveImagesTest.xml} | 161 +--- .../AdvanceCatalogSearchSimpleProductTest.xml | 112 --- ...ogSearchSimpleProductByDescriptionTest.xml | 27 + ...ceCatalogSearchSimpleProductByNameTest.xml | 30 + ...eCatalogSearchSimpleProductByPriceTest.xml | 40 + ...rchSimpleProductByShortDescriptionTest.xml | 27 + ...nceCatalogSearchSimpleProductBySkuTest.xml | 27 + ...AdvanceCatalogSearchVirtualProductTest.xml | 81 -- ...gSearchVirtualProductByDescriptionTest.xml | 24 + ...eCatalogSearchVirtualProductByNameTest.xml | 24 + ...CatalogSearchVirtualProductByPriceTest.xml | 24 + ...chVirtualProductByShortDescriptionTest.xml | 24 + ...ceCatalogSearchVirtualProductBySkuTest.xml | 24 + .../Test/CreateProductAttributeEntityTest.xml | 425 --------- .../CreateProductAttributeEntityDateTest.xml | 72 ++ ...eateProductAttributeEntityDropdownTest.xml | 96 ++ ...ibuteEntityDropdownWithSingleQuoteTest.xml | 78 ++ ...eProductAttributeEntityMultiSelectTest.xml | 95 ++ .../CreateProductAttributeEntityPriceTest.xml | 63 ++ ...ateProductAttributeEntityTextFieldTest.xml | 65 ++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 452 --------- .../EndToEndB2CGuestUserMysqlTest.xml | 228 +++++ .../EndToEndB2CGuestUserTest.xml | 233 +++++ ...oreFrontRecentlyViewedAtStoreLevelTest.xml | 15 +- ...rontRecentlyViewedAtStoreViewLevelTest.xml | 10 +- ...orefrontProductNameWithDoubleQuoteTest.xml | 64 +- ...refrontProductNameWithHTMLEntitiesTest.xml | 70 ++ ...rifyDefaultWYSIWYGToolbarOnProductTest.xml | 89 -- ...rifyDefaultWYSIWYGToolbarOnProductTest.xml | 49 + ...tcontrolsonproductshortdescriptionTest.xml | 49 + .../AdminCatalogPriceRuleGridSection.xml | 19 + ...AdminNewCatalogPriceRuleActionsSection.xml | 15 + ...inNewCatalogPriceRuleConditionsSection.xml | 26 + .../AdminNewCatalogPriceRuleSection.xml | 36 +- .../Test/AdminCreateCatalogPriceRuleTest.xml | 222 ----- ...AdminCreateCatalogPriceRuleByFixedTest.xml | 32 + ...minCreateCatalogPriceRuleByPercentTest.xml | 70 ++ ...teCatalogPriceRuleForCustomerGroupTest.xml | 68 ++ ...AdminCreateCatalogPriceRuleToFixedTest.xml | 32 + ...minCreateCatalogPriceRuleToPercentTest.xml | 32 + ...ateCatalogPriceRuleWithInvalidDataTest.xml | 32 + ...RuleEntityFromConfigurableProductTest.xml} | 85 +- ...ogPriceRuleEntityFromSimpleProductTest.xml | 91 ++ .../AdvanceCatalogSearchSimpleProductTest.xml | 102 --- ...ogSearchSimpleProductByDescriptionTest.xml | 27 + ...ceCatalogSearchSimpleProductByNameTest.xml | 28 + ...eCatalogSearchSimpleProductByPriceTest.xml | 30 + ...rchSimpleProductByShortDescriptionTest.xml | 28 + ...nceCatalogSearchSimpleProductBySkuTest.xml | 28 + .../EndToEndB2CGuestUserMysqlTest.xml} | 85 +- .../EndToEndB2CGuestUserTest.xml | 88 ++ .../Mftf/Test/SearchEntityResultsTest.xml | 610 ------------ ...ickSearchAndAddToCartBundleDynamicTest.xml | 68 ++ ...QuickSearchAndAddToCartBundleFixedTest.xml | 84 ++ ...uickSearchAndAddToCartConfigurableTest.xml | 54 ++ ...uickSearchAndAddToCartDownloadableTest.xml | 47 + .../QuickSearchAndAddToCartGroupedTest.xml | 47 + .../QuickSearchAndAddToCartTest.xml | 42 + .../QuickSearchAndAddToCartVirtualTest.xml | 42 + .../QuickSearchEmptyResultsTest.xml | 42 + .../QuickSearchProductBy128CharQueryTest.xml | 30 + .../QuickSearchProductByNameTest.xml | 25 + ...earchProductByNameWithSpecialCharsTest.xml | 30 + ...earchProductByNameWithThreeLettersTest.xml | 26 + .../QuickSearchProductBySkuTest.xml | 43 + ...archTwoProductsWithDifferentWeightTest.xml | 39 + ...ickSearchTwoProductsWithSameWeightTest.xml | 93 ++ ...uickSearchWithTwoCharsEmptyResultsTest.xml | 43 + .../CatalogWidgetSection.xml | 14 + .../InsertWidgetSection.xml} | 10 +- ...iewAndEditCartFromMiniCartActionGroup.xml} | 12 +- .../StorefrontOpenMiniCartActionGroup.xml | 19 + .../CheckCheckoutSuccessPageAsGuestTest.xml | 95 ++ ...koutSuccessPageAsRegisterCustomerTest.xml} | 90 +- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 453 --------- .../EndToEndB2CGuestUserMysqlTest.xml | 231 +++++ .../EndToEndB2CGuestUserTest.xml | 231 +++++ ...tItemsCountDisplayItemsQuantitiesTest.xml} | 27 +- ...ntCartItemsCountDisplayUniqueItemsTest.xml | 35 + .../Test/StorefrontCustomerCheckoutTest.xml | 273 ------ .../StorefrontCustomerCheckoutTest.xml | 84 ++ ...stWithMultipleAddressesAndTaxRatesTest.xml | 126 +++ ...tWithRestrictedCountriesForPaymentTest.xml | 82 ++ .../Mftf/Test/StorefrontGuestCheckoutTest.xml | 155 ---- .../StorefrontGuestCheckoutTest.xml | 76 ++ ...tWithRestrictedCountriesForPaymentTest.xml | 69 ++ ...ntGuestCheckoutWithSidebarDisabledTest.xml | 29 + .../DeleteBlockActionGroup.xml} | 19 +- .../SearchBlockOnGridPageActionGroup.xml | 23 + .../CmsNewBlockBlockActionsSection.xml | 34 - .../BlockContentSection.xml | 15 + .../BlockNewPagePageActionsSection.xml | 18 + .../BlockWYSIWYGSection.xml | 13 + .../CmsBlockBlockActionSection.xml | 14 + .../CmsNewBlockBlockActionsSection.xml | 13 + .../BlockNewPageBasicFieldsSection.xml} | 8 +- .../CmsNewBlockBlockBasicFieldsSection.xml | 15 + .../CmsDesignSection.xml | 14 + .../CmsNewPagePageContentSection.xml | 15 + .../CmsWYSIWYGSection.xml} | 12 +- .../Cms/Test/Mftf/Section/TinyMCESection.xml | 123 --- .../TinyMCESection/MediaGallerySection.xml | 39 + .../Section/TinyMCESection/TinyMCESection.xml | 36 + .../TinyMCESection/VariableSection.xml | 27 + .../Section/TinyMCESection/WidgetSection.xml | 50 + .../Config/Test/Mftf/Page/AdminConfigPage.xml | 30 - .../AdminConfigAdvancedAdminPage.xml | 13 + .../AdminConfigDeveloperPage.xml | 13 + .../AdminConfigGeneralPage.xml | 13 + .../Page/AdminConfigPage/AdminConfigPage.xml | 13 + .../AdminContentManagementPage.xml | 13 + .../AdminSalesTaxClassPage.xml | 13 + .../AdminConfigPage/CatalogConfigPage.xml | 13 + .../{ => CatalogSection}/CatalogSection.xml | 9 +- .../GenerateUrlRewritesConfirmSection.xml | 16 + .../Test/Mftf/Section/GeneralSection.xml | 47 - .../ContentManagementSection.xml | 21 + .../GeneralSection/CountryOptionsSection.xml | 15 + .../GeneralSection/DefaultLayoutsSection.xml | 15 + .../GeneralSection/StateOptionsSection.xml | 15 + .../GeneralSection/UrlOptionsSection.xml | 14 + .../Section/GeneralSection/WebSection.xml | 16 + .../AdminConfigurableProductFormSection.xml | 17 + ...ProductSelectAttributesSlideOutSection.xml | 13 + .../AdminProductFormConfigurationsSection.xml | 22 +- ...orefrontConfigurableProductPageSection.xml | 13 + ...rableProductAttributeNameDesignSection.xml | 66 -- .../CatalogProductsSection.xml | 23 + .../ConfigurableProductSection.xml | 18 + .../CreateProductConfigurationsSection.xml | 18 + .../NewProductSection.xml | 33 + .../AdminConfigurableProductCreateTest.xml | 71 ++ ...ctAfterGettingIncorrectSKUMessageTest.xml} | 67 +- ...dminConfigurableProductBulkDeleteTest.xml} | 97 +- .../AdminConfigurableProductDeleteTest.xml | 103 +++ ...AdminConfigurableProductOutOfStockTest.xml | 355 ------- ...figurableProductChildrenOutOfStockTest.xml | 135 +++ ...ductOutOfStockAndDeleteCombinationTest.xml | 124 +++ ...roductOutOfStockTestDeleteChildrenTest.xml | 113 +++ ...minConfigurableProductFilterByTypeTest.xml | 89 ++ .../AdminConfigurableProductSearchTest.xml | 83 +- ...ConfigurableProductUpdateAttributeTest.xml | 147 +++ ...urableProductUpdateChildAttributeTest.xml} | 145 +-- .../AdminConfigurableProductUpdateTest.xml | 378 -------- ...onfigurableProductAddConfigurationTest.xml | 64 ++ ...AdminConfigurableProductBulkUpdateTest.xml | 84 ++ ...ConfigurableProductDisableAnOptionTest.xml | 100 ++ ...nConfigurableProductRemoveAnOptionTest.xml | 107 +++ ...igurableProductRemoveConfigurationTest.xml | 58 ++ .../Test/AdminCreateAndSwitchProductType.xml | 193 ---- ...eConfigurableProductSwitchToSimpleTest.xml | 29 + ...ConfigurableProductSwitchToVirtualTest.xml | 26 + ...oadableProductSwitchToConfigurableTest.xml | 79 ++ ...eSimpleProductSwitchToConfigurableTest.xml | 51 ++ ...VirtualProductSwitchToConfigurableTest.xml | 47 + ...AdminProductTypeSwitchingOnEditingTest.xml | 193 ---- ...oductTypeSwitchingToVirtualProductTest.xml | 51 ++ ...TypeSwitchingToConfigurableProductTest.xml | 80 ++ ...TypeSwitchingToConfigurableProductTest.xml | 81 ++ .../AdvanceCatalogSearchConfigurableTest.xml | 331 ------- ...logSearchConfigurableByDescriptionTest.xml | 90 ++ ...nceCatalogSearchConfigurableByNameTest.xml | 90 ++ ...archConfigurableByShortDescriptionTest.xml | 90 ++ ...anceCatalogSearchConfigurableBySkuTest.xml | 90 ++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 501 ---------- .../EndToEndB2CGuestUserMysqlTest.xml | 255 ++++++ .../EndToEndB2CGuestUserTest.xml | 255 ++++++ ...orefrontConfigurableProductDetailsTest.xml | 288 ------ ...efrontConfigurableProductBasicInfoTest.xml | 54 ++ ...ontConfigurableProductCanAddToCartTest.xml | 51 ++ ...ntConfigurableProductCantAddToCartTest.xml | 48 + ...orefrontConfigurableProductOptionsTest.xml | 54 ++ ...frontConfigurableProductVariationsTest.xml | 116 +++ .../StorefrontConfigurableProductViewTest.xml | 130 --- ...efrontConfigurableProductAddToCartTest.xml | 50 + ...refrontConfigurableProductGridViewTest.xml | 50 + ...refrontConfigurableProductListViewTest.xml | 47 + ...urableProductWithSeveralAttributesTest.xml | 276 ++++-- ...rIsSubscribedToNewslettersActionGroup.xml} | 16 +- ...lettersAndSelectedStoreViewActionGroup.xml | 20 + ...cribeCustomerToNewslettersActionGroup.xml} | 15 +- ...wslettersAndSelectStoreViewActionGroup.xml | 19 + .../ActionGroup/_Deprecated_ActionGroup.xml | 184 ---- ...CustomerWithWebSiteAndGroupActionGroup.xml | 33 + ...CustomerAddressNoZipNoStateActionGroup.xml | 20 + ...etDefaultShippingAndBillingActionGroup.xml | 18 + .../EnterCustomerAddressInfoActionGroup.xml | 32 + ...ustomerAddressInfoFillStateActionGroup.xml | 17 + ...NavigateToCustomerGroupPageActionGroup.xml | 11 + ...erifyCustomerBillingAddressActionGroup.xml | 29 + ...omerBillingAddressWithStateActionGroup.xml | 28 + ...erifyCustomerNameOnFrontendActionGroup.xml | 25 + ...rifyCustomerShippingAddressActionGroup.xml | 28 + ...merShippingAddressWithStateActionGroup.xml | 28 + ...ustomerShoppingCartProductItemSection.xml} | 7 +- .../AdminCustomerShoppingCartSection.xml | 13 + .../StorefrontCustomerCreateFormSection.xml | 36 - ...FrontCustomerAdvancedAttributesSection.xml | 24 + .../StorefrontCustomerCreateFormSection.xml | 21 + .../StorefrontCustomerAddressSection.xml} | 12 +- ...omerDashboardAccountInformationSection.xml | 16 + .../StorefrontCustomerRecentOrdersSection.xml | 13 + .../StorefrontCustomerSignInFormSection.xml | 31 - .../StorefrontCustomerSignInFormSection.xml | 17 + .../StorefrontCustomerSignInLinkSection.xml | 16 + ...orefrontCustomerSignInPopupFormSection.xml | 17 + .../LoginFormSection.xml} | 8 +- .../SwitchAccountSection/SignOutSection.xml | 14 + .../Mftf/Test/AdminUpdateCustomerTest.xml | 308 ------- .../AdminDeleteCustomerAddressTest.xml | 112 +++ ...CustomerAddressNoBillingNoShippingTest.xml | 71 ++ ...nUpdateCustomerAddressNoZipNoStateTest.xml | 62 ++ ...ustomerInfoFromDefaultToNonDefaultTest.xml | 89 ++ ...inVerifyDisabledCustomerGroupFieldTest.xml | 4 +- .../ChangingAllCustomerGroupViaGridTest.xml | 28 + ...hangingSingleCustomerGroupViaGridTest.xml} | 22 +- .../Test/StorefrontAddCustomerAddressTest.xml | 121 --- ...torefrontAddCustomerDefaultAddressTest.xml | 42 + ...efrontAddCustomerNonDefaultAddressTest.xml | 40 + .../StorefrontAddNewCustomerAddressTest.xml | 43 + .../StorefrontUpdateCustomerAddressTest.xml | 122 --- ...frontUpdateCustomerAddressFromGridTest.xml | 42 + ...omerDefaultBillingAddressFromBlockTest.xml | 43 + ...merDefaultShippingAddressFromBlockTest.xml | 46 + .../StorefrontUpdateCustomerPasswordTest.xml | 81 -- ...asswordInvalidConfirmationPasswordTest.xml | 27 + ...omerPasswordInvalidCurrentPasswordTest.xml | 27 + ...stomerPasswordValidCurrentPasswordTest.xml | 46 + .../Test/Mftf/Test/_Deprecated_Test.xml | 13 - .../AddingProductWithExpiredSessionTest.xml | 11 + .../StorefrontCustomerForgotPasswordTest.xml | 11 + .../VerifyDisabledCustomerGroupFieldTest.xml | 11 + ...TypeSwitchingToConfigurableProductTest.xml | 27 + ...ypeSwitchingToDownloadableProductTest.xml} | 22 +- ...ceCatalogSearchDownloadableProductTest.xml | 131 --- ...logSearchDownloadableByDescriptionTest.xml | 34 + ...nceCatalogSearchDownloadableByNameTest.xml | 34 + ...ceCatalogSearchDownloadableByPriceTest.xml | 34 + ...archDownloadableByShortDescriptionTest.xml | 34 + ...anceCatalogSearchDownloadableBySkuTest.xml | 34 + .../Mftf/Page/AdminEmailTemplateEditPage.xml | 2 +- .../Mftf/Page/AdminEmailTemplateIndexPage.xml | 2 +- .../Page/AdminEmailTemplatePreviewPage.xml | 2 +- .../AdminAddProductsToGroupPanelSection.xml | 9 +- .../AdminAddedProductsToGroupGridSection.xml | 13 + ...AdvanceCatalogSearchGroupedProductTest.xml | 323 ------- ...chGroupedProductByDescriptionMysqlTest.xml | 41 + ...gSearchGroupedProductByDescriptionTest.xml | 45 + ...logSearchGroupedProductByNameMysqlTest.xml | 41 + ...eCatalogSearchGroupedProductByNameTest.xml | 45 + ...ogSearchGroupedProductByPriceMysqlTest.xml | 50 + ...CatalogSearchGroupedProductByPriceTest.xml | 54 ++ ...upedProductByShortDescriptionMysqlTest.xml | 41 + ...chGroupedProductByShortDescriptionTest.xml | 45 + ...ceCatalogSearchGroupedProductBySkuTest.xml | 40 + .../Mftf/ActionGroup/IndexerActionGroup.xml | 52 -- .../AdminReindexAndFlushCacheActionGroup.xml | 18 + .../UpdateIndexerByScheduleActionGroup.xml | 26 + .../UpdateIndexerOnSaveActionGroup.xml | 26 + .../LayeredNavigationSection.xml | 7 +- .../StorefrontLayeredNavigationSection.xml | 13 + .../AdminSalesOrderActionGroup.xml | 2 +- ...SalesOrderMatchesGrandTotalActionGroup.xml | 2 +- .../CheckingWithMinicartActionGroup.xml | 2 +- .../CheckingWithSingleAddressActionGroup.xml | 2 +- .../ActionGroup/PlaceOrderActionGroup.xml | 2 +- .../ActionGroup/ReviewOrderActionGroup.xml | 2 +- .../SelectBillingInfoActionGroup.xml | 2 +- .../SelectSingleShippingInfoActionGroup.xml | 2 +- ...pingSelectMultipleAddressesActionGroup.xml | 2 +- .../Test/Mftf/Section/MinicartSection.xml | 2 +- .../MultishippingSection.xml | 15 +- .../SingleShippingSection.xml | 16 + ...torefrontMultipleShippingMethodSection.xml | 16 + .../Mftf/Section/PaymentMethodSection.xml | 2 +- .../Test/Mftf/Section/ReviewOrderSection.xml | 2 +- .../Mftf/Section/ShippingMethodSection.xml | 2 +- .../Section/StorefrontSalesOrderSection.xml | 2 +- .../NewsletterTemplateFormPage.xml} | 6 +- .../NewsletterTemplateGridPage.xml | 14 + .../BasicFieldNewsletterSection.xml | 19 + .../BasicFrontendNewsletterFormSection.xml | 15 + .../NewsletterWYSIWYGSection.xml} | 60 +- .../CustomerMyAccountPageSection.xml | 13 + .../StorefrontCustomerCreateFormSection.xml} | 7 +- .../Mftf/Test/NewProductsListWidgetTest.xml | 29 - ...NewProductsListWidgetBundleProductTest.xml | 13 + ...ductsListWidgetConfigurableProductTest.xml | 13 + ...ductsListWidgetDownloadableProductTest.xml | 13 + ...ewProductsListWidgetGroupedProductTest.xml | 13 + ...NewProductsListWidgetSimpleProductTest.xml | 13 + ...ewProductsListWidgetVirtualProductTest.xml | 13 + .../OtherPayPalPaymentsConfigSection.xml | 31 - .../OtherPayPalPaymentsConfigSection.xml | 14 + ...HostedWithExpressCheckoutConfigSection.xml | 14 + .../WPSExpressConfigSection.xml | 14 + .../WPSOtherConfigSection.xml | 14 + .../WebsitePaymentsPlusConfigSection.xml | 14 + .../PayPalExpressCheckoutConfigSection.xml | 67 -- .../ButtonCustomizationSection.xml | 19 + .../CheckoutPaymentSection.xml | 17 + .../PayPalAdvancedSettingConfigSection.xml | 15 + .../PayPalButtonOnStorefrontSection.xml | 17 + .../PayPalExpressCheckoutConfigSection.xml | 21 + ...pressCheckoutOtherCountryConfigSection.xml | 14 + .../PayPalPaymentSection.xml | 23 + ...aymentsConflictResolutionForPayPalTest.xml | 276 ------ ...onflictResolutionForPayPalInFranceTest.xml | 50 + ...flictResolutionForPayPalInHongKongTest.xml | 50 + ...ConflictResolutionForPayPalInItalyTest.xml | 50 + ...ConflictResolutionForPayPalInJapanTest.xml | 50 + ...ConflictResolutionForPayPalInSpainTest.xml | 50 + ...ResolutionForPayPalInUnitedKingdomTest.xml | 75 ++ .../GeneratedReportSection.xml | 14 + .../OrderReportFilterSection.xml} | 13 +- .../OrderReportMainSection.xml | 14 + ...tomerOrderMatchesGrandTotalActionGroup.xml | 2 +- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 89 -- .../EndToEndB2CGuestUserMysqlTest.xml | 49 + .../EndToEndB2CGuestUserTest.xml | 49 + .../GoToShipmentIntoOrderActionGroup.xml | 19 + .../SeeProductInShipmentItemsActionGroup.xml | 20 + .../SubmitShipmentIntoOrderActionGroup.xml | 19 + ...fyBasicShipmentInformationActionGroup.xml} | 32 +- ...AdminShipmentAddressInformationSection.xml | 2 +- ...AdminShipmentCreatePackageMainSection.xml} | 6 +- ...hipmentCreatePackageProductGridSection.xml | 13 + .../AdminStoresGridControlsSection.xml | 15 + .../AdminStoresGridSection.xml | 18 +- ...eta.xml => SwatchProductAttributeMeta.xml} | 0 ...l => SwatchProductAttributeOptionMeta.xml} | 0 ...ductSwatchMinimumPriceCategoryPageTest.xml | 31 + ...ductSwatchMinimumPriceProductPageTest.xml} | 24 +- .../Mftf/Test/StorefrontTaxQuoteCartTest.xml | 470 ---------- .../StorefrontTaxQuoteCartGuestSimpleTest.xml | 121 +++ ...StorefrontTaxQuoteCartGuestVirtualTest.xml | 122 +++ ...orefrontTaxQuoteCartLoggedInSimpleTest.xml | 127 +++ ...refrontTaxQuoteCartLoggedInVirtualTest.xml | 125 +++ .../Test/StorefrontTaxQuoteCheckoutTest.xml | 477 ---------- ...refrontTaxQuoteCheckoutGuestSimpleTest.xml | 126 +++ ...efrontTaxQuoteCheckoutGuestVirtualTest.xml | 115 +++ ...rontTaxQuoteCheckoutLoggedInSimpleTest.xml | 131 +++ ...ontTaxQuoteCheckoutLoggedInVirtualTest.xml | 131 +++ .../NewsletterWYSIWYGSection.xml | 13 + .../ProductWYSIWYGSection.xml | 13 + .../TinyMCESection.xml} | 9 +- .../AdminFormSaveAndCloseActionGroup.xml | 19 + .../AdminFormSaveAndDuplicateActionGroup.xml} | 12 +- .../Mftf/Section/AdminGridControlsSection.xml | 81 -- .../AdminGridActionsMenuSection.xml | 14 + .../AdminGridColumnsControlsSection.xml | 18 + .../AdminGridConfirmActionSection.xml | 16 + .../AdminGridDefaultViewControlsSection.xml | 17 + .../AdminGridFilterControlsSection.xml | 16 + .../AdminGridHeadersSection.xml | 15 + .../AdminGridMainControlsSection.xml | 20 + .../AdminGridRowSection.xml | 19 + .../AdminGridRowsPerPageSection.xml | 13 + .../AdminGridSearchBoxSection.xml | 14 + .../AdminGridSelectRowsSection.xml | 16 + ...tipleStoreviewsDuringProductImportTest.xml | 130 +++ ...ortTestWithConfigurationTurnedOffTest.xml} | 132 +-- ...writesForProductInAnchorCategoriesTest.xml | 291 ------ ...writesForProductInAnchorCategoriesTest.xml | 85 ++ ...InAnchorCategoriesTestAllStoreViewTest.xml | 36 + ...toreViewWithConfigurationTurnedOffTest.xml | 36 + ...riesTestWithConfigurationTurnedOffTest.xml | 113 +++ ...ProductsWithConfigurationTurnedOffTest.xml | 57 ++ .../Mftf/Section/AdminRoleGridSection.xml | 25 - .../AdminDeleteRoleSection.xml | 17 + .../AdminRoleGridSection.xml | 18 + .../Mftf/Section/AdminUserGridSection.xml | 25 - .../AdminDeleteUserSection.xml | 17 + .../AdminUserGridSection.xml | 18 + composer.json | 2 +- composer.lock | 865 ++---------------- .../EndToEndB2CGuestUserMysqlTest.xml} | 30 +- .../EndToEndB2CGuestUserTest.xml | 35 + ...figurableProductInWishlistActionGroup.xml} | 17 +- ...bleProductInWishlistSidebarActionGroup.xml | 24 + 466 files changed, 14450 insertions(+), 12513 deletions(-) rename app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/{_Deprecated_ActionGroup.xml => _Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml} (57%) create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml create mode 100644 app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml rename app/code/Magento/Captcha/Test/Mftf/Test/{CaptchaFormsDisplayingTest.xml => CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml} (55%) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml rename app/code/Magento/Catalog/Test/Mftf/Section/{ => AdminProductAttributeSetSection}/AdminProductAttributeSetSection.xml (56%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml rename app/code/Magento/Catalog/Test/Mftf/Section/{ => AdminProductCustomizableOptionsSection}/AdminProductCustomizableOptionsSection.xml (85%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml rename app/code/Magento/Catalog/Test/Mftf/Section/{AdminProductRelatedUpSellCrossSellSection.xml => AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml} (80%) rename app/code/Magento/Catalog/Test/Mftf/Section/{ => AdminUpdateAttributesSection}/AdminUpdateAttributesSection.xml (88%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml rename app/code/Magento/Catalog/Test/Mftf/Section/{ => StorefrontCategorySidebarSection}/StorefrontCategorySidebarSection.xml (72%) rename app/code/Magento/Catalog/Test/Mftf/Test/{ => AdminApplyTierPriceToProductTest}/AdminApplyTierPriceToProductTest.xml (83%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{AdminCreateAndSwitchProductType.xml => AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml} (71%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{AdminCreateProductDuplicateUrlkeyTest.xml => AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml} (52%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{ => AdminMassUpdateProductAttributesStoreViewScopeTest}/AdminMassUpdateProductAttributesStoreViewScopeTest.xml (53%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{ => AdminMassUpdateProductStatusStoreViewScopeTest}/AdminMassUpdateProductStatusStoreViewScopeTest.xml (50%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{AdminProductTypeSwitchingOnEditingTest.xml => AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml} (59%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{AdminSimpleProductImagesTest.xml => AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml} (50%) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml rename app/code/Magento/Catalog/Test/Mftf/Test/{ => StorefrontProductNameWithDoubleQuoteTest}/StorefrontProductNameWithDoubleQuoteTest.xml (51%) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml rename app/code/Magento/CatalogRule/Test/Mftf/Section/{ => AdminNewCatalogPriceRuleSection}/AdminNewCatalogPriceRuleSection.xml (52%) delete mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml rename app/code/Magento/CatalogRule/Test/Mftf/Test/{AdminDeleteCatalogPriceRuleEntityTest.xml => AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml} (62%) create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml delete mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml rename app/code/Magento/CatalogSearch/Test/Mftf/Test/{EndToEndB2CGuestUserTest.xml => EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml} (50%) create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml delete mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml rename app/code/Magento/CatalogWidget/Test/Mftf/Section/{CatalogWidgetSection.xml => CatalogWidgetSection/InsertWidgetSection.xml} (78%) rename app/code/Magento/Checkout/Test/Mftf/ActionGroup/{StorefrontOpenMiniCartActionGroup.xml => StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml} (67%) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml rename app/code/Magento/Checkout/Test/Mftf/Test/{CheckCheckoutSuccessPageTest.xml => CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml} (60%) delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml rename app/code/Magento/Checkout/Test/Mftf/Test/{StorefrontCheckCartAndCheckoutItemsCountTest.xml => StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml} (67%) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{SearchBlockOnGridPageActionGroup.xml => SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml} (71%) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml rename app/code/Magento/Cms/Test/Mftf/Section/{CmsNewBlockBlockBasicFieldsSection.xml => CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml} (66%) create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml rename app/code/Magento/Cms/Test/Mftf/Section/{CmsNewPagePageContentSection.xml => CmsNewPagePageContentSection/CmsWYSIWYGSection.xml} (59%) delete mode 100644 app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml delete mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml rename app/code/Magento/Config/Test/Mftf/Section/{ => CatalogSection}/CatalogSection.xml (76%) create mode 100644 app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml delete mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml rename app/code/Magento/ConfigurableProduct/Test/Mftf/Section/{ => AdminProductFormConfigurationsSection}/AdminProductFormConfigurationsSection.xml (77%) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml rename app/code/Magento/ConfigurableProduct/Test/Mftf/Test/{AdminConfigurableProductCreateTest.xml => AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml} (54%) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Test/{AdminConfigurableProductDeleteTest.xml => AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml} (64%) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml rename app/code/Magento/ConfigurableProduct/Test/Mftf/Test/{ => AdminConfigurableProductSearchTest}/AdminConfigurableProductSearchTest.xml (50%) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml rename app/code/Magento/ConfigurableProduct/Test/Mftf/Test/{AdminConfigurableProductUpdateAttributeTest.xml => AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml} (51%) delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{AdminAssertCustomerSubscribeNewsletterActionGroup.xml => AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml} (54%) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{AdminCustomerSubscribeNewsletterActionGroup.xml => AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml} (60%) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml rename app/code/Magento/Customer/Test/Mftf/Section/{AdminCustomerShoppingCartSection.xml => AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml} (80%) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml rename app/code/Magento/Customer/Test/Mftf/Section/{StorefrontCustomerDashboardAccountInformationSection.xml => StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml} (62%) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml rename app/code/Magento/Customer/Test/Mftf/Section/{SwitchAccountSection.xml => SwitchAccountSection/LoginFormSection.xml} (61%) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml rename app/code/Magento/Customer/Test/Mftf/Test/{ChangeCustomerGroupTest.xml => ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml} (75%) delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml rename app/code/Magento/Downloadable/Test/Mftf/Test/{AdminProductTypeSwitchingOnEditingTest.xml => AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml} (75%) delete mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml rename app/code/Magento/GroupedProduct/Test/Mftf/Section/{ => AdminAddProductsToGroupPanelSection}/AdminAddProductsToGroupPanelSection.xml (77%) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml delete mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml delete mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml rename app/code/Magento/LayeredNavigation/Test/Mftf/Section/{ => LayeredNavigationSection}/LayeredNavigationSection.xml (80%) create mode 100644 app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml rename app/code/Magento/Multishipping/Test/Mftf/Section/{ => MultishippingSection}/MultishippingSection.xml (50%) create mode 100644 app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml create mode 100644 app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml rename app/code/Magento/Newsletter/Test/Mftf/Page/{NewsletterTemplatePage.xml => NewsletterTemplatePage/NewsletterTemplateFormPage.xml} (59%) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml rename app/code/Magento/Newsletter/Test/Mftf/Section/{NewsletterTemplateSection.xml => NewsletterTemplateSection/NewsletterWYSIWYGSection.xml} (64%) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml rename app/code/Magento/Newsletter/Test/Mftf/Section/{VerifySubscribedNewsLetterDisplayedSection.xml => VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml} (64%) delete mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml delete mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml delete mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml delete mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml create mode 100644 app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml rename app/code/Magento/Reports/Test/Mftf/Section/{OrderReportMainSection.xml => OrderReportMainSection/OrderReportFilterSection.xml} (57%) create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml delete mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml rename app/code/Magento/Shipping/Test/Mftf/ActionGroup/{_Deprecated_ActionGroup.xml => _Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml} (57%) rename app/code/Magento/Shipping/Test/Mftf/Section/{AdminShipmentCreatePackageSection.xml => AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml} (68%) create mode 100644 app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml rename app/code/Magento/Store/Test/Mftf/Section/{ => AdminStoresGridSection}/AdminStoresGridSection.xml (73%) rename app/code/Magento/Swatches/Test/Mftf/Metadata/{swatch_product_attribute-meta.xml => SwatchProductAttributeMeta.xml} (100%) rename app/code/Magento/Swatches/Test/Mftf/Metadata/{swatch_product_attribute_option-meta.xml => SwatchProductAttributeOptionMeta.xml} (100%) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml rename app/code/Magento/Swatches/Test/Mftf/Test/{StorefrontConfigurableProductSwatchMinimumPriceTest.xml => StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml} (86%) delete mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml delete mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml create mode 100644 app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml create mode 100644 app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml rename app/code/Magento/Tinymce3/Test/Mftf/Section/{AdminTinymce3FileldsSection.xml => AdminTinymce3FileldsSection/TinyMCESection.xml} (55%) create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml rename app/code/Magento/Ui/Test/Mftf/ActionGroup/{_Deprecated_ActionGroup.xml => _Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml} (60%) delete mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml rename app/code/Magento/UrlRewrite/Test/Mftf/Test/{AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml => AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml} (53%) delete mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml delete mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml delete mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml rename dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/{EndToEndB2CGuestUserTest.xml => EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml} (50%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml rename dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/{_Deprecated_ActionGroup.xml => _Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml} (54%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml similarity index 57% rename from app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml rename to app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml index fc66f9fb0ef74..15f6852b4767c 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -5,13 +5,9 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="LoginAdminWithCredentialsActionGroup" deprecated="Use AdminLoginActionGroup instead"> <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> </actionGroup> - <actionGroup name="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml new file mode 100644 index 0000000000000..a2d024bee8b74 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml @@ -0,0 +1,13 @@ +<?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="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml deleted file mode 100644 index d2f4496c8cd4e..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ /dev/null @@ -1,56 +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="LoginAsAnyUser" deprecated="Use LoginAdminWithCredentialsActionGroup instead"> - <arguments> - <argument name="uname" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/> - <argument name="passwd" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - </arguments> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> - <fillField userInput="{{uname}}" selector="{{LoginFormSection.username}}" stepKey="fillUsername"/> - <fillField userInput="{{passwd}}" selector="{{LoginFormSection.password}}" stepKey="fillPassword"/> - <click selector="{{LoginFormSection.signIn}}" stepKey="clickLogin"/> - </actionGroup> - - <actionGroup name="logout" deprecated="Use AdminLogoutActionGroup instead" extends="AdminLogoutActionGroup"/> - - <actionGroup name="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> - <annotations> - <description>Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> - </annotations> - <arguments> - <argument name="adminUser" type="entity" defaultValue="DefaultAdminUser"/> - </arguments> - - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser.username}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminUser.password}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> - </actionGroup> - - <actionGroup name="LoginActionGroup" deprecated="Use AdminLoginActionGroup instead" extends="AdminLoginActionGroup"/> - - <actionGroup name="LoginAdminWithCredentialsActionGroup" deprecated="Use AdminLoginActionGroup"> - <annotations> - <description>Login to Backend Admin using provided Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> - </annotations> - <arguments> - <argument name="adminUser" type="string"/> - <argument name="adminPassword" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml new file mode 100644 index 0000000000000..4afabe2d0eff3 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml @@ -0,0 +1,11 @@ +<?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="LoginActionGroup" deprecated="Use AdminLoginActionGroup instead" extends="AdminLoginActionGroup"/> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml new file mode 100644 index 0000000000000..cc124990c92ca --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoginAdminWithCredentialsActionGroup" deprecated="Use AdminLoginActionGroup"> + <annotations> + <description>Login to Backend Admin using provided Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> + </annotations> + <arguments> + <argument name="adminUser" type="string"/> + <argument name="adminPassword" type="string"/> + </arguments> + + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml new file mode 100644 index 0000000000000..6fe9f184f5ecf --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.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="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> + <annotations> + <description>Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> + </annotations> + <arguments> + <argument name="adminUser" type="entity" defaultValue="DefaultAdminUser"/> + </arguments> + + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser.username}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminUser.password}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml new file mode 100644 index 0000000000000..b6cec9056bd5d --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoginAsAnyUser" deprecated="Use LoginAdminWithCredentialsActionGroup instead"> + <arguments> + <argument name="uname" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/> + <argument name="passwd" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </arguments> + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> + <fillField userInput="{{uname}}" selector="{{LoginFormSection.username}}" stepKey="fillUsername"/> + <fillField userInput="{{passwd}}" selector="{{LoginFormSection.password}}" stepKey="fillPassword"/> + <click selector="{{LoginFormSection.signIn}}" stepKey="clickLogin"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml new file mode 100644 index 0000000000000..ec79c982e730c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml @@ -0,0 +1,11 @@ +<?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="logout" deprecated="Use AdminLogoutActionGroup instead" extends="AdminLogoutActionGroup"/> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml deleted file mode 100644 index 8afc2c5bbb32f..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="ConfigurationStoresPage" url="admin/system_config/edit/section/cms/" area="admin" module="Catalog"> - <section name="WYSIWYGOptionsSection"/> - </page> - <page name="WebConfigurationPage" url="admin/system_config/edit/section/web/" area="admin" module="Backend"> - <section name="WYSIWYGOptionsSection"/> - </page> - <page name="GeneralConfigurationPage" url="admin/system_config/edit/section/general/" area="admin" module="Backend"> - <section name="LocaleOptionsSection"/> - </page> -</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml new file mode 100644 index 0000000000000..81eb04692cc57 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="ConfigurationStoresPage" url="admin/system_config/edit/section/cms/" area="admin" module="Catalog"> + <section name="WYSIWYGOptionsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml new file mode 100644 index 0000000000000..59b461fb08a4d --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="GeneralConfigurationPage" url="admin/system_config/edit/section/general/" area="admin" module="Backend"> + <section name="LocaleOptionsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml new file mode 100644 index 0000000000000..b5429b091d755 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="WebConfigurationPage" url="admin/system_config/edit/section/web/" area="admin" module="Backend"> + <section name="WYSIWYGOptionsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml index d9f2b14a40e2f..37d5204efb2c1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BraintreeConfigurationPaymentSection"> <element name="creditCart" type="radio" selector="#braintree"/> <element name="paymentMethodContainer" type="block" selector=".payment-method-braintree"/> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml new file mode 100644 index 0000000000000..ee71a243a769d --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml @@ -0,0 +1,67 @@ +<?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="CaptchaFormsDisplayingTest"> + <annotations> + <features value="Captcha"/> + <stories value="MAGETWO-91552 - [github] CAPTCHA doesn't show when check out as guest"/> + <title value="Captcha forms displaying"/> + <description value="Captcha forms displaying"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93941"/> + <group value="captcha"/> + </annotations> + + <!--Login as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!--Go to Captcha--> + <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> + <waitForPageLoad stepKey="WaitForPageLoaded"/> + <!--Verify fields removed--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forms}}" stepKey="formItems"/> + <assertNotContains stepKey="checkoutAsGuest"> + <expectedResult type="string">{{CaptchaData.checkoutAsGuest}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <assertNotContains stepKey="register"> + <expectedResult type="string">{{CaptchaData.register}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <!--Verify fields existence--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.createUser}}" stepKey="createUser"/> + <assertEquals stepKey="CreateUserFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.createUser}}</expectedResult> + <actualResult type="variable">$createUser</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userLogin}}" stepKey="login"/> + <assertEquals stepKey="LoginFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.login}}</expectedResult> + <actualResult type="variable">login</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forgotpassword}}" stepKey="forgotpassword"/> + <assertEquals stepKey="PasswordFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.passwd}}</expectedResult> + <actualResult type="variable">$forgotpassword</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.contactUs}}" stepKey="contactUs"/> + <assertEquals stepKey="contactUsFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.contactUs}}</expectedResult> + <actualResult type="variable">$contactUs</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userEdit}}" stepKey="userEdit"/> + <assertEquals stepKey="userEditFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.changePasswd}}</expectedResult> + <actualResult type="variable">$userEdit</actualResult> + </assertEquals> + + <!--Roll back configuration--> + <scrollToTopOfPage stepKey="ScrollToTop"/> + <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> + </test> +</tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml similarity index 55% rename from app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml rename to app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml index b1c3ed52f163b..0fff28973fe61 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml @@ -5,66 +5,8 @@ * 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="CaptchaFormsDisplayingTest"> - <annotations> - <features value="Captcha"/> - <stories value="MAGETWO-91552 - [github] CAPTCHA doesn't show when check out as guest"/> - <title value="Captcha forms displaying"/> - <description value="Captcha forms displaying"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-93941"/> - <group value="captcha"/> - </annotations> - - <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <!--Go to Captcha--> - <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> - <waitForPageLoad stepKey="WaitForPageLoaded"/> - <!--Verify fields removed--> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forms}}" stepKey="formItems"/> - <assertNotContains stepKey="checkoutAsGuest"> - <expectedResult type="string">{{CaptchaData.checkoutAsGuest}}</expectedResult> - <actualResult type="variable">$formItems</actualResult> - </assertNotContains> - <assertNotContains stepKey="register"> - <expectedResult type="string">{{CaptchaData.register}}</expectedResult> - <actualResult type="variable">$formItems</actualResult> - </assertNotContains> - <!--Verify fields existence--> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.createUser}}" stepKey="createUser"/> - <assertEquals stepKey="CreateUserFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.createUser}}</expectedResult> - <actualResult type="variable">$createUser</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userLogin}}" stepKey="login"/> - <assertEquals stepKey="LoginFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.login}}</expectedResult> - <actualResult type="variable">login</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forgotpassword}}" stepKey="forgotpassword"/> - <assertEquals stepKey="PasswordFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.passwd}}</expectedResult> - <actualResult type="variable">$forgotpassword</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.contactUs}}" stepKey="contactUs"/> - <assertEquals stepKey="contactUsFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.contactUs}}</expectedResult> - <actualResult type="variable">$contactUs</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userEdit}}" stepKey="userEdit"/> - <assertEquals stepKey="userEditFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.changePasswd}}</expectedResult> - <actualResult type="variable">$userEdit</actualResult> - </assertEquals> - - <!--Roll back configuration--> - <scrollToTopOfPage stepKey="ScrollToTop"/> - <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> - </test> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CaptchaWithDisabledGuestCheckoutTest"> <annotations> <features value="Captcha"/> @@ -91,7 +33,7 @@ </after> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml index 1830a6abc992e..bf683a7baec1d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml @@ -17,6 +17,9 @@ <argument name="categoryLevel" type="string" defaultValue="3"/> </arguments> - <assertEquals expected="{{parentCategoryLevel}} + 1" actual="{{categoryLevel}}" message="wrongCategoryLevel" stepKey="compareCategoryLevel"/> + <assertEquals message="wrongCategoryLevel" stepKey="compareCategoryLevel"> + <actualResult type="const">{{categoryLevel}}</actualResult> + <expectedResult type="const">{{parentCategoryLevel}} + 1</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml index 8986db7af9246..d2f04629e1fe8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml @@ -17,6 +17,9 @@ <argument name="productPosition" type="string"/> </arguments> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName(productPosition)}}" stepKey="grabRelatedProductPosition"/> - <assertContains expected="{{productName}}" actual="$grabRelatedProductPosition" stepKey="assertRelatedProductName"/> + <assertContains stepKey="assertRelatedProductName"> + <actualResult type="const">$grabRelatedProductPosition</actualResult> + <expectedResult type="const">{{productName}}</expectedResult> + </assertContains> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml deleted file mode 100644 index d6017e801052b..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?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="AdminCategoryBasicFieldSection"> - <element name="IncludeInMenu" type="checkbox" selector="input[name='include_in_menu']"/> - <element name="includeInMenuLabel" type="text" selector="input[name='include_in_menu']+label"/> - <element name="includeInMenuUseDefault" type="checkbox" selector="input[name='use_default[include_in_menu]']"/> - <element name="EnableCategory" type="checkbox" selector="input[name='is_active']"/> - <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> - <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> - <element name="CategoryNameInput" type="input" selector="input[name='name']"/> - <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> - <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> - <element name="ContentTab" type="input" selector="input[name='name']"/> - <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> - <element name="panelFieldControl" type="input" selector='//aside//div[@data-index="{{arg1}}"]/descendant::*[@name="{{arg2}}"]' parameterized="true"/> - <element name="productsInCategory" type="input" selector="div[data-index='assign_products']" timeout="30"/> - </section> - <section name="CategoryContentSection"> - <element name="SelectFromGalleryBtn" type="button" selector="//label[text()='Select from Gallery']"/> - <element name="ImagePlaceHolder" type="button" selector=".file-uploader-summary.product-image-wrapper"/> - <element name="Upload" type="button" selector=".file-uploader-area input"/> - </section> - <section name="CategoryDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> - </section> - <section name="CategoryDisplaySettingsSection"> - <element name="DisplaySettingTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Display Settings']"/> - <element name="layeredNavigationPriceInput" type="input" selector="input[name='filter_price_range']"/> - <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> - <element name="filterPriceRangeUseConfig" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> - <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index={{arg1}}]>.admin__field-label span'), ':after').getPropertyValue('content');" parameterized="true"/> - <element name="displayMode" type="button" selector="select[name='display_mode']"/> - <element name="anchor" type="checkbox" selector="input[name='is_anchor']"/> - <element name="anchorLabel" type="text" selector="input[name='is_anchor']+label"/> - <element name="productListCheckBox" type="checkbox" selector="input[name='use_config[available_sort_by]']" /> - <element name="productList" type="text" selector="select[name='available_sort_by']"/> - <element name="defaultProductLisCheckBox" type="checkbox" selector="input[name='use_config[default_sort_by]']"/> - <element name="defaultProductList" type="text" selector="select[name='default_sort_by']"/> - <element name="layeredNavigationPriceCheckBox" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> - </section> - <section name="CatalogWYSIWYGSection"> - <element name="ShowHideBtn" type="button" selector="#togglecategory_form_description"/> - <element name="TinyMCE4" type="text" selector=".mce-branding"/> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml new file mode 100644 index 0000000000000..aff7ffe4d5763 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml @@ -0,0 +1,26 @@ +<?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="AdminCategoryBasicFieldSection"> + <element name="IncludeInMenu" type="checkbox" selector="input[name='include_in_menu']"/> + <element name="includeInMenuLabel" type="text" selector="input[name='include_in_menu']+label"/> + <element name="includeInMenuUseDefault" type="checkbox" selector="input[name='use_default[include_in_menu]']"/> + <element name="EnableCategory" type="checkbox" selector="input[name='is_active']"/> + <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> + <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> + <element name="CategoryNameInput" type="input" selector="input[name='name']"/> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> + <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> + <element name="ContentTab" type="input" selector="input[name='name']"/> + <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> + <element name="panelFieldControl" type="input" selector="//aside//div[@data-index="{{arg1}}"]/descendant::*[@name="{{arg2}}"]" parameterized="true"/> + <element name="productsInCategory" type="input" selector="div[data-index='assign_products']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml new file mode 100644 index 0000000000000..00656608ed9b8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml @@ -0,0 +1,46 @@ +<?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="CatalogWYSIWYGSection"> + <element name="ShowHideBtn" type="button" selector="#togglecategory_form_description"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml new file mode 100644 index 0000000000000..f1876bc7c5056 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml @@ -0,0 +1,15 @@ +<?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="CategoryContentSection"> + <element name="SelectFromGalleryBtn" type="button" selector="//label[text()='Select from Gallery']"/> + <element name="ImagePlaceHolder" type="button" selector=".file-uploader-summary.product-image-wrapper"/> + <element name="Upload" type="button" selector=".file-uploader-area input"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml new file mode 100644 index 0000000000000..3e6e00b845401 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CategoryDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml new file mode 100644 index 0000000000000..65cc6084d7509 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml @@ -0,0 +1,25 @@ +<?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="CategoryDisplaySettingsSection"> + <element name="DisplaySettingTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Display Settings']"/> + <element name="layeredNavigationPriceInput" type="input" selector="input[name='filter_price_range']"/> + <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> + <element name="filterPriceRangeUseConfig" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index={{arg1}}]>.admin__field-label span'), ':after').getPropertyValue('content');" parameterized="true"/> + <element name="displayMode" type="button" selector="select[name='display_mode']"/> + <element name="anchor" type="checkbox" selector="input[name='is_anchor']"/> + <element name="anchorLabel" type="text" selector="input[name='is_anchor']+label"/> + <element name="productListCheckBox" type="checkbox" selector="input[name='use_config[available_sort_by]']"/> + <element name="productList" type="text" selector="select[name='available_sort_by']"/> + <element name="defaultProductLisCheckBox" type="checkbox" selector="input[name='use_config[default_sort_by]']"/> + <element name="defaultProductList" type="text" selector="select[name='default_sort_by']"/> + <element name="layeredNavigationPriceCheckBox" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml deleted file mode 100644 index 0934e39dcb062..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ /dev/null @@ -1,106 +0,0 @@ -<?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="AttributePropertiesSection"> - <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> - <element name="DefaultLabel" type="input" selector="#attribute_label"/> - <element name="InputType" type="select" selector="#frontend_input"/> - <element name="ValueRequired" type="select" selector="#is_required"/> - <element name="UpdateProductPreviewImage" type="select" selector="[name='update_product_preview_image']"/> - <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> - <element name="DefaultValue" type="input" selector="#default_value_text"/> - <element name="Scope" type="select" selector="#is_global"/> - <element name="Save" type="button" selector="#save" timeout="30"/> - <element name="DeleteAttribute" type="button" selector="#delete" timeout="30"/> - <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> - <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//*[contains(@class,'mce-branding')]"/> - <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> - <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> - <element name="addSwatch" type="button" selector="#add_new_swatch_text_option_button"/> - <element name="dropdownAddOptions" type="button" selector="#add_new_option_button" timeout="30"/> - <!-- Manage Options nth child--> - <element name="dropdownNthOptionIsDefault" type="checkbox" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) .input-radio" parameterized="true"/> - <element name="dropdownNthOptionAdmin" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(3) input" parameterized="true"/> - <element name="dropdownNthOptionDefaultStoreView" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(4) input" parameterized="true"/> - <element name="dropdownNthOptionDelete" type="button" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) button[title='Delete']" parameterized="true"/> - </section> - <section name="AttributeDeleteModalSection"> - <element name="confirm" type="button" selector=".modal-popup.confirm .action-accept"/> - <element name="cancel" type="button" selector=".modal-popup.confirm .action-dismiss"/> - </section> - <section name="AttributeManageSwatchSection"> - <element name="swatchField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Swatch']" parameterized="true"/> - <element name="descriptionField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Description']" parameterized="true"/> - </section> - <section name="AttributeOptionsSection"> - <element name="AddOption" type="button" selector="#add_new_option_button"/> - </section> - <section name="StorefrontPropertiesSection"> - <element name="PageTitle" type="text" selector="//span[text()='Storefront Properties']" /> - <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> - <element name="EnableWYSIWYG" type="select" selector="#enabled"/> - <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> - <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> - <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> - </section> - <section name="WYSIWYGProductAttributeSection"> - <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> - <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> - <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> - <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> - <element name="TextArea" type="input" selector="#default_value_textarea" /> - </section> - <section name="AdvancedAttributePropertiesSection"> - <element name="AdvancedAttributePropertiesSectionToggle" - type="button" selector="#advanced_fieldset-wrapper"/> - <element name="AttributeCode" type="text" selector="#attribute_code"/> - <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> - <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> - <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> - <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> - <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> - <element name="Scope" type="select" selector="#is_global"/> - <element name="UniqueValue" type="select" selector="#is_unique"/> - <element name="AddToColumnOptions" type="select" selector="#is_used_in_grid"/> - <element name="UseInFilterOptions" type="select" selector="#is_filterable_in_grid"/> - <element name="UseInProductListing" type="select" selector="#used_in_product_listing"/> - <element name="UseInSearch" type="select" selector="#is_searchable"/> - <element name="VisibleInAdvancedSearch" type="select" selector="#is_visible_in_advanced_search"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml new file mode 100644 index 0000000000000..bcd05e139d17c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml @@ -0,0 +1,26 @@ +<?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="AdvancedAttributePropertiesSection"> + <element name="AdvancedAttributePropertiesSectionToggle" type="button" selector="#advanced_fieldset-wrapper"/> + <element name="AttributeCode" type="text" selector="#attribute_code"/> + <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> + <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> + <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> + <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> + <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> + <element name="Scope" type="select" selector="#is_global"/> + <element name="UniqueValue" type="select" selector="#is_unique"/> + <element name="AddToColumnOptions" type="select" selector="#is_used_in_grid"/> + <element name="UseInFilterOptions" type="select" selector="#is_filterable_in_grid"/> + <element name="UseInProductListing" type="select" selector="#used_in_product_listing"/> + <element name="UseInSearch" type="select" selector="#is_searchable"/> + <element name="VisibleInAdvancedSearch" type="select" selector="#is_visible_in_advanced_search"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml new file mode 100644 index 0000000000000..7f7be6e13516c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeDeleteModalSection"> + <element name="confirm" type="button" selector=".modal-popup.confirm .action-accept"/> + <element name="cancel" type="button" selector=".modal-popup.confirm .action-dismiss"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml new file mode 100644 index 0000000000000..6592f8e20cff5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeManageSwatchSection"> + <element name="swatchField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Swatch']" parameterized="true"/> + <element name="descriptionField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Description']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml new file mode 100644 index 0000000000000..ad58b19de1adc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml @@ -0,0 +1,13 @@ +<?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="AttributeOptionsSection"> + <element name="AddOption" type="button" selector="#add_new_option_button"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml new file mode 100644 index 0000000000000..df5ebab9c9b2b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml @@ -0,0 +1,33 @@ +<?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="AttributePropertiesSection"> + <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> + <element name="DefaultLabel" type="input" selector="#attribute_label"/> + <element name="InputType" type="select" selector="#frontend_input"/> + <element name="ValueRequired" type="select" selector="#is_required"/> + <element name="UpdateProductPreviewImage" type="select" selector="[name='update_product_preview_image']"/> + <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> + <element name="DefaultValue" type="input" selector="#default_value_text"/> + <element name="Scope" type="select" selector="#is_global"/> + <element name="Save" type="button" selector="#save" timeout="30"/> + <element name="DeleteAttribute" type="button" selector="#delete" timeout="30"/> + <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> + <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//*[contains(@class,'mce-branding')]"/> + <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> + <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> + <element name="addSwatch" type="button" selector="#add_new_swatch_text_option_button"/> + <element name="dropdownAddOptions" type="button" selector="#add_new_option_button" timeout="30"/> + <!-- Manage Options nth child--> + <element name="dropdownNthOptionIsDefault" type="checkbox" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) .input-radio" parameterized="true"/> + <element name="dropdownNthOptionAdmin" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(3) input" parameterized="true"/> + <element name="dropdownNthOptionDefaultStoreView" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(4) input" parameterized="true"/> + <element name="dropdownNthOptionDelete" type="button" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) button[title='Delete']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml new file mode 100644 index 0000000000000..9e6e99ff2e59e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml @@ -0,0 +1,18 @@ +<?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="StorefrontPropertiesSection"> + <element name="PageTitle" type="text" selector="//span[text()='Storefront Properties']"/> + <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> + <element name="EnableWYSIWYG" type="select" selector="#enabled"/> + <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> + <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> + <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml new file mode 100644 index 0000000000000..fc3c07b9634df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml @@ -0,0 +1,46 @@ +<?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="WYSIWYGProductAttributeSection"> + <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> + <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> + <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> + <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="TextArea" type="input" selector="#default_value_textarea"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml similarity index 56% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml index 8f635214ffffd..6790022d5fcff 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeSetSection"> <element name="name" type="input" selector="#attribute_set_name"/> <element name="basedOn" type="select" selector="#skeleton_set"/> @@ -18,17 +17,4 @@ <element name="newGroupName" type="input" selector="input[name='name']"/> <element name="modalOk" type="button" selector="button.action-accept" timeout="30"/> </section> - <section name="AttributeSetSection"> - <element name="Save" type="button" selector="button[title='Save']" /> - </section> - <section name="UnassignedAttributes"> - <element name="ProductAttributeName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> - </section> - <section name="Group"> - <element name="FolderName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> - </section> - <section name="ModifyAttributes"> - <!-- Parameter is the attribute name --> - <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../../..//select" parameterized="true"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml new file mode 100644 index 0000000000000..879b043d13df3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml @@ -0,0 +1,13 @@ +<?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="AttributeSetSection"> + <element name="Save" type="button" selector="button[title='Save']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml new file mode 100644 index 0000000000000..73ea6e0a86f8d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml @@ -0,0 +1,13 @@ +<?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="Group"> + <element name="FolderName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml new file mode 100644 index 0000000000000..4e52263f05717 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ModifyAttributes"> + <!-- Parameter is the attribute name --> + <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../../..//select" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml new file mode 100644 index 0000000000000..b6a29a8b60b12 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml @@ -0,0 +1,13 @@ +<?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="UnassignedAttributes"> + <element name="ProductAttributeName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml similarity index 85% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml index 8802372968f65..00ab3c67c7c93 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> <element name="requiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('#product_composite_configure_form_fields .admin__field.required .admin__field-label'), ':after').getPropertyValue('content');"/> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']" timeout="30"/> @@ -35,7 +34,7 @@ <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div/parent::div//select" parameterized="true"/> <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/parent::div/div//input[@type='checkbox']"/> <element name="checkboxUseDefaultOption" type="checkbox" selector="//table[@data-index='values']//tbody//tr[@data-repeat-index='{{var1}}']//div[@class='admin__field-control']//input[@type='checkbox']" parameterized="true"/> - <element name="requiredCheckBox" type="checkbox" selector="input[name='product[options][{{index}}][is_require]']" parameterized="true" /> + <element name="requiredCheckBox" type="checkbox" selector="input[name='product[options][{{index}}][is_require]']" parameterized="true"/> <element name="fillOptionValueSku" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='SKU']/parent::label/parent::div/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionCompatibleFileExtensions" type="input" selector="input[name='product[options][{{index}}][file_extension]']" parameterized="true"/> @@ -44,28 +43,19 @@ <element name="optionPriceTypeByTitle" type="select" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='price_type']//select" parameterized="true"/> <element name="optionSkuByTitle" type="input" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='sku']//input" parameterized="true"/> <element name="optionFileExtensionByTitle" type="input" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='file_extension']//input" parameterized="true"/> - <element name="lastOptionTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, '_required')]//input" /> - <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> + <element name="lastOptionTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, '_required')]//input"/> + <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]"/> <element name="lastOptionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@name, '[price]')]"/> <element name="lastOptionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@name, '[price_type]')]"/> <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> - <element name="optionType" type="block" selector="//*[@data-index='custom_options']//label[text()='{{var1}}'][ancestor::*[contains(@class, '_active')]]" parameterized="true" /> + <element name="optionType" type="block" selector="//*[@data-index='custom_options']//label[text()='{{var1}}'][ancestor::*[contains(@class, '_active')]]" parameterized="true"/> <element name="addValue" type="button" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@data-action='add_new_row']" timeout="30"/> - <element name="valueTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='title']//input" /> - <element name="valuePrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='price']//input" /> + <element name="valueTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='title']//input"/> + <element name="valuePrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='price']//input"/> <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][price]']" parameterized="true"/> <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{var}}][price_type]']" parameterized="true"/> <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][sku]']" parameterized="true"/> <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][file_extension]']" parameterized="true"/> <element name="importOptions" type="button" selector="//button[@data-index='button_import']" timeout="30"/> </section> - <section name="AdminProductImportOptionsSection"> - <element name="selectProductTitle" type="text" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//h1[contains(text(), 'Select Product')]" timeout="30"/> - <element name="filterButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-expand']" timeout="30"/> - <element name="nameField" type="input" selector="aside.product_form_product_form_import_options_modal input[name='name']" timeout="30"/> - <element name="applyFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-apply']" timeout="30"/> - <element name="resetFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-reset']" timeout="30"/> - <element name="firstRowItemCheckbox" type="input" selector="aside.product_form_product_form_import_options_modal input[data-action='select-row']" timeout="30"/> - <element name="importButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml new file mode 100644 index 0000000000000..7d35920cebf3c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml @@ -0,0 +1,19 @@ +<?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="AdminProductImportOptionsSection"> + <element name="selectProductTitle" type="text" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//h1[contains(text(), 'Select Product')]" timeout="30"/> + <element name="filterButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-expand']" timeout="30"/> + <element name="nameField" type="input" selector="aside.product_form_product_form_import_options_modal input[name='name']" timeout="30"/> + <element name="applyFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-apply']" timeout="30"/> + <element name="resetFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-reset']" timeout="30"/> + <element name="firstRowItemCheckbox" type="input" selector="aside.product_form_product_form_import_options_modal input[data-action='select-row']" timeout="30"/> + <element name="importButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml deleted file mode 100644 index b0aee1795dc3b..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?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="AdminProductFormAttributeSection"> - <element name="createNewAttribute" type="button" selector="//button[@data-index='add_new_attribute_button']" timeout="30"/> - </section> - <section name="AdminProductFormNewAttributeSection"> - <element name="attributeLabel" type="button" selector="//input[@name='frontend_label[0]']" timeout="30"/> - <element name="attributeType" type="select" selector="//select[@name='frontend_input']" timeout="30"/> - <element name="addValue" type="button" selector="//button[@data-action='add_new_row']" timeout="30"/> - <element name="optionViewName" type="text" selector="//table[@data-index='attribute_options_select']//span[contains(text(), '{{arg}}')]" parameterized="true" timeout="30"/> - <element name="optionValue" type="input" selector="(//input[contains(@name, 'option[value]')])[{{arg}}]" timeout="30" parameterized="true"/> - <element name="manageTitlesHeader" type="button" selector="//div[@class='fieldset-wrapper-title']//span[contains(text(), 'Manage Titles')]" timeout="30"/> - <element name="manageTitlesViewName" type="text" selector="//div[@data-index='manage-titles']//span[contains(text(), '{{arg}}')]" timeout="30" parameterized="true"/> - <element name="saveAttribute" type="button" selector="button#save" timeout="30"/> - <element name="saveInNewSet" type="button" selector="button#saveInNewSet" timeout="10"/> - </section> - <section name="AdminProductFormNewAttributeAdvancedSection"> - <element name="sectionHeader" type="button" selector="div[data-index='advanced_fieldset']"/> - <element name="defaultValue" type="textarea" selector="input[name='default_value_text']"/> - <element name="scope" type="select" selector="//div[@data-index='advanced_fieldset']//select[@name='is_global']"/> - </section> - <section name="AdminProductFormNewAttributeStorefrontSection"> - <element name="sectionHeader" type="button" selector="div[data-index='front_fieldset']"/> - <element name="useInSearch" type="checkbox" selector="div[data-index='is_searchable'] .admin__field-control label"/> - <element name="searchWeight" type="select" selector="select[name='search_weight']"/> - </section> - <section name="AdminProductFormNewAttributeNewSetSection"> - <element name="setName" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//input[contains(@class, 'admin__control-text')]"/> - <element name="accept" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-accept')]"/> - <element name="cancel" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-dismiss')]"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml new file mode 100644 index 0000000000000..83d478e399b98 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml @@ -0,0 +1,13 @@ +<?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="AdminProductFormAttributeSection"> + <element name="createNewAttribute" type="button" selector="//button[@data-index='add_new_attribute_button']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml new file mode 100644 index 0000000000000..42cd7a3fe8cdc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductFormNewAttributeAdvancedSection"> + <element name="sectionHeader" type="button" selector="div[data-index='advanced_fieldset']"/> + <element name="defaultValue" type="textarea" selector="input[name='default_value_text']"/> + <element name="scope" type="select" selector="//div[@data-index='advanced_fieldset']//select[@name='is_global']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml new file mode 100644 index 0000000000000..4c7d5acb658fb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductFormNewAttributeNewSetSection"> + <element name="setName" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//input[contains(@class, 'admin__control-text')]"/> + <element name="accept" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-accept')]"/> + <element name="cancel" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-dismiss')]"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml new file mode 100644 index 0000000000000..5e2c3350e975e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.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="AdminProductFormNewAttributeSection"> + <element name="attributeLabel" type="button" selector="//input[@name='frontend_label[0]']" timeout="30"/> + <element name="attributeType" type="select" selector="//select[@name='frontend_input']" timeout="30"/> + <element name="addValue" type="button" selector="//button[@data-action='add_new_row']" timeout="30"/> + <element name="optionViewName" type="text" selector="//table[@data-index='attribute_options_select']//span[contains(text(), '{{arg}}')]" parameterized="true" timeout="30"/> + <element name="optionValue" type="input" selector="(//input[contains(@name, 'option[value]')])[{{arg}}]" timeout="30" parameterized="true"/> + <element name="manageTitlesHeader" type="button" selector="//div[@class='fieldset-wrapper-title']//span[contains(text(), 'Manage Titles')]" timeout="30"/> + <element name="manageTitlesViewName" type="text" selector="//div[@data-index='manage-titles']//span[contains(text(), '{{arg}}')]" timeout="30" parameterized="true"/> + <element name="saveAttribute" type="button" selector="button#save" timeout="30"/> + <element name="saveInNewSet" type="button" selector="button#saveInNewSet" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml new file mode 100644 index 0000000000000..1bc40cfc40743 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductFormNewAttributeStorefrontSection"> + <element name="sectionHeader" type="button" selector="div[data-index='front_fieldset']"/> + <element name="useInSearch" type="checkbox" selector="div[data-index='is_searchable'] .admin__field-control label"/> + <element name="searchWeight" type="select" selector="select[name='search_weight']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml deleted file mode 100644 index 7649f6c344c3a..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ /dev/null @@ -1,225 +0,0 @@ -<?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="AdminProductFormSection"> - <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> - <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> - <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> - <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> - <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> - <element name="attributeSetFilterResultByName" type="text" selector="//label/span[text() = '{{var}}']" timeout="30" parameterized="true"/> - <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> - <element name="productNameDisabled" type="input" selector=".admin__field[data-index=name] input[disabled=true]"/> - <element name="RequiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="RequiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="productSkuDisabled" type="input" selector=".admin__field[data-index=sku] input[disabled=true]"/> - <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> - <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> - <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> - <element name="productStatusValue" type="checkbox" selector="input[name='product[status]'][value='{{value}}']" timeout="30" parameterized="true"/> - <element name="productStatusDisabled" type="checkbox" selector="input[name='product[status]'][disabled]"/> - <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> - <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> - <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> - <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> - <element name="productPriceDisabled" type="input" selector=".admin__field[data-index=price] input[disabled=true]"/> - <element name="productPriceUseDefault" type="checkbox" selector=".admin__field[data-index=price] [name='use_default[price]']"/> - <element name="productTaxClass" type="select" selector="//*[@name='product[tax_class_id]']"/> - <element name="productTaxClassDisabled" type="select" selector="select[name='product[tax_class_id]'][disabled=true]"/> - <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> - <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']" timeout="30"/> - <element name="currentCategory" type="text" selector=".admin__action-multiselect-crumb > span"/> - <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']" timeout="30"/> - <element name="unselectCategories" type="button" selector="//span[@class='admin__action-multiselect-crumb']/span[contains(.,'{{category}}')]/../button[@data-action='remove-selected-item']" parameterized="true" timeout="30"/> - <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> - <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']" timeout="30"/> - <element name="productStockStatusDisabled" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]'][disabled=true]"/> - <element name="stockStatus" type="select" selector="[data-index='product-details'] select[name='product[quantity_and_stock_status][is_in_stock]']"/> - <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> - <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> - <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> - <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> - <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> - <element name="addAttributeBtn" type="button" selector="#addAttribute"/> - <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> - <element name="save" type="button" selector="#save-button" timeout="30"/> - <element name="saveNewAttribute" type="button" selector="//aside[contains(@class, 'create_new_attribute_modal')]//button[@id='save']"/> - <element name="successMessage" type="text" selector="#messages"/> - <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> - <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> - <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> - <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> - <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> - <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> - <element name="visibilityDisabled" type="select" selector="select[name='product[visibility]'][disabled=true]"/> - <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> - <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="setProductAsNewFrom" type="input" selector="input[name='product[news_from_date]']"/> - <element name="setProductAsNewTo" type="input" selector="input[name='product[news_to_date]']"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> - <element name="attributeRequiredInput" type="input" selector="//input[contains(@name, 'product[{{attributeCode}}]')]" parameterized="true"/> - <element name="attributeFieldError" type="text" selector="//*[@class='admin__field _required _error']/..//label[contains(.,'This is a required field.')]"/> - <element name="customSelectField" type="select" selector="//select[@name='product[{{var}}]']" parameterized="true"/> - <element name="searchCategory" type="input" selector="//*[@data-index='category_ids']//input[contains(@class, 'multiselect-search')]" timeout="30"/> - <element name="selectCategory" type="input" selector="//*[@data-index='category_ids']//label[contains(., '{{categoryName}}')]" parameterized="true" timeout="30"/> - <element name="done" type="button" selector="//*[@data-index='category_ids']//button[@data-action='close-advanced-select']" timeout="30"/> - <element name="selectMultipleCategories" type="input" selector="//*[@data-index='container_category_ids']//*[contains(@class, '_selected')]"/> - <element name="countryOfManufacture" type="select" selector="select[name='product[country_of_manufacture]']"/> - <element name="newAddedAttribute" type="text" selector="//fieldset[@class='admin__fieldset']//div[contains(@data-index,'{{attributeCode}}')]" parameterized="true"/> - <element name="newCategoryButton" type="button" selector="button[data-index='create_category_button']" timeout="30"/> - <element name="footerBlock" type="block" selector="//footer"/> - </section> - <section name="ProductInWebsitesSection"> - <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> - <element name="sectionHeaderOpened" type="button" selector="[data-index='websites']._show" timeout="30"/> - <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> - <element name="websiteChecked" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox'][@value='1']" parameterized="true"/> - </section> - <section name="ProductDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='product[page_layout]']"/> - <element name="productOptionsContainer" type="select" selector="select[name='product[options_container]']"/> - </section> - <section name="AdminProductFormRelatedUpSellCrossSellSection"> - <element name="relatedProductsHeader" type="button" selector=".admin__collapsible-block-wrapper[data-index='related']" timeout="30"/> - <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> - <element name="addUpSellProduct" type="button" selector="button[data-index='button_upsell']" timeout="30"/> - </section> - <section name="AdminAddRelatedProductsModalSection"> - <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'related_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> - <element name="AddUpSellProductsButton" type="button" selector="//aside[contains(@class, 'upsell_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> - </section> - <section name="ProductWYSIWYGSection"> - <element name="Switcher" type="button" selector="//select[@id='dropdown-switcher']"/> - <element name="v436" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']" /> - <element name="v3" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']" /> - <element name="TinymceDescription3" type ="button" selector="//span[text()='Description']" /> - <element name="SaveConfig" type ="button" selector="#save" /> - <element name="v4" type="button" selector="#category_form_description_v4"/> - <element name="WYSIWYGBtn" type="button" selector=".//button[@class='action-default scalable action-wysiwyg']"/> - </section> - <section name="ProductAttributeWYSIWYGSection"> - <element name="TextArea" type ="text" selector="//div[@data-index='{{var1}}']//textarea" parameterized="true"/> - <element name="showHideBtn" type="button" selector="//button[contains(@id,'{{var1}}')]" parameterized="true"/> - <element name="InsertImageBtn" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Insert Image...']" parameterized="true"/> - <element name="Style" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Paragraph']" parameterized="true"/> - <element name="Bold" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> - <element name="Italic" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> - <element name="Underline" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-underline']" parameterized="true"/> - <element name="AlignLeft" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignleft']" parameterized="true"/> - <element name="AlignCenter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-aligncenter']" parameterized="true"/> - <element name="AlignRight" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignright']" parameterized="true"/> - <element name="Numlist" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bullist']" parameterized="true"/> - <element name="Bullet" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-numlist']" parameterized="true"/> - <element name="InsertLink" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-link']" parameterized="true"/> - <element name="InsertImageIcon" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-image']" parameterized="true"/> - <element name="InsertTable" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-table']" parameterized="true"/> - <element name="SpecialCharacter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-charmap']" parameterized="true"/> - <element name="TinyMCE4" type="text" selector="//div[contains(@id, '{{var1}}')]//*[contains(@class,'mce-branding')]" parameterized="true"/> - </section> - <section name="ProductDescriptionWYSIWYGToolbarSection"> - <element name="TinyMCE4" type ="button" selector="//div[@id='editorproduct_form_description']//*[contains(@class,'mce-branding')]" /> - <element name="showHideBtn" type="button" selector="#toggleproduct_form_description"/> - <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_description > .scalable.action-add-image.plugin" /> - <element name="Style" type="button" selector="//div[@id='editorproduct_form_description']//span[text()='Paragraph']" /> - <element name="Bold" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bold']" /> - <element name="Italic" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-italic']" /> - <element name="Underline" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-underline']" /> - <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignleft']" /> - <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-aligncenter']" /> - <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignright']" /> - <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bullist']" /> - <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-numlist']" /> - <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-link']" /> - <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> - <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-table']" /> - <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-charmap']" /> - <element name="Browse" type="button" selector=".mce-i-browse" timeout="30"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//button//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files" timeout="30"/> - <element name="CreateFolder" type="button" selector="#new_folder" timeout="30"/> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> - <element name="CancelBtn" type="button" selector=".page-actions #cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" timeout="30"/> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="FolderContainer" type="button" selector="div[data-role='tree']" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon" /> - <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> - <section name="ProductShortDescriptionWYSIWYGToolbarSection"> - <element name="TinyMCE4" type ="button" selector="//div[@id='editorproduct_form_short_description']//*[contains(@class,'mce-branding')]" /> - <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_short_description > .scalable.action-add-image.plugin" /> - <element name="showHideBtn" type="button" selector="#toggleproduct_form_short_description"/> - <element name="Style" type="button" selector="//div[@id='editorproduct_form_short_description']//span[text()='Paragraph']" /> - <element name="Bold" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bold']" /> - <element name="Italic" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-italic']" /> - <element name="Underline" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-underline']" /> - <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignleft']" /> - <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-aligncenter']" /> - <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignright']" /> - <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bullist']" /> - <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-numlist']" /> - <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-link']" /> - <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> - <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-table']" /> - <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-charmap']"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']" timeout="30"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> - <section name="ProductDescriptionWysiwygSection"> - <element name="EditArea" type="text" selector="#editorproduct_form_description .mce-edit-area"/> - <element name="attributeEditArea" type="textarea" selector="#product_form_{{attributeCode}}" parameterized="true" timeout="30"/> - </section> - <section name="ProductShortDescriptionWysiwygSection"> - <element name="EditArea" type="text" selector="#editorproduct_form_short_description .mce-edit-area"/> - </section> - <section name="AdminProductFormAdvancedPricingSection"> - <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> - <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="30"/> - <element name="useDefaultPrice" type="checkbox" selector="//input[@name='product[special_price]']/parent::div/following-sibling::div/input[@name='use_default[special_price]']"/> - </section> - <section name="AdminProductAttributeSection"> - <element name="attributeSectionHeader" type="button" selector="//div[@data-index='attributes']" timeout="30"/> - <element name="textAttributeByCode" type="text" selector="//input[@name='product[{{arg}}]']" parameterized="true"/> - <element name="textAttributeByName" type="text" selector="//div[@data-index='attributes']//fieldset[contains(@class, 'admin__field') and .//*[contains(.,'{{var}}')]]//input" parameterized="true"/> - <element name="dropDownAttribute" type="select" selector="//select[@name='product[{{arg}}]']" parameterized="true" timeout="30"/> - <element name="attributeSection" type="block" selector="//div[@data-index='attributes']/div[contains(@class, 'admin__collapsible-content _show')]" timeout="30"/> - <element name="customAttribute" type="text" selector="product[{{attributecode}}]" timeout="30" parameterized="true"/> - <element name="attributeGroupByName" type="button" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']" parameterized="true"/> - <element name="attributeByGroupAndName" type="text" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']/../../following-sibling::div//span[contains(text(),'attribute')]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml new file mode 100644 index 0000000000000..90c60f4a8c2f7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminAddRelatedProductsModalSection"> + <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'related_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> + <element name="AddUpSellProductsButton" type="button" selector="//aside[contains(@class, 'upsell_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml new file mode 100644 index 0000000000000..5349e1bb043a2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml @@ -0,0 +1,20 @@ +<?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="AdminProductAttributeSection"> + <element name="attributeSectionHeader" type="button" selector="//div[@data-index='attributes']" timeout="30"/> + <element name="textAttributeByCode" type="text" selector="//input[@name='product[{{arg}}]']" parameterized="true"/> + <element name="textAttributeByName" type="text" selector="//div[@data-index='attributes']//fieldset[contains(@class, 'admin__field') and .//*[contains(.,'{{var}}')]]//input" parameterized="true"/> + <element name="dropDownAttribute" type="select" selector="//select[@name='product[{{arg}}]']" parameterized="true" timeout="30"/> + <element name="attributeSection" type="block" selector="//div[@data-index='attributes']/div[contains(@class, 'admin__collapsible-content _show')]" timeout="30"/> + <element name="customAttribute" type="text" selector="product[{{attributecode}}]" timeout="30" parameterized="true"/> + <element name="attributeGroupByName" type="button" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']" parameterized="true"/> + <element name="attributeByGroupAndName" type="text" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']/../../following-sibling::div//span[contains(text(),'attribute')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml new file mode 100644 index 0000000000000..4a9aa801a95c9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductFormAdvancedPricingSection"> + <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> + <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="30"/> + <element name="useDefaultPrice" type="checkbox" selector="//input[@name='product[special_price]']/parent::div/following-sibling::div/input[@name='use_default[special_price]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml new file mode 100644 index 0000000000000..dd2e2fbe34ba0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml @@ -0,0 +1,15 @@ +<?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="AdminProductFormRelatedUpSellCrossSellSection"> + <element name="relatedProductsHeader" type="button" selector=".admin__collapsible-block-wrapper[data-index='related']" timeout="30"/> + <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> + <element name="addUpSellProduct" type="button" selector="button[data-index='button_upsell']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml new file mode 100644 index 0000000000000..5bdd3bd5abcc6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml @@ -0,0 +1,80 @@ +<?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="AdminProductFormSection"> + <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> + <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> + <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> + <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> + <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> + <element name="attributeSetFilterResultByName" type="text" selector="//label/span[text() = '{{var}}']" timeout="30" parameterized="true"/> + <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> + <element name="productNameDisabled" type="input" selector=".admin__field[data-index=name] input[disabled=true]"/> + <element name="RequiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="productSkuDisabled" type="input" selector=".admin__field[data-index=sku] input[disabled=true]"/> + <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> + <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> + <element name="productStatusValue" type="checkbox" selector="input[name='product[status]'][value='{{value}}']" timeout="30" parameterized="true"/> + <element name="productStatusDisabled" type="checkbox" selector="input[name='product[status]'][disabled]"/> + <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> + <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> + <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> + <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> + <element name="productPriceDisabled" type="input" selector=".admin__field[data-index=price] input[disabled=true]"/> + <element name="productPriceUseDefault" type="checkbox" selector=".admin__field[data-index=price] [name='use_default[price]']"/> + <element name="productTaxClass" type="select" selector="//*[@name='product[tax_class_id]']"/> + <element name="productTaxClassDisabled" type="select" selector="select[name='product[tax_class_id]'][disabled=true]"/> + <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> + <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']" timeout="30"/> + <element name="currentCategory" type="text" selector=".admin__action-multiselect-crumb > span"/> + <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']" timeout="30"/> + <element name="unselectCategories" type="button" selector="//span[@class='admin__action-multiselect-crumb']/span[contains(.,'{{category}}')]/../button[@data-action='remove-selected-item']" parameterized="true" timeout="30"/> + <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> + <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> + <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']" timeout="30"/> + <element name="productStockStatusDisabled" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]'][disabled=true]"/> + <element name="stockStatus" type="select" selector="[data-index='product-details'] select[name='product[quantity_and_stock_status][is_in_stock]']"/> + <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> + <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> + <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> + <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> + <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> + <element name="addAttributeBtn" type="button" selector="#addAttribute"/> + <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> + <element name="save" type="button" selector="#save-button" timeout="30"/> + <element name="saveNewAttribute" type="button" selector="//aside[contains(@class, 'create_new_attribute_modal')]//button[@id='save']"/> + <element name="successMessage" type="text" selector="#messages"/> + <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> + <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> + <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> + <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> + <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> + <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> + <element name="visibilityDisabled" type="select" selector="select[name='product[visibility]'][disabled=true]"/> + <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> + <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> + <element name="setProductAsNewFrom" type="input" selector="input[name='product[news_from_date]']"/> + <element name="setProductAsNewTo" type="input" selector="input[name='product[news_to_date]']"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeRequiredInput" type="input" selector="//input[contains(@name, 'product[{{attributeCode}}]')]" parameterized="true"/> + <element name="attributeFieldError" type="text" selector="//*[@class='admin__field _required _error']/..//label[contains(.,'This is a required field.')]"/> + <element name="customSelectField" type="select" selector="//select[@name='product[{{var}}]']" parameterized="true"/> + <element name="searchCategory" type="input" selector="//*[@data-index='category_ids']//input[contains(@class, 'multiselect-search')]" timeout="30"/> + <element name="selectCategory" type="input" selector="//*[@data-index='category_ids']//label[contains(., '{{categoryName}}')]" parameterized="true" timeout="30"/> + <element name="done" type="button" selector="//*[@data-index='category_ids']//button[@data-action='close-advanced-select']" timeout="30"/> + <element name="selectMultipleCategories" type="input" selector="//*[@data-index='container_category_ids']//*[contains(@class, '_selected')]"/> + <element name="countryOfManufacture" type="select" selector="select[name='product[country_of_manufacture]']"/> + <element name="newAddedAttribute" type="text" selector="//fieldset[@class='admin__fieldset']//div[contains(@data-index,'{{attributeCode}}')]" parameterized="true"/> + <element name="newCategoryButton" type="button" selector="button[data-index='create_category_button']" timeout="30"/> + <element name="footerBlock" type="block" selector="//footer"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml new file mode 100644 index 0000000000000..1714d8fb97c96 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml @@ -0,0 +1,29 @@ +<?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="ProductAttributeWYSIWYGSection"> + <element name="TextArea" type="text" selector="//div[@data-index='{{var1}}']//textarea" parameterized="true"/> + <element name="showHideBtn" type="button" selector="//button[contains(@id,'{{var1}}')]" parameterized="true"/> + <element name="InsertImageBtn" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Insert Image...']" parameterized="true"/> + <element name="Style" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Paragraph']" parameterized="true"/> + <element name="Bold" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> + <element name="Italic" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> + <element name="Underline" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-underline']" parameterized="true"/> + <element name="AlignLeft" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignleft']" parameterized="true"/> + <element name="AlignCenter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-aligncenter']" parameterized="true"/> + <element name="AlignRight" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignright']" parameterized="true"/> + <element name="Numlist" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bullist']" parameterized="true"/> + <element name="Bullet" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-numlist']" parameterized="true"/> + <element name="InsertLink" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-link']" parameterized="true"/> + <element name="InsertImageIcon" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-image']" parameterized="true"/> + <element name="InsertTable" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-table']" parameterized="true"/> + <element name="SpecialCharacter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-charmap']" parameterized="true"/> + <element name="TinyMCE4" type="text" selector="//div[contains(@id, '{{var1}}')]//*[contains(@class,'mce-branding')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml new file mode 100644 index 0000000000000..26946692ce050 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml @@ -0,0 +1,49 @@ +<?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="ProductDescriptionWYSIWYGToolbarSection"> + <element name="TinyMCE4" type="button" selector="//div[@id='editorproduct_form_description']//*[contains(@class,'mce-branding')]"/> + <element name="showHideBtn" type="button" selector="#toggleproduct_form_description"/> + <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_description > .scalable.action-add-image.plugin"/> + <element name="Style" type="button" selector="//div[@id='editorproduct_form_description']//span[text()='Paragraph']"/> + <element name="Bold" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bold']"/> + <element name="Italic" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-italic']"/> + <element name="Underline" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-underline']"/> + <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignleft']"/> + <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-aligncenter']"/> + <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignright']"/> + <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bullist']"/> + <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-numlist']"/> + <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-link']"/> + <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> + <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-table']"/> + <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-charmap']"/> + <element name="Browse" type="button" selector=".mce-i-browse" timeout="30"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//button//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files" timeout="30"/> + <element name="CreateFolder" type="button" selector="#new_folder" timeout="30"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> + <element name="CancelBtn" type="button" selector=".page-actions #cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="FolderContainer" type="button" selector="div[data-role='tree']"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon"/> + <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml new file mode 100644 index 0000000000000..78fbbac45dcd9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductDescriptionWysiwygSection"> + <element name="EditArea" type="text" selector="#editorproduct_form_description .mce-edit-area"/> + <element name="attributeEditArea" type="textarea" selector="#product_form_{{attributeCode}}" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml new file mode 100644 index 0000000000000..519be90c93255 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml @@ -0,0 +1,15 @@ +<?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="ProductDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='product[page_layout]']"/> + <element name="productOptionsContainer" type="select" selector="select[name='product[options_container]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml new file mode 100644 index 0000000000000..5ee77e773f6fd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.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="ProductInWebsitesSection"> + <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> + <element name="sectionHeaderOpened" type="button" selector="[data-index='websites']._show" timeout="30"/> + <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> + <element name="websiteChecked" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox'][@value='1']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml new file mode 100644 index 0000000000000..90c2ef61e228e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml @@ -0,0 +1,46 @@ +<?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="ProductShortDescriptionWYSIWYGToolbarSection"> + <element name="TinyMCE4" type="button" selector="//div[@id='editorproduct_form_short_description']//*[contains(@class,'mce-branding')]"/> + <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_short_description > .scalable.action-add-image.plugin"/> + <element name="showHideBtn" type="button" selector="#toggleproduct_form_short_description"/> + <element name="Style" type="button" selector="//div[@id='editorproduct_form_short_description']//span[text()='Paragraph']"/> + <element name="Bold" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bold']"/> + <element name="Italic" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-italic']"/> + <element name="Underline" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-underline']"/> + <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignleft']"/> + <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-aligncenter']"/> + <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignright']"/> + <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bullist']"/> + <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-numlist']"/> + <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-link']"/> + <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> + <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-table']"/> + <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-charmap']"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']" timeout="30"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml new file mode 100644 index 0000000000000..66be79557b8e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml @@ -0,0 +1,13 @@ +<?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="ProductShortDescriptionWysiwygSection"> + <element name="EditArea" type="text" selector="#editorproduct_form_short_description .mce-edit-area"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml new file mode 100644 index 0000000000000..544bdf85681c9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml @@ -0,0 +1,19 @@ +<?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="ProductWYSIWYGSection"> + <element name="Switcher" type="button" selector="//select[@id='dropdown-switcher']"/> + <element name="v436" type="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']"/> + <element name="v3" type="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']"/> + <element name="TinymceDescription3" type="button" selector="//span[text()='Description']"/> + <element name="SaveConfig" type="button" selector="#save"/> + <element name="v4" type="button" selector="#category_form_description_v4"/> + <element name="WYSIWYGBtn" type="button" selector=".//button[@class='action-default scalable action-wysiwyg']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml new file mode 100644 index 0000000000000..9d80484f6a865 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminAddUpSellProductsModalSection"> + <element name="Modal" type="button" selector=".product_form_product_form_related_upsell_modal"/> + <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_upsell_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml similarity index 80% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml index f3b0d3a895cb1..68c1121817926 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormRelatedUpSellCrossSellSection"> <element name="sectionHeader" type="block" selector=".fieldset-wrapper.admin__collapsible-block-wrapper[data-index='related']"/> <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> @@ -22,8 +21,4 @@ <element name="removeRelatedProduct" type="button" selector="//span[text()='Related Products']//..//..//..//span[text()='{{productName}}']//..//..//..//..//..//button[@class='action-delete']" parameterized="true"/> <element name="selectedProductSku" type="text" selector="//div[@data-index='{{section}}']//span[@data-index='sku']" parameterized="true" timeout="30"/> </section> - <section name="AdminAddUpSellProductsModalSection"> - <element name="Modal" type="button" selector=".product_form_product_form_related_upsell_modal"/> - <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_upsell_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml similarity index 88% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml index 69d7297628d56..55d3b1bcc6cd5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUpdateAttributesSection"> <element name="saveButton" type="button" selector="button[title='Save']" timeout="30"/> @@ -39,8 +38,4 @@ <element name="name" type="input" selector="#name"/> <element name="description" type="input" selector="#description"/> </section> - <section name="AdminUpdateAttributesWebsiteSection"> - <element name="website" type="button" selector="#attributes_update_tabs_websites"/> - <element name="addProductToWebsite" type="checkbox" selector="#add-products-to-website-content .website-checkbox"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml new file mode 100644 index 0000000000000..343112bdb8dfd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUpdateAttributesWebsiteSection"> + <element name="website" type="button" selector="#attributes_update_tabs_websites"/> + <element name="addProductToWebsite" type="checkbox" selector="#add-products-to-website-content .website-checkbox"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml new file mode 100644 index 0000000000000..68a8f207a356f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml @@ -0,0 +1,13 @@ +<?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="StorefrontCategorySidebarMobileSection"> + <element name="shopByButton" type="button" selector="//div[contains(@class, 'filter-title')]/strong[contains(text(), 'Shop By')]"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml similarity index 72% rename from app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml index 786131c93e5a4..186d0cf313d96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ + /** + * 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"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> @@ -18,7 +18,4 @@ <element name="activeFilterOptions" type="text" selector=".filter-options-item.active .items"/> <element name="activeFilterOptionItemByPosition" type="text" selector=".filter-options-item.active .items li:nth-child({{itemPosition}}) a" parameterized="true"/> </section> - <section name="StorefrontCategorySidebarMobileSection"> - <element name="shopByButton" type="button" selector="//div[contains(@class, 'filter-title')]/strong[contains(text(), 'Shop By')]"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml similarity index 83% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml index bdb2293bed9bc..016d11f8e660a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml @@ -5,7 +5,8 @@ * 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"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminApplyTierPriceToProductTest"> <annotations> <features value="Catalog"/> @@ -53,7 +54,7 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin1"> - <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + <argument name="Customer" value="$$createSimpleUSCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> @@ -61,7 +62,7 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_1"/> <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_2"/> @@ -85,7 +86,7 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_1"/> <dontSeeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_3"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin2"> - <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + <argument name="Customer" value="$$createSimpleUSCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage4"/> <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> @@ -117,7 +118,7 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('As low as')}}" stepKey="assertAsLowAsPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLinkAfterLabel('As low as', '82')}}" stepKey="assertPriceAfterAsLowAsLabel_1"/> <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer2"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage6"/> <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_3"/> @@ -137,7 +138,7 @@ </actionGroup> <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToCheckoutFromMinicart"/> <seeInField userInput="10" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField10"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> <assertEquals message="Shopping cart should contain subtotal $1,000" stepKey="assertSubtotalField1"> <expectedResult type="string">$1,000.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField1</actualResult> @@ -145,7 +146,7 @@ <fillField userInput="15" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity2"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton1"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField2"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField2"/> <assertEquals message="Shopping cart should contain subtotal $1,350" stepKey="assertSubtotalField2"> <expectedResult type="string">$1,350.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField2</actualResult> @@ -153,7 +154,7 @@ <fillField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity3"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton2"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField3"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField3"/> <assertEquals message="Shopping cart should contain subtotal $1,640" stepKey="assertSubtotalField3"> <expectedResult type="string">$1,640.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField3</actualResult> @@ -173,7 +174,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage1"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad1"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField4"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField4"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField4"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField4</actualResult> @@ -200,7 +201,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage2"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad2"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_2"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField5"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField5"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField5"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField5</actualResult> @@ -214,7 +215,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage3"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad3"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_3"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField6"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField6"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField6"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField6</actualResult> @@ -235,7 +236,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage5"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton5"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="waitForcustomerGroupPriceDeleteButton"/> - <scrollTo selector="(//*[contains(@class, 'product_form_product_form_advanced_pricing_modal')]//tr//button[@data-action='remove_row'])[1]" x="30" y="0" stepKey="scrollToDeleteFirstRowOfCustomerGroupPrice" /> + <scrollTo selector="(//*[contains(@class, 'product_form_product_form_advanced_pricing_modal')]//tr//button[@data-action='remove_row'])[1]" x="30" y="0" stepKey="scrollToDeleteFirstRowOfCustomerGroupPrice"/> <click selector="(//tr//button[@data-action='remove_row'])[1]" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteFirstRowOfCustomerGroupPrice"/> <click selector="//tr//button[@data-action='remove_row']" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteSecondRowOfCustomerGroupPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="clickDoneButton5"/> @@ -251,7 +252,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage4"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad4"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField7"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField7"/> <assertEquals message="Shopping cart should contain subtotal $4,000" stepKey="assertSubtotalField7"> <expectedResult type="string">$4,000.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField7</actualResult> @@ -269,56 +270,4 @@ <actualResult type="variable">grabTextFromMiniCartSubtotalField2</actualResult> </assertEquals> </test> - <test name="AdminApplyTierPriceToProductWithPercentageDiscountTest"> - <annotations> - <features value="Catalog"/> - <stories value="MC-5517 - System tries to save 0 in Advanced Pricing which is invalid for Discount field"/> - <title value="You should be able to apply tier price to a product with float percent discount."/> - <description value="You should be able to apply tier price to a product with float percent discount."/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-96881"/> - <group value="product"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">100</field> - </createData> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAndpercent"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="1" stepKey="fillProductTierPriceQtyInput"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="0.1" stepKey="selectProductTierPricePriceInput"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="goProductPageOnStorefront"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceProductPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceProductPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountProductPage"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceCategoryPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabelCategoryPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountCategoryPage"/> - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml new file mode 100644 index 0000000000000..848fb7d1155f3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml @@ -0,0 +1,62 @@ +<?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="AdminApplyTierPriceToProductWithPercentageDiscountTest"> + <annotations> + <features value="Catalog"/> + <stories value="MC-5517 - System tries to save 0 in Advanced Pricing which is invalid for Discount field"/> + <title value="You should be able to apply tier price to a product with float percent discount."/> + <description value="You should be able to apply tier price to a product with float percent discount."/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-96881"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100</field> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAndpercent"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="1" stepKey="fillProductTierPriceQtyInput"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="0.1" stepKey="selectProductTierPricePriceInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="goProductPageOnStorefront"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceProductPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceProductPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountProductPage"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceCategoryPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabelCategoryPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountCategoryPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml similarity index 71% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml index 12082e1daa6c3..fde018ddc181a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateSimpleProductSwitchToVirtualTest"> <annotations> <features value="Catalog"/> @@ -55,29 +54,9 @@ <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> </test> - <test name="AdminCreateVirtualProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from virtual to simple"/> - <description value="After selecting a virtual product when adding Admin should be switch to simple implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10928"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="virtual"/> - </actionGroup> - <!-- Fill form for Virtual Product Type --> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml new file mode 100644 index 0000000000000..f79072582035b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml @@ -0,0 +1,30 @@ +<?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="AdminCreateVirtualProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from virtual to simple"/> + <description value="After selecting a virtual product when adding Admin should be switch to simple implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10928"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="virtual"/> + </actionGroup> + <!-- Fill form for Virtual Product Type --> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml deleted file mode 100644 index 47069004e5009..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ /dev/null @@ -1,103 +0,0 @@ -<?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="AdminCreateCategoryTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Category via the Admin"/> - <title value="Admin should be able to create a Category"/> - <description value="Admin should be able to create a Category"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-72102"/> - <group value="category"/> - </annotations> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="enterURLKey"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> - - <!-- Literal URL below, need to refactor line + StorefrontCategoryPage when support for variable URL is implemented--> - <amOnPage url="/{{SimpleSubCategory.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> - <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertTitle"/> - <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="assertInfo1"/> - </test> - <test name="AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure the default layout for Category Page from System Configuration"/> - <description value="Admin should be able to configure the default layout for Category Page from System Configuration"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-89024"/> - <group value="category"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waitForDefaultCategoryLayout" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected" /> - <selectOption selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="2 columns with right bar" stepKey="select2ColumnsLayout"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToNewCatalog"/> - <waitForPageLoad stepKey="wait1"/> - <waitForLoadingMaskToDisappear stepKey="wait2" /> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <click selector="{{CategoryDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{CategoryDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{CategoryDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="see2ColumnsSelected" /> - </test> - <test name="AdminCategoryFormDisplaySettingsUIValidationTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Category should not be saved once layered navigation price step field is left empty"/> - <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-95797"/> - <group value="category"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> - <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> - <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> - <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> - <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> - <!-- Verify that the Layered navigation price step field has the required indicator --> - <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> - <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml new file mode 100644 index 0000000000000..a4fe694ea65a7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml @@ -0,0 +1,44 @@ +<?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="AdminCategoryFormDisplaySettingsUIValidationTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Category should not be saved once layered navigation price step field is left empty"/> + <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-95797"/> + <group value="category"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> + <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> + <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> + <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> + <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> + <!-- Verify that the Layered navigation price step field has the required indicator --> + <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment"/> + <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <actualResult type="variable">getRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..e6a2030e37d86 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,42 @@ +<?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="AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure the default layout for Category Page from System Configuration"/> + <description value="Admin should be able to configure the default layout for Category Page from System Configuration"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89024"/> + <group value="category"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waitForDefaultCategoryLayout"/> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected"/> + <selectOption selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="2 columns with right bar" stepKey="select2ColumnsLayout"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToNewCatalog"/> + <waitForPageLoad stepKey="wait1"/> + <waitForLoadingMaskToDisappear stepKey="wait2"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <click selector="{{CategoryDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{CategoryDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> + <seeOptionIsSelected selector="{{CategoryDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="see2ColumnsSelected"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml new file mode 100644 index 0000000000000..b6d1d4b81fffb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml @@ -0,0 +1,39 @@ +<?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="AdminCreateCategoryTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Category via the Admin"/> + <title value="Admin should be able to create a Category"/> + <description value="Admin should be able to create a Category"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-72102"/> + <group value="category"/> + </annotations> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + + <!-- Literal URL below, need to refactor line + StorefrontCategoryPage when support for variable URL is implemented--> + <amOnPage url="/{{SimpleSubCategory.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> + <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertTitle"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="assertInfo1"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml similarity index 52% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml index ecce1bb1517e1..3fb3db231c29c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml @@ -5,41 +5,8 @@ * 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="AdminCreateProductDuplicateUrlkeyTest"> - <annotations> - <features value="Catalog"/> - <stories value="Errors"/> - <title value="Admin should see an error when trying to save a product with a duplicate URL key"/> - <description value="Admin should see an error when trying to save a product with a duplicate URL key"/> - <severity value="MAJOR"/> - <testCaseId value="MC-112"/> - <group value="product"/> - </annotations> - <before> - <createData entity="SimpleTwo" stepKey="simpleProduct"> - </createData> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <fillField userInput="$$simpleProduct.name$$new" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="$$simpleProduct.sku$$new" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="$$simpleProduct.price$$" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="$$simpleProduct.quantity$$" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="$$simpleProduct.custom_attributes[url_key]$$" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <see userInput="The value specified in the URL Key field would generate a URL that already exists" selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="assertErrorMessage"/> - </test> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateProductDuplicateProductTest"> <annotations> <features value="Catalog"/> @@ -61,7 +28,7 @@ <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog" /> + <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml new file mode 100644 index 0000000000000..2ccec016b8e15 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml @@ -0,0 +1,42 @@ +<?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="AdminCreateProductDuplicateUrlkeyTest"> + <annotations> + <features value="Catalog"/> + <stories value="Errors"/> + <title value="Admin should see an error when trying to save a product with a duplicate URL key"/> + <description value="Admin should see an error when trying to save a product with a duplicate URL key"/> + <severity value="MAJOR"/> + <testCaseId value="MC-112"/> + <group value="product"/> + </annotations> + <before> + <createData entity="SimpleTwo" stepKey="simpleProduct"> + </createData> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="$$simpleProduct.name$$new" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="$$simpleProduct.sku$$new" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="$$simpleProduct.price$$" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="$$simpleProduct.quantity$$" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="$$simpleProduct.custom_attributes[url_key]$$" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <see userInput="The value specified in the URL Key field would generate a URL that already exists" selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="assertErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml deleted file mode 100644 index 9f479edb91ce6..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml +++ /dev/null @@ -1,112 +0,0 @@ -<?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="AdminCreateSimpleProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should be able to create a Simple Product"/> - <description value="Admin should be able to create a Simple Product"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-23414"/> - <group value="product"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - </before> - <after> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> - <argument name="category" value="$$createPreReqCategory$$"/> - <argument name="simpleProduct" value="_defaultProduct"/> - </actionGroup> - <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> - <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> - <argument name="category" value="$$createPreReqCategory$$"/> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - </test> - - <test name="AdminConfigDefaultProductLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure a default layout for Product Page from System Configuration"/> - <description value="Admin should be able to configure a default layout for Product Page from System Configuration"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-89023"/> - <group value="product"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.productLayout}}" stepKey="DefaultProductLayout" /> - <selectOption selector="{{DefaultLayoutsSection.productLayout}}" userInput="3 columns" stepKey="select3ColumnsLayout"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{ProductDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{ProductDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{ProductDesignSection.LayoutDropdown}}" userInput="3 columns" stepKey="see3ColumnsSelected" /> - </test> - - <test name="AdminCreateSimpleProductZeroPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should be able to create a product with zero price"/> - <description value="Admin should be able to create a product with zero price"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-89910"/> - <group value="product"/> - </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="fillPrice"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.name)}}" stepKey="viewProduct"/> - <waitForPageLoad stepKey="wait2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$0.00" stepKey="seeZeroPrice"/> - </test> - - <test name="AdminCreateSimpleProductNegativePriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should not be able to create a product with a negative price"/> - <description value="Admin should not be able to create a product with a negative price"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-89912"/> - <group value="product"/> - </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="-42" stepKey="fillPrice"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="Please enter a number 0 or greater in this field." stepKey="seePriceValidationError"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..f1bb67bf66784 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,39 @@ +<?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="AdminConfigDefaultProductLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure a default layout for Product Page from System Configuration"/> + <description value="Admin should be able to configure a default layout for Product Page from System Configuration"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89023"/> + <group value="product"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{DefaultLayoutsSection.productLayout}}" stepKey="DefaultProductLayout"/> + <selectOption selector="{{DefaultLayoutsSection.productLayout}}" userInput="3 columns" stepKey="select3ColumnsLayout"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{ProductDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{ProductDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> + <seeOptionIsSelected selector="{{ProductDesignSection.LayoutDropdown}}" userInput="3 columns" stepKey="see3ColumnsSelected"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml new file mode 100644 index 0000000000000..c3b74eb128ef6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml @@ -0,0 +1,28 @@ +<?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="AdminCreateSimpleProductNegativePriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should not be able to create a product with a negative price"/> + <description value="Admin should not be able to create a product with a negative price"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89912"/> + <group value="product"/> + </annotations> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="-42" stepKey="fillPrice"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="Please enter a number 0 or greater in this field." stepKey="seePriceValidationError"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml new file mode 100644 index 0000000000000..1a95824014deb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml @@ -0,0 +1,41 @@ +<?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="AdminCreateSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should be able to create a Simple Product"/> + <description value="Admin should be able to create a Simple Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml new file mode 100644 index 0000000000000..a0e51faf0897f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml @@ -0,0 +1,30 @@ +<?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="AdminCreateSimpleProductZeroPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should be able to create a product with zero price"/> + <description value="Admin should be able to create a product with zero price"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89910"/> + <group value="product"/> + </annotations> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="fillPrice"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.name)}}" stepKey="viewProduct"/> + <waitForPageLoad stepKey="wait2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$0.00" stepKey="seeZeroPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml new file mode 100644 index 0000000000000..ffaf8501ea71f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml @@ -0,0 +1,82 @@ +<?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="AdminMassUpdateProductAttributesStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product attributes"/> + <title value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20467"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="api-simple-product"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> + <!-- Switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> + <!-- Update attribute --> + <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> + <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront custom view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml similarity index 53% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index c9840fe455f84..308a240eda184 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassUpdateProductAttributesStoreViewScopeTest"> <annotations> <features value="Catalog"/> @@ -22,7 +21,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> <createData entity="ApiProductNameWithNoSpaces" stepKey="createProductThree"/> @@ -86,76 +85,4 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault1"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault1"/> </test> - <test name="AdminMassUpdateProductAttributesStoreViewScopeMysqlTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product attributes"/> - <title value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> - <description value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-20467"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> - <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> - <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> - </before> - <after> - <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> - <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="api-simple-product"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> - <!-- Switch store view --> - <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> - <!-- Update attribute --> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> - <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> - <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> - - <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Assert on storefront custom view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml new file mode 100644 index 0000000000000..63e22fc5a12d9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml @@ -0,0 +1,151 @@ +<?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="AdminMassUpdateProductStatusStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product status"/> + <title value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20471"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal"/> + <waitForPageLoad stepKey="waitForPageLoad2" time="180"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> + + <!--Create a Simple Product 1 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + + <!-- Filter to Second Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView"> + <argument name="customStore" value="'Second Store View'"/> + </actionGroup> + + <!-- Select Product 2 --> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> + + <!-- Filter to Default Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'"/> + </actionGroup> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Enable the product in Default store view --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> + <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> + <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml similarity index 50% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml index 6030e76dca721..54921d3fc2dda 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassUpdateProductStatusStoreViewScopeTest"> <annotations> <features value="Catalog"/> @@ -46,13 +45,13 @@ <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal"/> + <waitForPageLoad stepKey="waitForPageLoad2" time="180"/> <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> <!--Create a Simple Product 1 --> <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> @@ -92,8 +91,8 @@ <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <!-- Filter to Second Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > - <argument name="customStore" value="'Second Store View'" /> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView"> + <argument name="customStore" value="'Second Store View'"/> </actionGroup> <!-- Select Product 2 --> @@ -111,7 +110,7 @@ <!-- Filter to Default Store View --> <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> - <argument name="customStore" value="'Default'" /> + <argument name="customStore" value="'Default'"/> </actionGroup> <!-- Verify Product Statuses --> @@ -158,145 +157,4 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> </test> - <test name="AdminMassUpdateProductStatusStoreViewScopeMysqlTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product status"/> - <title value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> - <description value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-20471"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - - <!--Create Website --> - <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> - <argument name="newWebsiteName" value="Second Website"/> - <argument name="websiteCode" value="second_website"/> - </actionGroup> - - <!--Create Store --> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> - <argument name="website" value="Second Website"/> - <argument name="storeGroupName" value="Second Store"/> - <argument name="storeGroupCode" value="second_store"/> - </actionGroup> - - <!--Create Store view --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> - <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> - <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> - <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> - <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> - - <!--Create a Simple Product 1 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> - <argument name="product" value="simpleProductForMassUpdate"/> - <argument name="website" value="Second Website"/> - </actionGroup> - - <!--Create a Simple Product 2 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> - <argument name="product" value="simpleProductForMassUpdate2"/> - <argument name="website" value="Second Website"/> - </actionGroup> - </before> - <after> - <!--Delete website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> - <argument name="websiteName" value="Second Website"/> - </actionGroup> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - - <!--Delete Products --> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> - <argument name="productName" value="simpleProductForMassUpdate.name"/> - </actionGroup> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> - <argument name="productName" value="simpleProductForMassUpdate2.name"/> - </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - - <!-- Filter to Second Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > - <argument name="customStore" value="'Second Store View'" /> - </actionGroup> - - <!-- Select Product 2 --> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> - - <!-- Filter to Default Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> - <argument name="customStore" value="'Default'" /> - </actionGroup> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Enable the product in Default store view --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> - <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> - <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> - <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml new file mode 100644 index 0000000000000..9d82edd0fb50c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml @@ -0,0 +1,46 @@ +<?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="AdminDownloadableProductTypeSwitchingToSimpleProductTest" extends="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product type switching"/> + <title value="Downloadable product type switching on editing to Simple product"/> + <description value="Downloadable product type switching on editing to Simple product"/> + <testCaseId value="MC-17955"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!--Change product type to Simple--> + <comment userInput="Change product type to Simple Product" stepKey="commentCreateSimple"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + <!--Assert simple product on Admin product page grid--> + <comment userInput="Assert simple product in Admin product page grid" stepKey="commentAssertProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogSimpleProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeSimpleProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeSimpleProductTypeInGrid"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearSimpleProductFilters"/> + <!--Assert simple product on storefront--> + <comment userInput="Assert simple product on storefront" stepKey="commentAssertSimpleProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openSimpleProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontSimpleProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertSimpleProductInStock"/> + <dontSeeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="dontSeeDownloadableLink"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml similarity index 59% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml index bf5b3db1f1f33..4966e94463e98 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> <annotations> <features value="Catalog"/> @@ -63,42 +62,6 @@ <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> - <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink" /> - </test> - <test name="AdminDownloadableProductTypeSwitchingToSimpleProductTest" extends="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product type switching"/> - <title value="Downloadable product type switching on editing to Simple product"/> - <description value="Downloadable product type switching on editing to Simple product"/> - <testCaseId value="MC-17955"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!--Change product type to Simple--> - <comment userInput="Change product type to Simple Product" stepKey="commentCreateSimple"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToProductPage"> - <argument name="productId" value="$$createProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> - <!--Assert simple product on Admin product page grid--> - <comment userInput="Assert simple product in Admin product page grid" stepKey="commentAssertProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogSimpleProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeSimpleProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeSimpleProductTypeInGrid"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearSimpleProductFilters"/> - <!--Assert simple product on storefront--> - <comment userInput="Assert simple product on storefront" stepKey="commentAssertSimpleProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openSimpleProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontSimpleProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertSimpleProductInStock"/> - <dontSeeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="dontSeeDownloadableLink" /> + <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml new file mode 100644 index 0000000000000..8bb65045fdde9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml @@ -0,0 +1,167 @@ +<?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="AdminSimpleProductImagesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add images of different types and sizes to Simple Product"/> + <description value="Admin should be able to add images of different types and sizes to Simple Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-189"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="_defaultProduct" stepKey="firstProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="_defaultProduct" stepKey="secondProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Go to the first product edit page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="wait1"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> + <argument name="product" value="$$firstProduct$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> + + <!-- Set url key --> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$firstProduct.name$$" stepKey="fillUrlKey"/> + + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> + + <!-- *.bmp is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="bmp.bmp" stepKey="attachBmp"/> + <waitForPageLoad stepKey="waitForUploadBmp"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="bmp.bmp was not uploaded. Disallowed file type." stepKey="seeErrorBmp"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalBmp"/> + + <!-- *.ico is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="ico.ico" stepKey="attachIco"/> + <waitForPageLoad stepKey="waitForUploadIco"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="ico.ico was not uploaded. Disallowed file type." stepKey="seeErrorIco"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalIco"/> + + <!-- *.svg is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="svg.svg" stepKey="attachSvg"/> + <waitForPageLoad stepKey="waitForUploadSvg"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="svg.svg was not uploaded. Disallowed file type." stepKey="seeErrorSvg"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalSvg"/> + + <!-- 0kb size is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="empty.jpg" stepKey="attachEmpty"/> + <waitForPageLoad stepKey="waitForUploadEmpty"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="empty.jpg was not uploaded." stepKey="seeErrorEmpty"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalEmpty"/> + + <!-- 1~ kb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="small.jpg" stepKey="attachSmall"/> + <waitForPageLoad stepKey="waitForUploadSmall"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorSmall"/> + + <!-- 1~ mb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="medium.jpg" stepKey="attachMedium"/> + <waitForPageLoad stepKey="waitForUploadMedium"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium"/> + + <!-- 10~ mb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> + <waitForPageLoad stepKey="waitForUploadLarge"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/> + + <!-- *.gif is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="gif.gif" stepKey="attachGif"/> + <waitForPageLoad stepKey="waitForUploadGif"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorGif"/> + + <!-- *.jpg is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="jpg.jpg" stepKey="attachJpg"/> + <waitForPageLoad stepKey="waitForUploadJpg"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorJpg"/> + + <!-- *.png is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="png.png" stepKey="attachPng"/> + <waitForPageLoad stepKey="waitForUploadPng"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorPng"/> + + <!-- Save the first product and go to the storefront --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <amOnPage url="$$firstProduct.name$$.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + + <!-- See all of the images that we uploaded --> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('small')}}" stepKey="seeSmall"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('gif')}}" stepKey="seeGif"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('jpg')}}" stepKey="seeJpg"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('png')}}" stepKey="seePng"/> + + <!-- Go to the category page and see a placeholder image for the second product --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> + <seeElement selector=".products-grid img[src*='placeholder/small_image.jpg']" stepKey="seePlaceholder"/> + + <!-- Go to the second product edit page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> + <waitForPageLoad stepKey="wait2"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> + <argument name="product" value="$$secondProduct$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> + + <!-- Upload an image --> + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge2"/> + <waitForPageLoad stepKey="waitForUploadLarge2"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge2"/> + + <!-- Set url key --> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection2"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$secondProduct.name$$" stepKey="fillUrlKey2"/> + + <!-- Save the second product --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- Go to the admin grid and see the uploaded image --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> + <waitForPageLoad stepKey="wait3"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> + <argument name="product" value="$$secondProduct$$"/> + </actionGroup> + <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> + + <!-- Go to the category page and see the uploaded image --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> + <seeElement selector=".products-grid img[src*='/large']" stepKey="seeUploadedImg"/> + + <!-- Go to the product page and see the uploaded image --> + <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> + <waitForPageLoad stepKey="waitForStorefront2"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml similarity index 50% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml index cd80467c66d06..53ccb0257aa19 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml @@ -5,167 +5,8 @@ * 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="AdminSimpleProductImagesTest"> - <annotations> - <features value="Catalog"/> - <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add images of different types and sizes to Simple Product"/> - <description value="Admin should be able to add images of different types and sizes to Simple Product"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-189"/> - <group value="Catalog"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="category"/> - <createData entity="_defaultProduct" stepKey="firstProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="_defaultProduct" stepKey="secondProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Go to the first product edit page --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> - - <!-- Set url key --> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$firstProduct.name$$" stepKey="fillUrlKey"/> - - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> - - <!-- *.bmp is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="bmp.bmp" stepKey="attachBmp"/> - <waitForPageLoad stepKey="waitForUploadBmp"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="bmp.bmp was not uploaded. Disallowed file type." stepKey="seeErrorBmp"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalBmp"/> - - <!-- *.ico is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="ico.ico" stepKey="attachIco"/> - <waitForPageLoad stepKey="waitForUploadIco"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="ico.ico was not uploaded. Disallowed file type." stepKey="seeErrorIco"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalIco"/> - - <!-- *.svg is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="svg.svg" stepKey="attachSvg"/> - <waitForPageLoad stepKey="waitForUploadSvg"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="svg.svg was not uploaded. Disallowed file type." stepKey="seeErrorSvg"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalSvg"/> - - <!-- 0kb size is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="empty.jpg" stepKey="attachEmpty"/> - <waitForPageLoad stepKey="waitForUploadEmpty"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="empty.jpg was not uploaded." stepKey="seeErrorEmpty"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalEmpty"/> - - <!-- 1~ kb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="small.jpg" stepKey="attachSmall"/> - <waitForPageLoad stepKey="waitForUploadSmall"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorSmall"/> - - <!-- 1~ mb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="medium.jpg" stepKey="attachMedium"/> - <waitForPageLoad stepKey="waitForUploadMedium"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium"/> - - <!-- 10~ mb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> - <waitForPageLoad stepKey="waitForUploadLarge"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/> - - <!-- *.gif is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="gif.gif" stepKey="attachGif"/> - <waitForPageLoad stepKey="waitForUploadGif"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorGif"/> - - <!-- *.jpg is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="jpg.jpg" stepKey="attachJpg"/> - <waitForPageLoad stepKey="waitForUploadJpg"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorJpg"/> - - <!-- *.png is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="png.png" stepKey="attachPng"/> - <waitForPageLoad stepKey="waitForUploadPng"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorPng"/> - - <!-- Save the first product and go to the storefront --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <amOnPage url="$$firstProduct.name$$.html" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStorefront"/> - - <!-- See all of the images that we uploaded --> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('small')}}" stepKey="seeSmall"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('gif')}}" stepKey="seeGif"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('jpg')}}" stepKey="seeJpg"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('png')}}" stepKey="seePng"/> - - <!-- Go to the category page and see a placeholder image for the second product --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> - <seeElement selector=".products-grid img[src*='placeholder/small_image.jpg']" stepKey="seePlaceholder"/> - - <!-- Go to the second product edit page --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> - <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> - - <!-- Upload an image --> - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge2"/> - <waitForPageLoad stepKey="waitForUploadLarge2"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge2"/> - - <!-- Set url key --> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection2"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$secondProduct.name$$" stepKey="fillUrlKey2"/> - - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Go to the admin grid and see the uploaded image --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> - <waitForPageLoad stepKey="wait3"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> - - <!-- Go to the category page and see the uploaded image --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> - <seeElement selector=".products-grid img[src*='/large']" stepKey="seeUploadedImg"/> - - <!-- Go to the product page and see the uploaded image --> - <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> - <waitForPageLoad stepKey="waitForStorefront2"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge2"/> - </test> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductRemoveImagesTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml deleted file mode 100644 index 14f4a6f6d1cfb..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ /dev/null @@ -1,112 +0,0 @@ -<?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="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product name"/> - <description value="Guest customer should be able to advance search simple product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-132"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product sku"/> - <description value="Guest customer should be able to advance search simple product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-133"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescriptionAndUnderscoredSku" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product description"/> - <description value="Guest customer should be able to advance search simple product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-134"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product short description"/> - <description value="Guest customer should be able to advance search simple product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-135"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product price"/> - <description value="Guest customer should be able to advance search simple product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-136"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - - <createData entity="ApiProductWithDescription" stepKey="product"/> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="product"/> - </getData> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml new file mode 100644 index 0000000000000..30771fcfd947b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml @@ -0,0 +1,27 @@ +<?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="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product description"/> + <description value="Guest customer should be able to advance search simple product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-134"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml new file mode 100644 index 0000000000000..a667d08b8e25f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml @@ -0,0 +1,30 @@ +<?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="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product name"/> + <description value="Guest customer should be able to advance search simple product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-132"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml new file mode 100644 index 0000000000000..50ca013050ea7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml @@ -0,0 +1,40 @@ +<?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="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product price"/> + <description value="Guest customer should be able to advance search simple product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-136"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + + <createData entity="ApiProductWithDescription" stepKey="product"/> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="product"/> + </getData> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..5431a19461f76 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml @@ -0,0 +1,27 @@ +<?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="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product short description"/> + <description value="Guest customer should be able to advance search simple product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-135"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml new file mode 100644 index 0000000000000..dc6409043f67f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml @@ -0,0 +1,27 @@ +<?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="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product sku"/> + <description value="Guest customer should be able to advance search simple product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-133"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescriptionAndUnderscoredSku" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml deleted file mode 100644 index 07b802637b2e7..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?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="AdvanceCatalogSearchVirtualProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product name"/> - <description value="Guest customer should be able to advance search virtual product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-137"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product sku"/> - <description value="Guest customer should be able to advance search virtual product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-162"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescriptionAndUnderscoredSku" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product description"/> - <description value="Guest customer should be able to advance search virtual product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-163"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product short description"/> - <description value="Guest customer should be able to advance search virtual product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-164"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product price"/> - <description value="Guest customer should be able to advance search virtual product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-165"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml new file mode 100644 index 0000000000000..64da7e8599d07 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml @@ -0,0 +1,24 @@ +<?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="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product description"/> + <description value="Guest customer should be able to advance search virtual product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-163"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml new file mode 100644 index 0000000000000..12056962bac23 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml @@ -0,0 +1,24 @@ +<?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="AdvanceCatalogSearchVirtualProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product name"/> + <description value="Guest customer should be able to advance search virtual product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-137"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml new file mode 100644 index 0000000000000..68a69644d3d7b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml @@ -0,0 +1,24 @@ +<?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="AdvanceCatalogSearchVirtualProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product price"/> + <description value="Guest customer should be able to advance search virtual product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-165"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..f6cfb58bf71df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml @@ -0,0 +1,24 @@ +<?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="AdvanceCatalogSearchVirtualProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product short description"/> + <description value="Guest customer should be able to advance search virtual product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-164"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml new file mode 100644 index 0000000000000..132e82d49085e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml @@ -0,0 +1,24 @@ +<?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="AdvanceCatalogSearchVirtualProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product sku"/> + <description value="Guest customer should be able to advance search virtual product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-162"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescriptionAndUnderscoredSku" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml deleted file mode 100644 index ac6c34a8cc1f1..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ /dev/null @@ -1,425 +0,0 @@ -<?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="CreateProductAttributeEntityTextFieldTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a TextField product attribute"/> - <description value="Admin should be able to create a TextField product attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10894"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeWithTextFieldActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="textProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against textProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{textProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{textProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{textProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{textProductAttribute.attribute_code}}"/> - <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{textProductAttribute.default_value}}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.default_value}}"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityDateTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Date product attribute"/> - <description value="Admin should be able to create a Date product attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10895"/> - <group value="Catalog"/> - <skip> - <issueId value="MC-13817"/> - </skip> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Generate date for use as default value, needs to be MM/d/YYYY --> - <generateDate date="now" format="m/j/Y" stepKey="generateDefaultDate"/> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeWithDateFieldActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dateProductAttribute"/> - <argument name="date" value="{$generateDefaultDate}"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against textProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dateProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dateProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dateProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dateProductAttribute.attribute_code}}"/> - <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{$generateDefaultDate}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" userInput="{$generateDefaultDate}"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(dateProductAttribute.attribute_code)}}" userInput="{{dateProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Price product attribute"/> - <description value="Admin should be able to create a Price product attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10897"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute with Price--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="priceProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against priceProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{priceProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{priceProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{priceProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{priceProductAttribute.attribute_code}}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(priceProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(priceProductAttribute.attribute_code)}}" userInput="{{priceProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityDropdownTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Dropdown product attribute"/> - <description value="Admin should be able to create a Dropdown product attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10896"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dropdownProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> - <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> - <argument name="row" value="2"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> - <argument name="adminName" value="{{dropdownProductAttribute.option3_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option3_frontend}}"/> - <argument name="row" value="3"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttribute.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttribute.option1_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> - <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> - <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> - <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> - - <!--Go to New Product page, add Attribute and check dropdown values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> - <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> - <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> - </test> - - <test name="CreateProductAttributeEntityDropdownWithSingleQuoteTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Dropdown product attribute containing a single quote"/> - <description value="Admin should be able to create a Dropdown product attribute containing a single quote"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10898"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dropdownProductAttributeWithQuote"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Option and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttributeWithQuote.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttributeWithQuote.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - - <!--Go to New Product page, add Attribute and check dropdown values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - </test> - - <test name="CreateProductAttributeEntityMultiSelectTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a MultiSelect product attribute"/> - <description value="Admin should be able to create a MultiSelect product attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10888"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="multiselectProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> - <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> - <argument name="row" value="2"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> - <argument name="adminName" value="{{multiselectProductAttribute.option3_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option3_frontend}}"/> - <argument name="row" value="3"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against multiselectProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{multiselectProductAttribute.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{multiselectProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{multiselectProductAttribute.option1_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> - <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{multiselectProductAttribute.option2_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> - <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> - <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{multiselectProductAttribute.option3_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> - - <!--Go to New Product page, add Attribute and check multiselect values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> - <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> - <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml new file mode 100644 index 0000000000000..6ee37221acc93 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml @@ -0,0 +1,72 @@ +<?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="CreateProductAttributeEntityDateTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Date product attribute"/> + <description value="Admin should be able to create a Date product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10895"/> + <group value="Catalog"/> + <skip> + <issueId value="MC-13817"/> + </skip> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Generate date for use as default value, needs to be MM/d/YYYY --> + <generateDate date="now" format="m/j/Y" stepKey="generateDefaultDate"/> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeWithDateFieldActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dateProductAttribute"/> + <argument name="date" value="{$generateDefaultDate}"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against textProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dateProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dateProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dateProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dateProductAttribute.attribute_code}}"/> + <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{$generateDefaultDate}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" userInput="{$generateDefaultDate}"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(dateProductAttribute.attribute_code)}}" userInput="{{dateProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml new file mode 100644 index 0000000000000..dc38062a99c53 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml @@ -0,0 +1,96 @@ +<?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="CreateProductAttributeEntityDropdownTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Dropdown product attribute"/> + <description value="Admin should be able to create a Dropdown product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10896"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> + <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> + <argument name="adminName" value="{{dropdownProductAttribute.option3_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option3_frontend}}"/> + <argument name="row" value="3"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against dropdownProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttribute.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttribute.option1_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> + <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> + <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> + <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> + + <!--Go to New Product page, add Attribute and check dropdown values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml new file mode 100644 index 0000000000000..b8b2224abf5a1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml @@ -0,0 +1,78 @@ +<?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="CreateProductAttributeEntityDropdownWithSingleQuoteTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Dropdown product attribute containing a single quote"/> + <description value="Admin should be able to create a Dropdown product attribute containing a single quote"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10898"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttributeWithQuote"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Option and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against dropdownProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttributeWithQuote.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttributeWithQuote.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + + <!--Go to New Product page, add Attribute and check dropdown values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml new file mode 100644 index 0000000000000..50606994cace8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml @@ -0,0 +1,95 @@ +<?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="CreateProductAttributeEntityMultiSelectTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a MultiSelect product attribute"/> + <description value="Admin should be able to create a MultiSelect product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10888"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="multiselectProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> + <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> + <argument name="adminName" value="{{multiselectProductAttribute.option3_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option3_frontend}}"/> + <argument name="row" value="3"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against multiselectProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{multiselectProductAttribute.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{multiselectProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{multiselectProductAttribute.option1_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> + <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{multiselectProductAttribute.option2_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> + <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> + <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{multiselectProductAttribute.option3_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> + + <!--Go to New Product page, add Attribute and check multiselect values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml new file mode 100644 index 0000000000000..f1a19a9ece8d5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml @@ -0,0 +1,63 @@ +<?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="CreateProductAttributeEntityPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Price product attribute"/> + <description value="Admin should be able to create a Price product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10897"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute with Price--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="priceProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against priceProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{priceProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{priceProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{priceProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{priceProductAttribute.attribute_code}}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(priceProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(priceProductAttribute.attribute_code)}}" userInput="{{priceProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml new file mode 100644 index 0000000000000..3495069face61 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.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="CreateProductAttributeEntityTextFieldTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a TextField product attribute"/> + <description value="Admin should be able to create a TextField product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10894"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeWithTextFieldActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="textProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against textProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{textProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{textProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{textProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{textProductAttribute.attribute_code}}"/> + <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{textProductAttribute.default_value}}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.default_value}}"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index a89f49b3db346..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,452 +0,0 @@ -<?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="EndToEndB2CGuestUserTest"> - <annotations> - <features value="End to End scenarios"/> - <stories value="B2C guest user - MAGETWO-75411"/> - <group value="e2e"/> - <title value="You should be able to pass End to End B2C Guest User scenario"/> - <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-87435"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> - - <createData entity="ApiCategory" stepKey="createCategory"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> - <requiredEntity createDataKey="createSimpleProduct2"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> - <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> - <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!--Re-index--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!-- Step 1: User browses catalog --> - <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - - <!-- Open Category --> - <comment userInput="Open category" stepKey="commentOpenCategory" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <!-- Check simple product 1 in category --> - <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <!-- Check simple product 2 in category --> - <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Simple Product 1 --> - <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Simple Product 2 --> - <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> - - <!-- Step 4: User compares products --> - <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to comparison --> - <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory" /> - <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Add Simple Product 2 to comparison --> - <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products in comparison sidebar --> - <!-- Check simple product 1 in comparison sidebar --> - <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- Check simple product 2 in comparison sidebar --> - <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products on comparison page --> - <!-- Check simple product 1 on comparison page --> - <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> - <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> - <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <!-- Check simple product2 on comparison page --> - <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> - <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Clear comparison sidebar --> - <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> - - - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> - <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <annotations> - <features value="End to End scenarios"/> - <stories value="B2C guest user - MAGETWO-75411"/> - <group value="e2e"/> - <title value="You should be able to pass End to End B2C Guest User scenario using the Mysql search engine"/> - <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out using the Mysql search engine."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-20476"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> - - <createData entity="ApiCategory" stepKey="createCategory"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> - <requiredEntity createDataKey="createSimpleProduct2"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> - <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> - <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!-- Step 1: User browses catalog --> - <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - - <!-- Open Category --> - <comment userInput="Open category" stepKey="commentOpenCategory" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <!-- Check simple product 1 in category --> - <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <!-- Check simple product 2 in category --> - <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Simple Product 1 --> - <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Simple Product 2 --> - <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> - <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> - - <!-- Step 4: User compares products --> - <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to comparison --> - <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory" /> - <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Add Simple Product 2 to comparison --> - <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> - <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products in comparison sidebar --> - <!-- Check simple product 1 in comparison sidebar --> - <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- Check simple product 2 in comparison sidebar --> - <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products on comparison page --> - <!-- Check simple product 1 on comparison page --> - <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> - <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> - <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <!-- Check simple product2 on comparison page --> - <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> - <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Clear comparison sidebar --> - <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> - <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts" /> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..ee98e6e152d91 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,228 @@ +<?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="EndToEndB2CGuestUserMysqlTest"> + <annotations> + <features value="End to End scenarios"/> + <stories value="B2C guest user - MAGETWO-75411"/> + <group value="e2e"/> + <title value="You should be able to pass End to End B2C Guest User scenario using the Mysql search engine"/> + <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out using the Mysql search engine."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-20476"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + + <createData entity="ApiCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> + <requiredEntity createDataKey="createSimpleProduct2"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Step 1: User browses catalog --> + <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> + + <!-- Open Category --> + <comment userInput="Open category" stepKey="commentOpenCategory"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <!-- Check simple product 1 in category --> + <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product 2 in category --> + <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> + + <!-- Step 4: User compares products --> + <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to comparison --> + <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory"/> + <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Add Simple Product 2 to comparison --> + <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product 2 in comparison sidebar --> + <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product2 on comparison page --> + <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Clear comparison sidebar --> + <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> + <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..93b08e3c468de --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,233 @@ +<?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="EndToEndB2CGuestUserTest"> + <annotations> + <features value="End to End scenarios"/> + <stories value="B2C guest user - MAGETWO-75411"/> + <group value="e2e"/> + <title value="You should be able to pass End to End B2C Guest User scenario"/> + <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-87435"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + + <createData entity="ApiCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> + <requiredEntity createDataKey="createSimpleProduct2"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!--Re-index--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Step 1: User browses catalog --> + <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> + + <!-- Open Category --> + <comment userInput="Open category" stepKey="commentOpenCategory"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <!-- Check simple product 1 in category --> + <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product 2 in category --> + <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> + + <!-- Step 4: User compares products --> + <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to comparison --> + <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory"/> + <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Add Simple Product 2 to comparison --> + <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product 2 in comparison sidebar --> + <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product2 on comparison page --> + <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Clear comparison sidebar --> + <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + + + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> + <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml index c0c142fe2cba1..d84e2283b2dd4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -128,7 +128,10 @@ </actionGroup> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct3"/> - <assertNotContains expected="$$createSimpleProduct3.name$$" actual="$grabDontSeeHomeProduct3" stepKey="assertNotSeeProduct3"/> + <assertNotContains stepKey="assertNotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> <!-- Switch Storeview--> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStoreViewActionGroup"> @@ -144,7 +147,10 @@ </actionGroup> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabStoreView2DontSeeHomeProduct3"/> - <assertNotContains expected="$$createSimpleProduct3.name$$" actual="$grabDontSeeHomeProduct3" stepKey="assertStoreView2NotSeeProduct3"/> + <assertNotContains stepKey="assertStoreView2NotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> <!-- Switch to default store--> @@ -161,7 +167,10 @@ <argument name="productPosition" value="1"/> </actionGroup> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct1"/> - <assertNotContains expected="$$createSimpleProduct1.name$$" actual="$grabDontSeeHomeProduct1" stepKey="assertNotSeeProduct1"/> + <assertNotContains stepKey="assertNotSeeProduct1"> + <actualResult type="const">$grabDontSeeHomeProduct1</actualResult> + <expectedResult type="const">$$createSimpleProduct1.name$$</expectedResult> + </assertNotContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index 0e09635489d9c..d454a3d24e273 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -130,7 +130,10 @@ </actionGroup> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct3"/> - <assertNotContains expected="$$createSimpleProduct3.name$$" actual="$grabDontSeeHomeProduct3" stepKey="assertNotSeeProduct3"/> + <assertNotContains stepKey="assertNotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> <actionGroup ref="StorefrontSwitchDefaultStoreViewActionGroup" stepKey="switchToDefualtStoreView"/> @@ -145,6 +148,9 @@ </actionGroup> <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct1"/> - <assertNotContains expected="$$createSimpleProduct1.name$$" actual="$grabDontSeeHomeProduct1" stepKey="assertNotSeeProduct1"/> + <assertNotContains stepKey="assertNotSeeProduct1"> + <actualResult type="const">$grabDontSeeHomeProduct1</actualResult> + <expectedResult type="const">$$createSimpleProduct1.name$$</expectedResult> + </assertNotContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml similarity index 51% rename from app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml index 64b8d10b75419..5c13e5a47973a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductNameWithDoubleQuoteTest"> <annotations> <features value="Catalog"/> @@ -65,65 +64,4 @@ <argument name="product" value="SimpleProductNameWithDoubleQuote"/> </actionGroup> </test> - - <test name="StorefrontProductNameWithHTMLEntitiesTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create product"/> - <title value=":Proudct with html special characters in name"/> - <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> - <severity value="CRITICAL"/> - <group value="product"/> - <testCaseId value="MAGETWO-93794"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategoryOne"/> - <createData entity="productWithHTMLEntityOne" stepKey="productOne"> - <requiredEntity createDataKey="createCategoryOne"/> - </createData> - <createData entity="productWithHTMLEntityTwo" stepKey="productTwo"> - <requiredEntity createDataKey="createCategoryOne"/> - </createData> - </before> - <after> - <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> - <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> - <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> - </after> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!--Check product in category listing--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> - <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectNameProd1CategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> - - <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> - <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> - - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectName"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productWithHTMLEntityOne.sku}}" stepKey="seeCorrectSku"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{productWithHTMLEntityOne.price}}" stepKey="seeCorrectPrice"/> - - <!--Veriy the breadcrumbs on Product Display page--> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeCorrectBreadCrumbProduct"/> - - <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> - <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> - - <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" stepKey="clickProductToGoSecondProductPage"/> - <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> - - <!--Verify the breadcrumbs on Product Display page--> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> - - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml new file mode 100644 index 0000000000000..bd2c22c90318a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml @@ -0,0 +1,70 @@ +<?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="StorefrontProductNameWithHTMLEntitiesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create product"/> + <title value=":Proudct with html special characters in name"/> + <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> + <severity value="CRITICAL"/> + <group value="product"/> + <testCaseId value="MAGETWO-93794"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategoryOne"/> + <createData entity="productWithHTMLEntityOne" stepKey="productOne"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="productWithHTMLEntityTwo" stepKey="productTwo"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + </before> + <after> + <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> + </after> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!--Check product in category listing--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectNameProd1CategoryPage"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> + + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> + + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productWithHTMLEntityOne.sku}}" stepKey="seeCorrectSku"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{productWithHTMLEntityOne.price}}" stepKey="seeCorrectPrice"/> + + <!--Veriy the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeCorrectBreadCrumbProduct"/> + + <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> + + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" stepKey="clickProductToGoSecondProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> + + <!--Verify the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> + + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml deleted file mode 100644 index ae74a000d76e8..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?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="VerifyDefaultWYSIWYGToolbarOnProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="MAGETWO-70412-Default toolbar configuration in Magento"/> - <group value="Catalog"/> - <title value="Admin should be able to see default toolbar display on Description content area"/> - <description value="Admin should be able to see default toolbar display on Description content area"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-80505"/> - </annotations> - <before> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> - <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> - </before> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> - <waitForPageLoad stepKey="wait"/> - <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty" /> - <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab" /> - <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible1"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4Description" /> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo2"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo3"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo4"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo5"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo6"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo7"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo8"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo9"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo10"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo11"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInf12"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo13"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo14"/> - <after> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - </test> - <test name="VerifydefaultcontrolsonproductshortdescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default toolbar configuration in Magento-MAGETWO-70412"/> - <group value="WYSIWYG"/> - <title value="Admin should be able to see default toolbar display on Short Description content area"/> - <description value="Admin should be able to see default toolbar display on Short Description content area"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-80505"/> - </annotations> - <before> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> - <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> - </before> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> - <waitForPageLoad stepKey="wait"/> - <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty" /> - <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab" /> - <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible2"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4ShortDescription" /> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo16"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo17"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo18"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo19"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo20"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo21"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo22"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo23"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo324"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo25"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInfo26"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo27"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo28"/> - <after> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml new file mode 100644 index 0000000000000..456abecc63ccb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml @@ -0,0 +1,49 @@ +<?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="VerifyDefaultWYSIWYGToolbarOnProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="MAGETWO-70412-Default toolbar configuration in Magento"/> + <group value="Catalog"/> + <title value="Admin should be able to see default toolbar display on Description content area"/> + <description value="Admin should be able to see default toolbar display on Description content area"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-80505"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4"/> + </before> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> + <waitForPageLoad stepKey="wait"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty"/> + <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab"/> + <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible1"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4Description"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo2"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo3"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo4"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo5"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo6"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo7"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo8"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo9"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo10"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo11"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInf12"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo13"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo14"/> + <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml new file mode 100644 index 0000000000000..e929cbd752f81 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml @@ -0,0 +1,49 @@ +<?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="VerifydefaultcontrolsonproductshortdescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default toolbar configuration in Magento-MAGETWO-70412"/> + <group value="WYSIWYG"/> + <title value="Admin should be able to see default toolbar display on Short Description content area"/> + <description value="Admin should be able to see default toolbar display on Short Description content area"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-80505"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4"/> + </before> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> + <waitForPageLoad stepKey="wait"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty"/> + <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab"/> + <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible2"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4ShortDescription"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo16"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo17"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo18"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo19"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo20"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo21"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo22"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo23"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo324"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo25"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInfo26"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo27"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo28"/> + <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml new file mode 100644 index 0000000000000..aeab4eb369b02 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml @@ -0,0 +1,19 @@ +<?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="AdminCatalogPriceRuleGrid"> + <element name="applyRules" type="button" selector="#apply_rules" timeout="30"/> + <element name="updateMessage" type="text" selector="//div[@class='message message-notice notice']//div[contains(.,'We found updated rules that are not applied. Please click')]"/> + <element name="ruleFilter" type="input" selector="//td[@data-column='name']/input[@name='name']"/> + <element name="resetFilter" type="button" selector="//button[@title='Reset Filter']" timeout="30"/> + <element name="search" type="button" selector="//div[@id='promo_catalog_grid']//button[@title='Search']" timeout="30"/> + <element name="selectRowByRuleName" type="text" selector="//tr[@data-role='row']//td[contains(.,'{{ruleName}}')]" parameterized="true"/> + <element name="firstRow" type="text" selector="//tr[@data-role='row']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml new file mode 100644 index 0000000000000..7c52080ac2704 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml @@ -0,0 +1,15 @@ +<?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="AdminNewCatalogPriceRuleActions"> + <element name="apply" type="select" selector="[name='simple_action']"/> + <element name="discountAmount" type="input" selector="[name='discount_amount']"/> + <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml new file mode 100644 index 0000000000000..db064a0eedc85 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml @@ -0,0 +1,26 @@ +<?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="AdminNewCatalogPriceRuleConditions"> + <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> + <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> + <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> + <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> + <element name="ellipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '...')]" parameterized="true" timeout="30"/> + <element name="targetEllipsisSelect" type="select" selector="select#conditions__{{var1}}--{{var2}}__value" parameterized="true" timeout="30"/> + <element name="targetSelect" type="select" selector="//ul[@id='conditions__{{var}}__children']//select" parameterized="true" timeout="30"/> + <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> + <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true" timeout="30"/> + <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{condition}}']" parameterized="true"/> + <element name="activeOperatorSelect" type="select" selector=".rule-param-edit select[name*='[operator]']"/> + <element name="activeValueInput" type="input" selector=".rule-param-edit [name*='[value]']"/> + <element name="activeConditionApplyButton" type="button" selector=".rule-param-edit .rule-param-apply" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml similarity index 52% rename from app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml rename to app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml index 8be6b809aa77b..35d99ae90a1ec 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewCatalogPriceRule"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="save" type="button" selector="#save" timeout="30"/> @@ -46,37 +45,4 @@ <element name="actionsTabBody" type="block" selector="[data-index='actions'] .admin__fieldset-wrapper-content"/> <element name="fieldError" type="text" selector="//input[@name='{{fieldName}}']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> </section> - - <section name="AdminNewCatalogPriceRuleActions"> - <element name="apply" type="select" selector="[name='simple_action']"/> - <element name="discountAmount" type="input" selector="[name='discount_amount']"/> - <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> - </section> - - <section name="AdminNewCatalogPriceRuleConditions"> - <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> - <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> - <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> - <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> - <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> - <element name="ellipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '...')]" parameterized="true" timeout="30"/> - <element name="targetEllipsisSelect" type="select" selector="select#conditions__{{var1}}--{{var2}}__value" parameterized="true" timeout="30"/> - <element name="targetSelect" type="select" selector="//ul[@id='conditions__{{var}}__children']//select" parameterized="true" timeout="30"/> - <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> - <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true" timeout="30"/> - <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{condition}}']" parameterized="true"/> - <element name="activeOperatorSelect" type="select" selector=".rule-param-edit select[name*='[operator]']"/> - <element name="activeValueInput" type="input" selector=".rule-param-edit [name*='[value]']"/> - <element name="activeConditionApplyButton" type="button" selector=".rule-param-edit .rule-param-apply" timeout="30"/> - </section> - - <section name="AdminCatalogPriceRuleGrid"> - <element name="applyRules" type="button" selector="#apply_rules" timeout="30"/> - <element name="updateMessage" type="text" selector="//div[@class='message message-notice notice']//div[contains(.,'We found updated rules that are not applied. Please click')]"/> - <element name="ruleFilter" type="input" selector="//td[@data-column='name']/input[@name='name']"/> - <element name="resetFilter" type="button" selector="//button[@title='Reset Filter']" timeout="30"/> - <element name="search" type="button" selector="//div[@id='promo_catalog_grid']//button[@title='Search']" timeout="30"/> - <element name="selectRowByRuleName" type="text" selector="//tr[@data-role='row']//td[contains(.,'{{ruleName}}')]" parameterized="true"/> - <element name="firstRow" type="text" selector="//tr[@data-role='row']"/> - </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml deleted file mode 100644 index 10689b415b291..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ /dev/null @@ -1,222 +0,0 @@ -<?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="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> - <description value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-65"/> - <group value="CatalogRule"/> - </annotations> - <before> - <!-- Create the simple product and category that it will be in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> - <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> - <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> - </before> - <after> - <!-- delete the simple product and catalog price rule and logout --> - <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <!-- Go to category page and make sure that all of the prices are correct --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$110.70"/> - - <!-- Go to the simple product page and check that the prices are correct --> - <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> - <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$110.70"/> - - <!-- Add the product to cart and check that the price is correct there --> - <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForAddedToCart"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> - </test> - - <test name="AdminCreateCatalogPriceRuleByFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> - <description value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-93"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleByFixed"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleByFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleToPercentTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> - <description value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-69"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleToPercent"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleToPercent.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleToFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> - <description value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-60"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleForCustomerGroupTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Apply catalog price rule"/> - <title value="Admin should be able to apply the catalog rule by customer group"/> - <description value="Admin should be able to apply the catalog rule by customer group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-71"/> - <group value="CatalogRule"/> - </annotations> - <before> - <!-- Create a simple product and a category--> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!-- Delete the simple product and category --> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete the catalog rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToRulePage"/> - <waitForPageLoad stepKey="waitForRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create a catalog rule for the NOT LOGGED IN customer group --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- As a NOT LOGGED IN user, go to the storefront category page and should see the discount --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> - - <!-- Create a user account --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> - </actionGroup> - - <!-- As a logged in user, go to the storefront category page and should NOT see discount --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> - </test> - - <test name="AdminCreateCatalogPriceRuleWithInvalidDataTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create Catalog Price Rule"/> - <title value="Admin can not create catalog price rule with the invalid data"/> - <description value="Admin can not create catalog price rule with the invalid data"/> - <severity value="MAJOR"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <actionGroup ref="NewCatalogPriceRuleWithInvalidDataActionGroup" stepKey="createNewPriceRule"> - <argument name="catalogRule" value="catalogRuleWithInvalid"/> - </actionGroup> - - <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seeSortOrderError"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml new file mode 100644 index 0000000000000..3ba0f1d59f07d --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml @@ -0,0 +1,32 @@ +<?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="AdminCreateCatalogPriceRuleByFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> + <description value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-93"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleByFixed"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleByFixed.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml new file mode 100644 index 0000000000000..d033e03bb4916 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml @@ -0,0 +1,70 @@ +<?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="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> + <description value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-65"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create the simple product and category that it will be in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- log in and create the price rule --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> + <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> + <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> + <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> + </before> + <after> + <!-- delete the simple product and catalog price rule and logout --> + <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Go to category page and make sure that all of the prices are correct --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategory"/> + <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> + <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$110.70"/> + + <!-- Go to the simple product page and check that the prices are correct --> + <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> + <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> + <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$110.70"/> + + <!-- Add the product to cart and check that the price is correct there --> + <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForAddedToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml new file mode 100644 index 0000000000000..93291104e7fc9 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml @@ -0,0 +1,68 @@ +<?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="AdminCreateCatalogPriceRuleForCustomerGroupTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Apply catalog price rule"/> + <title value="Admin should be able to apply the catalog rule by customer group"/> + <description value="Admin should be able to apply the catalog rule by customer group"/> + <severity value="MAJOR"/> + <testCaseId value="MC-71"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create a simple product and a category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product and category --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete the catalog rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToRulePage"/> + <waitForPageLoad stepKey="waitForRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a catalog rule for the NOT LOGGED IN customer group --> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- As a NOT LOGGED IN user, go to the storefront category page and should see the discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> + + <!-- Create a user account --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + + <!-- As a logged in user, go to the storefront category page and should NOT see discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml new file mode 100644 index 0000000000000..538e472812d85 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml @@ -0,0 +1,32 @@ +<?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="AdminCreateCatalogPriceRuleToFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> + <description value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-60"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleToFixed"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleToFixed.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml new file mode 100644 index 0000000000000..23945412693f2 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml @@ -0,0 +1,32 @@ +<?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="AdminCreateCatalogPriceRuleToPercentTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> + <description value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-69"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleToPercent"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleToPercent.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml new file mode 100644 index 0000000000000..a4a7ba012ac98 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml @@ -0,0 +1,32 @@ +<?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="AdminCreateCatalogPriceRuleWithInvalidDataTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create Catalog Price Rule"/> + <title value="Admin can not create catalog price rule with the invalid data"/> + <description value="Admin can not create catalog price rule with the invalid data"/> + <severity value="MAJOR"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="NewCatalogPriceRuleWithInvalidDataActionGroup" stepKey="createNewPriceRule"> + <argument name="catalogRule" value="catalogRuleWithInvalid"/> + </actionGroup> + + <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seeSortOrderError"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml similarity index 62% rename from app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml rename to app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml index 17a959c374991..93797bcda5411 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml @@ -5,91 +5,8 @@ * 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="AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest"> - <annotations> - <stories value="Delete Catalog Price Rule"/> - <title value="Delete Catalog Price Rule for Simple Product"/> - <description value="Assert that Catalog Price Rule is not applied for simple product."/> - <testCaseId value="MC-14073"/> - <severity value="CRITICAL"/> - <group value="CatalogRule"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> - <createData entity="_defaultCategory" stepKey="createCategory1"/> - <createData entity="SimpleProduct" stepKey="createProduct1"> - <requiredEntity createDataKey="createCategory1"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - - <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> - <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> - <argument name="customerGroup" value="General"/> - <argument name="disregardRules" value="Yes"/> - </actionGroup> - - <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> - <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> - - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer1"/> - <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="createCategory1" stepKey="deleteCategoryFirst1"/> - </after> - - <!-- Delete the simple product and catalog price rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> - <waitForPageLoad stepKey="waitForPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> - <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - - <!-- Assert that the Success message is present after the delete --> - <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> - - <!-- Reindex --> - <magentoCLI command="cache:flush" stepKey="flushCache1"/> - <magentoCLI command="indexer:reindex" stepKey="reindex1"/> - - <!-- Assert that the rule isn't present on the Category page --> - <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <dontSee selector="{{StorefrontCategoryProductSection.ProductCatalogRulePriceTitleByName($$createProduct1.name$$)}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText1"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($$createProduct1.name$$)}}" stepKey="dontSeeSpecialPrice1"/> - - <!-- Assert that the rule isn't present on the Product page --> - <amOnPage url="$$createProduct1.name$$.html" stepKey="goToStorefrontProductPage1"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPRiceText2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct1.price$$" stepKey="seeTrueProductPrice1"/> - - <!-- Assert that the rule isn't present in the Shopping Cart --> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> - <argument name="productName" value="$$createProduct1.name$$"/> - </actionGroup> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> - <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> - - <!-- Assert that the rule isn't present on the Checkout page --> - <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout1"/> - <conditionalClick selector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" dependentSelector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" visible="true" stepKey="expandShoppingCartSummary1"/> - <see selector="{{CheckoutCartProductSection.ProductRegularPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPriceOnCheckout1"/> - </test> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest"> <annotations> <stories value="Delete Catalog Price Rule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml new file mode 100644 index 0000000000000..91ccfb458e98f --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml @@ -0,0 +1,91 @@ +<?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="AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest"> + <annotations> + <stories value="Delete Catalog Price Rule"/> + <title value="Delete Catalog Price Rule for Simple Product"/> + <description value="Assert that Catalog Price Rule is not applied for simple product."/> + <testCaseId value="MC-14073"/> + <severity value="CRITICAL"/> + <group value="CatalogRule"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> + <createData entity="_defaultCategory" stepKey="createCategory1"/> + <createData entity="SimpleProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + + <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> + <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> + <argument name="customerGroup" value="General"/> + <argument name="disregardRules" value="Yes"/> + </actionGroup> + + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> + + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer1"/> + <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createCategory1" stepKey="deleteCategoryFirst1"/> + </after> + + <!-- Delete the simple product and catalog price rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> + <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + + <!-- Assert that the Success message is present after the delete --> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> + + <!-- Reindex --> + <magentoCLI command="cache:flush" stepKey="flushCache1"/> + <magentoCLI command="indexer:reindex" stepKey="reindex1"/> + + <!-- Assert that the rule isn't present on the Category page --> + <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <dontSee selector="{{StorefrontCategoryProductSection.ProductCatalogRulePriceTitleByName($$createProduct1.name$$)}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText1"/> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($$createProduct1.name$$)}}" stepKey="dontSeeSpecialPrice1"/> + + <!-- Assert that the rule isn't present on the Product page --> + <amOnPage url="$$createProduct1.name$$.html" stepKey="goToStorefrontProductPage1"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPRiceText2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct1.price$$" stepKey="seeTrueProductPrice1"/> + + <!-- Assert that the rule isn't present in the Shopping Cart --> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> + <argument name="productName" value="$$createProduct1.name$$"/> + </actionGroup> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> + <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> + + <!-- Assert that the rule isn't present on the Checkout page --> + <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout1"/> + <conditionalClick selector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" dependentSelector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" visible="true" stepKey="expandShoppingCartSummary1"/> + <see selector="{{CheckoutCartProductSection.ProductRegularPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPriceOnCheckout1"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml deleted file mode 100644 index 0e92d9fb0c7ad..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ /dev/null @@ -1,102 +0,0 @@ -<?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="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> - <argument name="name" value="$$product.name$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> - <argument name="sku" value="$$product.sku$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> - <argument name="description" value="$$product.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> - <argument name="shortDescription" value="$$product.custom_attributes[short_description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> - <argument name="name" value="$$arg1.name$$"/> - <argument name="priceFrom" value="$$arg2.price$$"/> - <argument name="priceTo" value="$$arg3.price$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$arg1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml new file mode 100644 index 0000000000000..d23663d43dcd0 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml @@ -0,0 +1,27 @@ +<?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="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> + <argument name="description" value="$$product.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml new file mode 100644 index 0000000000000..0b3fb2fa42532 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml @@ -0,0 +1,28 @@ +<?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="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> + <argument name="name" value="$$product.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml new file mode 100644 index 0000000000000..517e200f8ce11 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml @@ -0,0 +1,30 @@ +<?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="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> + <argument name="name" value="$$arg1.name$$"/> + <argument name="priceFrom" value="$$arg2.price$$"/> + <argument name="priceTo" value="$$arg3.price$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$arg1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..0bd08d31e8ffa --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml @@ -0,0 +1,28 @@ +<?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="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> + <argument name="shortDescription" value="$$product.custom_attributes[short_description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml new file mode 100644 index 0000000000000..d273f9828dc95 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml @@ -0,0 +1,28 @@ +<?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="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> + <argument name="sku" value="$$product.sku$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml similarity index 50% rename from app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml rename to app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml index e347803ae3feb..9d5aebb43c616 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -5,87 +5,8 @@ * 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="EndToEndB2CGuestUserTest"> - <!-- Step 2: User searches for product --> - <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> - <!-- Advanced Search with Product Data --> - <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> - <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> - <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> - <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> - <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Quick Search with common part of product names --> - <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.apiSimpleProduct"/> - </actionGroup> - <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> - - <!-- Search simple product 1 --> - <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Quick Search with non-existent product name --> - <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.nonexistentProductName"/> - </actionGroup> - <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> - <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent" /> - </test> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserMysqlTest"> <!-- Step 2: User searches for product --> <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> @@ -156,12 +77,12 @@ </assertNotRegExp> <!-- Quick Search with non-existent product name --> - <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> + <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> <argument name="phrase" value="CONST.nonexistentProductName"/> </actionGroup> <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> - <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent" /> + <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent"/> </test> </tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..a0d856df1b93c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,88 @@ +<?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="EndToEndB2CGuestUserTest"> + <!-- Step 2: User searches for product --> + <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> + <!-- Advanced Search with Product Data --> + <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> + <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> + <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with common part of product names --> + <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.apiSimpleProduct"/> + </actionGroup> + <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> + + <!-- Search simple product 1 --> + <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Search simple product2 --> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with non-existent product name --> + <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.nonexistentProductName"/> + </actionGroup> + <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> + <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml deleted file mode 100644 index 03a1d89e0b47d..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ /dev/null @@ -1,610 +0,0 @@ -<?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="QuickSearchProductBySkuTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products"/> - <description value="Use Quick Search to find a product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14783"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.sku$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createSimpleProduct.name$"/> - <argument name="productUrlKey" value="$createSimpleProduct.custom_attributes[url_key]$"/> - </actionGroup> - </test> - <test name="QuickSearchProductByNameTest" extends="QuickSearchProductBySkuTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products via Name"/> - <description value="Use Quick Search to find a product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14791"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <!-- Overwrite search to use name --> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchProductByNameWithSpecialCharsTest" extends="QuickSearchProductBySkuTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search can find products with names that contain special characters"/> - <description value="Use Quick Search to find a product by name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14792"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productWithSpecialCharacters" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <!-- Overwrite search to use name --> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchEmptyResultsTest"> - <annotations> - <features value="CatalogSearch"/> - <stories value="Search Product on Storefront"/> - <title value="User should not get search results on query that doesn't return anything"/> - <description value="Use invalid query to return no products"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14793"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="ThisShouldn'tReturnAnything"/> - </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmpty"/> - </test> - - <test name="QuickSearchWithTwoCharsEmptyResultsTest" extends="QuickSearchEmptyResultsTest"> - <annotations> - <features value="CatalogSearch"/> - <stories value="Search Product on Storefront"/> - <title value="User should not get search results on query that only contains two characters"/> - <description value="Use of 2 character query to return no products"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14794"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> - </before> - - <after> - <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> - </after> - - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}} - 1); return ret;" before="searchStorefront" stepKey="getFirstLessThenConfigLetters"/> - - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> - <argument name="phrase" value="$getFirstLessThenConfigLetters"/> - <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> - <argument name="term" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - - <test name="QuickSearchProductByNameWithThreeLettersTest" extends="QuickSearchProductBySkuTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products by their first three letters"/> - <description value="Use Quick Search to find a product using only first three letters"/> - <severity value="MAJOR"/> - <testCaseId value="MC-15034"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" stepKey="getFirstThreeLetters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$getFirstThreeLetters}"/> - </actionGroup> - </test> - <test name="QuickSearchProductBy128CharQueryTest" extends="QuickSearchProductBySkuTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search product with long names, using first 128 letters"/> - <description value="Use Quick Search to find a product with name of 130 length with query of only 128"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14795"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productWith130CharName" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,128); return ret;" stepKey="get128Letters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$get128Letters}"/> - </actionGroup> - </test> - - <test name="QuickSearchTwoProductsWithSameWeightTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search should sort products with the same weight appropriately"/> - <description value="Use Quick Search to find a two products with the same weight"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14796"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="productAlphabeticalA" stepKey="product1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAlphabeticalB" stepKey="product2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - - <!-- Create and Assign Attribute to product1--> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> - <argument name="productId" value="$product1.id$"/> - </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product1.name$"/> - <argument name="attributeSetName" value="$product1.name$"/> - <argument name="weight" value="1"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet1"> - <argument name="attributeSetName" value="$product1.name$"/> - </actionGroup> - <!--fill in default--> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1a"/> - <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault1"> - <argument name="attributeName" value="$product1.name$"/> - <argument name="value" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> - <!-- Create and Assign Attribute to product2--> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2"> - <argument name="productId" value="$product2.id$"/> - </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product2.name$"/> - <argument name="attributeSetName" value="$product2.name$"/> - <argument name="weight" value="1"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet2"> - <argument name="attributeSetName" value="$product2.name$"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2a"/> - <!--fill in default--> - <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault2"> - <argument name="attributeName" value="$product2.name$"/> - <argument name="value" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2b"/> - </before> - <after> - <deleteData stepKey="deleteProduct1" createDataKey="product1"/> - <deleteData stepKey="deleteProduct2" createDataKey="product2"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> - <argument name="productName" value="$product1.name$"/> - <argument name="index" value="2"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> - <argument name="productName" value="$product2.name$"/> - <argument name="index" value="1"/> - </actionGroup> - </test> - <test name="QuickSearchTwoProductsWithDifferentWeightTest" extends="QuickSearchTwoProductsWithSameWeightTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search should sort products with the different weight appropriately"/> - <description value="Use Quick Search to find a two products with the different weight"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14797"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product1.name$"/> - <argument name="attributeSetName" value="$product1.name$"/> - <argument name="weight" value="5"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - </before> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> - <argument name="productName" value="$product1.name$"/> - <argument name="index" value="1"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> - <argument name="productName" value="$product2.name$"/> - <argument name="index" value="2"/> - </actionGroup> - </test> - - <test name="QuickSearchAndAddToCartTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a simple product and add it to cart"/> - <description value="Use Quick Search to find simple Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14784"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartVirtualTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a virtual product and add it to cart"/> - <description value="Use Quick Search to find virtual Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14785"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="VirtualProduct" stepKey="createVirtualProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createVirtualProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createVirtualProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createVirtualProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartConfigurableTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a configurable product and add it to cart"/> - <description value="Use Quick Search to find configurable Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14786"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="productUrlKey" value="{{_defaultProduct.urlKey}}"/> - </actionGroup> - <actionGroup ref="SelectSingleAttributeAndAddToCartActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="attributeCode" value="{{colorProductAttribute.default_label}}"/> - <argument name="optionName" value="{{colorProductAttribute1.name}}"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartDownloadableTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a downloadable product and add it to cart"/> - <description value="Use Quick Search to find downloadable Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14787"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="downloadableLink1" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="createProduct"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartGroupedTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a grouped product and add it to cart"/> - <description value="Use Quick Search to find grouped Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14788"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - <createData entity="ApiProductWithDescription" stepKey="simple1"/> - <createData entity="ApiGroupedProduct" stepKey="createProduct"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="createProduct"/> - <requiredEntity createDataKey="simple1"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteGroupedProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteSimpleProduct" createDataKey="simple1"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value=""$createProduct.name$""/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartBundleDynamicTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a Bundle Dynamic product and add it to cart"/> - <description value="Use Quick Search to find Bundle Dynamic Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14789"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create dynamic product--> - <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="createProduct"/> - <field key="qty">10</field> - </createData> - <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> - <argument name="productId" value="$$createBundleProduct.id$$"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createBundleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> - </actionGroup> - <actionGroup ref="StorefrontAddBundleProductFromProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createBundleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartBundleFixedTest"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a Bundle Fixed product and add it to cart"/> - <description value="Use Quick Search to find Bundle Fixed Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14790"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create fixed product--> - <!--Create 2 simple products--> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <!-- Create the bundle product based --> - <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> - <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="required">false</field> - </createData> - <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - - <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> - <argument name="productId" value="$$createBundleProduct.id$$"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - <comment userInput="$simpleProduct1.name$" stepKey="asdf"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createBundleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> - </actionGroup> - <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="optionName" value="$createBundleOption1_1.name$"/> - <argument name="value" value="$simpleProduct1.name$"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml new file mode 100644 index 0000000000000..f6cd95b4f5a3d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml @@ -0,0 +1,68 @@ +<?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="QuickSearchAndAddToCartBundleDynamicTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a Bundle Dynamic product and add it to cart"/> + <description value="Use Quick Search to find Bundle Dynamic Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14789"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create dynamic product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="createProduct"/> + <field key="qty">10</field> + </createData> + <!--Finish bundle creation--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createBundleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="StorefrontAddBundleProductFromProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createBundleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml new file mode 100644 index 0000000000000..844c100830bc2 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml @@ -0,0 +1,84 @@ +<?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="QuickSearchAndAddToCartBundleFixedTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a Bundle Fixed product and add it to cart"/> + <description value="Use Quick Search to find Bundle Fixed Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14790"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create fixed product--> + <!--Create 2 simple products--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <!-- Create the bundle product based --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + + <!--Finish bundle creation--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <comment userInput="$simpleProduct1.name$" stepKey="asdf"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createBundleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="optionName" value="$createBundleOption1_1.name$"/> + <argument name="value" value="$simpleProduct1.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml new file mode 100644 index 0000000000000..e4918536245cf --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml @@ -0,0 +1,54 @@ +<?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="QuickSearchAndAddToCartConfigurableTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a configurable product and add it to cart"/> + <description value="Use Quick Search to find configurable Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14786"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="productUrlKey" value="{{_defaultProduct.urlKey}}"/> + </actionGroup> + <actionGroup ref="SelectSingleAttributeAndAddToCartActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="attributeCode" value="{{colorProductAttribute.default_label}}"/> + <argument name="optionName" value="{{colorProductAttribute1.name}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml new file mode 100644 index 0000000000000..79a2fc8646c04 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml @@ -0,0 +1,47 @@ +<?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="QuickSearchAndAddToCartDownloadableTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a downloadable product and add it to cart"/> + <description value="Use Quick Search to find downloadable Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14787"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="downloadableLink1" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="createProduct"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml new file mode 100644 index 0000000000000..0d062c832090c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml @@ -0,0 +1,47 @@ +<?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="QuickSearchAndAddToCartGroupedTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a grouped product and add it to cart"/> + <description value="Use Quick Search to find grouped Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14788"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <createData entity="ApiProductWithDescription" stepKey="simple1"/> + <createData entity="ApiGroupedProduct" stepKey="createProduct"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="createProduct"/> + <requiredEntity createDataKey="simple1"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteGroupedProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteSimpleProduct" createDataKey="simple1"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value=""$createProduct.name$""/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml new file mode 100644 index 0000000000000..ba6fa813367c3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml @@ -0,0 +1,42 @@ +<?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="QuickSearchAndAddToCartTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a simple product and add it to cart"/> + <description value="Use Quick Search to find simple Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14784"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml new file mode 100644 index 0000000000000..b71388f5f409b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml @@ -0,0 +1,42 @@ +<?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="QuickSearchAndAddToCartVirtualTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a virtual product and add it to cart"/> + <description value="Use Quick Search to find virtual Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14785"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createVirtualProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createVirtualProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createVirtualProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml new file mode 100644 index 0000000000000..566b4d204751d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml @@ -0,0 +1,42 @@ +<?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="QuickSearchEmptyResultsTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Search Product on Storefront"/> + <title value="User should not get search results on query that doesn't return anything"/> + <description value="Use invalid query to return no products"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14793"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="ThisShouldn'tReturnAnything"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml new file mode 100644 index 0000000000000..b2b6bbb473091 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml @@ -0,0 +1,30 @@ +<?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="QuickSearchProductBy128CharQueryTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search product with long names, using first 128 letters"/> + <description value="Use Quick Search to find a product with name of 130 length with query of only 128"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14795"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productWith130CharName" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,128); return ret;" stepKey="get128Letters" before="searchStorefront"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{$get128Letters}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml new file mode 100644 index 0000000000000..62f4e3da1059c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml @@ -0,0 +1,25 @@ +<?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="QuickSearchProductByNameTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products via Name"/> + <description value="Use Quick Search to find a product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14791"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <!-- Overwrite search to use name --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml new file mode 100644 index 0000000000000..7f21972ce801b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml @@ -0,0 +1,30 @@ +<?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="QuickSearchProductByNameWithSpecialCharsTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search can find products with names that contain special characters"/> + <description value="Use Quick Search to find a product by name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14792"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productWithSpecialCharacters" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <!-- Overwrite search to use name --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml new file mode 100644 index 0000000000000..e5e7719dd63b3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml @@ -0,0 +1,26 @@ +<?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="QuickSearchProductByNameWithThreeLettersTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products by their first three letters"/> + <description value="Use Quick Search to find a product using only first three letters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-15034"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" stepKey="getFirstThreeLetters" before="searchStorefront"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{$getFirstThreeLetters}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml new file mode 100644 index 0000000000000..814e27182799f --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml @@ -0,0 +1,43 @@ +<?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="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products"/> + <description value="Use Quick Search to find a product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14783"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.sku$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createSimpleProduct.name$"/> + <argument name="productUrlKey" value="$createSimpleProduct.custom_attributes[url_key]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml new file mode 100644 index 0000000000000..ee126ce2ad874 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml @@ -0,0 +1,39 @@ +<?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="QuickSearchTwoProductsWithDifferentWeightTest" extends="QuickSearchTwoProductsWithSameWeightTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search should sort products with the different weight appropriately"/> + <description value="Use Quick Search to find a two products with the different weight"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14797"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product1.name$"/> + <argument name="attributeSetName" value="$product1.name$"/> + <argument name="weight" value="5"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + </before> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> + <argument name="productName" value="$product1.name$"/> + <argument name="index" value="1"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> + <argument name="productName" value="$product2.name$"/> + <argument name="index" value="2"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml new file mode 100644 index 0000000000000..0d2009f21aac1 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml @@ -0,0 +1,93 @@ +<?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="QuickSearchTwoProductsWithSameWeightTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search should sort products with the same weight appropriately"/> + <description value="Use Quick Search to find a two products with the same weight"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14796"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="productAlphabeticalA" stepKey="product1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAlphabeticalB" stepKey="product2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + + <!-- Create and Assign Attribute to product1--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> + <argument name="productId" value="$product1.id$"/> + </actionGroup> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product1.name$"/> + <argument name="attributeSetName" value="$product1.name$"/> + <argument name="weight" value="1"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet1"> + <argument name="attributeSetName" value="$product1.name$"/> + </actionGroup> + <!--fill in default--> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1a"/> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault1"> + <argument name="attributeName" value="$product1.name$"/> + <argument name="value" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> + <!-- Create and Assign Attribute to product2--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2"> + <argument name="productId" value="$product2.id$"/> + </actionGroup> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product2.name$"/> + <argument name="attributeSetName" value="$product2.name$"/> + <argument name="weight" value="1"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet2"> + <argument name="attributeSetName" value="$product2.name$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2a"/> + <!--fill in default--> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault2"> + <argument name="attributeName" value="$product2.name$"/> + <argument name="value" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2b"/> + </before> + <after> + <deleteData stepKey="deleteProduct1" createDataKey="product1"/> + <deleteData stepKey="deleteProduct2" createDataKey="product2"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> + <argument name="productName" value="$product1.name$"/> + <argument name="index" value="2"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> + <argument name="productName" value="$product2.name$"/> + <argument name="index" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml new file mode 100644 index 0000000000000..5f549cc1af5b6 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml @@ -0,0 +1,43 @@ +<?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="QuickSearchWithTwoCharsEmptyResultsTest" extends="QuickSearchEmptyResultsTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Search Product on Storefront"/> + <title value="User should not get search results on query that only contains two characters"/> + <description value="Use of 2 character query to return no products"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14794"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> + </before> + + <after> + <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> + </after> + + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}} - 1); return ret;" before="searchStorefront" stepKey="getFirstLessThenConfigLetters"/> + + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> + <argument name="phrase" value="$getFirstLessThenConfigLetters"/> + <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> + <argument name="term" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml new file mode 100644 index 0000000000000..b33fe7b48d2b1 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CatalogWidgetSection"> + <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> + <element name="storeViewOption" type="button" selector="//*[@name='store_id']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml similarity index 78% rename from app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml rename to app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml index 66aa4252f4b5f..9b40971611d6f 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml @@ -1,16 +1,12 @@ -<?xml version="1.0" encoding="utf-8"?> +<?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="CatalogWidgetSection"> - <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> - <element name="storeViewOption" type="button" selector="//*[@name='store_id']"/> - </section> - +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="InsertWidgetSection"> <element name="widgetTypeDropDown" type="select" selector="#select_widget_type"/> <element name="conditionsAddButton" type="button" selector=".rule-param.rule-param-new-child"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml similarity index 67% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml index 1dc0b743bdd16..87330318f67dd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="clickViewAndEditCartFromMiniCartActionGroup"> <annotations> <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on the 'View and Edit Cart' link. Validates that the URL is present and correct. PLEASE NOTE: The URL is Hardcoded.</description> @@ -20,13 +19,4 @@ <waitForPageLoad stepKey="waitForPageToLoad"/> <seeInCurrentUrl url="checkout/cart" stepKey="seeInCurrentUrl"/> </actionGroup> - <actionGroup name="StorefrontOpenMiniCartActionGroup"> - <annotations> - <description>Clicks on the Mini Shopping Cart icon in the Storefront Header.</description> - </annotations> - - <waitForElementVisible selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitForElementToBeVisible"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml new file mode 100644 index 0000000000000..2918f547da293 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenMiniCartActionGroup"> + <annotations> + <description>Clicks on the Mini Shopping Cart icon in the Storefront Header.</description> + </annotations> + + <waitForElementVisible selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitForElementToBeVisible"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml new file mode 100644 index 0000000000000..febeaa05be43e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml @@ -0,0 +1,95 @@ +<?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="CheckCheckoutSuccessPageAsGuestTest"> + <annotations> + <features value="Checkout"/> + <stories value="Success page elements are presented for placed order as Guest"/> + <title value="Guest Checkout - elements of success page are presented for placed order as guest"/> + <description value="To be sure that other elements of Success page are presented for placed order as Guest"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16490"/> + <group value="checkout"/> + </annotations> + + <before> + <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + </after> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + + <!--Check register section--> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> + <see selector="{{CheckoutSuccessRegisterSection.registerMessage}}" userInput="You can track your order status by creating an account." stepKey="seeRegisterMessage"/> + <see selector="{{CheckoutSuccessRegisterSection.customerEmail}}" userInput="Email Address: {{CustomerEntityOne.email}}" stepKey="seeCustomerEmail"/> + <seeElement selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="seeVisibleCreateAccountButton"/> + <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> + <seeInCurrentUrl url="{{StorefrontCustomerCreatePage.url}}" stepKey="seeCreateAccountPage"/> + <see userInput="Create New Customer Account" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeCreateAccountPageTitle"/> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection2"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> + + <!--Continue shopping--> + <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> + <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> + <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml similarity index 60% rename from app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml rename to app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml index eb49f53921ea4..a8bdde867a445 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CheckCheckoutSuccessPageAsRegisterCustomerTest"> <annotations> <features value="Checkout"/> @@ -121,7 +120,7 @@ <dontSeeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeInvisiblePrint"/> <resizeWindow width="1360" height="1020" stepKey="maximizeWindowKey1"/> <waitForElementVisible selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="waitVisiblePrint"/> - <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint2" /> + <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint2"/> <!--See print page--> <click selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="clickPrintLink"/> @@ -131,89 +130,4 @@ <seeElement selector="{{StorefrontCustomerOrderViewSection.orderTitle}}" stepKey="seeOrderTitleOnPrint"/> <switchToWindow stepKey="switchToWindow2"/> </test> - <test name="CheckCheckoutSuccessPageAsGuestTest"> - <annotations> - <features value="Checkout"/> - <stories value="Success page elements are presented for placed order as Guest"/> - <title value="Guest Checkout - elements of success page are presented for placed order as guest"/> - <description value="To be sure that other elements of Success page are presented for placed order as Guest"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16490"/> - <group value="checkout"/> - </annotations> - - <before> - <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - </after> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> - - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> - - <!--See success messages--> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> - <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> - - <!--Check register section--> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> - <see selector="{{CheckoutSuccessRegisterSection.registerMessage}}" userInput="You can track your order status by creating an account." stepKey="seeRegisterMessage"/> - <see selector="{{CheckoutSuccessRegisterSection.customerEmail}}" userInput="Email Address: {{CustomerEntityOne.email}}" stepKey="seeCustomerEmail"/> - <seeElement selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="seeVisibleCreateAccountButton"/> - <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> - <seeInCurrentUrl url="{{StorefrontCustomerCreatePage.url}}" stepKey="seeCreateAccountPage"/> - <see userInput="Create New Customer Account" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeCreateAccountPageTitle"/> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection2"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> - - <!--Continue shopping--> - <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> - <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> - <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> - </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index 93bb2acdcacfe..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,453 +0,0 @@ -<?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="EndToEndB2CGuestUserTest"> - <!-- Step 3: User adds products to cart --> - <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to cart --> - <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - - <!-- Add Simple Product 2 to cart --> - <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productCount" value="CONST.two"/> - </actionGroup> - - <!-- Check products in minicart --> - <!-- Check simple product 1 in minicart --> - <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- Check simple product2 in minicart --> - <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check products in cart --> - <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check simple product 1 in cart --> - <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> - <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check simple product 2 in cart --> - <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> - <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - - <!-- Step 6: Check out --> - <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode" /> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check order summary in checkout --> - <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection" /> - <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> - <argument name="subtotal" value="480.00"/> - <argument name="shippingTotal" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check ship to information in checkout --> - <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary" /> - <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check shipping method in checkout --> - <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation" /> - <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> - <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod" /> - </actionGroup> - - <!-- Verify Simple Product 1 is in checkout cart items --> - <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Verify Simple Product 2 is in checkout cart items --> - <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <!-- Step 3: User adds products to cart --> - <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to cart --> - <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - - <!-- Add Simple Product 2 to cart --> - <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productCount" value="CONST.two"/> - </actionGroup> - - <!-- Check products in minicart --> - <!-- Check simple product 1 in minicart --> - <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- Check simple product2 in minicart --> - <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> - <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check products in cart --> - <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check simple product 1 in cart --> - <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> - <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> - <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check simple product 2 in cart --> - <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> - <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> - <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - - <!-- Step 6: Check out --> - <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode" /> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check order summary in checkout --> - <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection" /> - <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> - <argument name="subtotal" value="480.00"/> - <argument name="shippingTotal" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check ship to information in checkout --> - <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary" /> - <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check shipping method in checkout --> - <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation" /> - <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> - <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod" /> - </actionGroup> - - <!-- Verify Simple Product 1 is in checkout cart items --> - <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Verify Simple Product 2 is in checkout cart items --> - <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder" /> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..c9d04b9843d95 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,231 @@ +<?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="EndToEndB2CGuestUserMysqlTest"> + <!-- Step 3: User adds products to cart --> + <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to cart --> + <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Add Simple Product 2 to cart --> + <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productCount" value="CONST.two"/> + </actionGroup> + + <!-- Check products in minicart --> + <!-- Check simple product 1 in minicart --> + <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check simple product2 in minicart --> + <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check products in cart --> + <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check simple product 1 in cart --> + <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check simple product 2 in cart --> + <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + + <!-- Step 6: Check out --> + <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check order summary in checkout --> + <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection"/> + <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> + <argument name="subtotal" value="480.00"/> + <argument name="shippingTotal" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check ship to information in checkout --> + <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary"/> + <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check shipping method in checkout --> + <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation"/> + <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> + <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod"/> + </actionGroup> + + <!-- Verify Simple Product 1 is in checkout cart items --> + <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Verify Simple Product 2 is in checkout cart items --> + <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..8a0da7ab82a82 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,231 @@ +<?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="EndToEndB2CGuestUserTest"> + <!-- Step 3: User adds products to cart --> + <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to cart --> + <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Add Simple Product 2 to cart --> + <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productCount" value="CONST.two"/> + </actionGroup> + + <!-- Check products in minicart --> + <!-- Check simple product 1 in minicart --> + <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check simple product2 in minicart --> + <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check products in cart --> + <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check simple product 1 in cart --> + <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check simple product 2 in cart --> + <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + + <!-- Step 6: Check out --> + <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check order summary in checkout --> + <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection"/> + <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> + <argument name="subtotal" value="480.00"/> + <argument name="shippingTotal" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check ship to information in checkout --> + <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary"/> + <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check shipping method in checkout --> + <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation"/> + <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> + <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod"/> + </actionGroup> + + <!-- Verify Simple Product 1 is in checkout cart items --> + <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Verify Simple Product 2 is in checkout cart items --> + <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml similarity index 67% rename from app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml rename to app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml index 8db1f8801ae1d..7ce7e12a2b704 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartItemsCountDisplayItemsQuantitiesTest"> <annotations> <stories value="Checkout order summary has wrong item count"/> @@ -57,29 +57,4 @@ <argument name="itemsText" value="3 Items in Cart"/> </actionGroup> </test> - <test name="StorefrontCartItemsCountDisplayUniqueItemsTest" extends="StorefrontCartItemsCountDisplayItemsQuantitiesTest"> - <annotations> - <stories value="Checkout order summary has wrong item count"/> - <title value="Checkout order summary has wrong item count - display unique items"/> - <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> - <testCaseId value="MC-18281"/> - <severity value="CRITICAL"/> - <group value="checkout"/> - </annotations> - - <!-- Assert Products Count in Mini Cart --> - <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> - <argument name="productCount" value="2"/> - <argument name="productCountText" value="2 Items in Cart"/> - </actionGroup> - <!-- Assert Products Count on checkout page --> - <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> - <argument name="itemsText" value="2 Items in Cart"/> - </actionGroup> - - <before> - <!--Set Display Cart Summary to display items quantities--> - <magentoCLI command="config:set {{DisplayUniqueItems.path}} {{DisplayUniqueItems.value}}" stepKey="setDisplayCartSummary"/> - </before> - </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml new file mode 100644 index 0000000000000..9fa7f64d38aa1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml @@ -0,0 +1,35 @@ +<?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="StorefrontCartItemsCountDisplayUniqueItemsTest" extends="StorefrontCartItemsCountDisplayItemsQuantitiesTest"> + <annotations> + <stories value="Checkout order summary has wrong item count"/> + <title value="Checkout order summary has wrong item count - display unique items"/> + <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> + <testCaseId value="MC-18281"/> + <severity value="CRITICAL"/> + <group value="checkout"/> + </annotations> + + <!-- Assert Products Count in Mini Cart --> + <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> + <argument name="productCount" value="2"/> + <argument name="productCountText" value="2 Items in Cart"/> + </actionGroup> + <!-- Assert Products Count on checkout page --> + <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> + <argument name="itemsText" value="2 Items in Cart"/> + </actionGroup> + + <before> + <!--Set Display Cart Summary to display items quantities--> + <magentoCLI command="config:set {{DisplayUniqueItems.path}} {{DisplayUniqueItems.value}}" stepKey="setDisplayCartSummary"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml deleted file mode 100644 index 08de5a9e6daa7..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ /dev/null @@ -1,273 +0,0 @@ -<?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="StorefrontCustomerCheckoutTest"> - <annotations> - <features value="Customer Checkout"/> - <stories value="Checkout via Storefront"/> - <title value="Customer Checkout via Storefront"/> - <description value="Should be able to place an order as a customer."/> - <severity value="BLOCKER"/> - <testCaseId value="MC-30274"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteSimpleCategory"/> - <deleteData createDataKey="createCustomer" stepKey="deleteUsCustomer"/> - <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="resetCustomerFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> - </after> - - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="storefrontCustomerLogin"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <waitForPageLoad stepKey="waitForCatalogPageLoad"/> - - <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="product" value="$$createSimpleProduct$$"/> - <argument name="productCount" value="CONST.one"/> - </actionGroup> - - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> - <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRate"/> - <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToReview"/> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder"/> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> - <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> - </actionGroup> - - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="orderNumber"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="addFilterToGridAndOpenOrder"> - <argument name="orderId" value="{$orderNumber}"/> - </actionGroup> - - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="verifyOrderStatus"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Customer" stepKey="verifyAccountInformation"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="$$createCustomer.email$$" stepKey="verifyCustomerEmail"/> - <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyBillingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyShippingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createSimpleProduct.name$$" stepKey="verifyProductName"/> - - <actionGroup ref="OpenEditCustomerFromAdminActionGroup" stepKey="openCustomerEditPage"> - <argument name="customer" value="$$createCustomer$$"/> - </actionGroup> - - <click selector="{{AdminEditCustomerInformationSection.orders}}" stepKey="navigateToOrdersTab"/> - <waitForElementVisible selector="{{AdminEditCustomerOrdersSection.orderGrid}}" stepKey="waitForOrdersGridVisible"/> - <see selector="{{AdminEditCustomerOrdersSection.orderGrid}}" userInput="$$createCustomer.firstname$$ $$createCustomer.lastname$$" stepKey="verifyOrder"/> - </test> - <test name="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest"> - <annotations> - <features value="Checkout"/> - <stories value="Customer checkout"/> - <title value="Customer Checkout with multiple addresses and tax rates"/> - <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> - <testCaseId value="MAGETWO-93109"/> - <severity value="AVERAGE"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="simplecategory"/> - <createData entity="SimpleProduct" stepKey="simpleproduct1"> - <requiredEntity createDataKey="simplecategory"/> - </createData> - <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!--TODO: REMOVE AFTER FIX MC-21717 --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> - <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> - <deleteData createDataKey="multiple_address_customer" stepKey="deleteCustomer"/> - </after> - - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$multiple_address_customer$$" /> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage1"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct1"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart1"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded1"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage1"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity1"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1" /> - - <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> - <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> - <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}" /> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> - <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" /> - <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" /> - <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> - <see stepKey="seeSuccessMessage1" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" /> - - <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage2"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct2"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart2"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded2"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage2"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity2"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2" /> - - <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> - <waitForElementNotVisible selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30" stepKey="waitForShippingMethodLoaderNotVisible"/> - <waitForElementVisible selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" time="30" stepKey="waitForShippingMethodRadioToBeVisible"/> - <waitForPageLoad stepKey="waitForPageLoad23"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForShippingMethodSelect2"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNextOnShippingMethodLoad2"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> - <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" stepKey="seeBillingAddressIsCorrect2" /> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrderButton2"/> - <waitForPageLoad stepKey="waitForOrderSuccessPage2"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="seeSuccessMessage2"/> - </test> - <test name="StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout flow if payment solutions are not available"/> - <title value="Checkout via Customer Checkout with restricted countries for payment"/> - <description value="Should be able to place an order as a Customer with restricted countries for payment."/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-42653"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <magentoCLI command="config:set checkout/options/display_billing_address_on 1" stepKey="setShowBillingAddressOnPaymentPage" /> - <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="allowSpecificValue" /> - <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="specificCountryValue" /> - <createData entity="Simple_US_Customer" stepKey="simpleuscustomer"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="allowSpecificValue" /> - <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="specificCountryValue" /> - <magentoCLI command="config:set checkout/options/display_billing_address_on 0" stepKey="setDisplayBillingAddressOnPaymentMethod" /> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> - </after> - <!-- Login as Customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$simpleuscustomer$$" /> - </actionGroup> - - <!-- Add product to cart --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - - <!-- Go to checkout page --> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromMinicart" /> - - <!-- Select address --> - <click stepKey="selectAddress" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> - <waitForElement stepKey="waitNextButton" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> - <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}" /> - <waitForPageLoad stepKey="waitBillingForm"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> - - <!-- Fill UK Address and verify that payment available and checkout successful --> - <uncheckOption selector="{{StorefrontCheckoutPaymentMethodSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> - <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> - <waitForPageLoad stepKey="waitNewAddressBillingForm"/> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="updateCustomerUKAddress"/> - <argument name="classPrefix" value="[aria-hidden=false]"/> - </actionGroup> - <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="customerSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml new file mode 100644 index 0000000000000..8c58827812d99 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml @@ -0,0 +1,84 @@ +<?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="StorefrontCustomerCheckoutTest"> + <annotations> + <features value="Customer Checkout"/> + <stories value="Checkout via Storefront"/> + <title value="Customer Checkout via Storefront"/> + <description value="Should be able to place an order as a customer."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-30274"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteSimpleCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteUsCustomer"/> + <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="resetCustomerFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="storefrontCustomerLogin"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="CONST.one"/> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRate"/> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToReview"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="orderNumber"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="addFilterToGridAndOpenOrder"> + <argument name="orderId" value="{$orderNumber}"/> + </actionGroup> + + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="verifyOrderStatus"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Customer" stepKey="verifyAccountInformation"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="$$createCustomer.email$$" stepKey="verifyCustomerEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyBillingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyShippingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createSimpleProduct.name$$" stepKey="verifyProductName"/> + + <actionGroup ref="OpenEditCustomerFromAdminActionGroup" stepKey="openCustomerEditPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <click selector="{{AdminEditCustomerInformationSection.orders}}" stepKey="navigateToOrdersTab"/> + <waitForElementVisible selector="{{AdminEditCustomerOrdersSection.orderGrid}}" stepKey="waitForOrdersGridVisible"/> + <see selector="{{AdminEditCustomerOrdersSection.orderGrid}}" userInput="$$createCustomer.firstname$$ $$createCustomer.lastname$$" stepKey="verifyOrder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml new file mode 100644 index 0000000000000..0edc919920103 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.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="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest"> + <annotations> + <features value="Checkout"/> + <stories value="Customer checkout"/> + <title value="Customer Checkout with multiple addresses and tax rates"/> + <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> + <testCaseId value="MAGETWO-93109"/> + <severity value="AVERAGE"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="simplecategory"/> + <createData entity="SimpleProduct" stepKey="simpleproduct1"> + <requiredEntity createDataKey="simplecategory"/> + </createData> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <!--TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> + <deleteData createDataKey="multiple_address_customer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$multiple_address_customer$$"/> + </actionGroup> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage1"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct1"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart1"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded1"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage1"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity1"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1"/> + + <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30"/> + <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}"/> + <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}"/> + <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> + <see stepKey="seeSuccessMessage1" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:"/> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct2"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart2"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded2"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage2"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity2"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + + <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> + <waitForElementNotVisible selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30" stepKey="waitForShippingMethodLoaderNotVisible"/> + <waitForElementVisible selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" time="30" stepKey="waitForShippingMethodRadioToBeVisible"/> + <waitForPageLoad stepKey="waitForPageLoad23"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForShippingMethodSelect2"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNextOnShippingMethodLoad2"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" stepKey="seeBillingAddressIsCorrect2"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrderButton2"/> + <waitForPageLoad stepKey="waitForOrderSuccessPage2"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="seeSuccessMessage2"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..d042a15e3c958 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,82 @@ +<?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="StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout flow if payment solutions are not available"/> + <title value="Checkout via Customer Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Customer with restricted countries for payment."/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-42653"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI command="config:set checkout/options/display_billing_address_on 1" stepKey="setShowBillingAddressOnPaymentPage"/> + <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="allowSpecificValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="specificCountryValue"/> + <createData entity="Simple_US_Customer" stepKey="simpleuscustomer"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="allowSpecificValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="specificCountryValue"/> + <magentoCLI command="config:set checkout/options/display_billing_address_on 0" stepKey="setDisplayBillingAddressOnPaymentMethod"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </after> + <!-- Login as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$simpleuscustomer$$"/> + </actionGroup> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromMinicart"/> + + <!-- Select address --> + <click stepKey="selectAddress" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitNextButton" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}"/> + <waitForPageLoad stepKey="waitBillingForm"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <uncheckOption selector="{{StorefrontCheckoutPaymentMethodSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> + <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> + <waitForPageLoad stepKey="waitNewAddressBillingForm"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="updateCustomerUKAddress"/> + <argument name="classPrefix" value="[aria-hidden=false]"/> + </actionGroup> + <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="customerSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml deleted file mode 100644 index 28bdd1d536d43..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ /dev/null @@ -1,155 +0,0 @@ -<?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="StorefrontGuestCheckoutTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout via Guest Checkout"/> - <title value="Guest Checkout - guest should be able to place an order"/> - <description value="Should be able to place an order as a Guest"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-12825"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - </after> - - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeAddress"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> - <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderBillingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderShippingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createProduct.name$$" stepKey="seeAdminOrderProduct"/> - </test> - <test name="StorefrontGuestCheckoutWithSidebarDisabledTest" extends="StorefrontGuestCheckoutTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout via Guest Checkout"/> - <title value="Guest Checkout when Cart sidebar disabled"/> - <description value="Should be able to place an order as a Guest when Cart sidebar is disabled"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16587"/> - <group value="checkout"/> - </annotations> - <before> - <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0" /> - </before> - <after> - <magentoCLI stepKey="enableSidebar" command="config:set checkout/sidebar/display 1" /> - </after> - <remove keyForRemoval="guestGoToCheckoutFromMinicart" /> - <actionGroup ref="GoToCheckoutFromCartActionGroup" stepKey="guestGoToCheckoutFromCart" after="seeCartQuantity" /> - </test> - <test name="StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout flow if payment solutions are not available"/> - <title value="Checkout via Guest Checkout with restricted countries for payment"/> - <description value="Should be able to place an order as a Guest with restricted countries for payment."/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-42653"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 1" /> - <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry GB" /> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 0" /> - <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry ''" /> - </after> - - <!-- Add product to cart --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - - <!-- Go to checkout page --> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> - - <!-- Fill US Address and verify that no payment available --> - <actionGroup ref="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - <argument name="paymentMethod" value="Check / Money order"/> - </actionGroup> - - <!-- Fill UK Address and verify that payment available and checkout successful --> - <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> - <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="UK_Not_Default_Address" /> - </actionGroup> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml new file mode 100644 index 0000000000000..05a136e4c312a --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml @@ -0,0 +1,76 @@ +<?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="StorefrontGuestCheckoutTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> + <title value="Guest Checkout - guest should be able to place an order"/> + <description value="Should be able to place an order as a Guest"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-12825"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeAddress"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderBillingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderShippingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createProduct.name$$" stepKey="seeAdminOrderProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..0520accdd4b84 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,69 @@ +<?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="StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout flow if payment solutions are not available"/> + <title value="Checkout via Guest Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Guest with restricted countries for payment."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-42653"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 1"/> + <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry GB"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 0"/> + <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry ''"/> + </after> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart"/> + + <!-- Fill US Address and verify that no payment available --> + <actionGroup ref="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="UK_Not_Default_Address"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml new file mode 100644 index 0000000000000..74ad9985e72f2 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml @@ -0,0 +1,29 @@ +<?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="StorefrontGuestCheckoutWithSidebarDisabledTest" extends="StorefrontGuestCheckoutTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> + <title value="Guest Checkout when Cart sidebar disabled"/> + <description value="Should be able to place an order as a Guest when Cart sidebar is disabled"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16587"/> + <group value="checkout"/> + </annotations> + <before> + <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0"/> + </before> + <after> + <magentoCLI stepKey="enableSidebar" command="config:set checkout/sidebar/display 1"/> + </after> + <remove keyForRemoval="guestGoToCheckoutFromMinicart"/> + <actionGroup ref="GoToCheckoutFromCartActionGroup" stepKey="guestGoToCheckoutFromCart" after="seeCartQuantity"/> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml similarity index 71% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml index 9f00f208b11e8..d0050356d382b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml @@ -5,24 +5,9 @@ * 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="searchBlockOnGridPage"> - <annotations> - <description>Searches the Admin CMS Blocks grid based on the provided Block.</description> - </annotations> - <arguments> - <argument name="Block" defaultValue=""/> - </arguments> - - <fillField selector="//input[@name='chooser_identifier']" userInput="{{Block.identifier}}" stepKey="fillEntityIdentifier"/> - <click selector="//div[@class='modal-inner-wrap']//button[@title='Search']" stepKey="clickSearchBtn"/> - <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish2"/> - <waitForElementVisible selector="{{WidgetSection.BlockPage(Block.identifier)}}" stepKey="waitForBlockTitle"/> - </actionGroup> - - <actionGroup name ="deleteBlock"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="deleteBlock"> <annotations> <description>Goes to the Admin CMS Blocks page. Filters the grid based on the provided Block. Deletes the Block via the grid.</description> </annotations> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml new file mode 100644 index 0000000000000..609a100fa380a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.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="searchBlockOnGridPage"> + <annotations> + <description>Searches the Admin CMS Blocks grid based on the provided Block.</description> + </annotations> + <arguments> + <argument name="Block" defaultValue=""/> + </arguments> + + <fillField selector="//input[@name='chooser_identifier']" userInput="{{Block.identifier}}" stepKey="fillEntityIdentifier"/> + <click selector="//div[@class='modal-inner-wrap']//button[@title='Search']" stepKey="clickSearchBtn"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish2"/> + <waitForElementVisible selector="{{WidgetSection.BlockPage(Block.identifier)}}" stepKey="waitForBlockTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml deleted file mode 100644 index 445279a8b1403..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?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="CmsNewBlockBlockActionsSection"> - <element name="savePage" type="button" selector="#save-button" timeout="30"/> - </section> - <section name="BlockNewPagePageActionsSection"> - <element name="saveBlock" type="button" selector="#save-button" timeout="10"/> - <element name="saveAndContinueEdit" type="button" selector="#save-button" timeout="10"/> - <element name="saveAndDuplicate" type="button" selector="#save_and_duplicate" timeout="10"/> - <element name="saveAndClose" type="button" selector="#save_and_close" timeout="10"/> - <element name="expandSplitButton" type="button" selector="//button[@data-ui-id='save-button-dropdown']" timeout="10"/> - <element name="back" type="button" selector="#back"/> - </section> - <section name="BlockWYSIWYGSection"> - <element name="ShowHideBtn" type="button" selector="#togglecms_block_form_content"/> - </section> - <section name="BlockContentSection"> - <element name="TextArea" type="input" selector="#cms_block_form_content"/> - <element name="image" type="file" selector="#tinymce img"/> - <element name="contentIframe" type="iframe" selector="cms_block_form_content_ifr"/> - </section> - <section name="CmsBlockBlockActionSection"> - <element name="deleteBlock" type="button" selector="#delete" timeout="30"/> - <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml new file mode 100644 index 0000000000000..1d5e8541dd497 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml @@ -0,0 +1,15 @@ +<?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="BlockContentSection"> + <element name="TextArea" type="input" selector="#cms_block_form_content"/> + <element name="image" type="file" selector="#tinymce img"/> + <element name="contentIframe" type="iframe" selector="cms_block_form_content_ifr"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml new file mode 100644 index 0000000000000..e9c96ba8a8b2e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml @@ -0,0 +1,18 @@ +<?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="BlockNewPagePageActionsSection"> + <element name="saveBlock" type="button" selector="#save-button" timeout="10"/> + <element name="saveAndContinueEdit" type="button" selector="#save-button" timeout="10"/> + <element name="saveAndDuplicate" type="button" selector="#save_and_duplicate" timeout="10"/> + <element name="saveAndClose" type="button" selector="#save_and_close" timeout="10"/> + <element name="expandSplitButton" type="button" selector="//button[@data-ui-id='save-button-dropdown']" timeout="10"/> + <element name="back" type="button" selector="#back"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml new file mode 100644 index 0000000000000..bb213969c9dab --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?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="BlockWYSIWYGSection"> + <element name="ShowHideBtn" type="button" selector="#togglecms_block_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml new file mode 100644 index 0000000000000..24a235bee2f78 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsBlockBlockActionSection"> + <element name="deleteBlock" type="button" selector="#delete" timeout="30"/> + <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml new file mode 100644 index 0000000000000..a3377488c04e0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml @@ -0,0 +1,13 @@ +<?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="CmsNewBlockBlockActionsSection"> + <element name="savePage" type="button" selector="#save-button" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml similarity index 66% rename from app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml rename to app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml index 79fc3bac0fb25..9b358dcb25769 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml @@ -5,14 +5,8 @@ * 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="CmsNewBlockBlockBasicFieldsSection"> - <element name="title" type="input" selector="input[name=title]"/> - <element name="identifier" type="input" selector="input[name=identifier]"/> - <element name="content_textarea" type="input" selector="#cms_block_form_content"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BlockNewPageBasicFieldsSection"> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> <element name="blockTitle" type="input" selector="input[name=title]"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml new file mode 100644 index 0000000000000..06fd4c17c274e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml @@ -0,0 +1,15 @@ +<?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="CmsNewBlockBlockBasicFieldsSection"> + <element name="title" type="input" selector="input[name=title]"/> + <element name="identifier" type="input" selector="input[name=identifier]"/> + <element name="content_textarea" type="input" selector="#cms_block_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml new file mode 100644 index 0000000000000..cf0f6797ce423 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml new file mode 100644 index 0000000000000..6b0569adb878e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml @@ -0,0 +1,15 @@ +<?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="CmsNewPagePageContentSection"> + <element name="header" type="button" selector="div[data-index=content]"/> + <element name="contentHeading" type="input" selector="input[name=content_heading]"/> + <element name="content" type="input" selector="#cms_page_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml similarity index 59% rename from app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml rename to app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml index 05a125b9cc6a8..d88838563ec0f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml @@ -5,14 +5,8 @@ * 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="CmsNewPagePageContentSection"> - <element name="header" type="button" selector="div[data-index=content]"/> - <element name="contentHeading" type="input" selector="input[name=content_heading]"/> - <element name="content" type="input" selector="#cms_page_form_content"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsWYSIWYGSection"> <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> <element name="ShowHideBtn" type="button" selector="#togglecms_page_form_content"/> @@ -22,8 +16,4 @@ <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> <element name="ImageAlt" type="text" selector="//img[contains(@alt,'{{var1}}')]" parameterized="true"/> </section> - <section name="CmsDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> - </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml deleted file mode 100644 index aebed8c9efec0..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?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="TinyMCESection"> - <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> - <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> - <element name="TinyMCE4" type="text" selector=".mce-branding" /> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> - <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> - <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> - <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> - <element name="WidgetButton" type="button" selector="span[class*='magento-widget mceNonEditable']"/> - <element name="EditorContent" type="input" selector="#tinymce"/> - </section> - <section name="MediaGallerySection"> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="browseForImage" type="button" selector="//*[@id='srcbrowser']"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> - <element name="lastImageOrImageCopy" type="text" selector="(//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')])[last()]" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="ImageDescriptionTinyMCE3" type="input" selector="#alt" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="insertBtn" type="button" selector="#insert"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteFolder" type="button" selector="#delete_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon" /> - <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - <element name="imageBlockByName" type="block" selector="//div[@data-row='file'][contains(., '{{imageName}}')]" parameterized="true"/> - </section> - <section name="VariableSection"> - <element name="InsertWidget" type="button" selector="#insert_variable"/> - <element name="InsertVariableBtnEnabled" type="button" selector="//button[@id='insert_variable' and not(contains(@class,'disabled'))]"/> - <element name="InsertVariableBtnDisabled" type="button" selector="//button[@id='insert_variable' and contains(@class,'disabled')]"/> - <element name="CancelBtnEnabled" type="button" selector="//button[@class='action-scalable cancel' and not(contains(@class,'disabled'))]"/> - <element name="Close" type="button" selector="#close"/> - <element name="SearchTxtbox" type="input" selector="input[placeholder='Search by keyword']"/> - <element name="ColName" type="text" selector="//table[@class='data-grid data-grid-draggable']/thead/tr/th/span[text()='{{var1}}']" parameterized="true"/> - <element name="Radio" type="input" selector="//input[@type='radio' and contains(@value, '{{var1}}')]" parameterized="true"/> - <element name="VariableRadio" type="input" selector="//div[text()='{{var1}}']/parent::td//preceding-sibling::td/input[@type='radio']" parameterized="true"/> - <element name="VariableInAscSort" type="input" selector="#variable"/> - <element name="VariableInDescSort" type="input" selector="#variable"/> - <element name="Type" type="input" selector="#value"/> - <element name="Code" type="input" selector="#code"/> - <element name="searchResult" type="text" selector="//table/tbody/tr//td/div[text()='{{var1}}']" parameterized="true" /> - <element name="VariableTitle" type="text" selector="//h1[contains(text(), 'Insert Variable')]"/> - </section> - <section name="WidgetSection"> - <element name="InsertWidgetTitle" type="text" selector="//h1[contains(text(),'Insert Widget')]"/> - <element name="DisplayType" type="select" selector="select[name='parameters[display_type]']"/> - <element name="SelectCategoryTitle" type="text" selector="//h1[contains(text(),'Select Category')]"/> - <element name="SelectProductTitle" type="text" selector="//h1[contains(text(),'Select Product')]"/> - <element name="SelectPageTitle" type="text" selector="//h1[contains(text(),'Select Page')]"/> - <element name="SelectBlockTitle" type="text" selector="//h1[contains(text(),'Select Block')]"/> - <element name="InsertWidget" type="button" selector="#insert_button" timeout="30"/> - <element name="InsertWidgetBtnDisabled" type="button" selector="//button[@id='insert_button' and contains(@class,'disabled')]"/> - <element name="InsertWidgetBtnEnabled" type="button" selector="//button[@id='insert_button' and not(contains(@class,'disabled'))]"/> - <element name="CancelBtnEnabled" type="button" selector="//button[@id='reset' and not(contains(@class,'disabled'))]"/> - <element name="Close" type="button" selector="#close"/> - <element name="WidgetType" type="button" selector="#select_widget_type"/> - <element name="WidgetTemplate" type="button" selector="select[name='parameters[template]']"/> - <element name="BtnChooser" type="button" selector=".btn-chooser"/> - <element name="CMSPage" type="text" selector="//td[contains(text(),'Home page')]"/> - <element name="BlockPage" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="PreCreateCategory" type="text" selector=" //span[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="PreCreateProduct" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="NoOfProductToDisplay" type="input" selector="input[data-ui-id='wysiwyg-widget-options-fieldset-element-text-parameters-products-count']"/> - <element name="AddParam" type="button" selector=".rule-param-add"/> - <element name="ConditionsDropdown" type="select" selector="#conditions__1__new_child"/> - <element name="RuleParam" type="button" selector="//a[text()='...']"/> - <element name="RuleParam1" type="button" selector="(//span[@class='rule-param']//a)[{{var}}]" parameterized="true"/> - <element name="RuleParamSelect" type="select" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//select" parameterized="true"/> - <element name="RuleParamInput" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//input" parameterized="true"/> - <element name="RuleParamLabel" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//a" parameterized="true"/> - <element name="Chooser" type="button" selector="//img[@title='Open Chooser']"/> - <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> - <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']" /> - <element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/> - <!--Compare on Storefront--> - <element name="ProductName" type="text" selector=".product.name.product-item-name" /> - <element name="CompareBtn" type="button" selector=".action.tocompare"/> - <element name="ClearCompare" type="button" selector="#compare-clear-all"/> - <element name="AcceptClear" type="button" selector=".action-primary.action-accept" /> - <element name="ChooserName" type="input" selector="input[name='chooser_name']" /> - <element name="SelectPageButton" type="button" selector="//button[@title='Select Page...']"/> - <element name="SelectPageFilterInput" type="input" selector="input.admin__control-text[name='{{filterName}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml new file mode 100644 index 0000000000000..112335e726270 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml @@ -0,0 +1,39 @@ +<?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="MediaGallerySection"> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="browseForImage" type="button" selector="//*[@id='srcbrowser']"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> + <element name="lastImageOrImageCopy" type="text" selector="(//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')])[last()]" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="ImageDescriptionTinyMCE3" type="input" selector="#alt"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="insertBtn" type="button" selector="#insert"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteFolder" type="button" selector="#delete_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon"/> + <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + <element name="imageBlockByName" type="block" selector="//div[@data-row='file'][contains(., '{{imageName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml new file mode 100644 index 0000000000000..e3e6ae9cffc02 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml @@ -0,0 +1,36 @@ +<?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="TinyMCESection"> + <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> + <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> + <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> + <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> + <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="WidgetButton" type="button" selector="span[class*='magento-widget mceNonEditable']"/> + <element name="EditorContent" type="input" selector="#tinymce"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml new file mode 100644 index 0000000000000..b63a355bc797e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml @@ -0,0 +1,27 @@ +<?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="VariableSection"> + <element name="InsertWidget" type="button" selector="#insert_variable"/> + <element name="InsertVariableBtnEnabled" type="button" selector="//button[@id='insert_variable' and not(contains(@class,'disabled'))]"/> + <element name="InsertVariableBtnDisabled" type="button" selector="//button[@id='insert_variable' and contains(@class,'disabled')]"/> + <element name="CancelBtnEnabled" type="button" selector="//button[@class='action-scalable cancel' and not(contains(@class,'disabled'))]"/> + <element name="Close" type="button" selector="#close"/> + <element name="SearchTxtbox" type="input" selector="input[placeholder='Search by keyword']"/> + <element name="ColName" type="text" selector="//table[@class='data-grid data-grid-draggable']/thead/tr/th/span[text()='{{var1}}']" parameterized="true"/> + <element name="Radio" type="input" selector="//input[@type='radio' and contains(@value, '{{var1}}')]" parameterized="true"/> + <element name="VariableRadio" type="input" selector="//div[text()='{{var1}}']/parent::td//preceding-sibling::td/input[@type='radio']" parameterized="true"/> + <element name="VariableInAscSort" type="input" selector="#variable"/> + <element name="VariableInDescSort" type="input" selector="#variable"/> + <element name="Type" type="input" selector="#value"/> + <element name="Code" type="input" selector="#code"/> + <element name="searchResult" type="text" selector="//table/tbody/tr//td/div[text()='{{var1}}']" parameterized="true"/> + <element name="VariableTitle" type="text" selector="//h1[contains(text(), 'Insert Variable')]"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml new file mode 100644 index 0000000000000..1869a6544c3d3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml @@ -0,0 +1,50 @@ +<?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="WidgetSection"> + <element name="InsertWidgetTitle" type="text" selector="//h1[contains(text(),'Insert Widget')]"/> + <element name="DisplayType" type="select" selector="select[name='parameters[display_type]']"/> + <element name="SelectCategoryTitle" type="text" selector="//h1[contains(text(),'Select Category')]"/> + <element name="SelectProductTitle" type="text" selector="//h1[contains(text(),'Select Product')]"/> + <element name="SelectPageTitle" type="text" selector="//h1[contains(text(),'Select Page')]"/> + <element name="SelectBlockTitle" type="text" selector="//h1[contains(text(),'Select Block')]"/> + <element name="InsertWidget" type="button" selector="#insert_button" timeout="30"/> + <element name="InsertWidgetBtnDisabled" type="button" selector="//button[@id='insert_button' and contains(@class,'disabled')]"/> + <element name="InsertWidgetBtnEnabled" type="button" selector="//button[@id='insert_button' and not(contains(@class,'disabled'))]"/> + <element name="CancelBtnEnabled" type="button" selector="//button[@id='reset' and not(contains(@class,'disabled'))]"/> + <element name="Close" type="button" selector="#close"/> + <element name="WidgetType" type="button" selector="#select_widget_type"/> + <element name="WidgetTemplate" type="button" selector="select[name='parameters[template]']"/> + <element name="BtnChooser" type="button" selector=".btn-chooser"/> + <element name="CMSPage" type="text" selector="//td[contains(text(),'Home page')]"/> + <element name="BlockPage" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="PreCreateCategory" type="text" selector=" //span[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="PreCreateProduct" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="NoOfProductToDisplay" type="input" selector="input[data-ui-id='wysiwyg-widget-options-fieldset-element-text-parameters-products-count']"/> + <element name="AddParam" type="button" selector=".rule-param-add"/> + <element name="ConditionsDropdown" type="select" selector="#conditions__1__new_child"/> + <element name="RuleParam" type="button" selector="//a[text()='...']"/> + <element name="RuleParam1" type="button" selector="(//span[@class='rule-param']//a)[{{var}}]" parameterized="true"/> + <element name="RuleParamSelect" type="select" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//select" parameterized="true"/> + <element name="RuleParamInput" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//input" parameterized="true"/> + <element name="RuleParamLabel" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//a" parameterized="true"/> + <element name="Chooser" type="button" selector="//img[@title='Open Chooser']"/> + <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> + <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']"/> + <element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/> + <!--Compare on Storefront--> + <element name="ProductName" type="text" selector=".product.name.product-item-name"/> + <element name="CompareBtn" type="button" selector=".action.tocompare"/> + <element name="ClearCompare" type="button" selector="#compare-clear-all"/> + <element name="AcceptClear" type="button" selector=".action-primary.action-accept"/> + <element name="ChooserName" type="input" selector="input[name='chooser_name']"/> + <element name="SelectPageButton" type="button" selector="//button[@title='Select Page...']"/> + <element name="SelectPageFilterInput" type="input" selector="input.admin__control-text[name='{{filterName}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml deleted file mode 100644 index a1ee5348d094c..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminConfigPage" url="admin/system_config/" area="admin" module="Magento_Config"> - <section name="AdminConfigSection"/> - </page> - <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> - <section name="ContentManagementSection"/> - </page> - <page name="CatalogConfigPage" url="admin/system_config/edit/section/catalog/" area="admin" module="Magento_Config"> - <section name="ContentManagementSection"/> - </page> - <page name="AdminSalesTaxClassPage" url="admin/system_config/edit/section/tax/" area="admin" module="Magento_Config"> - <section name="SalesTaxClassSection"/> - </page> - <page name="AdminConfigGeneralPage" url="admin/system_config/edit/section/general/" area="admin" module="Magento_Config"> - <section name="GeneralSection"/> - </page> - <page name="AdminConfigDeveloperPage" url="admin/system_config/edit/section/dev/" area="admin" module="Magento_Config"> - <section name="AdminConfigSection" /> - </page> - <page name="AdminConfigAdvancedAdmin" url="admin/system_config/edit/section/admin/" area="admin" module="Magento_Config"> - <section name="AdvanceAdminSection"/> - </page> -</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml new file mode 100644 index 0000000000000..5d1b92898753b --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigAdvancedAdmin" url="admin/system_config/edit/section/admin/" area="admin" module="Magento_Config"> + <section name="AdvanceAdminSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml new file mode 100644 index 0000000000000..36370eba6becc --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigDeveloperPage" url="admin/system_config/edit/section/dev/" area="admin" module="Magento_Config"> + <section name="AdminConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml new file mode 100644 index 0000000000000..08b7519826b13 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigGeneralPage" url="admin/system_config/edit/section/general/" area="admin" module="Magento_Config"> + <section name="GeneralSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml new file mode 100644 index 0000000000000..1c110681329df --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigPage" url="admin/system_config/" area="admin" module="Magento_Config"> + <section name="AdminConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml new file mode 100644 index 0000000000000..8e367ecc3ae04 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> + <section name="ContentManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml new file mode 100644 index 0000000000000..74fcfbc500231 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSalesTaxClassPage" url="admin/system_config/edit/section/tax/" area="admin" module="Magento_Config"> + <section name="SalesTaxClassSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml new file mode 100644 index 0000000000000..96cb199b51355 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CatalogConfigPage" url="admin/system_config/edit/section/catalog/" area="admin" module="Magento_Config"> + <section name="ContentManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml similarity index 76% rename from app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml rename to app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml index e024d9e5b2e47..851157c5d03c0 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CatalogSection"> <element name="storefront" type="select" selector="#catalog_frontend-head"/> <element name="CheckIfTabExpand" type="button" selector="#catalog_frontend-head:not(.open)"/> @@ -25,10 +24,4 @@ <element name="GenerateUrlRewrites" type="select" selector="#catalog_seo_generate_category_product_rewrites"/> <element name="successMessage" type="text" selector="#messages"/> </section> - <section name="GenerateUrlRewritesConfirm"> - <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> - <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> - <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> - <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> - </section> </sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml new file mode 100644 index 0000000000000..504a0f0f2f561 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.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="GenerateUrlRewritesConfirm"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml deleted file mode 100644 index 9bce5065317f8..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?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="ContentManagementSection"> - <element name="WYSIWYGOptions" type="button" selector="#cms_wysiwyg-head" timeout="60"/> - <element name="CheckIfTabExpand" type="button" selector="#cms_wysiwyg-head:not(.open)"/> - <element name="EnableSystemValue" type="button" selector="#cms_wysiwyg_enabled_inherit"/> - <element name="EnableWYSIWYG" type="button" selector="#cms_wysiwyg_enabled"/> - <element name="SwitcherSystemValue" type="button" selector="#cms_wysiwyg_editor_inherit" timeout="60"/> - <element name="Switcher" type="button" selector="#cms_wysiwyg_editor" /> - <element name="StaticURL" type="button" selector="#cms_wysiwyg_use_static_urls_in_catalog" /> - <element name="Save" type="button" selector="#save" timeout="30"/> - <element name="StoreConfigurationPageSuccessMessage" type="text" selector="#messages [data-ui-id='messages-message-success']"/> - </section> - <section name="WebSection"> - <element name="DefaultLayoutsTab" type="button" selector="#web_default_layouts-head"/> - <element name="CheckIfTabExpand" type="button" selector="#web_default_layouts-head:not(.open)"/> - <element name="UrlOptionsTab" type="button" selector="#web_url-head"/> - <element name="CheckIfUrlOptionsTabExpand" type="button" selector="#web_url-head:not(.open)"/> - </section> - <section name="DefaultLayoutsSection"> - <element name="productLayout" type="select" selector="#web_default_layouts_default_product_layout"/> - <element name="categoryLayout" type="select" selector="#web_default_layouts_default_category_layout"/> - <element name="pageLayout" type="select" selector="#web_default_layouts_default_cms_layout"/> - </section> - <section name="UrlOptionsSection"> - <element name="addStoreCodeToUrl" type="select" selector="#web_url_use_store"/> - <element name="systemValueForStoreCode" type="checkbox" selector="#web_url_use_store_inherit"/> - </section> - <section name="CountryOptionsSection"> - <element name="countryOptions" type="button" selector="#general_country-head"/> - <element name="countryOptionsOpen" type="button" selector="#general_country-head.open"/> - <element name="topDestinations" type="select" selector="#general_country_destinations"/> - </section> - <section name="StateOptionsSection"> - <element name="stateOptions" type="button" selector="#general_region-head"/> - <element name="countriesWithRequiredRegions" type="select" selector="#general_region_state_required"/> - <element name="allowToChooseState" type="select" selector="general_region_display_all"/> - </section> -</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml new file mode 100644 index 0000000000000..80b2c7e0c4645 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.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="ContentManagementSection"> + <element name="WYSIWYGOptions" type="button" selector="#cms_wysiwyg-head" timeout="60"/> + <element name="CheckIfTabExpand" type="button" selector="#cms_wysiwyg-head:not(.open)"/> + <element name="EnableSystemValue" type="button" selector="#cms_wysiwyg_enabled_inherit"/> + <element name="EnableWYSIWYG" type="button" selector="#cms_wysiwyg_enabled"/> + <element name="SwitcherSystemValue" type="button" selector="#cms_wysiwyg_editor_inherit" timeout="60"/> + <element name="Switcher" type="button" selector="#cms_wysiwyg_editor"/> + <element name="StaticURL" type="button" selector="#cms_wysiwyg_use_static_urls_in_catalog"/> + <element name="Save" type="button" selector="#save" timeout="30"/> + <element name="StoreConfigurationPageSuccessMessage" type="text" selector="#messages [data-ui-id='messages-message-success']"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml new file mode 100644 index 0000000000000..77d5af706247e --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml @@ -0,0 +1,15 @@ +<?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="CountryOptionsSection"> + <element name="countryOptions" type="button" selector="#general_country-head"/> + <element name="countryOptionsOpen" type="button" selector="#general_country-head.open"/> + <element name="topDestinations" type="select" selector="#general_country_destinations"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml new file mode 100644 index 0000000000000..25ec63e1354f1 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml @@ -0,0 +1,15 @@ +<?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="DefaultLayoutsSection"> + <element name="productLayout" type="select" selector="#web_default_layouts_default_product_layout"/> + <element name="categoryLayout" type="select" selector="#web_default_layouts_default_category_layout"/> + <element name="pageLayout" type="select" selector="#web_default_layouts_default_cms_layout"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml new file mode 100644 index 0000000000000..99a76a446aaa4 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml @@ -0,0 +1,15 @@ +<?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="StateOptionsSection"> + <element name="stateOptions" type="button" selector="#general_region-head"/> + <element name="countriesWithRequiredRegions" type="select" selector="#general_region_state_required"/> + <element name="allowToChooseState" type="select" selector="general_region_display_all"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml new file mode 100644 index 0000000000000..9aebec8123b57 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="UrlOptionsSection"> + <element name="addStoreCodeToUrl" type="select" selector="#web_url_use_store"/> + <element name="systemValueForStoreCode" type="checkbox" selector="#web_url_use_store_inherit"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml new file mode 100644 index 0000000000000..56ea1f844d002 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.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="WebSection"> + <element name="DefaultLayoutsTab" type="button" selector="#web_default_layouts-head"/> + <element name="CheckIfTabExpand" type="button" selector="#web_default_layouts-head:not(.open)"/> + <element name="UrlOptionsTab" type="button" selector="#web_url-head"/> + <element name="CheckIfUrlOptionsTabExpand" type="button" selector="#web_url-head:not(.open)"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml new file mode 100644 index 0000000000000..769ba76dedb76 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.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="AdminConfigurableProductFormSection"> + <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> + <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> + <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> + <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> + <element name="currentAttribute" type="text" selector="//fieldset[@class='admin__fieldset']/div[contains(@class, 'admin__field _disabled')]//span"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml new file mode 100644 index 0000000000000..5d4e30561ff97 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml @@ -0,0 +1,13 @@ +<?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="AdminConfigurableProductSelectAttributesSlideOut"> + <element name="grid" type="button" selector=".admin__data-grid-wrap tbody"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml similarity index 77% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml index 336f95aa55576..320b3d575f3c6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormConfigurationsSection"> <element name="sectionHeader" type="text" selector=".admin__collapsible-block-wrapper[data-index='configurable']"/> <element name="createdConfigurationsBlock" type="text" selector="div.admin__field.admin__field-wide"/> @@ -34,26 +33,13 @@ <element name="confProductWeightCell" type="input" selector="//*[.='Attributes']/ancestor::tr//span[contains(text(), '{{var}}')]/ancestor::tr/td[@data-index='price_weight']//input" parameterized="true"/> <element name="confProductOptionStatusCell" type="text" selector="//*[.='Attributes']/ancestor::tr//span[contains(text(), '{{productName}}')]/ancestor::tr/td[@data-index='status']" parameterized="true"/> <element name="confProductSkuMessage" type="text" selector="//*[@name='configurable-matrix[{{arg}}][sku]']/following-sibling::label" parameterized="true"/> - <element name="variationsSkuInputByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) input[name*='sku']" type="input" parameterized="true"/> - <element name="variationsSkuInputErrorByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) .admin__field-error" type="text" parameterized="true"/> + <element name="variationsSkuInputByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) input[name*='sku']" type="input" parameterized="true"/> + <element name="variationsSkuInputErrorByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) .admin__field-error" type="text" parameterized="true"/> <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="fileUploaderInput" type="file" selector="//input[@type='file' and @class='file-uploader-input']" /> + <element name="fileUploaderInput" type="file" selector="//input[@type='file' and @class='file-uploader-input']"/> <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]']"/> - <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> - <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> - <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> - <element name="currentAttribute" type="text" selector="//fieldset[@class='admin__fieldset']/div[contains(@class, 'admin__field _disabled')]//span"/> - </section> - <section name="AdminConfigurableProductSelectAttributesSlideOut"> - <element name="grid" type="button" selector=".admin__data-grid-wrap tbody"/> - </section> - <section name="StorefrontConfigurableProductPage"> - <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> - </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml new file mode 100644 index 0000000000000..2bf61604df605 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml @@ -0,0 +1,13 @@ +<?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="StorefrontConfigurableProductPage"> + <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml deleted file mode 100644 index ea5638f6816c9..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?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="CatalogProductsSection"> - <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> - <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> - <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> - <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/> - <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/> - <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> - <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> - <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> - <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> - <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> - <element name="resetFilter" type="button" selector="//span[contains(text(), 'Reset Filter')]"/> - </section> - - <section name="ConfigurableProductSection"> - <element name="addProductItem" type="button" selector="//*[@id='add_new_product']/button[2]"/> - <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/> - <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/> - <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/> - <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/> - <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/> - </section> - - <section name="NewProduct"> - <element name="productName" type="input" selector="//input[@name='product[name]']"/> - <element name="price" type="input" selector="//input[@name='product[price]']"/> - <element name="weight" type="input" selector="//input[@name='product[weight]']"/> - <element name="createConfigurationButton" type="button" selector="//*[contains(text(),'Create Configurations')]"/> - <element name="createNewAttributeButton" type="button" selector="//*[contains(text(),'Create New Attribute')]"/> - <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> - <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> - <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> - <element name="adminFieldRed" type="input" selector="//input[@name='option[value][option_0][0]']"/> - <element name="defaultStoreViewFieldRed" type="input" selector="//input[@name='option[value][option_0][1]']"/> - <element name="adminFieldBlue" type="input" selector="//input[@name='option[value][option_1][0]']"/> - <element name="defaultStoreViewFieldBlue" type="input" selector="//input[@name='option[value][option_1][1]']"/> - <element name="adminFieldYellow" type="input" selector="//input[@name='option[value][option_2][0]']"/> - <element name="defaultStoreViewFieldYellow" type="input" selector="//input[@name='option[value][option_2][1]']"/> - <element name="adminFieldGreen" type="input" selector="//input[@name='option[value][option_3][0]']"/> - <element name="defaultStoreViewFieldGreen" type="input" selector="//input[@name='option[value][option_3][1]']"/> - <element name="adminFieldBlack" type="input" selector="//input[@name='option[value][option_4][0]']"/> - <element name="defaultStoreViewFieldBlack" type="input" selector="//input[@name='option[value][option_4][1]']"/> - <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> - <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> - <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> - </section> - - <section name="CreateProductConfigurations"> - <element name="checkboxRed" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> - <element name="checkboxBlue" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> - <element name="checkboxYellow" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> - <element name="checkboxGreen" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> - <element name="checkboxBlack" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> - <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> - </section> -</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml new file mode 100644 index 0000000000000..fb71d6cbda2a8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml @@ -0,0 +1,23 @@ +<?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="CatalogProductsSection"> + <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> + <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> + <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> + <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/> + <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/> + <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> + <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> + <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> + <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> + <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> + <element name="resetFilter" type="button" selector="//span[contains(text(), 'Reset Filter')]"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml new file mode 100644 index 0000000000000..8099f30941f7d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml @@ -0,0 +1,18 @@ +<?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="ConfigurableProductSection"> + <element name="addProductItem" type="button" selector="//*[@id='add_new_product']/button[2]"/> + <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/> + <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/> + <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/> + <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/> + <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml new file mode 100644 index 0000000000000..02ef25dc71f6a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml @@ -0,0 +1,18 @@ +<?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="CreateProductConfigurations"> + <element name="checkboxRed" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> + <element name="checkboxBlue" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> + <element name="checkboxYellow" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> + <element name="checkboxGreen" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> + <element name="checkboxBlack" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> + <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml new file mode 100644 index 0000000000000..cab08666f8701 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml @@ -0,0 +1,33 @@ +<?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="NewProduct"> + <element name="productName" type="input" selector="//input[@name='product[name]']"/> + <element name="price" type="input" selector="//input[@name='product[price]']"/> + <element name="weight" type="input" selector="//input[@name='product[weight]']"/> + <element name="createConfigurationButton" type="button" selector="//*[contains(text(),'Create Configurations')]"/> + <element name="createNewAttributeButton" type="button" selector="//*[contains(text(),'Create New Attribute')]"/> + <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> + <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> + <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> + <element name="adminFieldRed" type="input" selector="//input[@name='option[value][option_0][0]']"/> + <element name="defaultStoreViewFieldRed" type="input" selector="//input[@name='option[value][option_0][1]']"/> + <element name="adminFieldBlue" type="input" selector="//input[@name='option[value][option_1][0]']"/> + <element name="defaultStoreViewFieldBlue" type="input" selector="//input[@name='option[value][option_1][1]']"/> + <element name="adminFieldYellow" type="input" selector="//input[@name='option[value][option_2][0]']"/> + <element name="defaultStoreViewFieldYellow" type="input" selector="//input[@name='option[value][option_2][1]']"/> + <element name="adminFieldGreen" type="input" selector="//input[@name='option[value][option_3][0]']"/> + <element name="defaultStoreViewFieldGreen" type="input" selector="//input[@name='option[value][option_3][1]']"/> + <element name="adminFieldBlack" type="input" selector="//input[@name='option[value][option_4][0]']"/> + <element name="defaultStoreViewFieldBlack" type="input" selector="//input[@name='option[value][option_4][1]']"/> + <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> + <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> + <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml new file mode 100644 index 0000000000000..9bd4161a80cf7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.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="AdminConfigurableProductCreateTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to create a configurable product with attributes"/> + <description value="admin should be able to create a configurable product with attributes"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-84"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- assert color configurations on the admin create product page --> + <dontSee selector="{{AdminProductFormConfigurationsSection.variationLabel}}" stepKey="seeLabelNotVisible"/> + <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" userInput="3" stepKey="seeNumberOfRows"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeName1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeName2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeName3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeSku1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeSku2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeSku3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute1.price}}" stepKey="seeUniquePrice1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute2.price}}" stepKey="seeUniquePrice2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute3.price}}" stepKey="seeUniquePrice3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsQuantityCells}}" userInput="{{colorProductAttribute.attribute_quantity}}" stepKey="seeQuantityInField"/> + + <!-- assert storefront category list page --> + <amOnPage url="/" stepKey="amOnStorefront"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <see userInput="{{_defaultProduct.name}}" stepKey="assertProductPresent"/> + <see userInput="{{colorProductAttribute1.price}}" stepKey="assertProductPricePresent"/> + + <!-- assert storefront product details page --> + <click userInput="{{_defaultProduct.name}}" stepKey="clickOnProductName"/> + <waitForPageLoad stepKey="waitForPageLoad5"/> + <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="assertProductNameTitle"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{colorProductAttribute1.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml similarity index 54% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml index e14e181ae5cce..ab0336637c12c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml @@ -5,71 +5,8 @@ * 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="AdminConfigurableProductCreateTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to create a configurable product with attributes"/> - <description value="admin should be able to create a configurable product with attributes"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-84"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- assert color configurations on the admin create product page --> - <dontSee selector="{{AdminProductFormConfigurationsSection.variationLabel}}" stepKey="seeLabelNotVisible"/> - <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" userInput="3" stepKey="seeNumberOfRows"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeName1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeName2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeName3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeSku1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeSku2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeSku3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute1.price}}" stepKey="seeUniquePrice1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute2.price}}" stepKey="seeUniquePrice2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute3.price}}" stepKey="seeUniquePrice3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsQuantityCells}}" userInput="{{colorProductAttribute.attribute_quantity}}" stepKey="seeQuantityInField"/> - - <!-- assert storefront category list page --> - <amOnPage url="/" stepKey="amOnStorefront"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <see userInput="{{_defaultProduct.name}}" stepKey="assertProductPresent"/> - <see userInput="{{colorProductAttribute1.price}}" stepKey="assertProductPricePresent"/> - - <!-- assert storefront product details page --> - <click userInput="{{_defaultProduct.name}}" stepKey="clickOnProductName"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> - <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="assertProductNameTitle"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{colorProductAttribute1.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> - <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - </test> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest"> <annotations> <features value="ConfigurableProduct"/> @@ -114,7 +51,7 @@ <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> <waitForPageLoad stepKey="waitForGenerateConfigure"/> - <grabValueFrom selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" stepKey="grabTextFromContent"/> + <grabValueFrom selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" stepKey="grabTextFromContent"/> <fillField stepKey="fillMoreThan64Symbols" selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" userInput="01234567890123456789012345678901234567890123456789012345678901234"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct1"/> <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" visible="true" stepKey="clickOnCloseInPopup"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml similarity index 64% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml index 1016a46ebc213..7c474a135c334 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml @@ -5,103 +5,8 @@ * 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="AdminConfigurableProductDeleteTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to delete a configurable product"/> - <description value="admin should be able to delete a configurable product"/> - <testCaseId value="MC-87"/> - <group value="ConfigurableProduct"/> - <severity value="BLOCKER"/> - </annotations> - - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- assert product visible in storefront --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> - - <!-- go to admin and delete --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - <click selector="label.data-grid-checkbox-cell-inner" stepKey="clickCheckbox"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMsg"/> - - <!-- after delete, assert product page is 404 --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront2"/> - <waitForPageLoad stepKey="wait3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="dontSeeProduct"/> - </test> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductBulkDeleteTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml new file mode 100644 index 0000000000000..de904cf854171 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml @@ -0,0 +1,103 @@ +<?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="AdminConfigurableProductDeleteTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to delete a configurable product"/> + <description value="admin should be able to delete a configurable product"/> + <testCaseId value="MC-87"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- assert product visible in storefront --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> + + <!-- go to admin and delete --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + <click selector="label.data-grid-checkbox-cell-inner" stepKey="clickCheckbox"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMsg"/> + + <!-- after delete, assert product page is 404 --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront2"/> + <waitForPageLoad stepKey="wait3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="dontSeeProduct"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml deleted file mode 100644 index a6fd3ba05c1fc..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml +++ /dev/null @@ -1,355 +0,0 @@ -<?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="AdminConfigurableProductChildrenOutOfStockTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-181"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Find the first simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> - <argument name="product" value="ApiSimpleOne"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - - <!-- Edit the quantity of the simple first product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Find the second simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> - <argument name="product" value="ApiSimpleTwo"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - - <!-- Edit the quantity of the second simple product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> - - <test name="AdminConfigurableProductOutOfStockTestDeleteChildrenTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-3042"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleOne.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Delete the second simple product --> - <actionGroup stepKey="deleteProduct2" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleTwo.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> - - <test name="AdminConfigurableProductOutOfStockAndDeleteCombinationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-3046"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleOne.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Find the second simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> - <argument name="product" value="ApiSimpleTwo"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - - <!-- Edit the quantity of the second simple product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml new file mode 100644 index 0000000000000..a5d7e97c33b66 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml @@ -0,0 +1,135 @@ +<?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="AdminConfigurableProductChildrenOutOfStockTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-181"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> + <argument name="product" value="ApiSimpleOne"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- Edit the quantity of the simple first product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the second simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> + <argument name="product" value="ApiSimpleTwo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Edit the quantity of the second simple product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml new file mode 100644 index 0000000000000..d0f686f9cbc0a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml @@ -0,0 +1,124 @@ +<?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="AdminConfigurableProductOutOfStockAndDeleteCombinationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3046"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleOne.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the second simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> + <argument name="product" value="ApiSimpleTwo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Edit the quantity of the second simple product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml new file mode 100644 index 0000000000000..e9fc3443a9d56 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml @@ -0,0 +1,113 @@ +<?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="AdminConfigurableProductOutOfStockTestDeleteChildrenTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3042"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleOne.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the second simple product --> + <actionGroup stepKey="deleteProduct2" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleTwo.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml new file mode 100644 index 0000000000000..4dd73ea3ce562 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.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="AdminConfigurableProductFilterByTypeTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Search"/> + <title value="admin should be able to filter by type configurable product"/> + <description value="admin should be able to filter by type configurable product"/> + <testCaseId value="MC-66"/> + <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> + </annotations> + + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickShowFilters"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="selectConfigurableType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Type: Configurable Product" stepKey="seeFilter"/> + <see selector="{{AdminProductGridSection.table}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml similarity index 50% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml index 6eb6d7a11f767..d2d5aa969bd50 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductSearchTest"> <annotations> <features value="ConfigurableProduct"/> @@ -89,84 +88,4 @@ <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInGrid"/> <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInActiveFilters"/> </test> - - <test name="AdminConfigurableProductFilterByTypeTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Search"/> - <title value="admin should be able to filter by type configurable product"/> - <description value="admin should be able to filter by type configurable product"/> - <testCaseId value="MC-66"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickShowFilters"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="selectConfigurableType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Type: Configurable Product" stepKey="seeFilter"/> - <see selector="{{AdminProductGridSection.table}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> - </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml new file mode 100644 index 0000000000000..4f1ab935a84d6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml @@ -0,0 +1,147 @@ +<?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="AdminConfigurableProductUpdateAttributeTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to update existing attributes of a configurable product"/> + <description value="Admin should be able to update existing attributes of a configurable product"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-179"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <!-- Create the attribute we will be modifying --> + <createData entity="productAttributeWithTwoOptions" stepKey="createModifiableProductAttribute"/> + + <!-- Create the two attributes the product will have --> + <createData entity="productAttributeOption1" stepKey="createModifiableProductAttributeOption1"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createModifiableProductAttributeOption2"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + + <!-- Add the product to the default set --> + <createData entity="AddToDefaultSet" stepKey="createModifiableAddToAttributeSet"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category the product will be a part of --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + + <!-- Create the two attributes the product will have --> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the product to the default set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the two attributes --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the two children product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Create the two configurable product with both children --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- login --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Delete everything that was created in the before block --> + <deleteData createDataKey="createCategory" stepKey="deleteCatagory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createModifiableProductAttribute" stepKey="deleteModifiableProductAttribute"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Get the current option of the attribute before it was changed --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + + <grabTextFrom stepKey="getBeforeOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> + + <!-- Find the product that we just created using the product grid --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> + <argument name="product" value="ApiConfigurableProduct"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductFilterLoad"/> + + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- change the option on the first attribute --> + <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createModifiableProductAttribute.default_frontend_label$$)}}" userInput="option1"/> + + <!-- Save the product --> + <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> + <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> + + <!-- Go back to the configurable product page and check to see if it has changed --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad2"/> + <grabTextFrom stepKey="getCurrentOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$getCurrentOption}</actualResult> + <expectedResult type="string">{$getBeforeOption}</expectedResult> + </assertNotEquals> + + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml similarity index 51% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml index 752cc5e307f60..8b7fc64d265a0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml @@ -5,147 +5,8 @@ * 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="AdminConfigurableProductUpdateAttributeTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to update existing attributes of a configurable product"/> - <description value="Admin should be able to update existing attributes of a configurable product"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-179"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <!-- Create the attribute we will be modifying --> - <createData entity="productAttributeWithTwoOptions" stepKey="createModifiableProductAttribute"/> - - <!-- Create the two attributes the product will have --> - <createData entity="productAttributeOption1" stepKey="createModifiableProductAttributeOption1"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createModifiableProductAttributeOption2"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - - <!-- Add the product to the default set --> - <createData entity="AddToDefaultSet" stepKey="createModifiableAddToAttributeSet"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category the product will be a part of --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - - <!-- Create the two attributes the product will have --> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Add the product to the default set --> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Get the two attributes --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the two children product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Create the two configurable product with both children --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- login --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - - <!-- Delete everything that was created in the before block --> - <deleteData createDataKey="createCategory" stepKey="deleteCatagory" /> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createModifiableProductAttribute" stepKey="deleteModifiableProductAttribute"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Get the current option of the attribute before it was changed --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - - <grabTextFrom stepKey="getBeforeOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> - - <!-- Find the product that we just created using the product grid --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> - <argument name="product" value="ApiConfigurableProduct"/> - </actionGroup> - <waitForPageLoad stepKey="waitForProductFilterLoad"/> - - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - - <!-- change the option on the first attribute --> - <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createModifiableProductAttribute.default_frontend_label$$)}}" userInput="option1"/> - - <!-- Save the product --> - <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> - <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> - - <!-- Go back to the configurable product page and check to see if it has changed --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad2"/> - <grabTextFrom stepKey="getCurrentOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> - <assertNotEquals stepKey="assertNotEquals"> - <actualResult type="string">{$getCurrentOption}</actualResult> - <expectedResult type="string">{$getBeforeOption}</expectedResult> - </assertNotEquals> - - </test> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductUpdateChildAttributeTest"> <annotations> <features value="ConfigurableProduct"/> @@ -222,7 +83,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <!-- Delete everything that was created in the before block --> - <deleteData createDataKey="createCategory" stepKey="deleteCatagory" /> + <deleteData createDataKey="createCategory" stepKey="deleteCatagory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> @@ -241,7 +102,7 @@ </actionGroup> <waitForPageLoad stepKey="waitForProductFilterLoad"/> <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openProduct"> - <argument name="product" value="$$createConfigProduct$$" /> + <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <!-- Open the wizard for editing configurations and fill out a new attribute --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml deleted file mode 100644 index b56d8823643df..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ /dev/null @@ -1,378 +0,0 @@ -<?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="AdminConfigurableProductBulkUpdateTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to bulk update attributes of configurable products"/> - <description value="admin should be able to bulk update attributes of configurable products"/> - <testCaseId value="MC-88"/> - <group value="ConfigurableProduct"/> - <severity value="BLOCKER"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct1" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="createProduct2" stepKey="deleteSecondProduct"/> - <deleteData createDataKey="createProduct3" stepKey="deleteThirdProduct"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!-- Search for prefix of the 3 products we created via api --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - - <!-- Select all, then start the bulk update attributes flow --> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdate"/> - <waitForPageLoad stepKey="wait2"/> - - <!-- Update the description --> - <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> - <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> - <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="clickSave"/> - <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" time="60" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> - - <!-- Run cron twice --> - <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> - <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!-- Check storefront for description --> - <amOnPage url="{{StorefrontProductPage.url($$createProduct1.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct2.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct3.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> - </test> - - <test name="AdminConfigurableProductRemoveAnOptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="Admin should be able to remove a product configuration"/> - <description value="Admin should be able to remove a product configuration"/> - <testCaseId value="MC-63"/> - <group value="ConfigurableProduct"/> - <severity value="BLOCKER"/> - </annotations> - - <before> - <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!--check storefront for both options--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> - - <!--check admin for both options--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> - <argument name="productId" value="$$createConfigProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="wait2"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeOption1Admin"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct2.name$$" stepKey="seeOption2Admin"/> - - <!--remove an option--> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemove"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="wait3"/> - - <!--check admin for one option--> - <dontSee selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="dontSeeOption1Admin"/> - - <!--check storefront for one option--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> - <waitForPageLoad stepKey="wait4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> - </test> - - <test name="AdminConfigurableProductDisableAnOptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="Admin should be able to disable a product configuration"/> - <description value="Admin should be able to disable a product configuration"/> - <testCaseId value="MC-119"/> - <group value="ConfigurableProduct"/> - <severity value="BLOCKER"/> - </annotations> - - <before> - <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!--check storefront for both options--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> - - <!--go to admin and disable an option--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> - <argument name="productId" value="$$createConfigProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> - <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisable"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="wait3"/> - - <!--check storefront for one option--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> - <waitForPageLoad stepKey="wait4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> - </test> - - <test name="AdminConfigurableProductRemoveConfigurationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to remove a configuration from a Configurable Product"/> - <description value="Admin should be able to remove a configuration from a Configurable Product"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-86"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- - After saving, we are still on the product edit page. There is no need to reload or go to this page - again, because a round trip to the server has already happened. - --> - - <!-- Remove a configuration option from the configurable product --> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckboxByIndex('1')}}" stepKey="deselectOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - - <!-- Verify that the removed option is not present in the storefront --> - <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPage"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - </test> - - <test name="AdminConfigurableProductAddConfigurationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> - <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-95"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- - After saving, we are still on the product edit page. There is no need to reload or go to this page - again, because a round trip to the server has already happened. - --> - - <!-- Add a configuration option to the configurable product --> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> - <fillField userInput="{{colorProductAttribute4.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="42" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - - <!-- Verify that the added option is present in the storefront --> - <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute4.name}}" stepKey="seeInDropDown4"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml new file mode 100644 index 0000000000000..a6301978dc96f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml @@ -0,0 +1,64 @@ +<?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="AdminConfigurableProductAddConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-95"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Add a configuration option to the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute4.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="42" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the added option is present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute4.name}}" stepKey="seeInDropDown4"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml new file mode 100644 index 0000000000000..b2a2c1300d00a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml @@ -0,0 +1,84 @@ +<?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="AdminConfigurableProductBulkUpdateTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to bulk update attributes of configurable products"/> + <description value="admin should be able to bulk update attributes of configurable products"/> + <testCaseId value="MC-88"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct1" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createProduct2" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createProduct3" stepKey="deleteThirdProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Search for prefix of the 3 products we created via api --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + + <!-- Select all, then start the bulk update attributes flow --> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdate"/> + <waitForPageLoad stepKey="wait2"/> + + <!-- Update the description --> + <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> + <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" time="60" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Check storefront for description --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct1.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct2.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct3.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml new file mode 100644 index 0000000000000..bd1b17ca46099 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml @@ -0,0 +1,100 @@ +<?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="AdminConfigurableProductDisableAnOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="Admin should be able to disable a product configuration"/> + <description value="Admin should be able to disable a product configuration"/> + <testCaseId value="MC-119"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!--check storefront for both options--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> + + <!--go to admin and disable an option--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> + <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisable"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="wait3"/> + + <!--check storefront for one option--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> + <waitForPageLoad stepKey="wait4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml new file mode 100644 index 0000000000000..09407c40db8a1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml @@ -0,0 +1,107 @@ +<?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="AdminConfigurableProductRemoveAnOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="Admin should be able to remove a product configuration"/> + <description value="Admin should be able to remove a product configuration"/> + <testCaseId value="MC-63"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!--check storefront for both options--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> + + <!--check admin for both options--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="wait2"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeOption1Admin"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct2.name$$" stepKey="seeOption2Admin"/> + + <!--remove an option--> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemove"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="wait3"/> + + <!--check admin for one option--> + <dontSee selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="dontSeeOption1Admin"/> + + <!--check storefront for one option--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> + <waitForPageLoad stepKey="wait4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml new file mode 100644 index 0000000000000..68d6423b43c07 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml @@ -0,0 +1,58 @@ +<?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="AdminConfigurableProductRemoveConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to remove a configuration from a Configurable Product"/> + <description value="Admin should be able to remove a configuration from a Configurable Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-86"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Remove a configuration option from the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckboxByIndex('1')}}" stepKey="deselectOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the removed option is not present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml deleted file mode 100644 index 7e2639c7e6e25..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ /dev/null @@ -1,193 +0,0 @@ -<?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="AdminCreateConfigurableProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from configurable to simple"/> - <description value="After selecting a configurable product when adding Admin should be switch to simple implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10926"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="configurable"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> - </test> - <test name="AdminCreateConfigurableProductSwitchToVirtualTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from configurable to virtual"/> - <description value="After selecting a configurable product when adding Admin should be switch to virtual implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10927"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="configurable"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> - </test> - <test name="AdminCreateVirtualProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from virtual to configurable"/> - <description value="After selecting a virtual product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10930"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - </after> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="virtual"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> - <test name="AdminCreateSimpleProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from simple to configurable"/> - <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <useCaseId value="MAGETWO-44165"/> - <testCaseId value="MAGETWO-29398"/> - <group value="catalog"/> - </annotations> - <before> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - </after> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="simple"/> - </actionGroup> - <!-- Create configurable product from simple product page--> - <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <!-- Verify product on store front --> - <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> - <test name="AdminCreateDownloadableProductSwitchToConfigurableTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from downloadable to configurable"/> - <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <useCaseId value="MAGETWO-44165"/> - <testCaseId value="MAGETWO-29398"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - <magentoCLI command="cron:run --group=index" stepKey="runCron"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <!-- Create configurable product from downloadable product page--> - <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Open Dropdown and select downloadable product option --> - <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="downloadable"/> - </actionGroup> - <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo" /> - <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> - <!-- Fill form for Downloadable Product Type --> - <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <!-- Check that product was added with implicit type change --> - <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefrontProductPage"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml new file mode 100644 index 0000000000000..98bd5a0fed4ed --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml @@ -0,0 +1,29 @@ +<?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="AdminCreateConfigurableProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from configurable to simple"/> + <description value="After selecting a configurable product when adding Admin should be switch to simple implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10926"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="configurable"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml new file mode 100644 index 0000000000000..756cdfd5d5d6f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml @@ -0,0 +1,26 @@ +<?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="AdminCreateConfigurableProductSwitchToVirtualTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from configurable to virtual"/> + <description value="After selecting a configurable product when adding Admin should be switch to virtual implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10927"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="configurable"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..7593242c4f716 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml @@ -0,0 +1,79 @@ +<?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="AdminCreateDownloadableProductSwitchToConfigurableTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from downloadable to configurable"/> + <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Create configurable product from downloadable product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <!-- Open Dropdown and select downloadable product option --> + <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="downloadable"/> + </actionGroup> + <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo"/> + <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> + <!-- Fill form for Downloadable Product Type --> + <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <!-- Check that product was added with implicit type change --> + <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefrontProductPage"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..cbfa1cc2b8bd6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml @@ -0,0 +1,51 @@ +<?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="AdminCreateSimpleProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from simple to configurable"/> + <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + </after> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="simple"/> + </actionGroup> + <!-- Create configurable product from simple product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <!-- Verify product on store front --> + <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..cfeb95afc4924 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml @@ -0,0 +1,47 @@ +<?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="AdminCreateVirtualProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from virtual to configurable"/> + <description value="After selecting a virtual product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10930"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + </after> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="virtual"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml deleted file mode 100644 index 0bccd2f76982a..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ /dev/null @@ -1,193 +0,0 @@ -<?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="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Simple product type switching on editing to configurable product"/> - <description value="Simple product type switching on editing to configurable product"/> - <testCaseId value="MAGETWO-29633"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="SimpleProduct2" stepKey="createProduct"/> - <!--Create attribute with options--> - <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> - <argument name="product" value="$$createProduct$$"/> - </actionGroup> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - <!--Add configurations to product--> - <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToSimpleProductPage"> - <argument name="productId" value="$$createProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveConfigProductForm"/> - <!--Assert configurable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeProductNameInGrid1"/> - <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeProductNameInGrid2"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <!--Assert configurable product on storefront--> - <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> - <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickAttributeDropDown"/> - <see userInput="option1" stepKey="verifyOption1Exists"/> - <see userInput="option2" stepKey="verifyOption2Exists"/> - </test> - <test name="AdminConfigurableProductTypeSwitchingToVirtualProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Configurable product type switching on editing to virtual product"/> - <description value="Configurable product type switching on editing to virtual product"/> - <testCaseId value="MC-17952"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!--Delete product configurations--> - <comment userInput="Delete product configuration" stepKey="commentDeleteConfigs"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> - <argument name="productId" value="$$createProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <conditionalClick selector="{{ AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption1Actions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption1"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption2Actions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption2"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct2.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProduct2.quantity}}" stepKey="fillProductQty"/> - <clearField selector="{{AdminProductFormSection.productWeight}}" stepKey="clearWeightField"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeight"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProductForm"/> - <!--Assert virtual product on Admin product page grid--> - <comment userInput="Assert virtual product on Admin product page grid" stepKey="commentAssertVirtualProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForVirtual"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForVirtual"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeVirtualProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeVirtualProductTypeInGrid"/> - <!--Assert virtual product on storefront--> - <comment userInput="Assert virtual product on storefront" stepKey="commentAssertVirtualProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openVirtualProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontVirtualProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertVirtualProductInStock"/> - </test> - <test name="AdminVirtualProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Virtual product type switching on editing to configurable product"/> - <description value="Virtual product type switching on editing to configurable product"/> - <testCaseId value="MC-17953"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="VirtualProduct" stepKey="createProduct"/> - <!--Create attribute with options--> - <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> - <argument name="product" value="$$createProduct$$"/> - </actionGroup> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - <!--Add configurations to product--> - <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> - <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> - <argument name="productId" value="$$createProduct.id$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveNewConfigurableProductForm"/> - <!--Assert configurable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigurableProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForConfigurable"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForConfigurable"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeConfigurableProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeConfigurableProductTypeInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeConfigurableProductNameInGrid1"/> - <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeConfigurableProductNameInGrid2"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearConfigurableProductFilters"/> - <!--Assert configurable product on storefront--> - <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigurableProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontConfigurableProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertConfigurableProductInStock"/> - <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickConfigurableAttributeDropDown"/> - <see userInput="option1" stepKey="verifyConfigurableProductOption1Exists"/> - <see userInput="option2" stepKey="verifyConfigurableProductOption2Exists"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml new file mode 100644 index 0000000000000..36450313f9f3a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml @@ -0,0 +1,51 @@ +<?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="AdminConfigurableProductTypeSwitchingToVirtualProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Configurable product type switching on editing to virtual product"/> + <description value="Configurable product type switching on editing to virtual product"/> + <testCaseId value="MC-17952"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!--Delete product configurations--> + <comment userInput="Delete product configuration" stepKey="commentDeleteConfigs"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> + <conditionalClick selector="{{ AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption1Actions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption1"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption2Actions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption2"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct2.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProduct2.quantity}}" stepKey="fillProductQty"/> + <clearField selector="{{AdminProductFormSection.productWeight}}" stepKey="clearWeightField"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeight"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProductForm"/> + <!--Assert virtual product on Admin product page grid--> + <comment userInput="Assert virtual product on Admin product page grid" stepKey="commentAssertVirtualProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForVirtual"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForVirtual"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeVirtualProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeVirtualProductTypeInGrid"/> + <!--Assert virtual product on storefront--> + <comment userInput="Assert virtual product on storefront" stepKey="commentAssertVirtualProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openVirtualProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontVirtualProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertVirtualProductInStock"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..e7006591c2a8f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,80 @@ +<?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="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Simple product type switching on editing to configurable product"/> + <description value="Simple product type switching on editing to configurable product"/> + <testCaseId value="MAGETWO-29633"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + <!--Create attribute with options--> + <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!--Add configurations to product--> + <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToSimpleProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveConfigProductForm"/> + <!--Assert configurable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeProductNameInGrid1"/> + <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeProductNameInGrid2"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <!--Assert configurable product on storefront--> + <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickAttributeDropDown"/> + <see userInput="option1" stepKey="verifyOption1Exists"/> + <see userInput="option2" stepKey="verifyOption2Exists"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..43f00fcef9993 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,81 @@ +<?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="AdminVirtualProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Virtual product type switching on editing to configurable product"/> + <description value="Virtual product type switching on editing to configurable product"/> + <testCaseId value="MC-17953"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="VirtualProduct" stepKey="createProduct"/> + <!--Create attribute with options--> + <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!--Add configurations to product--> + <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveNewConfigurableProductForm"/> + <!--Assert configurable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigurableProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForConfigurable"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForConfigurable"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeConfigurableProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeConfigurableProductTypeInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeConfigurableProductNameInGrid1"/> + <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeConfigurableProductNameInGrid2"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearConfigurableProductFilters"/> + <!--Assert configurable product on storefront--> + <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigurableProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontConfigurableProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertConfigurableProductInStock"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickConfigurableAttributeDropDown"/> + <see userInput="option1" stepKey="verifyConfigurableProductOption1Exists"/> + <see userInput="option2" stepKey="verifyConfigurableProductOption2Exists"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml deleted file mode 100644 index 0458238d7a479..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ /dev/null @@ -1,331 +0,0 @@ -<?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="AdvanceCatalogSearchConfigurableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product name"/> - <description value="Guest customer should be able to advance search configurable product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-138"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product sku"/> - <description value="Guest customer should be able to advance search configurable product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-144"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescriptionUnderscoredSku" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product description"/> - <description value="Guest customer should be able to advance search configurable product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-237"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product short description"/> - <description value="Guest customer should be able to advance search configurable product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-240"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml new file mode 100644 index 0000000000000..04f4fc65c805a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml @@ -0,0 +1,90 @@ +<?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="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product description"/> + <description value="Guest customer should be able to advance search configurable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-237"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml new file mode 100644 index 0000000000000..739b8e83d770f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml @@ -0,0 +1,90 @@ +<?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="AdvanceCatalogSearchConfigurableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product name"/> + <description value="Guest customer should be able to advance search configurable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-138"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml new file mode 100644 index 0000000000000..c4595158e55d3 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml @@ -0,0 +1,90 @@ +<?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="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product short description"/> + <description value="Guest customer should be able to advance search configurable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-240"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml new file mode 100644 index 0000000000000..34c0fcd634373 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml @@ -0,0 +1,90 @@ +<?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="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product sku"/> + <description value="Guest customer should be able to advance search configurable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-144"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescriptionUnderscoredSku" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index af9f3df588351..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,501 +0,0 @@ -<?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="EndToEndB2CGuestUserTest"> - <before> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> - <requiredEntity createDataKey="createConfigProduct"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> - </before> - <after> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Verify Configurable Product in checkout cart items --> - <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" /> - <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" /> - </actionGroup> - - <!-- Check configurable product in category --> - <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> - <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Configurable Product --> - <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> - <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Add Configurable Product to cart --> - <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> - <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> - <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> - <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> - <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - - <!-- Check configurable product in minicart --> - <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> - <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> - <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check configurable product in cart --> - <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> - <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> - <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> - <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Add Configurable Product to comparison --> - <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> - <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product in comparison sidebar --> - <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product on comparison page --> - <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison" /> - <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> - <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <before> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> - <requiredEntity createDataKey="createConfigProduct"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> - </before> - <after> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Verify Configurable Product in checkout cart items --> - <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" /> - <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" /> - </actionGroup> - - <!-- Check configurable product in category --> - <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> - <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- View Configurable Product --> - <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> - <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Add Configurable Product to cart --> - <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> - <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> - <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> - <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> - <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - - <!-- Check configurable product in minicart --> - <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> - <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> - <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Check configurable product in cart --> - <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> - <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> - <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> - <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - - <!-- Add Configurable Product to comparison --> - <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> - <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product in comparison sidebar --> - <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product on comparison page --> - <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison" /> - <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> - <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..aaf3da2dab21f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,255 @@ +<?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="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + </before> + <after> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Verify Configurable Product in checkout cart items --> + <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$"/> + </actionGroup> + + <!-- Check configurable product in category --> + <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Configurable Product --> + <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to cart --> + <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + + <!-- Check configurable product in minicart --> + <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check configurable product in cart --> + <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> + <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to comparison --> + <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product in comparison sidebar --> + <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product on comparison page --> + <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..02f054e405bb7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,255 @@ +<?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="EndToEndB2CGuestUserTest"> + <before> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + </before> + <after> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Verify Configurable Product in checkout cart items --> + <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$"/> + </actionGroup> + + <!-- Check configurable product in category --> + <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Configurable Product --> + <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to cart --> + <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + + <!-- Check configurable product in minicart --> + <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check configurable product in cart --> + <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> + <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to comparison --> + <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product in comparison sidebar --> + <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product on comparison page --> + <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml deleted file mode 100644 index 56d9fee72daf2..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ /dev/null @@ -1,288 +0,0 @@ -<?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="StorefrontConfigurableProductBasicInfoTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should see basic Configurable Product details"/> - <description value="Guest customer should see basic Configurable Product details"/> - <severity value="MAJOR"/> - <testCaseId value="MC-77"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify configurable product details in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeProductSku"/> - <see userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="seeProductPriceLabel"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="seeProductStockStatus"/> - <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice"/> - <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeProductAttributeTitle"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> - <see userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown1"/> - <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> - <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> - </test> - - <test name="StorefrontConfigurableProductOptionsTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should be able to see product configuration options"/> - <description value="Guest customer should be able to see product configuration options"/> - <severity value="MAJOR"/> - <testCaseId value="MC-92"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify configurable product options in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> - <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice1"/> - <selectOption userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> - <see userInput="2.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> - <selectOption userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption3"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> - <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> - </test> - - <test name="StorefrontConfigurableProductCanAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should be able to successfully add the product to the cart"/> - <description value="Guest customer should be able to successfully add the product to the cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-97"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify adding configurable product to cart after an option is selected in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="product" value="_defaultProduct"/> - <argument name="productCount" value="1"/> - </actionGroup> - </test> - - <test name="StorefrontConfigurableProductCantAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should not be able to add the product to the cart if options are not selected"/> - <description value="Guest customer should not be able to add the product to the cart if options are not selected"/> - <severity value="MAJOR"/> - <testCaseId value="MC-81"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify not able to add configurable product to cart when no option is selected in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> - <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> - <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> - </test> - - <test name="StorefrontConfigurableProductVariationsTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Configurable Product"/> - <title value="Customer should get the right options list"/> - <description value="Customer should get the right options list for each variation of configurable product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-23027"/> - <useCaseId value="MC-22732"/> - <group value="configurable_product"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Add first attribute with options --> - <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> - <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> - <requiredEntity createDataKey="createFirstAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> - <requiredEntity createDataKey="createFirstAttribute"/> - </createData> - <!-- Add second attribute with options --> - <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> - <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> - <requiredEntity createDataKey="createSecondAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> - <requiredEntity createDataKey="createSecondAttribute"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> - </actionGroup> - <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> - <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="navigateToCreateProductPage"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> - <argument name="categoryName" value="$createCategory.name$"/> - </actionGroup> - <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> - <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> - </actionGroup> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> - <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> - <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> - </actionGroup> - <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> - <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> - </actionGroup> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> - <waitForPageLoad stepKey="waitForStepLoad"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> - <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> - <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> - - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> - <argument name="rowIndex" value="0"/> - <argument name="quantity" value="0"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> - <argument name="rowIndex" value="1"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> - <argument name="rowIndex" value="2"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> - <argument name="rowIndex" value="3"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveConfigurableProduct"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> - <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> - <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> - <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> - <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml new file mode 100644 index 0000000000000..fa635dfb94e78 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml @@ -0,0 +1,54 @@ +<?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="StorefrontConfigurableProductBasicInfoTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should see basic Configurable Product details"/> + <description value="Guest customer should see basic Configurable Product details"/> + <severity value="MAJOR"/> + <testCaseId value="MC-77"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify configurable product details in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeProductSku"/> + <see userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="seeProductPriceLabel"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="seeProductStockStatus"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice"/> + <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeProductAttributeTitle"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> + <see userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown1"/> + <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> + <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml new file mode 100644 index 0000000000000..a9e3d725bb079 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml @@ -0,0 +1,51 @@ +<?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="StorefrontConfigurableProductCanAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to successfully add the product to the cart"/> + <description value="Guest customer should be able to successfully add the product to the cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-97"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify adding configurable product to cart after an option is selected in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="_defaultProduct"/> + <argument name="productCount" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml new file mode 100644 index 0000000000000..154fcca60ed6b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml @@ -0,0 +1,48 @@ +<?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="StorefrontConfigurableProductCantAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should not be able to add the product to the cart if options are not selected"/> + <description value="Guest customer should not be able to add the product to the cart if options are not selected"/> + <severity value="MAJOR"/> + <testCaseId value="MC-81"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify not able to add configurable product to cart when no option is selected in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> + <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml new file mode 100644 index 0000000000000..88171e3668119 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml @@ -0,0 +1,54 @@ +<?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="StorefrontConfigurableProductOptionsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to see product configuration options"/> + <description value="Guest customer should be able to see product configuration options"/> + <severity value="MAJOR"/> + <testCaseId value="MC-92"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify configurable product options in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice1"/> + <selectOption userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> + <see userInput="2.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> + <selectOption userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption3"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> + <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml new file mode 100644 index 0000000000000..4dc6030e2ce4c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.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="StorefrontConfigurableProductVariationsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Configurable Product"/> + <title value="Customer should get the right options list"/> + <description value="Customer should get the right options list for each variation of configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-23027"/> + <useCaseId value="MC-22732"/> + <group value="configurable_product"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Add first attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <!-- Add second attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> + <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="navigateToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> + <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> + </actionGroup> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> + <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> + </actionGroup> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> + <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> + </actionGroup> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForPageLoad stepKey="waitForStepLoad"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> + + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> + <argument name="rowIndex" value="0"/> + <argument name="quantity" value="0"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> + <argument name="rowIndex" value="1"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> + <argument name="rowIndex" value="2"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> + <argument name="rowIndex" value="3"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> + <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> + <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> + <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml deleted file mode 100644 index 7e9ee68b9f2f7..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml +++ /dev/null @@ -1,130 +0,0 @@ -<?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="StorefrontConfigurableProductGridViewTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should see the configurable product in the category grid"/> - <description value="customer should see the configurable product in the category grid"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-94"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - <!-- TODO: REMOVE AFTER FIX MC-21717 --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush eav" stepKey="flushCache"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup stepKey="deleteProduct" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify the storefront category grid view --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> - <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> - <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> - </test> - - <test name="StorefrontConfigurableProductListViewTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should see the configurable product in the category list"/> - <description value="customer should see the configurable product in the category list"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-61"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Verify storefront category list view --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> - <waitForPageLoad stepKey="wait2"/> - <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> - <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> - <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> - </test> - - <test name="StorefrontConfigurableProductAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should be taken to the product details page when clicking add to cart"/> - <description value="customer should be taken to the product details page when clicking add to cart"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-89"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!-- Should be taken to product details page when adding to cart because an option needs to be selected --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="clickAddToCart"/> - <waitForPageLoad stepKey="wait3"/> - <grabFromCurrentUrl stepKey="grabUrl"/> - <assertContains stepKey="assertUrl"> - <expectedResult type="string">{{_defaultProduct.urlKey}}</expectedResult> - <actualResult type="string">{$grabUrl}</actualResult> - </assertContains> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml new file mode 100644 index 0000000000000..d1768783e46e6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml @@ -0,0 +1,50 @@ +<?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="StorefrontConfigurableProductAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should be taken to the product details page when clicking add to cart"/> + <description value="customer should be taken to the product details page when clicking add to cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-89"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Should be taken to product details page when adding to cart because an option needs to be selected --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="clickAddToCart"/> + <waitForPageLoad stepKey="wait3"/> + <grabFromCurrentUrl stepKey="grabUrl"/> + <assertContains stepKey="assertUrl"> + <expectedResult type="string">{{_defaultProduct.urlKey}}</expectedResult> + <actualResult type="string">{$grabUrl}</actualResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml new file mode 100644 index 0000000000000..009a5008875e5 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml @@ -0,0 +1,50 @@ +<?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="StorefrontConfigurableProductGridViewTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should see the configurable product in the category grid"/> + <description value="customer should see the configurable product in the category grid"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-94"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush eav" stepKey="flushCache"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup stepKey="deleteProduct" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify the storefront category grid view --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml new file mode 100644 index 0000000000000..7097641c67530 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml @@ -0,0 +1,47 @@ +<?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="StorefrontConfigurableProductListViewTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should see the configurable product in the category list"/> + <description value="customer should see the configurable product in the category list"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-61"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify storefront category list view --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> + <waitForPageLoad stepKey="wait2"/> + <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml index a39352ce16189..e71e7dbef5137 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesTest.xml @@ -171,12 +171,18 @@ <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case0"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case0"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case0"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0"> + <actualResult type="string">$getListOfThumbnailsCase0[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case0"> + <actualResult type="string">$getListOfThumbnailsCase0[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case0"> + <actualResult type="string">$getListOfThumbnailsCase0[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase0"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase0"> @@ -189,18 +195,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case1"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case1"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase1[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case1"/> - <assertRegExp expected="|{{placeholderThumbnailImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase1[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case1"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case1"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case1"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case1"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[0]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[1]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[2]</actualResult> + <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case1"> + <actualResult type="string">$getListOfThumbnailsCase1[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase1"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase1"> @@ -213,18 +231,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case2"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase2[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case2"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case2"/> - <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase2[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case2"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case2"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case2"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case2"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case2"> + <actualResult type="string">$getListOfThumbnailsCase2[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase2"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase2"> @@ -237,12 +267,18 @@ <selectOption userInput="$dropdownAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB2Case3"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case3"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case3"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case3"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3"> + <actualResult type="string">$getListOfThumbnailsCase3[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case3"> + <actualResult type="string">$getListOfThumbnailsCase3[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case3"> + <actualResult type="string">$getListOfThumbnailsCase3[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase3"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase3"> @@ -255,14 +291,22 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case4"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> - <assertRegExp expected="|{{ProductImage.filename}}.*.png|" actual="$getListOfThumbnailsCase4[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case4"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case4"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case4"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case4"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4"> + <actualResult type="string">$getListOfThumbnailsCase4[0]</actualResult> + <expectedResult type="string">|{{ProductImage.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case4"> + <actualResult type="string">$getListOfThumbnailsCase4[1]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case4"> + <actualResult type="string">$getListOfThumbnailsCase4[2]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case4"> + <actualResult type="string">$getListOfThumbnailsCase4[3]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase4"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase4"> @@ -275,12 +319,18 @@ <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF2Case5"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase5"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase5"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case5"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case5"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase5[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case5"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case5"> + <actualResult type="string">$getListOfThumbnailsCase5[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case5"> + <actualResult type="string">$getListOfThumbnailsCase5[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case5"> + <actualResult type="string">$getListOfThumbnailsCase5[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase5"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase5"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase5"> @@ -293,18 +343,30 @@ <selectOption userInput="$dropdownAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB1Case6"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase6"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase6"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase6[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case6"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case6"/> - <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase6[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case6"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case6"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case6"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase6[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case6"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case6"> + <actualResult type="string">$getListOfThumbnailsCase6[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase6"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase6"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase6"> @@ -317,18 +379,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case7"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase7"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase7"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case7"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase7[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case7"/> - <assertRegExp expected="|{{placeholderThumbnailImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase7[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case7"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case7"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case7"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase7[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case7"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[0]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[1]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[2]</actualResult> + <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case7"> + <actualResult type="string">$getListOfThumbnailsCase7[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase7"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase7"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase7"> @@ -341,18 +415,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case8"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase8"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase8"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase8[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case8"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case8"/> - <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase8[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case8"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case8"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case8"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase8[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case8"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case8"> + <actualResult type="string">$getListOfThumbnailsCase8[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase8"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase8"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase8"> @@ -366,12 +452,18 @@ <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF1Case9"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase9"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase9"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case9"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case9"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase9[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case9"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case9"> + <actualResult type="string">$getListOfThumbnailsCase9[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case9"> + <actualResult type="string">$getListOfThumbnailsCase9[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case9"> + <actualResult type="string">$getListOfThumbnailsCase9[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase9"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase9"/> <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase9"> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml similarity index 54% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml index 49373bb7bebf9..5c3789417a636 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminAssertCustomerIsSubscribedToNewsletters"> <annotations> <description>Verify that check box "Newsletter subscribed" is checked on "Newsletter" tab on customer edit page.</description> @@ -20,15 +19,4 @@ <waitForPageLoad stepKey="waitForShowNewsletterTab"/> <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="assertSubscribedToNewsletter"/> </actionGroup> - - <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> - <annotations> - <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="storeView"/> - </arguments> - - <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml new file mode 100644 index 0000000000000..809e244031068 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + + <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml similarity index 60% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml index 49ea772569cc0..87253fb833a1f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSubscribeCustomerToNewsletters"> <annotations> <description>Set checkbox "Newsletter subscribed" on "Newsletter" tab on customer edit page.</description> @@ -23,14 +22,4 @@ <waitForPageLoad stepKey="waitForSaving"/> <see userInput="You saved the customer." stepKey="seeSuccessMessage"/> </actionGroup> - - <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> - <annotations> - <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="storeView"/> - </arguments> - <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml new file mode 100644 index 0000000000000..f80a84e083435 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml deleted file mode 100644 index 46f45ce3802f1..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ /dev/null @@ -1,184 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<!-- -NOTICE: Action Groups in this file are DEPRECATED and SHOULD NOT BE USED anymore. - Please find the Comment with proper replacement for each of ActionGroups provided. ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="NavigateToCustomerGroupPage" extends="AdminNavigateToCustomerGroupPageActionGroup" deprecated="Use AdminNavigateToCustomerGroupPageActionGroup"/> - <actionGroup name="AdminCreateCustomerWithWebSiteAndGroup" deprecated="Use `AdminCreateCustomerWithWebSiteAndGroupActionGroup` instead"> - <annotations> - <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="customerData" defaultValue="Simple_US_Customer"/> - <argument name="website" type="string" defaultValue="{{_defaultWebsite.name}}"/> - <argument name="storeView" type="string" defaultValue="{{_defaultStore.name}}"/> - </arguments> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> - <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> - <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> - <selectOption selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerData.group}}" stepKey="selectCustomerGroup"/> - <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> - <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> - <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> - <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> - <waitForElement selector="{{AdminCustomerAccountInformationSection.storeView}}" stepKey="waitForCustomerStoreViewExpand"/> - <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> - <waitForPageLoad stepKey="waitForCustomersPage"/> - <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> - </actionGroup> - - <actionGroup name="AdminEditCustomerAddressNoZipNoState" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressNoZipNoStateActionGroup` instead"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'. Clicks on 'Set Default' for Billing/Shipping.</description> - </annotations> - - <remove keyForRemoval="selectState"/> - <remove keyForRemoval="fillZipCode"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> - - <actionGroup name="AdminEditCustomerAddressSetDefaultShippingAndBilling" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup` instead"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'.</description> - </annotations> - - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> - - <actionGroup name="EnterCustomerAddressInfo" deprecated="Use `EnterCustomerAddressInfoActionGroup` instead"> - <annotations> - <description>Fills in the provided Customer details (First/Last Name, Company, Phone # and Address) on the Admin Customer creation/edit page. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="Address"/> - </arguments> - - <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPage"/> - <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> - <fillField stepKey="fillLastName" selector="{{StorefrontCustomerAddressSection.lastName}}" userInput="{{Address.lastname}}"/> - <fillField stepKey="fillCompany" selector="{{StorefrontCustomerAddressSection.company}}" userInput="{{Address.company}}"/> - <fillField stepKey="fillPhoneNumber" selector="{{StorefrontCustomerAddressSection.phoneNumber}}" userInput="{{Address.telephone}}"/> - <fillField stepKey="fillStreetAddress1" selector="{{StorefrontCustomerAddressSection.streetAddress1}}" userInput="{{Address.street[0]}}"/> - <fillField stepKey="fillStreetAddress2" selector="{{StorefrontCustomerAddressSection.streetAddress2}}" userInput="{{Address.street[1]}}"/> - <fillField stepKey="fillCityName" selector="{{StorefrontCustomerAddressSection.city}}" userInput="{{Address.city}}"/> - <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> - <selectOption stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvince}}" userInput="{{Address.state}}"/> - <fillField stepKey="fillZip" selector="{{StorefrontCustomerAddressSection.zip}}" userInput="{{Address.postcode}}"/> - <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> - </actionGroup> - - <!-- Fills State Field instead of selecting it--> - <actionGroup name="EnterCustomerAddressInfoFillState" extends="EnterCustomerAddressInfo" deprecated="Use `CreateSystemBackupActionGroup` instead"> - <annotations> - <description>EXTENDS: EnterCustomerAddressInfo. Fills the State field.</description> - </annotations> - - <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> - </actionGroup> - - <actionGroup name="VerifyCustomerBillingAddress" deprecated="Use `VerifyCustomerBillingAddressActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address is present and correct on the Storefront Customer Dashboard Address section.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerShippingAddress" deprecated="Use `VerifyCustomerShippingAddressActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerBillingAddressWithState" deprecated="Use `VerifyCustomerBillingAddressWithStateActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerShippingAddressWithState" deprecated="Use `VerifyCustomerShippingAddressWithStateActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerNameOnFrontend" deprecated="Use `VerifyCustomerNameOnFrontendActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard page. Validates that the Customer First/Last Name is present and correct.</description> - </annotations> - <arguments> - <argument name="customer"/> - </arguments> - - <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> - <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.firstName}}" userInput="{{customer.firstname}}" stepKey="seeAssertCustomerFirstName"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.lastName}}" userInput="{{customer.lastname}}" stepKey="seeAssertCustomerLastName"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml new file mode 100644 index 0000000000000..37b543f68935a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml @@ -0,0 +1,33 @@ +<?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="AdminCreateCustomerWithWebSiteAndGroup" deprecated="Use `AdminCreateCustomerWithWebSiteAndGroupActionGroup` instead"> + <annotations> + <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="customerData" defaultValue="Simple_US_Customer"/> + <argument name="website" type="string" defaultValue="{{_defaultWebsite.name}}"/> + <argument name="storeView" type="string" defaultValue="{{_defaultStore.name}}"/> + </arguments> + + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> + <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> + <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> + <selectOption selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerData.group}}" stepKey="selectCustomerGroup"/> + <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> + <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> + <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> + <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> + <waitForElement selector="{{AdminCustomerAccountInformationSection.storeView}}" stepKey="waitForCustomerStoreViewExpand"/> + <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> + <waitForPageLoad stepKey="waitForCustomersPage"/> + <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml new file mode 100644 index 0000000000000..ebfd011115459 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEditCustomerAddressNoZipNoState" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressNoZipNoStateActionGroup` instead"> + <annotations> + <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'. Clicks on 'Set Default' for Billing/Shipping.</description> + </annotations> + + <remove keyForRemoval="selectState"/> + <remove keyForRemoval="fillZipCode"/> + <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> + <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml new file mode 100644 index 0000000000000..326eee275f1f1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AdminEditCustomerAddressSetDefaultShippingAndBilling" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup` instead"> + <annotations> + <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'.</description> + </annotations> + + <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> + <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml new file mode 100644 index 0000000000000..0634e0564f04b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml @@ -0,0 +1,32 @@ +<?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="EnterCustomerAddressInfo" deprecated="Use `EnterCustomerAddressInfoActionGroup` instead"> + <annotations> + <description>Fills in the provided Customer details (First/Last Name, Company, Phone # and Address) on the Admin Customer creation/edit page. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="Address"/> + </arguments> + + <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPage"/> + <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{StorefrontCustomerAddressSection.lastName}}" userInput="{{Address.lastname}}"/> + <fillField stepKey="fillCompany" selector="{{StorefrontCustomerAddressSection.company}}" userInput="{{Address.company}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{StorefrontCustomerAddressSection.phoneNumber}}" userInput="{{Address.telephone}}"/> + <fillField stepKey="fillStreetAddress1" selector="{{StorefrontCustomerAddressSection.streetAddress1}}" userInput="{{Address.street[0]}}"/> + <fillField stepKey="fillStreetAddress2" selector="{{StorefrontCustomerAddressSection.streetAddress2}}" userInput="{{Address.street[1]}}"/> + <fillField stepKey="fillCityName" selector="{{StorefrontCustomerAddressSection.city}}" userInput="{{Address.city}}"/> + <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> + <selectOption stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvince}}" userInput="{{Address.state}}"/> + <fillField stepKey="fillZip" selector="{{StorefrontCustomerAddressSection.zip}}" userInput="{{Address.postcode}}"/> + <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml new file mode 100644 index 0000000000000..0705d1d0c8f2a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml @@ -0,0 +1,17 @@ +<?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="EnterCustomerAddressInfoFillState" extends="EnterCustomerAddressInfo" deprecated="Use `CreateSystemBackupActionGroup` instead"> + <annotations> + <description>EXTENDS: EnterCustomerAddressInfo. Fills the State field.</description> + </annotations> + + <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml new file mode 100644 index 0000000000000..606089acf15d6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml @@ -0,0 +1,11 @@ +<?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="NavigateToCustomerGroupPage" extends="AdminNavigateToCustomerGroupPageActionGroup" deprecated="Use AdminNavigateToCustomerGroupPageActionGroup"/> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml new file mode 100644 index 0000000000000..dd5bff2d5488c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml @@ -0,0 +1,29 @@ +<?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="VerifyCustomerBillingAddress" deprecated="Use `VerifyCustomerBillingAddressActionGroup` instead"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address is present and correct on the Storefront Customer Dashboard Address section.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml new file mode 100644 index 0000000000000..47403b23d7026 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml @@ -0,0 +1,28 @@ +<?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="VerifyCustomerBillingAddressWithState" deprecated="Use `VerifyCustomerBillingAddressWithStateActionGroup` instead"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address, including the State, is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml new file mode 100644 index 0000000000000..cc01f3ade85ac --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerNameOnFrontend" deprecated="Use `VerifyCustomerNameOnFrontendActionGroup` instead"> + <annotations> + <description>Goes to the Storefront Customer Dashboard page. Validates that the Customer First/Last Name is present and correct.</description> + </annotations> + <arguments> + <argument name="customer"/> + </arguments> + + <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> + <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> + <seeInField selector="{{StorefrontCustomerAccountInformationSection.firstName}}" userInput="{{customer.firstname}}" stepKey="seeAssertCustomerFirstName"/> + <seeInField selector="{{StorefrontCustomerAccountInformationSection.lastName}}" userInput="{{customer.lastname}}" stepKey="seeAssertCustomerLastName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml new file mode 100644 index 0000000000000..1d9f3fd600155 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml @@ -0,0 +1,28 @@ +<?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="VerifyCustomerShippingAddress" deprecated="Use `VerifyCustomerShippingAddressActionGroup` instead"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml new file mode 100644 index 0000000000000..3291f9c8a7ef2 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml @@ -0,0 +1,28 @@ +<?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="VerifyCustomerShippingAddressWithState" deprecated="Use `VerifyCustomerShippingAddressWithStateActionGroup` instead"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address, including the State, is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml similarity index 80% rename from app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml rename to app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml index c4a4d650c1e59..40fc23b6c72c1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml @@ -5,13 +5,8 @@ * 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="AdminCustomerShoppingCartSection"> - <element name="createOrderButton" type="button" selector="button[title='Create Order']"/> - </section> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerShoppingCartProductItemSection"> <element name="productItem" type="button" selector="#dt-products"/> <element name="productNameFilter" type="input" selector="#source_products_filter_name"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml new file mode 100644 index 0000000000000..06db56c481fd4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml @@ -0,0 +1,13 @@ +<?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="AdminCustomerShoppingCartSection"> + <element name="createOrderButton" type="button" selector="button[title='Create Order']"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml deleted file mode 100644 index 9fc26a03b04ee..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?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="StorefrontCustomerCreateFormSection"> - <element name="firstnameField" type="input" selector="#firstname"/> - <element name="lastnameField" type="input" selector="#lastname"/> - <element name="lastnameLabel" type="text" selector="//label[@for='lastname']"/> - <element name="signUpForNewsletter" type="checkbox" selector="//div/input[@name='is_subscribed']"/> - <element name="emailField" type="input" selector="#email_address"/> - <element name="passwordField" type="input" selector="#password"/> - <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> - <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> - <element name="passwordErrorMessages" type="text" selector="#password-error"/> - </section> - <section name="StoreFrontCustomerAdvancedAttributesSection"> - <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> - <element name="textAreaAttribute" type="input" selector="//textarea[@id='{{var}}']" parameterized="true" /> - <element name="multiLineFirstAttribute" type="input" selector="//input[@id='{{var}}_0']" parameterized="true" /> - <element name="multiLineSecondAttribute" type="input" selector="//input[@id='{{var}}_1']" parameterized="true" /> - <element name="datedAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> - <element name="dropDownAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true" /> - <element name="dropDownOptionAttribute" type="text" selector="//*[@id='{{var}}']/option[2]" parameterized="true" /> - <element name="multiSelectFirstOptionAttribute" type="text" selector="//select[@id='{{var}}']/option[3]" parameterized="true" /> - <element name="yesNoAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true" /> - <element name="yesNoOptionAttribute" type="select" selector="//select[@id='{{var}}']/option[2]" parameterized="true" /> - <element name="selectedOption" type="text" selector="//select[@id='{{var}}']/option[@selected='selected']" parameterized="true"/> - <element name="attributeLabel" type="text" selector="//span[text()='{{attributeLabel}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml new file mode 100644 index 0000000000000..b2f96eb539c08 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml @@ -0,0 +1,24 @@ +<?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="StoreFrontCustomerAdvancedAttributesSection"> + <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true"/> + <element name="textAreaAttribute" type="input" selector="//textarea[@id='{{var}}']" parameterized="true"/> + <element name="multiLineFirstAttribute" type="input" selector="//input[@id='{{var}}_0']" parameterized="true"/> + <element name="multiLineSecondAttribute" type="input" selector="//input[@id='{{var}}_1']" parameterized="true"/> + <element name="datedAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true"/> + <element name="dropDownAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true"/> + <element name="dropDownOptionAttribute" type="text" selector="//*[@id='{{var}}']/option[2]" parameterized="true"/> + <element name="multiSelectFirstOptionAttribute" type="text" selector="//select[@id='{{var}}']/option[3]" parameterized="true"/> + <element name="yesNoAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true"/> + <element name="yesNoOptionAttribute" type="select" selector="//select[@id='{{var}}']/option[2]" parameterized="true"/> + <element name="selectedOption" type="text" selector="//select[@id='{{var}}']/option[@selected='selected']" parameterized="true"/> + <element name="attributeLabel" type="text" selector="//span[text()='{{attributeLabel}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml new file mode 100644 index 0000000000000..0fb05c319c90a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.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="StorefrontCustomerCreateFormSection"> + <element name="firstnameField" type="input" selector="#firstname"/> + <element name="lastnameField" type="input" selector="#lastname"/> + <element name="lastnameLabel" type="text" selector="//label[@for='lastname']"/> + <element name="signUpForNewsletter" type="checkbox" selector="//div/input[@name='is_subscribed']"/> + <element name="emailField" type="input" selector="#email_address"/> + <element name="passwordField" type="input" selector="#password"/> + <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> + <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> + <element name="passwordErrorMessages" type="text" selector="#password-error"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml similarity index 62% rename from app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml rename to app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml index 85d0fd166a77e..c1e0ac6573be7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml @@ -5,15 +5,8 @@ * 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="StorefrontCustomerDashboardAccountInformationSection"> - <element name="ContactInformation" type="textarea" selector=".box.box-information .box-content"/> - <element name="edit" type="block" selector=".action.edit" timeout="15"/> - <element name="changePassword" type="block" selector=".action.change-password" timeout="15"/> - <element name="editLink" type="text" selector=".box-information .edit"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerAddressSection"> <element name="firstName" type="input" selector="#firstname"/> <element name="lastName" type="input" selector="#lastname"/> @@ -28,7 +21,4 @@ <element name="country" type="select" selector="#country"/> <element name="saveAddress" type="button" selector="[data-action='save-address']" timeout="30"/> </section> - <section name="StorefrontCustomerRecentOrdersSection"> - <element name="orderTotal" type="text" selector=".total .price"/> - </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml new file mode 100644 index 0000000000000..74efa8daf0aca --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.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="StorefrontCustomerDashboardAccountInformationSection"> + <element name="ContactInformation" type="textarea" selector=".box.box-information .box-content"/> + <element name="edit" type="block" selector=".action.edit" timeout="15"/> + <element name="changePassword" type="block" selector=".action.change-password" timeout="15"/> + <element name="editLink" type="text" selector=".box-information .edit"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml new file mode 100644 index 0000000000000..324b44354bf4f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml @@ -0,0 +1,13 @@ +<?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="StorefrontCustomerRecentOrdersSection"> + <element name="orderTotal" type="text" selector=".total .price"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml deleted file mode 100644 index 6b333a0c714c0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?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="StorefrontCustomerSignInFormSection"> - <element name="emailField" type="input" selector="#email"/> - <element name="passwordField" type="input" selector="#pass"/> - <element name="signInAccountButton" type="button" selector="#send2" timeout="30"/> - <element name="forgotPasswordLink" type="button" selector=".action.remind" timeout="10"/> - <element name="customerLoginBlock" type="text" selector=".login-container .block.block-customer-login"/> - </section> - <section name="StorefrontCustomerSignInPopupFormSection"> - <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> - <element name="email" type="input" selector="#customer-email"/> - <element name="password" type="input" selector="#pass"/> - <element name="signIn" type="button" selector="#send2" timeout="30"/> - <element name="createAnAccount" type="button" selector="//div[contains(@class,'actions-toolbar')]//a[contains(.,'Create an Account')]" timeout="30"/> - </section> - <section name="StorefrontCustomerSignInLinkSection"> - <element name="signInLink" type="button" selector=".action-auth-toggle" timeout="30"/> - <element name="email" type="input" selector="#login-email"/> - <element name="password" type="input" selector="#login-password"/> - <element name="signInBtn" type="button" selector="//button[contains(@class, 'action-login') and not(contains(@id,'send2'))]" timeout="30"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml new file mode 100644 index 0000000000000..2e40610c18f82 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.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="StorefrontCustomerSignInFormSection"> + <element name="emailField" type="input" selector="#email"/> + <element name="passwordField" type="input" selector="#pass"/> + <element name="signInAccountButton" type="button" selector="#send2" timeout="30"/> + <element name="forgotPasswordLink" type="button" selector=".action.remind" timeout="10"/> + <element name="customerLoginBlock" type="text" selector=".login-container .block.block-customer-login"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml new file mode 100644 index 0000000000000..3f6867499399e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.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="StorefrontCustomerSignInLinkSection"> + <element name="signInLink" type="button" selector=".action-auth-toggle" timeout="30"/> + <element name="email" type="input" selector="#login-email"/> + <element name="password" type="input" selector="#login-password"/> + <element name="signInBtn" type="button" selector="//button[contains(@class, 'action-login') and not(contains(@id,'send2'))]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml new file mode 100644 index 0000000000000..c33736a54f442 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.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="StorefrontCustomerSignInPopupFormSection"> + <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> + <element name="email" type="input" selector="#customer-email"/> + <element name="password" type="input" selector="#pass"/> + <element name="signIn" type="button" selector="#send2" timeout="30"/> + <element name="createAnAccount" type="button" selector="//div[contains(@class,'actions-toolbar')]//a[contains(.,'Create an Account')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml similarity index 61% rename from app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml rename to app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml index 4442e317694ee..56b8731e80f72 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml @@ -5,17 +5,11 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="LoginFormSection"> <element name="username" type="input" selector="#username"/> <element name="password" type="input" selector="#login"/> <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> </section> - - <section name="SignOutSection"> - <element name="admin" type="button" selector=".admin__action-dropdown-text"/> - <element name="logout" type="button" selector="//*[contains(text(), 'Sign Out')]"/> - </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml new file mode 100644 index 0000000000000..763c09923ea2d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="SignOutSection"> + <element name="admin" type="button" selector=".admin__action-dropdown-text"/> + <element name="logout" type="button" selector="//*[contains(text(), 'Sign Out')]"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml deleted file mode 100644 index 426015b75fdb9..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml +++ /dev/null @@ -1,308 +0,0 @@ -<?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="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Info from Default to Non-Default in Admin"/> - <description value="Update Customer Info from Default to Non-Default in Admin"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-13619"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_Customer_Without_Address"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <!-- Reset customer grid filter --> - <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> - <waitForPageLoad stepKey="waitForCustomersGrid"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> - <waitForPageLoad stepKey="waitForCustomerEditPage"/> - <!-- Update Customer Account Information --> - <actionGroup stepKey="editCustomerInformation" ref="AdminEditCustomerAccountInformationActionGroup"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <!--Update Customer Addresses --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup"> - <argument name="customerAddress" value="CustomerAddressSimple"/> - </actionGroup> - <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> - <!-- Assert Customer in Customer grid --> - <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> - <waitForPageLoad stepKey="waitForCustomersGrid"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> - <actionGroup stepKey="filterByEamil" ref="AdminFilterCustomerGridByEmail"> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <actionGroup stepKey="checkCustomerInGrid" ref="AdminAssertCustomerInCustomersGrid"> - <argument name="text" value="updated$$customer.email$$"/> - <argument name="row" value="1"/> - </actionGroup> - <!-- Assert Customer in Customer Form --> - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPageAfterSave"/> - <waitForPageLoad stepKey="waitForCustomerEditPageAfterSave"/> - <!-- Assert Customer Account Information --> - <actionGroup stepKey="checkCustomerAccountInformation" ref="AdminAssertCustomerAccountInformation"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> - <argument name="state" value="{{CustomerAddressSimple.state}}"/> - <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> - <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> - <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> - <argument name="state" value="{{CustomerAddressSimple.state}}"/> - <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> - <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> - <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - </test> - - <test name="AdminUpdateCustomerAddressNoZipNoStateTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> - <description value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13621"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <after> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - </after> - - <!-- Remove steps that are not used for this test --> - <remove keyForRemoval="editCustomerInformation"/> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - <remove keyForRemoval="filterByEamil"/> - <remove keyForRemoval="checkCustomerInGrid"/> - <remove keyForRemoval="checkCustomerAccountInformation"/> - - <!--Update Customer Addresses With No Zip and No State --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressNoZipNoStateActionGroup"> - <argument name="customerAddress" value="addressNoZipNoState"/> - </actionGroup> - - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> - <argument name="country" value="{{addressNoZipNoState.country_id}}"/> - <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> - <argument name="country" value="{{addressNoZipNoState.country_id}}"/> - <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> - </actionGroup> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="checkDefaultShipping" > - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </test> - - <test name="AdminUpdateCustomerAddressNoBillingNoShippingTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Address, default billing/shipping unchecked in Admin"/> - <description value="Update Customer Address, default billing/shipping unchecked in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13622"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <after> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - </after> - - <!-- Remove steps that are not used for this test --> - <remove keyForRemoval="editCustomerInformation"/> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - <remove keyForRemoval="filterByEamil"/> - <remove keyForRemoval="checkCustomerInGrid"/> - <remove keyForRemoval="checkCustomerAccountInformation"/> - <remove keyForRemoval="checkDefaultBilling"/> - <remove keyForRemoval="checkDefaultShipping"/> - - <!--Update Customer Addresses With Default Billing and Shipping Unchecked --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressesFromActionGroup"> - <argument name="customerAddress" value="CustomerAddressSimple"/> - </actionGroup> - - <!-- Check Customer Address in Customer Form --> - <actionGroup stepKey="checkNoDefaultBilling" ref="AdminAssertCustomerNoDefaultBillingAddress" after="waitForCustomerEditPageAfterSave"/> - <actionGroup stepKey="checkNoDefaultShipping" ref="AdminAssertCustomerNoDefaultShippingAddress" after="checkNoDefaultBilling"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid" after="checkNoDefaultShipping"/> - <actionGroup stepKey="searchAddress" ref="AdminFilterCustomerAddressGridByPhoneNumber" after="resetFilter"> - <argument name="phone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="searchAddress"> - <argument name="text" value="{{CustomerAddressSimple.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStreetInGrid"> - <argument name="text" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressPhoneInGrid"> - <argument name="text" value="{{CustomerAddressSimple.state}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStateInGrid"> - <argument name="text" value="{{CustomerAddressSimple.city}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressCityInGrid"> - <argument name="text" value="{{CustomerAddressSimple.country_id}}"/> - </actionGroup> - <actionGroup stepKey="resetFilterWhenDone" ref="AdminResetFilterInCustomerAddressGrid" after="checkAddressCountryInGrid"/> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="resetFilterWhenDone" > - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </test> - - <test name="AdminDeleteCustomerAddressTest"> - <annotations> - <features value="Customer"/> - <stories value="Delete Customer Address in Admin"/> - <title value="Delete Customer Address in Admin"/> - <description value="Delete Customer Address in Admin"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-13623"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_US_Customer_Multiple_Addresses"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> - <waitForPageLoad stepKey="waitForCustomerEditPage"/> - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{US_Address_NY.street[0]}}"/> - <argument name="state" value="{{US_Address_NY.state}}"/> - <argument name="postcode" value="{{US_Address_NY.postcode}}"/> - <argument name="country" value="{{US_Address_NY.country}}"/> - <argument name="telephone" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{US_Address_NY.street[0]}}"/> - <argument name="state" value="{{US_Address_NY.state}}"/> - <argument name="postcode" value="{{US_Address_NY.postcode}}"/> - <argument name="country" value="{{US_Address_NY.country}}"/> - <argument name="telephone" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid"/> - <!-- Assert 2 records in Customer Address Grid --> - <actionGroup stepKey="see2Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> - <argument name="number" value="2"/> - </actionGroup> - <!-- Assert Address 1 in Grid --> - <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.state}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.city}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.country}}"/> - </actionGroup> - <!-- Assert Address 2 in Grid --> - <actionGroup stepKey="checkAddressStreetInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.city}}"/> - </actionGroup> - <!-- Delete Customer in Customer Address Grid --> - <actionGroup stepKey="deleteAddress" ref="AdminDeleteAddressInCustomersAddressGrid"> - <argument name="row" value="0"/> - </actionGroup> - <!-- Assert 1 record in Customer Address Grid --> - <actionGroup stepKey="see1Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> - <argument name="number" value="1"/> - </actionGroup> - <actionGroup stepKey="saveAndContinue" ref="AdminCustomerSaveAndContinue"/> - <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <!-- Assert Customer Address Book --> - <actionGroup stepKey="goToAddressBook" ref="StorefrontCustomerGoToSidebarMenu"> - <argument name="menu" value="Address Book"/> - </actionGroup> - <actionGroup stepKey="assertAddressNumber" ref="StorefrontCustomerAddressBookNumberOfAddresses"> - <argument name="number" value="1"/> - </actionGroup> - <actionGroup stepKey="assertNoAddress1" ref="StorefrontCustomerAddressBookNotContains"> - <argument name="text" value="{{US_Address_NY.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="assertAddress2" ref="StorefrontCustomerAddressBookContains"> - <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml new file mode 100644 index 0000000000000..b344458363999 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml @@ -0,0 +1,112 @@ +<?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="AdminDeleteCustomerAddressTest"> + <annotations> + <features value="Customer"/> + <stories value="Delete Customer Address in Admin"/> + <title value="Delete Customer Address in Admin"/> + <description value="Delete Customer Address in Admin"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-13623"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_US_Customer_Multiple_Addresses"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> + <waitForPageLoad stepKey="waitForCustomerEditPage"/> + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{US_Address_NY.street[0]}}"/> + <argument name="state" value="{{US_Address_NY.state}}"/> + <argument name="postcode" value="{{US_Address_NY.postcode}}"/> + <argument name="country" value="{{US_Address_NY.country}}"/> + <argument name="telephone" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{US_Address_NY.street[0]}}"/> + <argument name="state" value="{{US_Address_NY.state}}"/> + <argument name="postcode" value="{{US_Address_NY.postcode}}"/> + <argument name="country" value="{{US_Address_NY.country}}"/> + <argument name="telephone" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid"/> + <!-- Assert 2 records in Customer Address Grid --> + <actionGroup stepKey="see2Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> + <argument name="number" value="2"/> + </actionGroup> + <!-- Assert Address 1 in Grid --> + <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.state}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.city}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.country}}"/> + </actionGroup> + <!-- Assert Address 2 in Grid --> + <actionGroup stepKey="checkAddressStreetInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.city}}"/> + </actionGroup> + <!-- Delete Customer in Customer Address Grid --> + <actionGroup stepKey="deleteAddress" ref="AdminDeleteAddressInCustomersAddressGrid"> + <argument name="row" value="0"/> + </actionGroup> + <!-- Assert 1 record in Customer Address Grid --> + <actionGroup stepKey="see1Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> + <argument name="number" value="1"/> + </actionGroup> + <actionGroup stepKey="saveAndContinue" ref="AdminCustomerSaveAndContinue"/> + <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Assert Customer Address Book --> + <actionGroup stepKey="goToAddressBook" ref="StorefrontCustomerGoToSidebarMenu"> + <argument name="menu" value="Address Book"/> + </actionGroup> + <actionGroup stepKey="assertAddressNumber" ref="StorefrontCustomerAddressBookNumberOfAddresses"> + <argument name="number" value="1"/> + </actionGroup> + <actionGroup stepKey="assertNoAddress1" ref="StorefrontCustomerAddressBookNotContains"> + <argument name="text" value="{{US_Address_NY.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="assertAddress2" ref="StorefrontCustomerAddressBookContains"> + <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml new file mode 100644 index 0000000000000..c6e370fb6a76b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.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="AdminUpdateCustomerAddressNoBillingNoShippingTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Address, default billing/shipping unchecked in Admin"/> + <description value="Update Customer Address, default billing/shipping unchecked in Admin"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13622"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + </after> + + <!-- Remove steps that are not used for this test --> + <remove keyForRemoval="editCustomerInformation"/> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + <remove keyForRemoval="filterByEamil"/> + <remove keyForRemoval="checkCustomerInGrid"/> + <remove keyForRemoval="checkCustomerAccountInformation"/> + <remove keyForRemoval="checkDefaultBilling"/> + <remove keyForRemoval="checkDefaultShipping"/> + + <!--Update Customer Addresses With Default Billing and Shipping Unchecked --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressesFromActionGroup"> + <argument name="customerAddress" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check Customer Address in Customer Form --> + <actionGroup stepKey="checkNoDefaultBilling" ref="AdminAssertCustomerNoDefaultBillingAddress" after="waitForCustomerEditPageAfterSave"/> + <actionGroup stepKey="checkNoDefaultShipping" ref="AdminAssertCustomerNoDefaultShippingAddress" after="checkNoDefaultBilling"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid" after="checkNoDefaultShipping"/> + <actionGroup stepKey="searchAddress" ref="AdminFilterCustomerAddressGridByPhoneNumber" after="resetFilter"> + <argument name="phone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="searchAddress"> + <argument name="text" value="{{CustomerAddressSimple.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStreetInGrid"> + <argument name="text" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressPhoneInGrid"> + <argument name="text" value="{{CustomerAddressSimple.state}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStateInGrid"> + <argument name="text" value="{{CustomerAddressSimple.city}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressCityInGrid"> + <argument name="text" value="{{CustomerAddressSimple.country_id}}"/> + </actionGroup> + <actionGroup stepKey="resetFilterWhenDone" ref="AdminResetFilterInCustomerAddressGrid" after="checkAddressCountryInGrid"/> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="resetFilterWhenDone"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml new file mode 100644 index 0000000000000..d81d7da6b5b07 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml @@ -0,0 +1,62 @@ +<?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="AdminUpdateCustomerAddressNoZipNoStateTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> + <description value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13621"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + </after> + + <!-- Remove steps that are not used for this test --> + <remove keyForRemoval="editCustomerInformation"/> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + <remove keyForRemoval="filterByEamil"/> + <remove keyForRemoval="checkCustomerInGrid"/> + <remove keyForRemoval="checkCustomerAccountInformation"/> + + <!--Update Customer Addresses With No Zip and No State --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressNoZipNoStateActionGroup"> + <argument name="customerAddress" value="addressNoZipNoState"/> + </actionGroup> + + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> + <argument name="country" value="{{addressNoZipNoState.country_id}}"/> + <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> + <argument name="country" value="{{addressNoZipNoState.country_id}}"/> + <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> + </actionGroup> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="checkDefaultShipping"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml new file mode 100644 index 0000000000000..a6ccfda4939f9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.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="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Info from Default to Non-Default in Admin"/> + <description value="Update Customer Info from Default to Non-Default in Admin"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-13619"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_Customer_Without_Address"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <!-- Reset customer grid filter --> + <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> + <waitForPageLoad stepKey="waitForCustomerEditPage"/> + <!-- Update Customer Account Information --> + <actionGroup stepKey="editCustomerInformation" ref="AdminEditCustomerAccountInformationActionGroup"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <!--Update Customer Addresses --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup"> + <argument name="customerAddress" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> + <!-- Assert Customer in Customer grid --> + <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> + <actionGroup stepKey="filterByEamil" ref="AdminFilterCustomerGridByEmail"> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <actionGroup stepKey="checkCustomerInGrid" ref="AdminAssertCustomerInCustomersGrid"> + <argument name="text" value="updated$$customer.email$$"/> + <argument name="row" value="1"/> + </actionGroup> + <!-- Assert Customer in Customer Form --> + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPageAfterSave"/> + <waitForPageLoad stepKey="waitForCustomerEditPageAfterSave"/> + <!-- Assert Customer Account Information --> + <actionGroup stepKey="checkCustomerAccountInformation" ref="AdminAssertCustomerAccountInformation"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> + <argument name="state" value="{{CustomerAddressSimple.state}}"/> + <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> + <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> + <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> + <argument name="state" value="{{CustomerAddressSimple.state}}"/> + <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> + <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> + <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml index e1342f26809ee..ff60ac92853c5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml @@ -31,7 +31,9 @@ </actionGroup> <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> - <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> + <assertElementContainsAttribute stepKey="checkIfGroupNameIsDisabled"> + <expectedResult selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" type="string">true</expectedResult> + </assertElementContainsAttribute> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </test> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml new file mode 100644 index 0000000000000..e6c114334000e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml @@ -0,0 +1,28 @@ +<?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="ChangingAllCustomerGroupViaGridTest" extends="ChangingSingleCustomerGroupViaGridTest"> + <annotations> + <title value="DEPRECATED Change all customers' group via grid"/> + <description value="Select All customers to change their group"/> + <severity value="MAJOR"/> + <testCaseId value="MC-10924"/> + <stories value="Change Customer Group"/> + <group value="customer"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="DEPRECATED">Use AdminChangeAllCustomersGroupViaGridTest instead</issueId> + </skip> + </annotations> + + <remove keyForRemoval="filterCustomer"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters" before="selectCustomer"/> + <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml similarity index 75% rename from app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml rename to app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml index 0eca9811f17f1..ce6ba7ce05e16 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ChangingSingleCustomerGroupViaGridTest"> <annotations> <title value="DEPRECATED Change a single customer group via grid"/> @@ -60,23 +59,4 @@ <argument name="groupName" value="{{CustomerGroupChange.code}}"/> </actionGroup> </test> - - <test name="ChangingAllCustomerGroupViaGridTest" extends="ChangingSingleCustomerGroupViaGridTest"> - <annotations> - <title value="DEPRECATED Change all customers' group via grid"/> - <description value="Select All customers to change their group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-10924"/> - <stories value="Change Customer Group"/> - <group value="customer"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminChangeAllCustomersGroupViaGridTest instead</issueId> - </skip> - </annotations> - - <remove keyForRemoval="filterCustomer"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters" before="selectCustomer"/> - <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> - </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml deleted file mode 100644 index e9b52fd64135b..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml +++ /dev/null @@ -1,121 +0,0 @@ -<?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="StorefrontAddNewCustomerAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new address"/> - <description value="Storefront user should be able to create a new address via the storefront"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-97364"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> - <test name="StorefrontAddCustomerDefaultAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new default billing/shipping address"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-97364"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddCustomerDefaultAddressActionGroup" stepKey="AddNewDefaultAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> - <test name="StorefrontAddCustomerNonDefaultAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new non default billing/shipping address"/> - <description value="Storefront user should be able to create a new non default address via the storefront"/> - <severity value="BLOCKER"/> - <testCaseId value="MAGETWO-97500"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewNonDefaultAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml new file mode 100644 index 0000000000000..d4f851ee21c25 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml @@ -0,0 +1,42 @@ +<?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="StorefrontAddCustomerDefaultAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new default billing/shipping address"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97364"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCustomerDefaultAddressActionGroup" stepKey="AddNewDefaultAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml new file mode 100644 index 0000000000000..cec7f8460de5a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml @@ -0,0 +1,40 @@ +<?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="StorefrontAddCustomerNonDefaultAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new non default billing/shipping address"/> + <description value="Storefront user should be able to create a new non default address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97500"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewNonDefaultAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml new file mode 100644 index 0000000000000..c3c8bd5d7c40e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml @@ -0,0 +1,43 @@ +<?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="StorefrontAddNewCustomerAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new address"/> + <description value="Storefront user should be able to create a new address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97364"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml deleted file mode 100644 index d9d1c9f2e05a0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml +++ /dev/null @@ -1,122 +0,0 @@ -<?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="StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Add default customer address via the Storefront6"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97501"/> - <group value="customer"/> - <group value="update"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click stepKey="ClickEditDefaultBillingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultBillingAddress}}"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstNameBilling" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> - <see userInput="EditedLastNameBilling" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> - <see userInput="{{US_Address_NY_Default_Shipping.firstname}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> - <see userInput="{{US_Address_NY_Default_Shipping.lastname}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> - </test> - <test name="StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Add default customer address via the Storefront611"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97501"/> - <group value="customer"/> - <group value="update"/> - <skip> - <issueId value="MAGETWO-97504"/> - </skip> - </annotations> - <before> - <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click stepKey="ClickEditDefaultShippingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultShippingAddress}}"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstNameShipping" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> - <see userInput="EditedLastNameShipping" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> - <see userInput="{{US_Address_TX_Default_Billing.firstname}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> - <see userInput="{{US_Address_TX_Default_Billing.lastname}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> - </test> - <test name="StorefrontUpdateCustomerAddressFromGridTest"> - <annotations> - <features value="Customer address"/> - <stories value="Add default customer address via the Storefront7"/> - <title value="Add default customer address via the Storefront7"/> - <description value="Storefront user should be able to create a new default address via the storefront2"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-97502"/> - <group value="customer"/> - <group value="update"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="editAdditionalAddress"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstName" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastName" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstName" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressFirstNameOnGrid"/> - <see userInput="EditedLastName" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressLastNameOnGrid"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml new file mode 100644 index 0000000000000..d41b1cf86da59 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml @@ -0,0 +1,42 @@ +<?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="StorefrontUpdateCustomerAddressFromGridTest"> + <annotations> + <features value="Customer address"/> + <stories value="Add default customer address via the Storefront7"/> + <title value="Add default customer address via the Storefront7"/> + <description value="Storefront user should be able to create a new default address via the storefront2"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-97502"/> + <group value="customer"/> + <group value="update"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="editAdditionalAddress"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstName" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastName" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstName" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressFirstNameOnGrid"/> + <see userInput="EditedLastName" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressLastNameOnGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml new file mode 100644 index 0000000000000..438e875d93749 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml @@ -0,0 +1,43 @@ +<?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="StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Add default customer address via the Storefront6"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-97501"/> + <group value="customer"/> + <group value="update"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click stepKey="ClickEditDefaultBillingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultBillingAddress}}"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> + <see userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> + <see userInput="{{US_Address_NY_Default_Shipping.firstname}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> + <see userInput="{{US_Address_NY_Default_Shipping.lastname}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml new file mode 100644 index 0000000000000..f79ef0a397e27 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml @@ -0,0 +1,46 @@ +<?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="StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Add default customer address via the Storefront611"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-97501"/> + <group value="customer"/> + <group value="update"/> + <skip> + <issueId value="MAGETWO-97504"/> + </skip> + </annotations> + <before> + <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click stepKey="ClickEditDefaultShippingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultShippingAddress}}"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> + <see userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> + <see userInput="{{US_Address_TX_Default_Billing.firstname}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> + <see userInput="{{US_Address_TX_Default_Billing.lastname}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml deleted file mode 100644 index 58c13898de3ee..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?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="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Valid Current Password"/> - <description value="Update Customer Password on Storefront, Valid Current Password"/> - <severity value="BLOCKER"/> - <testCaseId value="MC-10916"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_US_Customer"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer" /> - </after> - - <!--Log in to Storefront as Customer --> - <actionGroup stepKey="login" ref="LoginToStorefrontActionGroup"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <seeInCurrentUrl stepKey="onCustomerAccountPage" url="customer/account"/> - <click stepKey="clickChangePassword" selector="{{StorefrontCustomerDashboardAccountInformationSection.changePassword}}"/> - <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> - <fillField stepKey="fillNewPassword" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> - <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> - <click stepKey="saveChange" selector="{{StorefrontCustomerAccountInformationSection.saveButton}}"/> - <see stepKey="verifyMessage" userInput="You saved the account information." selector="{{StorefrontCustomerMessagesSection.successMessage}}"/> - <actionGroup stepKey="logout" ref="StorefrontCustomerLogoutActionGroup"/> - <actionGroup stepKey="loginWithNewPassword" ref="LoginToStorefrontWithEmailAndPassword"> - <argument name="email" value="$$customer.email$$"/> - <argument name="password" value="$$customer.password$$#"/> - </actionGroup> - <see stepKey="seeMyEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> - </test> - <test name="StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Invalid Current Password"/> - <description value="Update Customer Password on Storefront, Invalid Current Password"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10917"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - - <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> - <see stepKey="verifyMessage" userInput="The password doesn't match this account. Verify the password and try again." selector="{{StorefrontCustomerMessagesSection.errorMessage}}"/> - <remove keyForRemoval="loginWithNewPassword"/> - <remove keyForRemoval="seeMyEmail"/> - </test> - <test name="StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Invalid Confirmation Password"/> - <description value="Update Customer Password on Storefront, Invalid Confirmation Password"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10918"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - - <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> - <see stepKey="verifyMessage" userInput="Please enter the same value again." selector="{{StorefrontCustomerAccountInformationSection.confirmNewPasswordError}}"/> - <remove keyForRemoval="loginWithNewPassword"/> - <remove keyForRemoval="seeMyEmail"/> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml new file mode 100644 index 0000000000000..9e5be5abe95a3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml @@ -0,0 +1,27 @@ +<?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="StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Invalid Confirmation Password"/> + <description value="Update Customer Password on Storefront, Invalid Confirmation Password"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10918"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + + <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> + <see stepKey="verifyMessage" userInput="Please enter the same value again." selector="{{StorefrontCustomerAccountInformationSection.confirmNewPasswordError}}"/> + <remove keyForRemoval="loginWithNewPassword"/> + <remove keyForRemoval="seeMyEmail"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml new file mode 100644 index 0000000000000..1f2c07c325c15 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml @@ -0,0 +1,27 @@ +<?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="StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Invalid Current Password"/> + <description value="Update Customer Password on Storefront, Invalid Current Password"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10917"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + + <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <see stepKey="verifyMessage" userInput="The password doesn't match this account. Verify the password and try again." selector="{{StorefrontCustomerMessagesSection.errorMessage}}"/> + <remove keyForRemoval="loginWithNewPassword"/> + <remove keyForRemoval="seeMyEmail"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml new file mode 100644 index 0000000000000..00871e5f38688 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml @@ -0,0 +1,46 @@ +<?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="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Valid Current Password"/> + <description value="Update Customer Password on Storefront, Valid Current Password"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10916"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + </after> + + <!--Log in to Storefront as Customer --> + <actionGroup stepKey="login" ref="LoginToStorefrontActionGroup"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <seeInCurrentUrl stepKey="onCustomerAccountPage" url="customer/account"/> + <click stepKey="clickChangePassword" selector="{{StorefrontCustomerDashboardAccountInformationSection.changePassword}}"/> + <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <fillField stepKey="fillNewPassword" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> + <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> + <click stepKey="saveChange" selector="{{StorefrontCustomerAccountInformationSection.saveButton}}"/> + <see stepKey="verifyMessage" userInput="You saved the account information." selector="{{StorefrontCustomerMessagesSection.successMessage}}"/> + <actionGroup stepKey="logout" ref="StorefrontCustomerLogoutActionGroup"/> + <actionGroup stepKey="loginWithNewPassword" ref="LoginToStorefrontWithEmailAndPassword"> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="$$customer.password$$#"/> + </actionGroup> + <see stepKey="seeMyEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml deleted file mode 100644 index 424622ef2d735..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?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="VerifyDisabledCustomerGroupFieldTest" extends="AdminVerifyDisabledCustomerGroupFieldTest" deprecated="Use AdminVerifyDisabledCustomerGroupFieldTest instead"/> - <test name="AddingProductWithExpiredSessionTest" extends="StorefrontAddProductToCartWithExpiredSessionTest" deprecated="Use StorefrontAddProductToCartWithExpiredSessionTest"/> - <test name="StorefrontCustomerForgotPasswordTest" extends="StorefrontResetCustomerPasswordSuccessTest" deprecated="Use StorefrontResetCustomerPasswordSuccessTest"/> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml new file mode 100644 index 0000000000000..2d580ab55075c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml @@ -0,0 +1,11 @@ +<?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="AddingProductWithExpiredSessionTest" extends="StorefrontAddProductToCartWithExpiredSessionTest" deprecated="Use StorefrontAddProductToCartWithExpiredSessionTest"/> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml new file mode 100644 index 0000000000000..7c29e00b6a3a9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml @@ -0,0 +1,11 @@ +<?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="StorefrontCustomerForgotPasswordTest" extends="StorefrontResetCustomerPasswordSuccessTest" deprecated="Use StorefrontResetCustomerPasswordSuccessTest"/> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml new file mode 100644 index 0000000000000..58e815b03126d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml @@ -0,0 +1,11 @@ +<?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="VerifyDisabledCustomerGroupFieldTest" extends="AdminVerifyDisabledCustomerGroupFieldTest" deprecated="Use AdminVerifyDisabledCustomerGroupFieldTest instead"/> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..bfa53f9beb4f8 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,27 @@ +<?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="AdminDownloadableProductTypeSwitchingToConfigurableProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Product type switching"/> + <title value="Downloadable product type switching on editing to configurable product"/> + <description value="Downloadable product type switching on editing to configurable product"/> + <testCaseId value="MC-17957"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!-- Open Dropdown and select downloadable product option --> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="waitForSimpleProductPageLoad"/> + <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct" after="checkOptionIsDownloadable"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml similarity index 75% rename from app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml rename to app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml index 07c84e3f8e6e0..a9ceb5e057c3f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml @@ -5,26 +5,8 @@ * 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="AdminDownloadableProductTypeSwitchingToConfigurableProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Product type switching"/> - <title value="Downloadable product type switching on editing to configurable product"/> - <description value="Downloadable product type switching on editing to configurable product"/> - <testCaseId value="MC-17957"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!-- Open Dropdown and select downloadable product option --> - <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="waitForSimpleProductPageLoad"/> - <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct" after="checkOptionIsDownloadable"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> - </test> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductTypeSwitchingToDownloadableProductTest"> <annotations> <features value="Downloadable"/> @@ -80,6 +62,6 @@ <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> - <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink" /> + <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink"/> </test> </tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml deleted file mode 100644 index b5f437996d69b..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ /dev/null @@ -1,131 +0,0 @@ -<?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="AdvanceCatalogSearchDownloadableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product name"/> - <description value="Guest customer should be able to advance search Downloadable product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-142"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product sku"/> - <description value="Guest customer should be able to advance search Downloadable product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-252"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProductUnderscoredSku" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product description"/> - <description value="Guest customer should be able to advance search Downloadable product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-243"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product short description"/> - <description value="Guest customer should be able to advance search Downloadable product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-245"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product price"/> - <description value="Guest customer should be able to advance search Downloadable product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-246"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml new file mode 100644 index 0000000000000..24af53fdf613b --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml @@ -0,0 +1,34 @@ +<?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="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product description"/> + <description value="Guest customer should be able to advance search Downloadable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-243"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml new file mode 100644 index 0000000000000..603270c70913c --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml @@ -0,0 +1,34 @@ +<?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="AdvanceCatalogSearchDownloadableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product name"/> + <description value="Guest customer should be able to advance search Downloadable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-142"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml new file mode 100644 index 0000000000000..c2011ae11a0ac --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml @@ -0,0 +1,34 @@ +<?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="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product price"/> + <description value="Guest customer should be able to advance search Downloadable product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-246"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml new file mode 100644 index 0000000000000..308d631ff230a --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml @@ -0,0 +1,34 @@ +<?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="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product short description"/> + <description value="Guest customer should be able to advance search Downloadable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-245"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml new file mode 100644 index 0000000000000..3ec4177a55556 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml @@ -0,0 +1,34 @@ +<?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="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product sku"/> + <description value="Guest customer should be able to advance search Downloadable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-252"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProductUnderscoredSku" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml index f369e84abf374..9a0d852cada2c 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplateEditPage" url="/admin/email_template/edit/id/{{templateId}}/" area="admin" module="Magento_Email" parameterized="true"> <section name="AdminEmailTemplateEditSection"/> </page> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml index c4ba7aa006203..ba61c42893d41 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplateIndexPage" url="/admin/email_template/" area="admin" module="Magento_Email"> <section name="AdminEmailTemplateIndexSection"/> </page> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml index aae010be27fd8..ab9c84b057c1c 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplatePreviewPage" url="/admin/email_template/preview/" area="admin" module="Magento_Email"> <section name="AdminEmailTemplatePreviewSection"/> </page> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml similarity index 77% rename from app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml rename to app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml index f71bb49aea10a..93ea32fa8f677 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminAddProductsToGroupPanel"> <element name="addSelectedProducts" type="button" selector=".product_form_product_form_grouped_grouped_products_modal button.action-primary" timeout="30"/> <element name="filters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-expand']" timeout="30"/> @@ -17,8 +16,4 @@ <element name="nThCheckbox" type="input" selector="tr[data-repeat-index='{{n}}'] .admin__control-checkbox" parameterized="true"/> <element name="clearFilters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-reset']" timeout="30"/> </section> - - <section name="AdminAddedProductsToGroupGrid"> - <element name="inputByProductName" type="input" selector="//div[@data-index='grouped']//table//tr[td[@data-index='name']//span[text()='{{productName}}']]//td[@data-index='qty']//input" parameterized="true"/> - </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml new file mode 100644 index 0000000000000..290c88b87429f --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml @@ -0,0 +1,13 @@ +<?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="AdminAddedProductsToGroupGrid"> + <element name="inputByProductName" type="input" selector="//div[@data-index='grouped']//table//tr[td[@data-index='name']//span[text()='{{productName}}']]//td[@data-index='qty']//input" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml deleted file mode 100644 index 4fd06ccaa27ec..0000000000000 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml +++ /dev/null @@ -1,323 +0,0 @@ -<?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="AdvanceCatalogSearchGroupedProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product name"/> - <description value="Guest customer should be able to advance search Grouped product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-141"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20464"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product sku"/> - <description value="Guest customer should be able to advance search Grouped product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-146"/> - <group value="GroupedProduct"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProductAndUnderscoredSku" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product description"/> - <description value="Guest customer should be able to advance search Grouped product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-282"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20468"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product short description"/> - <description value="Guest customer should be able to advance search Grouped product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-283"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20469"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product price"/> - <description value="Guest customer should be able to advance search Grouped product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-284"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <getData entity="GetProduct3" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20470"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <getData entity="GetProduct3" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..910c770fb1d3e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml @@ -0,0 +1,41 @@ +<?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="AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20468"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml new file mode 100644 index 0000000000000..599736e7e817e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml @@ -0,0 +1,45 @@ +<?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="AdvanceCatalogSearchGroupedProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description"/> + <description value="Guest customer should be able to advance search Grouped product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-282"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml new file mode 100644 index 0000000000000..b6e4fb71aa40b --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml @@ -0,0 +1,41 @@ +<?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="AdvanceCatalogSearchGroupedProductByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20464"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml new file mode 100644 index 0000000000000..853304c557c8f --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml @@ -0,0 +1,45 @@ +<?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="AdvanceCatalogSearchGroupedProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name"/> + <description value="Guest customer should be able to advance search Grouped product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-141"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml new file mode 100644 index 0000000000000..74dd95fd3249e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml @@ -0,0 +1,50 @@ +<?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="AdvanceCatalogSearchGroupedProductByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20470"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml new file mode 100644 index 0000000000000..4e67f2bd50439 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml @@ -0,0 +1,54 @@ +<?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="AdvanceCatalogSearchGroupedProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price"/> + <description value="Guest customer should be able to advance search Grouped product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-284"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..45822ce9b63e6 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml @@ -0,0 +1,41 @@ +<?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="AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20469"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..4b86a2c085003 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml @@ -0,0 +1,45 @@ +<?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="AdvanceCatalogSearchGroupedProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description"/> + <description value="Guest customer should be able to advance search Grouped product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-283"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml new file mode 100644 index 0000000000000..6e67e41fa447b --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml @@ -0,0 +1,40 @@ +<?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="AdvanceCatalogSearchGroupedProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product sku"/> + <description value="Guest customer should be able to advance search Grouped product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-146"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProductAndUnderscoredSku" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml deleted file mode 100644 index 82dbb416122d8..0000000000000 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml +++ /dev/null @@ -1,52 +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="updateIndexerBySchedule"> - <annotations> - <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update by Schedule'. Clicks on Submit.</description> - </annotations> - <arguments> - <argument name="indexerName" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage"/> - <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> - <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> - <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> - <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> - <waitForPageLoad stepKey="waitForSave"/> - </actionGroup> - - <actionGroup name="updateIndexerOnSave"> - <annotations> - <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update on Save'. Clicks on Submit.</description> - </annotations> - <arguments> - <argument name="indexerName" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage2"/> - <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> - <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> - <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm2"/> - <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> - <waitForPageLoad stepKey="waitForSave2"/> - </actionGroup> - <actionGroup name="AdminReindexAndFlushCache"> - <annotations> - <description>Run reindex and flush cache.</description> - </annotations> - - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml new file mode 100644 index 0000000000000..42b6b047ae73e --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AdminReindexAndFlushCache"> + <annotations> + <description>Run reindex and flush cache.</description> + </annotations> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml new file mode 100644 index 0000000000000..580f288aff10d --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml @@ -0,0 +1,26 @@ +<?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="updateIndexerBySchedule"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update by Schedule'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml new file mode 100644 index 0000000000000..53064e33a5657 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml @@ -0,0 +1,26 @@ +<?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="updateIndexerOnSave"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update on Save'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage2"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm2"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml similarity index 80% rename from app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml rename to app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml index b3e0c430b12e7..944c78e9f7145 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="LayeredNavigationSection"> <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredNavigation" type="select" selector="#catalog_layered_navigation-head"/> @@ -18,8 +17,4 @@ <element name="PriceNavigationStep" type="button" selector="#catalog_layered_navigation_price_range_step"/> <element name="PriceNavigationStepSystemValue" type="button" selector="#catalog_layered_navigation_price_range_step_inherit"/> </section> - - <section name="StorefrontLayeredNavigationSection"> - <element name="shoppingOptionsByName" type="button" selector="//*[text()='Shopping Options']/..//*[contains(text(),'{{arg}}')]" parameterized="true"/> - </section> </sections> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml new file mode 100644 index 0000000000000..d3a3005c296b2 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml @@ -0,0 +1,13 @@ +<?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="StorefrontLayeredNavigationSection"> + <element name="shoppingOptionsByName" type="button" selector="//*[text()='Shopping Options']/..//*[contains(text(),'{{arg}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml index dcd8bfd8d141b..c03a812f7d372 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSalesOrderActionGroup"> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> <grabTextFrom selector="{{AdminOrderTotalSection.subTotal}}" stepKey="grabValueForSubtotal"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml index 559d759e0468d..6ff1524d907e3 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup"> <arguments> <argument name="dataHref" type="string"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml index 35c42225d458b..973a3d759bb56 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckingWithMinicartActionGroup"> <click selector="{{MinicartSection.clickOnCollapsibleDiv}}" stepKey="clickOnCollapsibleDiv"/> <click selector="{{MinicartSection.shippingMethodRadioButton}}" stepKey="clickOnShippingMethodRadioButton"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml index d911488925427..8d33c7fc4a36e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckingWithSingleAddressActionGroup"> <click selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}" stepKey="clickOnCheckoutWithMultipleAddresses"/> <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml index 8cfbe2665d685..a602eb6417205 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="PlaceOrderActionGroup"> <click selector="{{SingleShippingSection.placeOrder}}" stepKey="checkoutMultiShipmentPlaceOrder"/> <waitForPageLoad stepKey="waitForSuccessfullyPlacedOrder"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml index 8a63c42006443..98683f24af48e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ReviewOrderForSingleShipmentActionGroup"> <arguments> <argument name="totalName" type="string" defaultValue="Shipping & Handling"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml index 63fbaea72cc50..3a4fd49f1cb88 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SelectBillingInfoActionGroup"> <waitForPageLoad stepKey="waitForBillingInfoPageLoad"/> <click selector="{{PaymentMethodSection.goToReviewOrder}}" stepKey="goToReviewOrder"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml index 329f1451788cd..1528b1d15654f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SelectSingleShippingInfoActionGroup"> <arguments> <argument name="shippingMethodType" type="string" defaultValue="Fixed"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml index b698f00078bd6..f3c736feecaf2 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontCheckoutShippingSelectMultipleAddressesActionGroup"> <arguments> <argument name="firstAddress" type="string" defaultValue="{{CustomerAddressSimple.street[0]}}"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml index 1a31911bd185e..1d7dca36c176f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="MinicartSection"> <element name="shippingMethodRadioButton" type="select" selector="//input[@class='radio'][position()=1]"/> <element name="shippingMethodRadioText" type="text" selector="//label[@class='label'][position()=1]//span//span"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml similarity index 50% rename from app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml rename to app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml index cd408f5600e3d..fab513cc8d11a 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml @@ -5,15 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <section name="SingleShippingSection"> - <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> - <element name="updateAddress" type="button" selector="//button[@class='action update']"/> - <element name="goToShippingInfo" type="button" selector="//span[text()='Go to Shipping Information']"/> - <element name="placeOrder" type="button" selector="#review-button"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="MultishippingSection"> <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> <element name="shippingMultipleCheckout" type="button" selector=".action.multicheckout"/> @@ -21,10 +14,4 @@ <element name="shippingAddressOptions" type="select" selector="#multiship-addresses-table tbody tr:nth-of-type({{addressPosition}}) .col.address select option:nth-of-type({{optionIndex}})" parameterized="true"/> <element name="selectShippingAddress" type="select" selector="(//table[@id='multiship-addresses-table'] //div[@class='field address'] //select)[{{sequenceNumber}}]" parameterized="true"/> </section> - <section name="StorefrontMultipleShippingMethodSection"> - <element name="orderId" type="text" selector=".shipping-list:nth-child({{rowNum}}) .order-id" parameterized="true"/> - <element name="goToReviewYourOrderButton" type="button" selector="#payment-continue"/> - <element name="continueToBillingInformationButton" type="button" selector=".action.primary.continue"/> - <element name="successMessage" type="text" selector=".multicheckout.success"/> - </section> </sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml new file mode 100644 index 0000000000000..c0749176b1008 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.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="SingleShippingSection"> + <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> + <element name="updateAddress" type="button" selector="//button[@class='action update']"/> + <element name="goToShippingInfo" type="button" selector="//span[text()='Go to Shipping Information']"/> + <element name="placeOrder" type="button" selector="#review-button"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml new file mode 100644 index 0000000000000..52899327c4df3 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.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="StorefrontMultipleShippingMethodSection"> + <element name="orderId" type="text" selector=".shipping-list:nth-child({{rowNum}}) .order-id" parameterized="true"/> + <element name="goToReviewYourOrderButton" type="button" selector="#payment-continue"/> + <element name="continueToBillingInformationButton" type="button" selector=".action.primary.continue"/> + <element name="successMessage" type="text" selector=".multicheckout.success"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml index 2d47b54d84b9c..fe69cba0059fd 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="PaymentMethodSection"> <element name="goToReviewOrder" type="button" selector="#payment-continue"/> </section> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml index de33c28bfb1f2..2886f04e4806a 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ReviewOrderSection"> <element name="shippingMethodBasePrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//div[@class='box box-shipping-method'][position()=1]//span[@class='price']" parameterized="true"/> <element name="shippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//td[@class='amount'][contains(@data-th,'{{priceType}}')]//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml index c4dd2494f7fe8..ef41ed3f47f3a 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ShippingMethodSection"> <element name="shippingMethodRadioButton" type="select" selector="//input[@class='radio']"/> <element name="selectShippingMethod" type="radio" selector="//div[@class='block block-shipping'][position()={{shippingBlockPosition}}]//dd[position()={{shippingMethodPosition}}]//input[@class='radio']" parameterized="true" timeout="5"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml index 94546dcfef9a0..d38b1f239ef45 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontSalesOrderSection"> <element name="orderLinkByPosition" type="text" selector="//li[@class='shipping-list'][position()={{orderLinkPosition}}]//a" parameterized="true"/> <element name="viewOrderLink" type="text" selector="//td[@data-th='Actions']//a[contains(@href,'{{orderLink}}')]//span[text()='View Order']" parameterized="true" timeout="5"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml similarity index 59% rename from app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml rename to app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml index fa655fadab551..c24185f6afac6 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml @@ -6,13 +6,9 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="NewsletterTemplateForm" url="/newsletter/template/new/" area="admin" module="Magento_Cms"> <section name="StorefrontNewsletterSection"/> <section name="StorefrontNewsletterSection"/> </page> - <page name="NewsletterTemplateGrid" url="/newsletter/template/" area="admin" module="Magento_Cms"> - <section name="StorefrontNewsletterSection"/> - <section name="NewsletterTemplateSection"/> - </page> </pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml new file mode 100644 index 0000000000000..ce725e1b2de25 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="NewsletterTemplateGrid" url="/newsletter/template/" area="admin" module="Magento_Cms"> + <section name="StorefrontNewsletterSection"/> + <section name="NewsletterTemplateSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml new file mode 100644 index 0000000000000..8df5053a5bf0c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml @@ -0,0 +1,19 @@ +<?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="BasicFieldNewsletterSection"> + <element name="templateName" type="input" selector="#code"/> + <element name="templateSubject" type="input" selector="#subject"/> + <element name="senderName" type="input" selector="#sender_name"/> + <element name="senderEmail" type="input" selector="#sender_email"/> + <element name="save" type="button" selector="button[data-role='template-save']" timeout="60"/> + <element name="searchButton" type="button" selector=".admin__filter-actions button[title=Search]"/> + <element name="searchInput" type="input" selector="input[name=code]"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml new file mode 100644 index 0000000000000..8475fb4d55b9e --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml @@ -0,0 +1,15 @@ +<?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="BasicFrontendNewsletterFormSection"> + <element name="newsletterEmail" type="input" selector="#newsletter"/> + <element name="subscribeButton" type="button" selector=".subscribe" timeout="30"/> + <element name="subscribeForm" type="input" selector="#newsletter-validate-detail" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml similarity index 64% rename from app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml rename to app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml index ffc871c56800b..eb97da28c9437 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml @@ -6,24 +6,10 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="BasicFrontendNewsletterFormSection"> - <element name="newsletterEmail" type="input" selector="#newsletter"/> - <element name="subscribeButton" type="button" selector=".subscribe" timeout="30"/> - <element name="subscribeForm" type="input" selector="#newsletter-validate-detail" timeout="30"/> - </section> - <section name="BasicFieldNewsletterSection"> - <element name="templateName" type="input" selector="#code"/> - <element name="templateSubject" type="input" selector="#subject"/> - <element name="senderName" type="input" selector="#sender_name"/> - <element name="senderEmail" type="input" selector="#sender_email"/> - <element name="save" type="button" selector="button[data-role='template-save']" timeout="60"/> - <element name="searchButton" type="button" selector=".admin__filter-actions button[title=Search]"/> - <element name="searchInput" type="input" selector="input[name=code]"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="NewsletterWYSIWYGSection"> - <element name="TextArea" type="text" selector="#text" /> - <element name="TinyMCE4" type="text" selector=".mce-branding" /> + <element name="TextArea" type="text" selector="#text"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> <element name="ShowHideBtn" type="button" selector="#toggletext" timeout="60"/> <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> @@ -32,30 +18,30 @@ <element name="InsertImageBtn" type="button" selector=".action-add-image"/> <element name="InsertImageIcon" type="button" selector="div[aria-label='Insert/edit image']"/> <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> + <element name="CreateFolder" type="button" selector="#new_folder"/> <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> <element name="Preview" type="text" selector="//td[contains(text(),'{{var1}}')]//following-sibling::td/select//option[contains(text(), 'Preview')]" parameterized="true" timeout="60"/> </section> </sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml new file mode 100644 index 0000000000000..d7056c185e928 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml @@ -0,0 +1,13 @@ +<?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="CustomerMyAccountPage"> + <element name="DescriptionNewsletter" type="text" selector=".box-newsletter p"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml similarity index 64% rename from app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml rename to app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml index bb651784d4dcf..e5897052e6164 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml @@ -5,14 +5,9 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerCreateFormSection"> <element name="signUpForNewsletter" type="checkbox" selector="//span[contains(text(), 'Sign Up for Newsletter')]"/> </section> - - <section name="CustomerMyAccountPage"> - <element name="DescriptionNewsletter" type="text" selector=".box-newsletter p"/> - </section> </sections> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml deleted file mode 100644 index e7dc40c20f4dd..0000000000000 --- a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?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="NewProductsListWidgetSimpleProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> - </test> - <test name="NewProductsListWidgetConfigurableProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetGroupedProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetVirtualProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> - </test> - <test name="NewProductsListWidgetBundleProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetDownloadableProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> -</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml new file mode 100644 index 0000000000000..d58455c9312d3 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetBundleProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml new file mode 100644 index 0000000000000..741d697a3643f --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetConfigurableProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml new file mode 100644 index 0000000000000..dc9cf7f03686f --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetDownloadableProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml new file mode 100644 index 0000000000000..29f023cb6ea50 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetGroupedProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml new file mode 100644 index 0000000000000..c8bd51b30c4cd --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetSimpleProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml new file mode 100644 index 0000000000000..1b840433a17b3 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml @@ -0,0 +1,13 @@ +<?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="NewProductsListWidgetVirtualProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml deleted file mode 100644 index ca8438d5ee06a..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?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="OtherPayPalPaymentsConfigSection"> - <element name="expandTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head" parameterized="true"/> - <element name="expandedTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head.open" parameterized="true"/> - </section> - <section name="WPSExpressConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="PaymentsProHostedWithExpressCheckoutConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_enable" parameterized="true"/> - </section> - <section name="WPSOtherConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="WebsitePaymentsPlusConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}_pphs_required_settings_pphs_enable" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml new file mode 100644 index 0000000000000..bef5949162f18 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OtherPayPalPaymentsConfigSection"> + <element name="expandTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head" parameterized="true"/> + <element name="expandedTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head.open" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml new file mode 100644 index 0000000000000..6a0e0ead051ea --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PaymentsProHostedWithExpressCheckoutConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_enable" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml new file mode 100644 index 0000000000000..a9e530590048c --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WPSExpressConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml new file mode 100644 index 0000000000000..f5cc20a2bbd24 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WPSOtherConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml new file mode 100644 index 0000000000000..5a2b9aec4adf2 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WebsitePaymentsPlusConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}_pphs_required_settings_pphs_enable" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml deleted file mode 100644 index a1610926b3f36..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?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="PayPalExpressCheckoutConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}-head" parameterized="true"/> - <element name="email" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_business_account" parameterized="true"/> - <element name="apiMethod" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_authentication" parameterized="true"/> - <element name="username" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_username" parameterized="true"/> - <element name="password" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_password" parameterized="true"/> - <element name="signature" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_signature" parameterized="true"/> - <element name="sandboxMode" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_sandbox_flag" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_enable_express_checkout" parameterized="true"/> - <element name="merchantID" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_merchant_id" parameterized="true"/> - </section> - <section name="PayPalExpressCheckoutOtherCountryConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_express_checkout_other-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_express_checkout_other_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="PayPalAdvancedSettingConfigSection"> - <element name="advancedSettingTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced-head" parameterized="true"/> - <element name="frontendExperienceSettingsTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend-head" parameterized="true"/> - <element name="checkoutPageTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button-head" parameterized="true"/> - </section> - <section name="ButtonCustomization"> - <element name="customizeDrpDown" type="button" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]"/> - <element name="customizeNo" type="text" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]/option[@value='0' and @selected='selected']"/> - <element name="label" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_label')]"/> - <element name="layout" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_layout')]"/> - <element name="size" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_size')]"/> - <element name="shape" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_shape')]"/> - <element name="color" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_color')]"/> - </section> - <section name="PayPalButtonOnStorefront"> - <element name="label" type="text" selector="[aria-label='{{label}}']" parameterized="true"/> - <element name="layout" type="text" selector="[data-layout='{{layout}}']" parameterized="true"/> - <element name="size" type="text" selector="[data-size='{{size}}']" parameterized="true"/> - <element name="shape" type="text" selector=".paypal-button-shape-{{shape}}" parameterized="true"/> - <element name="color" type="text" selector=".paypal-button-color-{{color}}" parameterized="true"/> - </section> - <section name="CheckoutPaymentSection"> - <element name="email" type="input" selector="#checkout-customer-email"/> - <element name="payPalPaymentBraintree" type="radio" selector="#braintree_paypal"/> - <element name="payPalFrame" type="iframe" selector="//iframe[contains(@class, 'zoid-component-frame zoid-visible')]" timeout="5"/> - <element name="PayPalPaymentRadio" type="radio" selector="input#paypal_express.radio" timeout="30"/> - <element name="PayPalBtn" type="radio" selector=".paypal-button.paypal-button-number-0" timeout="30"/> - </section> - <section name="PayPalPaymentSection"> - <element name="guestCheckout" type="input" selector="#guest"/> - <element name="loginSection" type="input" selector=" #main>#login"/> - <element name="email" type="input" selector="//input[contains(@name, 'email') and not(contains(@style, 'display:none'))]"/> - <element name="password" type="input" selector="//input[contains(@name, 'password') and not(contains(@style, 'display:none'))]"/> - <element name="loginBtn" type="input" selector="button#btnLogin"/> - <element name="reviewUserInfo" type="text" selector="[data-testid=personalized-banner-content]"/> - <element name="cartIcon" type="text" selector="[data-testid='header-show-cart-dropdown-btn']"/> - <element name="itemName" type="text" selector="//p[contains(@class,'CartDropdown_line') and text()='{{productName}}']" parameterized="true"/> - <element name="PayPalSubmitBtn" type="text" selector="#payment-submit-btn"/> - <element name="nextButton" type="button" selector="#btnNext"/> - <element name="continueButton" type="button" selector=".continueButton"/> - </section> -</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml new file mode 100644 index 0000000000000..2f95c4eff3d08 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml @@ -0,0 +1,19 @@ +<?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="ButtonCustomization"> + <element name="customizeDrpDown" type="button" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]"/> + <element name="customizeNo" type="text" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]/option[@value='0' and @selected='selected']"/> + <element name="label" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_label')]"/> + <element name="layout" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_layout')]"/> + <element name="size" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_size')]"/> + <element name="shape" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_shape')]"/> + <element name="color" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_color')]"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml new file mode 100644 index 0000000000000..9c22dd940890e --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.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="CheckoutPaymentSection"> + <element name="email" type="input" selector="#checkout-customer-email"/> + <element name="payPalPaymentBraintree" type="radio" selector="#braintree_paypal"/> + <element name="payPalFrame" type="iframe" selector="//iframe[contains(@class, 'zoid-component-frame zoid-visible')]" timeout="5"/> + <element name="PayPalPaymentRadio" type="radio" selector="input#paypal_express.radio" timeout="30"/> + <element name="PayPalBtn" type="radio" selector=".paypal-button.paypal-button-number-0" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml new file mode 100644 index 0000000000000..5b4b73804b9dd --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml @@ -0,0 +1,15 @@ +<?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="PayPalAdvancedSettingConfigSection"> + <element name="advancedSettingTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced-head" parameterized="true"/> + <element name="frontendExperienceSettingsTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend-head" parameterized="true"/> + <element name="checkoutPageTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button-head" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml new file mode 100644 index 0000000000000..d97c0260adb0d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.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="PayPalButtonOnStorefront"> + <element name="label" type="text" selector="[aria-label='{{label}}']" parameterized="true"/> + <element name="layout" type="text" selector="[data-layout='{{layout}}']" parameterized="true"/> + <element name="size" type="text" selector="[data-size='{{size}}']" parameterized="true"/> + <element name="shape" type="text" selector=".paypal-button-shape-{{shape}}" parameterized="true"/> + <element name="color" type="text" selector=".paypal-button-color-{{color}}" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml new file mode 100644 index 0000000000000..180ca4f744c1d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.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="PayPalExpressCheckoutConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}-head" parameterized="true"/> + <element name="email" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_business_account" parameterized="true"/> + <element name="apiMethod" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_authentication" parameterized="true"/> + <element name="username" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_username" parameterized="true"/> + <element name="password" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_password" parameterized="true"/> + <element name="signature" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_signature" parameterized="true"/> + <element name="sandboxMode" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_sandbox_flag" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_enable_express_checkout" parameterized="true"/> + <element name="merchantID" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_merchant_id" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml new file mode 100644 index 0000000000000..103537da4144a --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalExpressCheckoutOtherCountryConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_express_checkout_other-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_express_checkout_other_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml new file mode 100644 index 0000000000000..a64ff13e8f627 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml @@ -0,0 +1,23 @@ +<?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="PayPalPaymentSection"> + <element name="guestCheckout" type="input" selector="#guest"/> + <element name="loginSection" type="input" selector=" #main>#login"/> + <element name="email" type="input" selector="//input[contains(@name, 'email') and not(contains(@style, 'display:none'))]"/> + <element name="password" type="input" selector="//input[contains(@name, 'password') and not(contains(@style, 'display:none'))]"/> + <element name="loginBtn" type="input" selector="button#btnLogin"/> + <element name="reviewUserInfo" type="text" selector="[data-testid=personalized-banner-content]"/> + <element name="cartIcon" type="text" selector="[data-testid='header-show-cart-dropdown-btn']"/> + <element name="itemName" type="text" selector="//p[contains(@class,'CartDropdown_line') and text()='{{productName}}']" parameterized="true"/> + <element name="PayPalSubmitBtn" type="text" selector="#payment-submit-btn"/> + <element name="nextButton" type="button" selector="#btnNext"/> + <element name="continueButton" type="button" selector=".continueButton"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest.xml deleted file mode 100644 index db5fb3848dcf1..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest.xml +++ /dev/null @@ -1,276 +0,0 @@ -<?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="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in United Kingdom"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country United Kingdom"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16679"/> - <group value="paypal"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> - <argument name="credentials" value="SamplePaypalExpressConfig"/> - </actionGroup> - </before> - <after> - <magentoCLI command="config:set paypal/general/merchant_country US" stepKey="setMerchantCountry"/> - <magentoCLI command="config:set payment/paypal_express/active 0" stepKey="disablePayPalExpress"/> - <magentoCLI command="config:set payment/wps_express/active 0" stepKey="disableWPSExpress"/> - <magentoCLI command="config:set payment/hosted_pro/active 0" stepKey="disableHostedProExpress"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <!-- Change Merchant Country --> - <comment userInput="Change Merchant Country" stepKey="changeMerchantCountryComment"/> - <waitForElementVisible selector="{{PaymentsConfigSection.merchantCountry}}" stepKey="waitForMerchantCountry"/> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="United Kingdom" stepKey="setMerchantCountry"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <!-- Enable WPS Express --> - <comment userInput="Enable WPS Express" stepKey="enableWPSExpressComment"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Check only the correct solution is enabled --> - <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment1"/> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Enable Pro Hosted With Express Checkout --> - <comment userInput="Enable Pro Hosted With Express Checkout" stepKey="enableProHostedWithExpressCheckoutComment"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Check only the correct solution is enabled --> - <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment2"/> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInJapanTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Japan"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Japan"/> - <severity value="MAJOR"/> - <testCaseId value="MC-13146"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Japan" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInFranceTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in France"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country France"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16675"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Hong Kong"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Hong Kong"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16676"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInItalyTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Italy"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Italy"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16677"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="it"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInSpainTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Spain"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Spain"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16678"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="es"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml new file mode 100644 index 0000000000000..3b70bc84037ce --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml @@ -0,0 +1,50 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInFranceTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in France"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country France"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16675"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml new file mode 100644 index 0000000000000..038ee1c04c482 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml @@ -0,0 +1,50 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Hong Kong"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Hong Kong"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16676"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml new file mode 100644 index 0000000000000..ad24d2c2c95d5 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml @@ -0,0 +1,50 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInItalyTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Italy"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Italy"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16677"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="it"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml new file mode 100644 index 0000000000000..846f4e6dd5ae4 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml @@ -0,0 +1,50 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInJapanTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Japan"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Japan"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13146"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Japan" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml new file mode 100644 index 0000000000000..b0317f9ac7a3d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml @@ -0,0 +1,50 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInSpainTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Spain"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Spain"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16678"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="es"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml new file mode 100644 index 0000000000000..1d2e8ce4ae156 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml @@ -0,0 +1,75 @@ +<?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="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in United Kingdom"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country United Kingdom"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16679"/> + <group value="paypal"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> + <argument name="credentials" value="SamplePaypalExpressConfig"/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set paypal/general/merchant_country US" stepKey="setMerchantCountry"/> + <magentoCLI command="config:set payment/paypal_express/active 0" stepKey="disablePayPalExpress"/> + <magentoCLI command="config:set payment/wps_express/active 0" stepKey="disableWPSExpress"/> + <magentoCLI command="config:set payment/hosted_pro/active 0" stepKey="disableHostedProExpress"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Change Merchant Country --> + <comment userInput="Change Merchant Country" stepKey="changeMerchantCountryComment"/> + <waitForElementVisible selector="{{PaymentsConfigSection.merchantCountry}}" stepKey="waitForMerchantCountry"/> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="United Kingdom" stepKey="setMerchantCountry"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <!-- Enable WPS Express --> + <comment userInput="Enable WPS Express" stepKey="enableWPSExpressComment"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Check only the correct solution is enabled --> + <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment1"/> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Enable Pro Hosted With Express Checkout --> + <comment userInput="Enable Pro Hosted With Express Checkout" stepKey="enableProHostedWithExpressCheckoutComment"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Check only the correct solution is enabled --> + <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment2"/> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml new file mode 100644 index 0000000000000..d9b0bfd956f80 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="GeneratedReportSection"> + <element name="ordersCount" type="text" selector="//tr[@class='totals']/th[@class=' col-orders col-orders_count col-number']"/> + <element name="canceledOrders" type="text" selector="//tr[@class='totals']/th[@class=' col-canceled col-total_canceled_amount a-right']"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml similarity index 57% rename from app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml rename to app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml index bc8ac46424040..980904ba08183 100644 --- a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml @@ -5,14 +5,8 @@ * 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="OrderReportMainSection"> - <element name="showReport" type="button" selector="#filter_form_submit" timeout="60"/> - <element name="here" type="text" selector="//a[contains(text(), 'here')]" timeout="60"/> - </section> - + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="OrderReportFilterSection"> <element name="dateFrom" type="input" selector="#sales_report_from"/> <element name="dateTo" type="input" selector="#sales_report_to"/> @@ -21,9 +15,4 @@ <element name="optionSpecified" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Specified')]"/> <element name="orderStatusSpecified" type="select" selector="#sales_report_order_statuses"/> </section> - - <section name="GeneratedReportSection"> - <element name="ordersCount" type="text" selector="//tr[@class='totals']/th[@class=' col-orders col-orders_count col-number']"/> - <element name="canceledOrders" type="text" selector="//tr[@class='totals']/th[@class=' col-canceled col-total_canceled_amount a-right']"/> - </section> </sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml new file mode 100644 index 0000000000000..4368842e640d2 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderReportMainSection"> + <element name="showReport" type="button" selector="#filter_form_submit" timeout="60"/> + <element name="here" type="text" selector="//a[contains(text(), 'here')]" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml index b5361363c489b..a297d98fd370f 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup"> <annotations> <description>Check that order grand total equals sum of all totals.</description> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index e4e9a62780948..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?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="EndToEndB2CGuestUserTest"> - <before> - <createData entity="ApiSalesRule" stepKey="createSalesRule"/> - <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> - <requiredEntity createDataKey="createSalesRule"/> - </createData> - </before> - <after> - <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - </after> - - <!-- Step 5: User uses coupon codes --> - <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> - - <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> - <argument name="coupon" value="$$createSalesRuleCoupon$$"/> - </actionGroup> - - <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> - <argument name="rule" value="$$createSalesRule$$"/> - <argument name="discount" value="48.00"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="447.00"/> - </actionGroup> - - <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <before> - <createData entity="ApiSalesRule" stepKey="createSalesRule"/> - <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> - <requiredEntity createDataKey="createSalesRule"/> - </createData> - </before> - <after> - <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - </after> - - <!-- Step 5: User uses coupon codes --> - <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> - - <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> - <argument name="coupon" value="$$createSalesRuleCoupon$$"/> - </actionGroup> - - <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> - <argument name="rule" value="$$createSalesRule$$"/> - <argument name="discount" value="48.00"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="447.00"/> - </actionGroup> - - <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon" /> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..c128c5aeeff7a --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,49 @@ +<?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="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiSalesRule" stepKey="createSalesRule"/> + <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + </after> + + <!-- Step 5: User uses coupon codes --> + <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> + + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> + <argument name="coupon" value="$$createSalesRuleCoupon$$"/> + </actionGroup> + + <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> + <argument name="rule" value="$$createSalesRule$$"/> + <argument name="discount" value="48.00"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="447.00"/> + </actionGroup> + + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..f227e8cf3e5e0 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,49 @@ +<?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="EndToEndB2CGuestUserTest"> + <before> + <createData entity="ApiSalesRule" stepKey="createSalesRule"/> + <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + </after> + + <!-- Step 5: User uses coupon codes --> + <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> + + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> + <argument name="coupon" value="$$createSalesRuleCoupon$$"/> + </actionGroup> + + <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> + <argument name="rule" value="$$createSalesRule$$"/> + <argument name="discount" value="48.00"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="447.00"/> + </actionGroup> + + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon"/> + </test> +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml new file mode 100644 index 0000000000000..71341593ff036 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="goToShipmentIntoOrder"> + <annotations> + <description>Clicks on the 'Ship' button on the view Admin Order page. Validates that the URL and Page Title are present and correct.</description> + </annotations> + + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Shipment" stepKey="seePageNameNewInvoicePage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml new file mode 100644 index 0000000000000..10ef44b090a8b --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="seeProductInShipmentItems"> + <annotations> + <description>Validates that the provided Product is present and correct on the view Admin Order Shipment page under the 'Items Shipped' section.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <see selector="{{AdminShipmentItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml new file mode 100644 index 0000000000000..c067a39e30f6c --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="submitShipmentIntoOrder"> + <annotations> + <description>Clicks on the 'Submit Shipment' button on the view Admin Order Shipment page. Validates that the URL and Page Title are present and correct.</description> + </annotations> + + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml similarity index 57% rename from app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml rename to app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml index 28160ae9e4ebb..8d0d0e273e767 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml @@ -5,38 +5,8 @@ * 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"> - <!-- Temporary file to pass CI verification --> - <actionGroup name="goToShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Ship' button on the view Admin Order page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Shipment" stepKey="seePageNameNewInvoicePage"/> - </actionGroup> - <actionGroup name="submitShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Submit Shipment' button on the view Admin Order Shipment page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> - </actionGroup> - <actionGroup name="seeProductInShipmentItems"> - <annotations> - <description>Validates that the provided Product is present and correct on the view Admin Order Shipment page under the 'Items Shipped' section.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminShipmentItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - </actionGroup> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="verifyBasicShipmentInformation"> <annotations> <description>Validates that the provided Customer, Shipping Address, Billing Address and Customer Group are present and correct on the view Admin Order page.</description> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml index 10878310c262f..61ffedae88c57 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml similarity index 68% rename from app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml rename to app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml index 5f33921b5a44f..13a9ca2214b3e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml @@ -5,16 +5,12 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentCreatePackageMainSection"> <element name="addProductsToPackage" type="button" selector="#package_block_1 button[data-action='package-add-items']"/> <element name="addSelectedProductToPackage" type="button" selector="#package_block_1 button[data-action='package-save-items']"/> <element name="save" type="button" selector="button[data-action='save-packages']"/> <element name="saveButtonDisabled" type="button" selector="button[data-action='save-packages']._disabled"/> </section> - <section name="AdminShipmentCreatePackageProductGridSection"> - <element name="concreteProductCheckbox" type="checkbox" selector="//td[contains(text(), '{{productName}}')]/parent::tr//input[contains(@class,'checkbox')]" parameterized="true"/> - </section> </sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml new file mode 100644 index 0000000000000..3597258d503c7 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml @@ -0,0 +1,13 @@ +<?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="AdminShipmentCreatePackageProductGridSection"> + <element name="concreteProductCheckbox" type="checkbox" selector="//td[contains(text(), '{{productName}}')]/parent::tr//input[contains(@class,'checkbox')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml new file mode 100644 index 0000000000000..acfe8ff9748f8 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml @@ -0,0 +1,15 @@ +<?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="AdminStoresGridControlsSection"> + <element name="createStoreView" type="button" selector="#add_store"/> + <element name="createStore" type="button" selector="#add_group"/> + <element name="createWebsite" type="button" selector="#add"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml similarity index 73% rename from app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml rename to app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml index 367d239518768..e56836c491276 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml @@ -1,25 +1,21 @@ -<?xml version="1.0" encoding="utf-8"?> +<?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="AdminStoresGridControlsSection"> - <element name="createStoreView" type="button" selector="#add_store"/> - <element name="createStore" type="button" selector="#add_group"/> - <element name="createWebsite" type="button" selector="#add"/> - </section> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresGridSection"> <element name="storeGrpFilterTextField" type="input" selector="#storeGrid_filter_group_title"/> <element name="websiteFilterTextField" type="input" selector="#storeGrid_filter_website_title"/> <element name="storeFilterTextField" type="input" selector="#storeGrid_filter_store_title" timeout="90"/> <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]" timeout="30"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']" timeout="30"/> - <element name="websiteNameInFirstRow" type="text" selector="tr:nth-of-type(1) > .col-website_title > a"/> - <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> - <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']" timeout="30"/> + <element name="websiteNameInFirstRow" type="text" selector="tr:nth-of-type(1) > .col-website_title > a"/> + <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> + <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> <element name="nthRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[{{rownum}}]" parameterized="true"/> <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeMeta.xml similarity index 100% rename from app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute-meta.xml rename to app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeMeta.xml diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeOptionMeta.xml similarity index 100% rename from app/code/Magento/Swatches/Test/Mftf/Metadata/swatch_product_attribute_option-meta.xml rename to app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeOptionMeta.xml diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml new file mode 100644 index 0000000000000..576faed573fab --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml @@ -0,0 +1,31 @@ +<?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="StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest" extends="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Swatch option should show the lowest price possible on category page"/> + <description value="Swatch option should show the lowest price possible on category page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19683"/> + <group value="Swatches"/> + </annotations> + + <!--Go to category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForConfigurableProductPage"/> + + <!--Verify that the minimum price is 10--> + <actionGroup ref="StorefrontAssertProductPriceOnCategoryPageActionGroup" stepKey="assertProductPrice"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="productPrice" value="10.00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml similarity index 86% rename from app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml rename to app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml index 6651692bcada4..fdc8fb631dd8a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> <annotations> <features value="Swatches"/> @@ -143,25 +142,4 @@ <argument name="optionPrice" value="16.00"/> </actionGroup> </test> - <test name="StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest" extends="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> - <annotations> - <features value="Swatches"/> - <stories value="Configurable product with swatch attribute"/> - <title value="Swatch option should show the lowest price possible on category page"/> - <description value="Swatch option should show the lowest price possible on category page"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19683"/> - <group value="Swatches"/> - </annotations> - - <!--Go to category page--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForConfigurableProductPage"/> - - <!--Verify that the minimum price is 10--> - <actionGroup ref="StorefrontAssertProductPriceOnCategoryPageActionGroup" stepKey="assertProductPrice"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="productPrice" value="10.00"/> - </actionGroup> - </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml deleted file mode 100644 index 70211a1080951..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ /dev/null @@ -1,470 +0,0 @@ -<?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="StorefrontTaxQuoteCartLoggedInSimpleTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-295"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <waitForText userInput="$5.00" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" time="30" stepKey="waitForCorrectShippingAmount"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for CA --> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - - <test name="StorefrontTaxQuoteCartLoggedInVirtualTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-296"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for CA --> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> - - <test name="StorefrontTaxQuoteCartGuestSimpleTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer#anch"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-297"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for CA --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for NY --> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCartGuestVirtualTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-298"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for NY --> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml new file mode 100644 index 0000000000000..b4070081519d7 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml @@ -0,0 +1,121 @@ +<?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="StorefrontTaxQuoteCartGuestSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer#anch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-297"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for NY --> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml new file mode 100644 index 0000000000000..9469f6a7a6f20 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml @@ -0,0 +1,122 @@ +<?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="StorefrontTaxQuoteCartGuestVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-298"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for NY --> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml new file mode 100644 index 0000000000000..4f7e5534e85bb --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml @@ -0,0 +1,127 @@ +<?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="StorefrontTaxQuoteCartLoggedInSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-295"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <waitForText userInput="$5.00" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" time="30" stepKey="waitForCorrectShippingAmount"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for CA --> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml new file mode 100644 index 0000000000000..bd935113fe051 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml @@ -0,0 +1,125 @@ +<?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="StorefrontTaxQuoteCartLoggedInVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-296"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for CA --> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml deleted file mode 100644 index 4c3ab91cf909c..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ /dev/null @@ -1,477 +0,0 @@ -<?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="StorefrontTaxQuoteCheckoutGuestVirtualTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-255"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for CA --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="customerVar" value="Simple_US_Customer_NY"/> - <argument name="customerAddressVar" value="US_Address_NY"/> - <argument name="paymentMethod" value="Check / Money order"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <see stepKey="seeEditButton" selector="{{CheckoutShippingSection.editAddressButton}}" userInput="Edit"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> - - <!-- Assert that taxes are applied correctly for NY --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutLoggedInSimpleTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-256"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxNY.state}}"/> - <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForReviewAndPayments"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Go back to the shipping page and change the address --> - <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> - <click stepKey="addNewAddress" selector="{{CheckoutShippingSection.newAddressButton}}"/> - <waitForPageLoad stepKey="waitForAddressPopUp"/> - - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - <argument name="classPrefix" value="._show"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.saveAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxCA.state}}"/> - <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForReviewAndPayments2"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutGuestSimpleTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-258"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Fill in address for CA --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{Simple_US_Customer_CA.email}}" stepKey="enterEmail"/> - <waitForLoadingMaskToDisappear stepKey="waitEmailLoad" /> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - </actionGroup> - <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForAddressToLoad"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> - <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Go back to the shipping page and change the address --> - <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress2"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForShippingToLoad"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> - - <!-- Assert that taxes are applied correctly for NY --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutLoggedInVirtualTest"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-259"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <click stepKey="editAddress" selector="{{CheckoutShippingSection.editAddressButton}}"/> - <selectOption stepKey="addNewAddress" selector="{{CheckoutShippingSection.addressDropdown}}" userInput="New Address"/> - <waitForPageLoad stepKey="waitForNewAddressForm"/> - - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - <argument name="classPrefix" value="[aria-hidden=false]"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml new file mode 100644 index 0000000000000..14615c101b9a7 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.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="StorefrontTaxQuoteCheckoutGuestSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-258"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Fill in address for CA --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{Simple_US_Customer_CA.email}}" stepKey="enterEmail"/> + <waitForLoadingMaskToDisappear stepKey="waitEmailLoad"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + </actionGroup> + <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForAddressToLoad"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> + <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Go back to the shipping page and change the address --> + <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress2"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForShippingToLoad"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> + + <!-- Assert that taxes are applied correctly for NY --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml new file mode 100644 index 0000000000000..248828aa94a4b --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml @@ -0,0 +1,115 @@ +<?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="StorefrontTaxQuoteCheckoutGuestVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-255"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerVar" value="Simple_US_Customer_NY"/> + <argument name="customerAddressVar" value="US_Address_NY"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <see stepKey="seeEditButton" selector="{{CheckoutShippingSection.editAddressButton}}" userInput="Edit"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> + + <!-- Assert that taxes are applied correctly for NY --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml new file mode 100644 index 0000000000000..187f079434ddc --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml @@ -0,0 +1,131 @@ +<?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="StorefrontTaxQuoteCheckoutLoggedInSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-256"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxNY.state}}"/> + <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForReviewAndPayments"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Go back to the shipping page and change the address --> + <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> + <click stepKey="addNewAddress" selector="{{CheckoutShippingSection.newAddressButton}}"/> + <waitForPageLoad stepKey="waitForAddressPopUp"/> + + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + <argument name="classPrefix" value="._show"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.saveAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxCA.state}}"/> + <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForReviewAndPayments2"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml new file mode 100644 index 0000000000000..a0396cd2793bf --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml @@ -0,0 +1,131 @@ +<?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="StorefrontTaxQuoteCheckoutLoggedInVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-259"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <click stepKey="editAddress" selector="{{CheckoutShippingSection.editAddressButton}}"/> + <selectOption stepKey="addNewAddress" selector="{{CheckoutShippingSection.addressDropdown}}" userInput="New Address"/> + <waitForPageLoad stepKey="waitForNewAddressForm"/> + + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + <argument name="classPrefix" value="[aria-hidden=false]"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml new file mode 100644 index 0000000000000..14002028d9da4 --- /dev/null +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?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="NewsletterWYSIWYGSection"> + <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> + </section> +</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml new file mode 100644 index 0000000000000..9ce4e067169ec --- /dev/null +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?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="ProductWYSIWYGSection"> + <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> + </section> +</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml similarity index 55% rename from app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml rename to app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml index 76c0a9e1fe797..cb46bed781e5a 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml @@ -5,17 +5,10 @@ * 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="ProductWYSIWYGSection"> - <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> - </section> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="TinyMCESection"> <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> <element name="InsertImageBtnTinyMCE3" type="button" selector="#cms_page_form_content_image"/> </section> - <section name="NewsletterWYSIWYGSection"> - <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> - </section> </sections> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml new file mode 100644 index 0000000000000..d712c1a116068 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFormSaveAndClose"> + <annotations> + <description>Clicks on 'Save and Close'. Validates that the Success Message is present.</description> + </annotations> + + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml similarity index 60% rename from app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml rename to app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml index 681ada9484618..51031a7225889 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml @@ -5,18 +5,8 @@ * 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="AdminFormSaveAndClose"> - <annotations> - <description>Clicks on 'Save and Close'. Validates that the Success Message is present.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - </actionGroup> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminFormSaveAndDuplicate"> <annotations> <description>Clicks on 'Save and Duplicate'. Validates that the Success Message is present and correct.</description> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml deleted file mode 100644 index 978a09db82b16..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?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"> - <!-- TODO: Search, Notifications, Admin Menu --> - <section name="AdminGridMainControls"> - <element name="add" type="button" selector="#add" timeout="30"/> - <element name="back" type="button" selector="#back" timeout="30"/> - <element name="reset" type="button" selector="#reset" timeout="30"/> - <element name="save" type="button" selector="#save-button" timeout="30"/> - <element name="saveAndContinue" type="button" selector="#save-button" timeout="30"/> - <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="5"/> - <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> - <element name="saveAndNew" type="button" selector="span[title='Save & New']" timeout="30"/> - </section> - <section name="AdminGridSearchBox"> - <element name="searchByKeyword" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword']"/> - <element name="search" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword'] + .action-submit" timeout="30"/> - </section> - <section name="AdminGridFilterControls"> - <element name="filters" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-expand']" timeout="5"/> - <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> - <element name="cancel" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> - <element name="clearAll" type="button" selector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//button[contains(@data-action, 'reset')])[1]" timeout="5"/> - </section> - <section name="AdminGridDefaultViewControls"> - <element name="defaultView" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-bookmarks" timeout="5"/> - <element name="viewByName" type="button" selector="//a[@class='action-dropdown-menu-link'][contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> - <element name="saveViewAs" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-dropdown-menu-item-last a" timeout="5"/> - <element name="viewName" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit input"/> - <element name="save" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit button" timeout="30"/> - </section> - <section name="AdminGridColumnsControls"> - <element name="columns" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-columns" timeout="5"/> - - <element name="columnName" type="button" selector="//label[contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> - - <element name="reset" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Reset')]" timeout="5"/> - <element name="cancel" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Cancel')]" timeout="5"/> - </section> - <section name="AdminGridActionsMenu"> - <element name="dropDown" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-select" timeout="30"/> - <element name="delete" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-menu-items li" timeout="5"/> - </section> - <section name="AdminGridRowsPerPage"> - <element name="count" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .selectmenu-value input" timeout="30"/> - </section> - <!-- TODO: Pagination controls --> - <section name="AdminGridHeaders"> - <element name="title" type="text" selector=".page-title-wrapper h1"/> - <element name="headerByName" type="text" selector="//div[@data-role='grid-wrapper']//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> - <element name="columnsNames" type="text" selector="[data-role='grid-wrapper'] .data-grid-th > span"/> - </section> - <section name="AdminGridRow"> - <element name="rowOne" type="text" selector="tr[data-repeat-index='0']"/> - <element name="rowByIndex" type="text" selector="tr[data-repeat-index='{{var1}}']" parameterized="true"/> - - <element name="editByValue" type="button" selector="//a[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> - - <element name="checkboxByValue" type="checkbox" selector="//input[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> - <element name="checkboxByIndex" type="checkbox" selector=".data-row[data-repeat-index='{{var1}}'] .admin__control-checkbox" parameterized="true"/> - </section> - <section name="AdminGridSelectRows"> - <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> - <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> - <element name="bulkActionDropdown" type="button" selector="div.admin__data-grid-header-row.row div.action-select-wrap button.action-select"/> - <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> - </section> - <section name="AdminGridConfirmActionSection"> - <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> - <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> - <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> - <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> - </section> -</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml new file mode 100644 index 0000000000000..fc9e83f9693c0 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridActionsMenu"> + <element name="dropDown" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-select" timeout="30"/> + <element name="delete" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-menu-items li" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml new file mode 100644 index 0000000000000..30edbe4aade18 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml @@ -0,0 +1,18 @@ +<?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="AdminGridColumnsControls"> + <element name="columns" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-columns" timeout="5"/> + + <element name="columnName" type="button" selector="//label[contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> + + <element name="reset" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Reset')]" timeout="5"/> + <element name="cancel" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Cancel')]" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml new file mode 100644 index 0000000000000..d2d17b1afd055 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.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="AdminGridConfirmActionSection"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml new file mode 100644 index 0000000000000..b3900f5a641e3 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.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="AdminGridDefaultViewControls"> + <element name="defaultView" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-bookmarks" timeout="5"/> + <element name="viewByName" type="button" selector="//a[@class='action-dropdown-menu-link'][contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> + <element name="saveViewAs" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-dropdown-menu-item-last a" timeout="5"/> + <element name="viewName" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit input"/> + <element name="save" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit button" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml new file mode 100644 index 0000000000000..db3f5de3a97ee --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.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="AdminGridFilterControls"> + <element name="filters" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-expand']" timeout="5"/> + <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="cancel" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> + <element name="clearAll" type="button" selector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//button[contains(@data-action, 'reset')])[1]" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml new file mode 100644 index 0000000000000..89831359657bf --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml @@ -0,0 +1,15 @@ +<?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="AdminGridHeaders"> + <element name="title" type="text" selector=".page-title-wrapper h1"/> + <element name="headerByName" type="text" selector="//div[@data-role='grid-wrapper']//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> + <element name="columnsNames" type="text" selector="[data-role='grid-wrapper'] .data-grid-th > span"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml new file mode 100644 index 0000000000000..1ccc3e82b15ce --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml @@ -0,0 +1,20 @@ +<?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="AdminGridMainControls"> + <element name="add" type="button" selector="#add" timeout="30"/> + <element name="back" type="button" selector="#back" timeout="30"/> + <element name="reset" type="button" selector="#reset" timeout="30"/> + <element name="save" type="button" selector="#save-button" timeout="30"/> + <element name="saveAndContinue" type="button" selector="#save-button" timeout="30"/> + <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="5"/> + <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> + <element name="saveAndNew" type="button" selector="span[title='Save & New']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml new file mode 100644 index 0000000000000..eb7a690e8fed1 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml @@ -0,0 +1,19 @@ +<?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="AdminGridRow"> + <element name="rowOne" type="text" selector="tr[data-repeat-index='0']"/> + <element name="rowByIndex" type="text" selector="tr[data-repeat-index='{{var1}}']" parameterized="true"/> + + <element name="editByValue" type="button" selector="//a[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> + + <element name="checkboxByValue" type="checkbox" selector="//input[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> + <element name="checkboxByIndex" type="checkbox" selector=".data-row[data-repeat-index='{{var1}}'] .admin__control-checkbox" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml new file mode 100644 index 0000000000000..de87f2290bf88 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml @@ -0,0 +1,13 @@ +<?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="AdminGridRowsPerPage"> + <element name="count" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .selectmenu-value input" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml new file mode 100644 index 0000000000000..a46151184c158 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridSearchBox"> + <element name="searchByKeyword" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword']"/> + <element name="search" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword'] + .action-submit" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml new file mode 100644 index 0000000000000..15e700799817a --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.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="AdminGridSelectRows"> + <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> + <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> + <element name="bulkActionDropdown" type="button" selector="div.admin__data-grid-header-row.row div.action-select-wrap button.action-select"/> + <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml new file mode 100644 index 0000000000000..70ce7e2d64fb3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -0,0 +1,130 @@ +<?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="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites for Multiple Storeviews"/> + <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> + <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-12656"/> + <group value="urlRewrite"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create Store View EN --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <!-- Create Store View NL --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> + <argument name="Store" value="customStoreENNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> + <argument name="value" value="category-english"/> + </actionGroup> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> + <argument name="Store" value="customStoreNLNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> + <argument name="value" value="category-dutch"/> + </actionGroup> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> + <argument name="productName" value="productformagetwo68980"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> + <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters2"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters3"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters3"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters4"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english/productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters4"/> + <waitForPageLoad stepKey="waitForPageToLoad4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters5"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch/productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView3"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters5"/> + <waitForPageLoad stepKey="waitForPageToLoad5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml similarity index 53% rename from app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml index cdabcc31fd856..a9a5cf4a05b66 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml @@ -5,128 +5,8 @@ * 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="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url Rewrites for Multiple Storeviews"/> - <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> - <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-12656"/> - <group value="urlRewrite"/> - </annotations> - <before> - <createData entity="ApiCategory" stepKey="createCategory"> - <field key="name">category-admin</field> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Create Store View EN --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> - </actionGroup> - <!-- Create Store View NL --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/> - <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> - <argument name="sku" value="productformagetwo68980"/> - <argument name="name" value="productformagetwo68980"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> - <argument name="Store" value="customStoreENNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> - <argument name="value" value="category-english"/> - </actionGroup> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> - <argument name="Store" value="customStoreNLNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> - <argument name="value" value="category-dutch"/> - </actionGroup> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> - <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> - <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> - <argument name="productName" value="productformagetwo68980"/> - </actionGroup> - <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> - <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters2"/> - <waitForPageLoad stepKey="waitForPageToLoad2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters3"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters3"/> - <waitForPageLoad stepKey="waitForPageToLoad3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters4"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english/productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters4"/> - <waitForPageLoad stepKey="waitForPageToLoad4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> - - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters5"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch/productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView3"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters5"/> - <waitForPageLoad stepKey="waitForPageToLoad5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> - </test> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest"> <annotations> <features value="Url Rewrite"/> @@ -139,7 +19,7 @@ </annotations> <before> <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache1"/> @@ -156,7 +36,7 @@ <argument name="customStore" value="customStoreNLNotUnique"/> </actionGroup> <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache"/> @@ -186,7 +66,7 @@ </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> <argument name="value" value="category-english"/> </actionGroup> @@ -196,7 +76,7 @@ </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> <argument name="value" value="category-dutch"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml deleted file mode 100644 index 32795649c9375..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ /dev/null @@ -1,291 +0,0 @@ -<?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="AdminUrlRewritesForProductInAnchorCategoriesTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories"/> - <title value="Url-rewrites for product in anchor categories"/> - <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> - <severity value="BLOCKER"/> - <testCaseId value="MC-16568"/> - <group value="urlRewrite"/> - </annotations> - - <!-- Preconditions--> - <!-- Create 3 categories --> - <before> - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory2"/> - </createData> - <!-- Create Simple product 1 and assign it to Category 3 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites--> - <comment userInput="Enable config to generate category/product URL Rewrites " stepKey="commentEnableConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - <!-- Steps --> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> - - <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="_defaultStore.name"/> - <argument name="CatName" value="$$simpleSubCategory1.name$$"/> - </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - - <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories"/> - <title value="Url-rewrites for product in anchor categories with configuration turned off"/> - <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6844"/> - <group value="urlRewrite"/> - </annotations> - - <!-- Preconditions--> - <!-- Create 3 categories --> - <before> - <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory2"/> - </createData> - <!-- Create Simple product 1 and assign it to Category 3 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache2"/> - </after> - <!-- Steps --> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> - - <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="_defaultStore.name"/> - <argument name="CatName" value="$$simpleSubCategory1.name$$"/> - </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - - <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName1"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> - - <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> - - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> - - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories for all store views"/> - <title value="Url-rewrites for product in anchor categories for all store views"/> - <description value="Verify that Saving category do not delete UrlRewrites for subcategories and all products in them."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16681"/> - <group value="urlRewrite"/> - </annotations> - <before> - <remove keyForRemoval="createSimpleProduct"/> - <!-- Create Simple product 1 and assign it to all the threee categories above --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory1"/> - <requiredEntity createDataKey="simpleSubCategory2"/> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - </before> - <remove keyForRemoval="switchStoreView"/> - <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> - <argument name="Category" value="$$simpleSubCategory1$$"/> - </actionGroup> - <remove keyForRemoval="uncheckRedirect2"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories for all store views"/> - <title value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> - <description value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6964"/> - <group value="urlRewrite"/> - </annotations> - <before> - <remove keyForRemoval="createSimpleProduct"/> - <!-- Create Simple product 1 and assign it to all the threee categories above --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory1"/> - <requiredEntity createDataKey="simpleSubCategory2"/> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - </before> - <remove keyForRemoval="switchStoreView"/> - <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> - <argument name="Category" value="$$simpleSubCategory1$$"/> - </actionGroup> - <remove keyForRemoval="uncheckRedirect2"/> - </test> - - <test name="AdminUrlRewritesForProductsWithConfigurationTurnedOffTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="No Url-rewrites for product if configuration to generate url rewrite for Generate 'category/product' URL Rewrites is enabled "/> - <title value="No auto generated of request path for simple product when assigned to subCategory"/> - <description value="No auto generated of request path when SEO configuration to Generate url rewrite for Generate 'category/product' URL Rewrites is set to No"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-94803"/> - <group value="urlRewrite"/> - </annotations> - <before> - <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <!-- Create Simple product 1 and assign it to Category 1 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache2"/> - </after> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> - </test> -</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml new file mode 100644 index 0000000000000..e666a2aad1738 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -0,0 +1,85 @@ +<?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="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-16568"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites--> + <comment userInput="Enable config to generate category/product URL Rewrites " stepKey="commentEnableConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> + <argument name="Store" value="_defaultStore.name"/> + <argument name="CatName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml new file mode 100644 index 0000000000000..1876b001eb5bc --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml @@ -0,0 +1,36 @@ +<?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="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views"/> + <description value="Verify that Saving category do not delete UrlRewrites for subcategories and all products in them."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16681"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..14f7c9fb7cbe3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml @@ -0,0 +1,36 @@ +<?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="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <description value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6964"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..e3fc730a3549a --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml @@ -0,0 +1,113 @@ +<?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="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories with configuration turned off"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6844"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> + <argument name="Store" value="_defaultStore.name"/> + <argument name="CatName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + + <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..4618e901d42e3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml @@ -0,0 +1,57 @@ +<?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="AdminUrlRewritesForProductsWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="No Url-rewrites for product if configuration to generate url rewrite for Generate 'category/product' URL Rewrites is enabled "/> + <title value="No auto generated of request path for simple product when assigned to subCategory"/> + <description value="No auto generated of request path when SEO configuration to Generate url rewrite for Generate 'category/product' URL Rewrites is set to No"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-94803"/> + <group value="urlRewrite"/> + </annotations> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <!-- Create Simple product 1 and assign it to Category 1 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml deleted file mode 100644 index 4675c5f96ea68..0000000000000 --- a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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="AdminRoleGridSection"> - <element name="idFilterTextField" type="input" selector="#roleGrid_filter_role_id"/> - <element name="roleNameFilterTextField" type="input" selector="#roleGrid_filter_role_name"/> - <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> - <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> - <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> - </section> - - <section name="AdminDeleteRoleSection"> - <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> - <element name="role" parameterized="true" selector="//td[contains(text(), '{{args}}')]" type="button"/> - <element name="current_pass" type="button" selector="#current_password"/> - <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> - <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> - </section> -</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml new file mode 100644 index 0000000000000..e369d037d28f6 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.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="AdminDeleteRoleSection"> + <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> + <element name="role" parameterized="true" selector="//td[contains(text(), '{{args}}')]" type="button"/> + <element name="current_pass" type="button" selector="#current_password"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> + <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml new file mode 100644 index 0000000000000..3d0c8b855e53b --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml @@ -0,0 +1,18 @@ +<?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="AdminRoleGridSection"> + <element name="idFilterTextField" type="input" selector="#roleGrid_filter_role_id"/> + <element name="roleNameFilterTextField" type="input" selector="#roleGrid_filter_role_name"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml deleted file mode 100644 index caff47114687e..0000000000000 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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="AdminUserGridSection"> - <element name="usernameFilterTextField" type="input" selector="#permissionsUserGrid_filter_username"/> - <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> - <element name="usernameInFirstRow" type="text" selector=".col-username"/> - <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> - <element name="successMessage" type="text" selector=".message-success"/> - </section> - - <section name="AdminDeleteUserSection"> - <element name="theUser" selector="//td[contains(text(), '{{userName}}')]" type="button" parameterized="true"/> - <element name="password" selector="#user_current_password" type="input"/> - <element name="delete" selector="//button/span[contains(text(), 'Delete User')]" type="button"/> - <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> - <element name="role" parameterized="true" selector="//td[contains(text(), '{{args}}')]" type="button"/> - </section> -</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml new file mode 100644 index 0000000000000..d4718ca43d6cf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.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="AdminDeleteUserSection"> + <element name="theUser" selector="//td[contains(text(), '{{userName}}')]" type="button" parameterized="true"/> + <element name="password" selector="#user_current_password" type="input"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete User')]" type="button"/> + <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> + <element name="role" parameterized="true" selector="//td[contains(text(), '{{args}}')]" type="button"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml new file mode 100644 index 0000000000000..137cfd233577f --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml @@ -0,0 +1,18 @@ +<?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="AdminUserGridSection"> + <element name="usernameFilterTextField" type="input" selector="#permissionsUserGrid_filter_username"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="usernameInFirstRow" type="text" selector=".col-username"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="successMessage" type="text" selector=".message-success"/> + </section> +</sections> diff --git a/composer.json b/composer.json index 187a0ecd2471b..fbd5b0ea3bbea 100644 --- a/composer.json +++ b/composer.json @@ -89,7 +89,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "dev-MFTF3.0.0-RC1", + "magento/magento2-functional-testing-framework": "dev-3.0.0-RC1", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index 937567f5fe64a..d3cdaa36326f6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5d91717fba26046648157ebf69639b5a", + "content-hash": "3b2c88252ab70b75998cb4e200257796", "packages": [ { "name": "braintree/braintree_php", @@ -3248,16 +3248,16 @@ }, { "name": "laminas/laminas-zendframework-bridge", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d" + "reference": "faf68f6109ceeff24241226033ab59640c7eb63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/0fb9675b84a1666ab45182b6c5b29956921e818d", - "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/faf68f6109ceeff24241226033ab59640c7eb63b", + "reference": "faf68f6109ceeff24241226033ab59640c7eb63b", "shasum": "" }, "require": { @@ -3296,7 +3296,7 @@ "laminas", "zf" ], - "time": "2020-01-07T22:58:31+00:00" + "time": "2020-03-26T16:07:12+00:00" }, { "name": "magento/composer", @@ -4054,16 +4054,16 @@ }, { "name": "psr/log", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { @@ -4097,7 +4097,7 @@ "psr", "psr-3" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2020-03-23T09:12:05+00:00" }, { "name": "ralouphie/getallheaders", @@ -4718,16 +4718,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", "shasum": "" }, "require": { @@ -4739,7 +4739,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4772,20 +4772,20 @@ "polyfill", "portable" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", "shasum": "" }, "require": { @@ -4797,7 +4797,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4831,20 +4831,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", "shasum": "" }, "require": { @@ -4853,7 +4853,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4889,7 +4889,7 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/process", @@ -5414,16 +5414,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.133.42", + "version": "3.133.45", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "4f0259689e66237e429f9fc1d879b2bcdce6dadb" + "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4f0259689e66237e429f9fc1d879b2bcdce6dadb", - "reference": "4f0259689e66237e429f9fc1d879b2bcdce6dadb", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/928a23e2ee7e195a66f93d0758895e26958c3b7d", + "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d", "shasum": "" }, "require": { @@ -5494,7 +5494,7 @@ "s3", "sdk" ], - "time": "2020-03-23T18:17:07+00:00" + "time": "2020-03-26T18:12:15+00:00" }, { "name": "behat/gherkin", @@ -5815,413 +5815,6 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "time": "2019-03-02T15:35:10+00:00" }, - { - "name": "consolidation/annotated-command", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/annotated-command.git", - "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/33e472d3cceb0f22a527d13ccfa3f76c4d21c178", - "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178", - "shasum": "" - }, - "require": { - "consolidation/output-formatters": "^4.1", - "php": ">=7.1.3", - "psr/log": "^1|^2", - "symfony/console": "^4|^5", - "symfony/event-dispatcher": "^4|^5", - "symfony/finder": "^4|^5" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - } - } - }, - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\AnnotatedCommand\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2020-02-07T03:35:30+00:00" - }, - { - "name": "consolidation/config", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/config.git", - "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", - "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "grasmash/expander": "^1", - "php": ">=5.4.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5", - "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3|^4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "suggest": { - "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require-dev": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require-dev": { - "symfony/console": "^2.8", - "symfony/event-dispatcher": "^2.8", - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Provide configuration services for a commandline tool.", - "time": "2019-03-03T19:37:04+00:00" - }, - { - "name": "consolidation/log", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/log.git", - "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/446f804476db4f73957fa4bcb66ab2facf5397ff", - "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff", - "shasum": "" - }, - "require": { - "php": ">=5.4.5", - "psr/log": "^1.0", - "symfony/console": "^4|^5" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2020-02-07T01:22:27+00:00" - }, - { - "name": "consolidation/output-formatters", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/output-formatters.git", - "reference": "eae721c3a916707c40d4390efbf48d4c799709cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/eae721c3a916707c40d4390efbf48d4c799709cc", - "reference": "eae721c3a916707c40d4390efbf48d4c799709cc", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=7.1.3", - "symfony/console": "^4|^5", - "symfony/finder": "^4|^5" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^3", - "symfony/var-dumper": "^4", - "symfony/yaml": "^4", - "victorjonsson/markdowndocs": "^1.3" - }, - "suggest": { - "symfony/var-dumper": "For using the var_dump formatter" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - } - } - }, - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\OutputFormatters\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2020-02-07T03:22:30+00:00" - }, - { - "name": "consolidation/robo", - "version": "1.4.12", - "source": { - "type": "git", - "url": "https://github.com/consolidation/Robo.git", - "reference": "eb45606f498b3426b9a98b7c85e300666a968e51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/eb45606f498b3426b9a98b7c85e300666a968e51", - "reference": "eb45606f498b3426b9a98b7c85e300666a968e51", - "shasum": "" - }, - "require": { - "consolidation/annotated-command": "^2.11.0|^4.1", - "consolidation/config": "^1.2.1", - "consolidation/log": "^1.1.1|^2", - "consolidation/output-formatters": "^3.1.13|^4.1", - "consolidation/self-update": "^1.1.5", - "grasmash/yaml-expander": "^1.4", - "league/container": "^2.4.1", - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/filesystem": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4", - "symfony/process": "^2.5|^3|^4" - }, - "replace": { - "codegyre/robo": "< 1.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "natxet/cssmin": "3.0.4", - "patchwork/jsqueeze": "^2", - "pear/archive_tar": "^1.4.4", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5.7.27", - "squizlabs/php_codesniffer": "^3" - }, - "suggest": { - "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", - "natxet/CssMin": "For minifying CSS files in taskMinify", - "patchwork/jsqueeze": "For minifying JS files in taskMinify", - "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." - }, - "bin": [ - "robo" - ], - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.5.9" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - } - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Robo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "Modern task runner", - "time": "2020-02-18T17:31:26+00:00" - }, - { - "name": "consolidation/self-update", - "version": "1.1.5", - "source": { - "type": "git", - "url": "https://github.com/consolidation/self-update.git", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/a1c273b14ce334789825a09d06d4c87c0a02ad54", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/filesystem": "^2.5|^3|^4" - }, - "bin": [ - "scripts/release" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "SelfUpdate\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - }, - { - "name": "Alexander Menk", - "email": "menk@mestrona.net" - } - ], - "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-10-28T01:52:03+00:00" - }, { "name": "csharpru/vault-php", "version": "3.5.3", @@ -6374,65 +5967,6 @@ ], "time": "2018-10-26T13:21:45+00:00" }, - { - "name": "dflydev/dot-access-data", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\DotAccessData": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "time": "2017-01-20T21:14:22+00:00" - }, { "name": "doctrine/annotations", "version": "v1.8.0", @@ -6768,47 +6302,6 @@ ], "time": "2019-10-30T14:39:59+00:00" }, - { - "name": "flow/jsonpath", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "b9738858c75d008c1211612b973e9510f8b7f8ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/b9738858c75d008c1211612b973e9510f8b7f8ea", - "reference": "b9738858c75d008c1211612b973e9510f8b7f8ea", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "peekmo/jsonpath": "dev-master", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Flow\\JSONPath": "src/", - "Flow\\JSONPath\\Test": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Frank", - "email": "stephen@flowsa.com" - } - ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2019-07-15T17:23:22+00:00" - }, { "name": "friendsofphp/php-cs-fixer", "version": "v2.14.6", @@ -6898,151 +6391,6 @@ "description": "A tool to automatically fix PHP code style", "time": "2019-08-31T12:47:52+00:00" }, - { - "name": "fzaninotto/faker", - "version": "v1.9.1", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/fc10d778e4b84d5bd315dad194661e091d307c6f", - "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^2.9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2019-12-12T13:22:17+00:00" - }, - { - "name": "grasmash/expander", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/expander.git", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\Expander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in PHP arrays file.", - "time": "2017-12-21T22:14:55+00:00" - }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "time": "2017-12-16T16:06:03+00:00" - }, { "name": "jms/metadata", "version": "1.7.0", @@ -7217,71 +6565,6 @@ ], "time": "2020-02-22T20:59:37+00:00" }, - { - "name": "league/container", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/container.git", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.2", - "php": "^5.4.0 || ^7.0" - }, - "provide": { - "container-interop/container-interop-implementation": "^1.2", - "psr/container-implementation": "^1.0" - }, - "replace": { - "orno/di": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Container\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Bennett", - "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" - } - ], - "description": "A fast and intuitive dependency injection container.", - "homepage": "https://github.com/thephpleague/container", - "keywords": [ - "container", - "dependency", - "di", - "injection", - "league", - "provider", - "service" - ], - "time": "2017-05-10T09:20:27+00:00" - }, { "name": "league/flysystem", "version": "1.0.66", @@ -7474,36 +6757,37 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.6.3", + "version": "dev-3.0.0-RC1", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "f1d9f9ede3fea875427f5d1b012561da1dca6206" + "reference": "5a3d146d082f8073a328d4e961da1c9f56a2c572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/f1d9f9ede3fea875427f5d1b012561da1dca6206", - "reference": "f1d9f9ede3fea875427f5d1b012561da1dca6206", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5a3d146d082f8073a328d4e961da1c9f56a2c572", + "reference": "5a3d146d082f8073a328d4e961da1c9f56a2c572", "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.3.0", "aws/aws-sdk-php": "^3.132", "codeception/codeception": "~2.4.5", - "composer/composer": "^1.4", - "consolidation/robo": "^1.0.0", + "composer/composer": "^1.6", "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "ext-curl": "*", + "ext-dom": "*", "ext-json": "*", "ext-openssl": "*", - "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", - "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", + "php": "~7.2.0||~7.3.0", "php-webdriver/webdriver": "^1.8.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0", + "symfony/console": "^4.4", + "symfony/finder": "^4.4", + "symfony/mime": "^5.0", + "symfony/process": "^4.4", "vlucas/phpdotenv": "^2.4" }, "replace": { @@ -7541,7 +6825,7 @@ ], "psr-4": { "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" + "MFTF\\": "dev/tests/functional/tests/MFTF" } }, "notification-url": "https://packagist.org/downloads/", @@ -7555,7 +6839,7 @@ "magento", "testing" ], - "time": "2020-02-27T19:56:31+00:00" + "time": "2020-03-24T22:00:41+00:00" }, { "name": "mikey179/vfsstream", @@ -9943,31 +9227,31 @@ }, { "name": "symfony/http-foundation", - "version": "v4.4.5", + "version": "v5.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648" + "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648", - "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", + "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/mime": "^4.3|^5.0", + "php": "^7.2.5", + "symfony/mime": "^4.4|^5.0", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "^3.4|^4.0|^5.0" + "symfony/expression-language": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -9994,7 +9278,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-02-13T19:40:01+00:00" + "time": "2020-02-14T07:43:07+00:00" }, { "name": "symfony/mime", @@ -10114,16 +9398,16 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a" + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a", - "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", "shasum": "" }, "require": { @@ -10137,7 +9421,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -10172,20 +9456,20 @@ "portable", "shim" ], - "time": "2020-01-17T12:01:36+00:00" + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "419c4940024c30ccc033650373a1fe13890d3255" + "reference": "2a18e37a489803559284416df58c71ccebe50bf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/419c4940024c30ccc033650373a1fe13890d3255", - "reference": "419c4940024c30ccc033650373a1fe13890d3255", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0", "shasum": "" }, "require": { @@ -10195,7 +9479,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -10231,20 +9515,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" + "reference": "37b0976c78b94856543260ce09b460a7bc852747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", "shasum": "" }, "require": { @@ -10253,7 +9537,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -10286,7 +9570,7 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/stopwatch", @@ -10618,6 +9902,7 @@ "minimum-stability": "stable", "stability-flags": { "magento/composer": 20, + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml similarity index 50% rename from dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml index b3ab8669a1b6b..77040cbbd3a03 100644 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -5,37 +5,11 @@ * 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="EndToEndB2CGuestUserTest"> - <!-- Search configurable product --> - <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> - <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> - </assertNotRegExp> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> - <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> - <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> - </assertNotRegExp> - </test> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserMysqlTest"> <!-- Search configurable product --> - <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault"/> <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..531c3601da587 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,35 @@ +<?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="EndToEndB2CGuestUserTest"> + <!-- Search configurable product --> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml similarity index 54% rename from dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml index e00a96b41b974..d6d6819b20bfa 100644 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml @@ -5,9 +5,8 @@ * 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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlist"> <annotations> <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List.</description> @@ -23,18 +22,4 @@ <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> </actionGroup> - <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlistSidebar"> - <annotations> - <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List sidebar.</description> - </annotations> - <arguments> - <argument name="productVar"/> - <argument name="optionProductVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> - <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> - </actionGroup> </actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml new file mode 100644 index 0000000000000..4b22145f5130b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.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="StorefrontCustomerCheckConfigurableProductInWishlistSidebar"> + <annotations> + <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List sidebar.</description> + </annotations> + <arguments> + <argument name="productVar"/> + <argument name="optionProductVar"/> + </arguments> + + <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> + <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> + </actionGroup> +</actionGroups> From 89abdaf1506b904e7cb4dd7190de7ab7fbe08ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 26 Mar 2020 22:43:51 +0100 Subject: [PATCH 593/734] Implement ActionInterface for /search/term/popular --- .../Search/Controller/Term/Popular.php | 81 ++++++++------ .../Test/Unit/Controller/Term/PopularTest.php | 103 ++++++++++++++++++ 2 files changed, 153 insertions(+), 31 deletions(-) create mode 100644 app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php diff --git a/app/code/Magento/Search/Controller/Term/Popular.php b/app/code/Magento/Search/Controller/Term/Popular.php index 0573fe6a81a12..2b3ac5c73c9dd 100644 --- a/app/code/Magento/Search/Controller/Term/Popular.php +++ b/app/code/Magento/Search/Controller/Term/Popular.php @@ -3,59 +3,78 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Search\Controller\Term; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\ForwardFactory as ResultForwardFactory; +use Magento\Framework\View\Result\PageFactory as ResultPageFactory; use Magento\Store\Model\ScopeInterface; -use Magento\Framework\Controller\ResultFactory; -class Popular extends Action +/** + * Popular search terms page + */ +class Popular implements HttpGetActionInterface { + private const XML_PATH_SEO_SEARCH_TERMS = 'catalog/seo/search_terms'; + /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ResultForwardFactory */ - protected $scopeConfig; + private $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @var ResultPageFactory */ - public function __construct(Context $context, ScopeConfigInterface $scopeConfig) - { + private $resultPageFactory; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ResultForwardFactory $resultForwardFactory + * @param ResultPageFactory $resultPageFactory + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ResultForwardFactory $resultForwardFactory, + ResultPageFactory $resultPageFactory, + ScopeConfigInterface $scopeConfig + ) { + $this->resultForwardFactory = $resultForwardFactory; + $this->resultPageFactory = $resultPageFactory; $this->scopeConfig = $scopeConfig; - parent::__construct($context); } /** - * Dispatch request - * - * @param \Magento\Framework\App\RequestInterface $request - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException + * @inheritDoc */ - public function dispatch(RequestInterface $request) + public function execute() { - $searchTerms = $this->scopeConfig->getValue( - 'catalog/seo/search_terms', - ScopeInterface::SCOPE_STORE - ); - if (!$searchTerms) { - $this->_redirect('noroute'); - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + if (!$this->checkEnabledSearchTerms()) { + $resultForward = $this->resultForwardFactory->create(); + $resultForward->forward('noroute'); + + return $resultForward; } - return parent::dispatch($request); + + return $this->resultPageFactory->create(); } /** - * @return \Magento\Framework\View\Result\Page + * Check if search terms are enabled + * + * @return bool */ - public function execute() + private function checkEnabledSearchTerms(): bool { - /** @var \Magento\Framework\View\Result\Page $resultPage */ - $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE); - return $resultPage; + return $this->scopeConfig->isSetFlag( + self::XML_PATH_SEO_SEARCH_TERMS, + ScopeInterface::SCOPE_STORE + ); } } diff --git a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php new file mode 100644 index 0000000000000..5086de7f26d43 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Search\Test\Unit\Controller\Term; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Controller\Result\Forward as ResultForward; +use Magento\Framework\Controller\Result\ForwardFactory as ResultForwardFactory; +use Magento\Framework\View\Result\Page as ResultPage; +use Magento\Framework\View\Result\PageFactory as ResultPageFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Search\Controller\Term\Popular; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class PopularTest extends TestCase +{ + private const XML_PATH_SEO_SEARCH_TERMS = 'catalog/seo/search_terms'; + + /** + * @var Popular + */ + private $action; + + /** + * @var ResultForwardFactory|MockObject + */ + private $resultForwardFactoryMock; + + /** + * @var ResultPageFactory|MockObject + */ + private $resultPageFactoryMock; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + protected function setUp() + { + $this->resultForwardFactoryMock = $this->getMockBuilder(ResultForwardFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultPageFactoryMock = $this->getMockBuilder(ResultPageFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->getMockForAbstractClass(); + + $objectManager = new ObjectManager($this); + $this->action = $objectManager->getObject( + Popular::class, + [ + 'resultForwardFactory' => $this->resultForwardFactoryMock, + 'resultPageFactory' => $this->resultPageFactoryMock, + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + public function testResult() + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(static::XML_PATH_SEO_SEARCH_TERMS, ScopeInterface::SCOPE_STORE) + ->willReturn(true); + $resultPageMock = $this->getMockBuilder(ResultPage::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultPageFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultPageMock); + + $this->action->execute(); + } + + public function testResultWithDisabledPage() + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(static::XML_PATH_SEO_SEARCH_TERMS, ScopeInterface::SCOPE_STORE) + ->willReturn(false); + $resultForwardMock = $this->getMockBuilder(ResultForward::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultForwardFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultForwardMock); + $resultForwardMock->expects($this->once()) + ->method('forward') + ->with('noroute'); + + $this->action->execute(); + } +} From 7e8387912bd3ba5bf397076f3113bb1285387413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 26 Mar 2020 23:41:27 +0100 Subject: [PATCH 594/734] Implement ActionInterface for /checkout/sidebar/removeItem --- .../Controller/Sidebar/RemoveItem.php | 116 ++++--- .../Controller/Sidebar/RemoveItemTest.php | 309 ++++++++---------- 2 files changed, 190 insertions(+), 235 deletions(-) diff --git a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php index f589e702de950..3a17f4f718019 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php @@ -3,104 +3,102 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Controller\Sidebar; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Exception; +use Magento\Checkout\Model\Sidebar; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Controller\Result\RedirectFactory as ResultRedirectFactory; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\LocalizedException; +use Psr\Log\LoggerInterface; -class RemoveItem extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface +class RemoveItem implements HttpPostActionInterface { /** - * @var \Magento\Checkout\Model\Sidebar + * @var RequestInterface */ - protected $sidebar; + private $request; /** - * @var \Psr\Log\LoggerInterface + * @var ResultJsonFactory */ - protected $logger; + private $resultJsonFactory; /** - * @var \Magento\Framework\Json\Helper\Data + * @var ResultRedirectFactory */ - protected $jsonHelper; + private $resultRedirectFactory; /** - * @var \Magento\Framework\View\Result\PageFactory + * @var Sidebar */ - protected $resultPageFactory; + protected $sidebar; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator + * @var Validator */ private $formKeyValidator; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Checkout\Model\Sidebar $sidebar - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Json\Helper\Data $jsonHelper - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @var LoggerInterface + */ + protected $logger; + + /** + * @param RequestInterface $request + * @param ResultJsonFactory $resultJsonFactory + * @param ResultRedirectFactory $resultRedirectFactory + * @param Sidebar $sidebar + * @param Validator $formKeyValidator + * @param LoggerInterface $logger */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Checkout\Model\Sidebar $sidebar, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Json\Helper\Data $jsonHelper, - \Magento\Framework\View\Result\PageFactory $resultPageFactory + RequestInterface $request, + ResultJsonFactory $resultJsonFactory, + ResultRedirectFactory $resultRedirectFactory, + Sidebar $sidebar, + Validator $formKeyValidator, + LoggerInterface $logger ) { + $this->request = $request; + $this->resultJsonFactory = $resultJsonFactory; + $this->resultRedirectFactory = $resultRedirectFactory; $this->sidebar = $sidebar; + $this->formKeyValidator = $formKeyValidator; $this->logger = $logger; - $this->jsonHelper = $jsonHelper; - $this->resultPageFactory = $resultPageFactory; - parent::__construct($context); } /** - * @return $this + * @inheritDoc */ public function execute() { - if (!$this->getFormKeyValidator()->validate($this->getRequest())) { - return $this->resultRedirectFactory->create()->setPath('*/cart/'); + if (!$this->formKeyValidator->validate($this->request)) { + return $this->resultRedirectFactory->create() + ->setPath('*/cart/'); } - $itemId = (int)$this->getRequest()->getParam('item_id'); + + $itemId = (int)$this->request->getParam('item_id'); + $error = ''; + try { $this->sidebar->checkQuoteItem($itemId); $this->sidebar->removeQuoteItem($itemId); - return $this->jsonResponse(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - return $this->jsonResponse($e->getMessage()); - } catch (\Exception $e) { + } catch (LocalizedException $e) { + $error = $e->getMessage(); + } catch (Exception $e) { $this->logger->critical($e); - return $this->jsonResponse($e->getMessage()); + $error = $e->getMessage(); } - } - - /** - * Compile JSON response - * - * @param string $error - * @return \Magento\Framework\App\Response\Http - */ - protected function jsonResponse($error = '') - { - $response = $this->sidebar->getResponseData($error); - return $this->getResponse()->representJson( - $this->jsonHelper->jsonEncode($response) - ); - } + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData($this->sidebar->getResponseData($error)); - /** - * @return \Magento\Framework\Data\Form\FormKey\Validator - * @deprecated 100.0.9 - */ - private function getFormKeyValidator() - { - if (!$this->formKeyValidator) { - $this->formKeyValidator = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Data\Form\FormKey\Validator::class); - } - return $this->formKeyValidator; + return $resultJson; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php index 7653a51b2f9b7..c7dacbb1fe307 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php @@ -3,88 +3,105 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Test\Unit\Controller\Sidebar; +use Magento\Checkout\Controller\Sidebar\RemoveItem; +use Magento\Checkout\Model\Sidebar; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Json as ResultJson; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Controller\Result\Redirect as ResultRedirect; +use Magento\Framework\Controller\Result\RedirectFactory as ResultRedirectFactory; +use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RemoveItemTest extends \PHPUnit\Framework\TestCase +class RemoveItemTest extends TestCase { - /** @var \Magento\Checkout\Controller\Sidebar\RemoveItem */ - protected $removeItem; - - /** @var ObjectManagerHelper */ - protected $objectManagerHelper; - - /** @var \Magento\Checkout\Model\Sidebar|\PHPUnit_Framework_MockObject_MockObject */ - protected $sidebarMock; + /** + * @var RemoveItem + */ + private $action; - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $loggerMock; + /** + * @var RequestInterface|MockObject + */ + private $requestMock; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $jsonHelperMock; + /** + * @var ResultJsonFactory|MockObject + */ + private $resultJsonFactoryMock; - /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $requestMock; + /** + * @var ResultRedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; - /** @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $responseMock; + /** + * @var Sidebar|MockObject + */ + private $sidebarMock; - /** @var \Magento\Framework\View\Result\PageFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $resultPageFactoryMock; + /** + * @var Validator|MockObject + */ + private $formKeyValidatorMock; /** - * @var \Magento\Framework\Controller\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ - private $resultRedirectFactory; + private $loggerMock; protected function setUp() { - $this->sidebarMock = $this->createMock(\Magento\Checkout\Model\Sidebar::class); - $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->jsonHelperMock = $this->createMock(\Magento\Framework\Json\Helper\Data::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $this->responseMock = $this->getMockForAbstractClass( - \Magento\Framework\App\ResponseInterface::class, - [], - '', - false, - true, - true, - ['representJson'] + $this->requestMock = $this->createMock(RequestInterface::class); + $this->resultJsonFactoryMock = $this->createPartialMock( + ResultJsonFactory::class, + ['create'] ); - $this->resultPageFactoryMock = $this->createMock(\Magento\Framework\View\Result\PageFactory::class); - $this->resultRedirectFactory = $this->createPartialMock( - \Magento\Framework\Controller\Result\RedirectFactory::class, + $this->resultRedirectFactoryMock = $this->createPartialMock( + ResultRedirectFactory::class, ['create'] ); + $this->sidebarMock = $this->createMock(Sidebar::class); + $this->formKeyValidatorMock = $this->createMock(Validator::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->removeItem = $this->objectManagerHelper->getObject( - \Magento\Checkout\Controller\Sidebar\RemoveItem::class, + $objectManager = new ObjectManager($this); + $this->action = $objectManager->getObject( + RemoveItem::class, [ - 'sidebar' => $this->sidebarMock, - 'logger' => $this->loggerMock, - 'jsonHelper' => $this->jsonHelperMock, 'request' => $this->requestMock, - 'response' => $this->responseMock, - 'resultPageFactory' => $this->resultPageFactoryMock, - 'resultRedirectFactory' => $this->resultRedirectFactory - + 'resultJsonFactory' => $this->resultJsonFactoryMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'sidebar' => $this->sidebarMock, + 'formKeyValidator' => $this->formKeyValidatorMock, + 'logger' => $this->loggerMock ] ); - $formKeyValidatorMock = $this->createMock(\Magento\Framework\Data\Form\FormKey\Validator::class); - $this->setPropertyValue($this->removeItem, 'formKeyValidator', $formKeyValidatorMock); } public function testExecute() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $responseData = [ + 'cleanup' => true, + 'data' => [ + 'summary_qty' => 0, + 'summary_text' => __(' items'), + 'subtotal' => 0, + ], + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -101,46 +118,33 @@ public function testExecute() ->method('removeQuoteItem') ->with(1) ->willReturnSelf(); + $this->sidebarMock->expects($this->once()) ->method('getResponseData') ->with('') - ->willReturn( - [ - 'cleanup' => true, - 'data' => [ - 'summary_qty' => 0, - 'summary_text' => __(' items'), - 'subtotal' => 0, - ], - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'cleanup' => true, - 'data' => [ - 'summary_qty' => 0, - 'summary_text' => __(' items'), - 'subtotal' => 0, - ], - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWithLocalizedException() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $errorMessage = 'Error message!'; + $responseData = [ + 'success' => false, + 'error_message' => $errorMessage + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -152,40 +156,34 @@ public function testExecuteWithLocalizedException() $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) - ->willThrowException(new LocalizedException(__('Error message!'))); + ->willThrowException(new LocalizedException(__($errorMessage))); $this->sidebarMock->expects($this->once()) ->method('getResponseData') - ->with('Error message!') - ->willReturn( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->with($errorMessage) + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWithException() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $errorMessage = 'Error message!'; + $responseData = [ + 'success' => false, + 'error_message' => $errorMessage + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -194,7 +192,7 @@ public function testExecuteWithException() ->with('item_id', null) ->willReturn('1'); - $exception = new \Exception('Error message!'); + $exception = new \Exception($errorMessage); $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') @@ -208,77 +206,36 @@ public function testExecuteWithException() $this->sidebarMock->expects($this->once()) ->method('getResponseData') - ->with('Error message!') - ->willReturn( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->with($errorMessage) + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWhenFormKeyValidationFailed() { - $resultRedirect = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); - $resultRedirect->expects($this->once())->method('setPath')->with('*/cart/')->willReturnSelf(); - $this->resultRedirectFactory->expects($this->once())->method('create')->willReturn($resultRedirect); - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $resultRedirect = $this->createMock(ResultRedirect::class); + $resultRedirect->expects($this->once()) + ->method('setPath') + ->with('*/cart/') + ->willReturnSelf(); + $this->resultRedirectFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultRedirect); + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(false); - $this->assertEquals($resultRedirect, $this->removeItem->execute()); - } - - /** - * Get any object property value. - * - * @param $object - * @param $property - * @return mixed - * @deprecated - */ - protected function getPropertyValue($object, $property) - { - $reflection = new \ReflectionClass(get_class($object)); - $reflectionProperty = $reflection->getProperty($property); - $reflectionProperty->setAccessible(true); - - return $reflectionProperty->getValue($object); - } - - /** - * Set object property value. - * - * @param $object - * @param $property - * @param $value - * @deprecated - */ - protected function setPropertyValue(&$object, $property, $value) - { - $reflection = new \ReflectionClass(get_class($object)); - $reflectionProperty = $reflection->getProperty($property); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($object, $value); - return $object; + $this->assertEquals($resultRedirect, $this->action->execute()); } } From 8e93b1494ab34ad9be056db9b7b4481d3beb7f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Fri, 27 Mar 2020 09:11:30 +0100 Subject: [PATCH 595/734] Add page type assertion to unit tests --- .../Magento/Search/Test/Unit/Controller/Term/PopularTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php index 5086de7f26d43..228ad2fec7da1 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php @@ -79,7 +79,7 @@ public function testResult() ->method('create') ->willReturn($resultPageMock); - $this->action->execute(); + $this->assertEquals($resultPageMock, $this->action->execute()); } public function testResultWithDisabledPage() @@ -98,6 +98,6 @@ public function testResultWithDisabledPage() ->method('forward') ->with('noroute'); - $this->action->execute(); + $this->assertEquals($resultForwardMock, $this->action->execute()); } } From 0e051ae05d8897165f9149367d7f4ca938db9edd Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Fri, 27 Mar 2020 10:16:01 +0200 Subject: [PATCH 596/734] MC-32423: Backend - Braintree payments validation overlay issue --- app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js index 845ed03725bfb..393b07fc30403 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js @@ -309,6 +309,7 @@ define([ self.hostedFieldsInstance.tokenize(function (err, payload) { if (err) { + $('body').trigger('processStop'); self.error($t('Some payment input fields are invalid.')); return false; From d073d724a9409653afdb5eb8cdf4904230bbbfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Fri, 27 Mar 2020 09:52:08 +0100 Subject: [PATCH 597/734] Modify assertion to assertSame --- .../Magento/Search/Test/Unit/Controller/Term/PopularTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php index 228ad2fec7da1..590d959b95ff3 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php @@ -79,7 +79,7 @@ public function testResult() ->method('create') ->willReturn($resultPageMock); - $this->assertEquals($resultPageMock, $this->action->execute()); + $this->assertSame($resultPageMock, $this->action->execute()); } public function testResultWithDisabledPage() @@ -98,6 +98,6 @@ public function testResultWithDisabledPage() ->method('forward') ->with('noroute'); - $this->assertEquals($resultForwardMock, $this->action->execute()); + $this->assertSame($resultForwardMock, $this->action->execute()); } } From c6fe33a2a0616189dfa0a08b00ab7342c69fae5a Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 27 Mar 2020 11:05:47 +0200 Subject: [PATCH 598/734] MC-32478: [TSG] [PR] Stabilization 2.4-develop-pr18 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 3b92c901aac8e..f3dff26fbcf88 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9e13134f3e209140ec6c43092c189da9", + "content-hash": "bf5e456936a97bb395035fb5eceab844", "packages": [ { "name": "braintree/braintree_php", @@ -9962,12 +9962,12 @@ }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "^4.4|^5.0" + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9994,7 +9994,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-02-13T19:40:01+00:00" }, { "name": "symfony/mime", From 2e9ceb33608486ec25178346d5be643f1e106934 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 27 Mar 2020 12:37:41 +0200 Subject: [PATCH 599/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...onfigurableProductWithSeveralAttributesPrependMediaTest.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index 9b7e9119de660..0b3a279e2d615 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -19,9 +19,6 @@ <group value="catalog"/> <group value="configurableProduct"/> <group value="swatch"/> - <skip> - <issueId value="MC-32197"/> - </skip> </annotations> <before> From 3b5ac1920fab0cb66898cb9da9e6ab9b05ae12e9 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 27 Mar 2020 13:51:03 +0200 Subject: [PATCH 600/734] MC-32697: [2.4] AbstractSimpleObjectBuilder.php throws exception when Interceptor instance comes. --- .../Magento/Framework/Api/AbstractSimpleObjectBuilder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php index cf0a887249049..c371887d74699 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php @@ -9,6 +9,7 @@ /** * Base Builder Class for simple data Objects + * @deprecated 102.0.4 * @SuppressWarnings(PHPMD.NumberOfChildren) */ abstract class AbstractSimpleObjectBuilder implements SimpleBuilderInterface From c433c024f908b1649c2e8d32b452a94237c41910 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 27 Mar 2020 16:48:50 +0200 Subject: [PATCH 601/734] MC-32697: [2.4] AbstractSimpleObjectBuilder.php throws exception when Interceptor instance comes. --- .../Magento/Framework/Api/AbstractSimpleObjectBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php index c371887d74699..cf7f7732c116c 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php @@ -9,7 +9,7 @@ /** * Base Builder Class for simple data Objects - * @deprecated 102.0.4 + * @deprecated Every builder should have their own implementation of \Magento\Framework\Api\SimpleBuilderInterface * @SuppressWarnings(PHPMD.NumberOfChildren) */ abstract class AbstractSimpleObjectBuilder implements SimpleBuilderInterface From bd4a5992696a300bb5d2b5e6ce725d7e09d8b0ba Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Fri, 27 Mar 2020 14:53:24 -0500 Subject: [PATCH 602/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Refactored logic on resolver --- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 315e03b2c697c..cd960dc001a4e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -57,9 +57,8 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; - if (!$customerAddressId && $addressInput) { - $addressInput['save_in_address_book'] = isset($billingAddressInput['address']['save_in_address_book']) - ? (bool) $billingAddressInput['address']['save_in_address_book'] : true; + if (!$customerAddressId && !isset($billingAddressInput['address']['save_in_address_book']) && $addressInput) { + $addressInput['save_in_address_book'] = true; } // Need to keep this for BC of `use_for_shipping` field @@ -68,6 +67,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $sameAsShipping = isset($billingAddressInput['same_as_shipping']) ? (bool)$billingAddressInput['same_as_shipping'] : $sameAsShipping; + if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( __('The billing address must contain either "customer_address_id" or "address".') From fb72f9da0910c900c694fe6fc192ff9f910b68dc Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Sun, 29 Mar 2020 22:02:17 +0300 Subject: [PATCH 603/734] Fix unit tests --- .../PayPal/VaultDetailsHandlerTest.php | 15 ++++--- .../Response/VaultDetailsHandlerTest.php | 9 +++- .../Test/Unit/Model/LinkManagementTest.php | 18 ++++---- .../Unit/Pricing/Price/BundleOptionsTest.php | 43 +++++++++++++------ .../Unit/Model/Plugin/CustomerPluginTest.php | 21 +++++++-- .../Test/Unit/FixedProductTaxTest.php | 2 +- .../Unit/CustomerWishlistResolverTest.php | 2 +- 7 files changed, 75 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php index b3a7f8b9ee76a..657aa930a841f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php @@ -8,8 +8,8 @@ use Braintree\Result\Successful; use Braintree\Transaction; use Braintree\Transaction\PayPalDetails; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Framework\Intl\DateTimeFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; @@ -99,7 +99,12 @@ protected function setUp() ->getMock(); $this->paymentExtensionMock = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods([ + 'setVaultPaymentToken', + 'getVaultPaymentToken', + 'setNotificationMessage', + 'getNotificationMessage' + ]) ->disableOriginalConstructor() ->getMock(); $this->paymentExtensionFactoryMock = $this->getMockBuilder(OrderPaymentExtensionInterfaceFactory::class) @@ -119,7 +124,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - + $this->handler = new VaultDetailsHandler( $this->paymentTokenFactoryMock, $this->paymentExtensionFactoryMock, @@ -139,7 +144,7 @@ public function testHandle() ->with($this->paymentTokenMock); $this->paymentExtensionMock->method('getVaultPaymentToken') ->willReturn($this->paymentTokenMock); - + $this->paymentDataObjectMock->method('getPayment') ->willReturn($this->paymentInfoMock); @@ -154,7 +159,7 @@ public function testHandle() $expirationDate = '2017-07-05 00:00:00'; $this->dateTimeFactoryMock->method('create') ->willReturn($dateTime); - + $this->handler->handle($this->subject, $response); $extensionAttributes = $this->paymentInfoMock->getExtensionAttributes(); diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php index c8ec52560be29..131d94f3206c8 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php @@ -25,7 +25,7 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * VaultDetailsHandler Test + * Verify class VaultDetailsHandler * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -216,7 +216,12 @@ private function getConfigMock(): Config private function initPaymentExtensionAttributesMock() { $this->paymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods([ + 'setVaultPaymentToken', + 'getVaultPaymentToken', + 'setNotificationMessage', + 'getNotificationMessage' + ]) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php index d67613a46ad66..b31b82a6a72bf 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php @@ -391,7 +391,7 @@ public function testAddChildNonExistingOption() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage The bundle product can't contain another composite product. */ public function testAddChildLinkedProductIsComposite() @@ -449,7 +449,7 @@ public function testAddChildLinkedProductIsComposite() } /** - * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testAddChildProductAlreadyExistsInOption() { @@ -525,7 +525,7 @@ public function testAddChildProductAlreadyExistsInOption() } /** - * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testAddChildCouldNotSave() { @@ -759,7 +759,7 @@ public function testSaveChild() } /** - * @ex@expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testSaveChildFailedToSave() { @@ -842,7 +842,7 @@ public function testSaveChildFailedToSave() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithoutId() { @@ -874,7 +874,7 @@ public function testSaveChildWithoutId() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage The product link with the "12345" ID field wasn't found. Verify the ID and try again. */ public function testSaveChildWithInvalidId() @@ -925,7 +925,7 @@ public function testSaveChildWithInvalidId() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithCompositeProductLink() { @@ -954,7 +954,7 @@ public function testSaveChildWithCompositeProductLink() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException */ public function testSaveChildWithSimpleProduct() { @@ -1016,7 +1016,7 @@ public function testRemoveChild() } /** - * @ex@expectedException \Magento\Framework\Exception\InputException + * @expectedException \Magento\Framework\Exception\InputException */ public function testRemoveChildForbidden() { diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php index 37973b9b8ae28..d795af793675b 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php @@ -7,23 +7,24 @@ namespace Magento\Bundle\Test\Unit\Pricing\Price; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Framework\Pricing\Amount\AmountFactory; -use Magento\Framework\Pricing\Adjustment\Calculator as AdjustmentCalculator; -use Magento\Framework\Pricing\PriceInfo\Base as BasePriceInfo; -use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Framework\Pricing\Amount\AmountInterface; -use Magento\Framework\Pricing\Amount\Base as BaseAmount; -use Magento\Bundle\Pricing\Price\BundleOptions; -use Magento\Bundle\Pricing\Price\BundleSelectionPrice; -use Magento\Bundle\Pricing\Price\BundleSelectionFactory; -use Magento\Bundle\Pricing\Adjustment\Calculator as BundleAdjustmentCalculator; use Magento\Bundle\Model\Option as BundleOption; use Magento\Bundle\Model\Product\Type as BundleProductType; use Magento\Bundle\Model\ResourceModel\Option\Collection as BundleOptionCollection; +use Magento\Bundle\Pricing\Adjustment\Calculator as BundleAdjustmentCalculator; +use Magento\Bundle\Pricing\Adjustment\SelectionPriceListProviderInterface; +use Magento\Bundle\Pricing\Price\BundleOptions; +use Magento\Bundle\Pricing\Price\BundleSelectionFactory; +use Magento\Bundle\Pricing\Price\BundleSelectionPrice; use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\Adjustment\Calculator as AdjustmentCalculator; +use Magento\Framework\Pricing\Amount\AmountFactory; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\Amount\Base as BaseAmount; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Pricing\PriceInfo\Base as BasePriceInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Tax\Helper\Data as TaxHelperData; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Test for Magento\Bundle\Pricing\Price\BundleOptions @@ -71,6 +72,11 @@ class BundleOptionsTest extends \PHPUnit\Framework\TestCase */ private $priceInfoMock; + /** + * @var SelectionPriceListProviderInterface + */ + private $selectionPriceListProviderMock; + protected function setUp() { $this->priceInfoMock = $this->getMockBuilder(BasePriceInfo::class) @@ -102,9 +108,20 @@ function ($fullAmount, $adjustments) { ->disableOriginalConstructor() ->getMock(); + $this->selectionPriceListProviderMock = $this->getMockBuilder(SelectionPriceListProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->bundleCalculatorMock = $this->getMockBuilder(BundleAdjustmentCalculator::class) ->setConstructorArgs( - [$this->baseCalculator, $this->amountFactory, $this->selectionFactoryMock, $taxData, $priceCurrency] + [ + $this->baseCalculator, + $this->amountFactory, + $this->selectionFactoryMock, + $taxData, + $priceCurrency, + $this->selectionPriceListProviderMock + ] ) ->setMethods(['getOptionsAmount']) ->getMock(); diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php index 4dbaa93b1d134..c8a846cb2a758 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php @@ -6,10 +6,10 @@ namespace Magento\Newsletter\Test\Unit\Model\Plugin; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Config\Share; use Magento\Customer\Model\ResourceModel\CustomerRepository; -use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Newsletter\Model\Plugin\CustomerPlugin; @@ -134,7 +134,13 @@ public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expe } $this->subscriberFactory->method('create')->willReturn($subscriber); - $customerExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['getIsSubscribed']); + $customerExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + [ + 'getIsSubscribed', + 'CustomerExtensionInterface' + ] + ); $customerExtension->method('getIsSubscribed')->willReturn($newValue); /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); @@ -152,7 +158,14 @@ public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expe $this->subscriptionManager->expects($this->never())->method('subscribeCustomer'); $this->subscriptionManager->expects($this->never())->method('unsubscribeCustomer'); } - $resultExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['setIsSubscribed']); + $resultExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + [ + 'setIsSubscribed', + 'getIsSubscribed', + 'CustomerExtensionInterface' + ] + ); $resultExtension->expects($this->once())->method('setIsSubscribed')->with($resultIsSubscribed); /** @var CustomerInterface|MockObject $result */ $result = $this->createMock(CustomerInterface::class); @@ -291,7 +304,7 @@ public function testAfterGetByIdCreatesExtensionAttributes(): void $customerExtension = $this->createPartialMock( CustomerExtensionInterface::class, - ['getIsSubscribed', 'setIsSubscribed'] + ['getIsSubscribed', 'setIsSubscribed','CustomerExtensionInterface'] ); $customerExtension->expects($this->once())->method('setIsSubscribed')->with($subscribed); $this->extensionFactory->expects($this->once())->method('create')->willReturn($customerExtension); diff --git a/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php index 9e5812282545a..b289c7d1dac55 100644 --- a/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php +++ b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php @@ -58,7 +58,7 @@ protected function setUp() ->getMock(); $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) - ->setMethods(['getStore']) + ->setMethods(['getStore', 'setStore', 'getIsCustomer', 'setIsCustomer']) ->getMock(); $this->contextMock->method('getExtensionAttributes') diff --git a/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php index f5baa5183e558..67c38561c7e41 100644 --- a/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php +++ b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php @@ -58,7 +58,7 @@ protected function setUp() ->getMock(); $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) - ->setMethods(['getIsCustomer']) + ->setMethods(['getStore', 'setStore', 'getIsCustomer', 'setIsCustomer']) ->getMock(); $this->contextMock->method('getExtensionAttributes') From f9a0eb45426f09af6aa9350863b8f0f1f3ccea42 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 30 Mar 2020 09:18:00 +0300 Subject: [PATCH 604/734] MC-32478: [TSG] [PR] Stabilization 2.4-develop-pr18 --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index d3cdaa36326f6..550de1d904aec 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3b2c88252ab70b75998cb4e200257796", + "content-hash": "b801d92bd57ad3f2d51d2b264e9c37ac", "packages": [ { "name": "braintree/braintree_php", From 4046760db00a93d147b3b40609c1c0b6cdda8ca0 Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Wed, 18 Mar 2020 20:24:03 +0200 Subject: [PATCH 605/734] Added improvements for credit memo (new page) total fields --- .../Order/Creditmemo/Create/Adjustments.php | 23 +++++++ .../Sales/Model/Order/CreditmemoFactory.php | 63 ++++++++++++++++--- ...dminCreditMemoNewPageTotalsActionGroup.xml | 27 ++++++++ ...minCreditMemoViewPageTotalsActionGroup.xml | 27 ++++++++ ...nCheckNewCreditMemoTotalsForFranceTest.xml | 52 +++++++++++++++ ...CreateCreditMemoWithCashOnDeliveryTest.xml | 10 +-- .../create/totals/adjustments.phtml | 39 ++++++++---- 7 files changed, 214 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php index 3af058ef978cf..a7649fecaf2bb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php @@ -6,6 +6,8 @@ namespace Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Sales\Model\Order; +use Zend_Currency; /** * Credit memo adjustments block @@ -68,6 +70,27 @@ public function initTotals() return $this; } + /** + * Format value based on order currency + * + * @param null|float $value + * + * @return string + */ + public function formatValue($value) + { + /** @var Order $order */ + $order = $this->getSource()->getOrder(); + + return $order->getOrderCurrency()->formatPrecision( + $value, + 2, + ['display' => Zend_Currency::NO_SYMBOL], + false, + false + ); + } + /** * Get source object * diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index 73afd0a06f710..8138e193e7978 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -6,10 +6,15 @@ namespace Magento\Sales\Model\Order; use Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\FormatInterface; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; use Magento\Sales\Api\Data\OrderItemInterface; /** * Factory class for @see \Magento\Sales\Model\Order\Creditmemo + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CreditmemoFactory { @@ -32,7 +37,12 @@ class CreditmemoFactory protected $unserialize; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var FormatInterface + */ + private $localeFormat; + + /** + * @var JsonSerializer */ private $serializer; @@ -41,18 +51,19 @@ class CreditmemoFactory * * @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory * @param \Magento\Tax\Model\Config $taxConfig - * @param \Magento\Framework\Serialize\Serializer\Json $serializer + * @param JsonSerializer $serializer + * @param FormatInterface $localeFormat */ public function __construct( \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory, \Magento\Tax\Model\Config $taxConfig, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + JsonSerializer $serializer = null, + FormatInterface $localeFormat = null ) { $this->convertor = $convertOrderFactory->create(); $this->taxConfig = $taxConfig; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Serialize\Serializer\Json::class - ); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(JsonSerializer::class); + $this->localeFormat = $localeFormat ?: ObjectManager::getInstance()->get(FormatInterface::class); } /** @@ -166,6 +177,7 @@ protected function canRefundItem($item, $qtys = [], $invoiceQtysRefundLimits = [ return isset($qtys[$parent->getId()]) && $qtys[$parent->getId()] > 0; } } + return false; } else { return $this->canRefundNoDummyItem($item, $invoiceQtysRefundLimits); } @@ -199,14 +211,17 @@ protected function canRefundNoDummyItem($item, $invoiceQtysRefundLimits = []) protected function initData($creditmemo, $data) { if (isset($data['shipping_amount'])) { - $creditmemo->setBaseShippingAmount((double)$data['shipping_amount']); - $creditmemo->setBaseShippingInclTax((double)$data['shipping_amount']); + $shippingAmount = $this->parseNumber($data['shipping_amount']); + $creditmemo->setBaseShippingAmount($shippingAmount); + $creditmemo->setBaseShippingInclTax($shippingAmount); } if (isset($data['adjustment_positive'])) { - $creditmemo->setAdjustmentPositive($data['adjustment_positive']); + $adjustmentPositiveAmount = $this->parseAdjustmentAmount($data['adjustment_positive']); + $creditmemo->setAdjustmentPositive($adjustmentPositiveAmount); } if (isset($data['adjustment_negative'])) { - $creditmemo->setAdjustmentNegative($data['adjustment_negative']); + $adjustmentNegativeAmount = $this->parseAdjustmentAmount($data['adjustment_negative']); + $creditmemo->setAdjustmentNegative($adjustmentNegativeAmount); } } @@ -340,4 +355,32 @@ private function getShippingAmount(Invoice $invoice): float return (float)$amount; } + + /** + * Parse adjustment amount value to number + * + * @param string|null $amount + * + * @return float|null + */ + private function parseAdjustmentAmount($amount) + { + $amount = trim($amount); + $percentAmount = substr($amount, -1) == '%'; + $amount = $this->parseNumber($amount); + + return $percentAmount ? $amount . '%' : $amount; + } + + /** + * Parse value to number + * + * @param string|null $value + * + * @return float|null + */ + private function parseNumber($value) + { + return $this->localeFormat->getNumber($value); + } } diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml new file mode 100644 index 0000000000000..8465383e0c40c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml @@ -0,0 +1,27 @@ +<?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="AssertAdminCreditMemoNewPageTotalsActionGroup"> + <annotations> + <description>Checks totals values on the Credit Memo new page.</description> + </annotations> + <arguments> + <argument name="refundShipping" type="string" defaultValue="0"/> + <argument name="adjustmentRefund" type="string" defaultValue="0"/> + <argument name="adjustmentFee" type="string" defaultValue="0"/> + <argument name="grandTotal" type="string" defaultValue="0"/> + </arguments> + + <seeInField selector="{{AdminCreditMemoTotalSection.refundShipping}}" userInput="{{refundShipping}}" stepKey="seeRefundShipping"/> + <seeInField selector="{{AdminCreditMemoTotalSection.adjustmentRefund}}" userInput="{{adjustmentRefund}}" stepKey="seeAdjustmentRefund"/> + <seeInField selector="{{AdminCreditMemoTotalSection.adjustmentFee}}" userInput="{{adjustmentFee}}" stepKey="seeAdjustmentFee"/> + <see selector="{{AdminCreditMemoTotalSection.grandTotal}}" userInput="{{grandTotal}}" stepKey="seeGrandTotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml new file mode 100644 index 0000000000000..7bea25a6c5252 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml @@ -0,0 +1,27 @@ +<?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="AssertAdminCreditMemoViewPageTotalsActionGroup"> + <annotations> + <description>Checks totals values on the Credit Memo view page.</description> + </annotations> + <arguments> + <argument name="subtotal" type="string" defaultValue="0"/> + <argument name="adjustmentRefund" type="string" defaultValue="0"/> + <argument name="adjustmentFee" type="string" defaultValue="0"/> + <argument name="grandTotal" type="string" defaultValue="0"/> + </arguments> + + <see selector="{{AdminCreditMemoViewTotalSection.subtotal}}" userInput="{{subtotal}}" stepKey="seeSubtotal"/> + <see selector="{{AdminCreditMemoViewTotalSection.adjustmentRefund}}" userInput="{{adjustmentRefund}}" stepKey="seeAdjustmentRefund"/> + <see selector="{{AdminCreditMemoViewTotalSection.adjustmentFee}}" userInput="{{adjustmentFee}}" stepKey="seeAdjustmentFee"/> + <see selector="{{AdminCreditMemoViewTotalSection.grandTotal}}" userInput="{{grandTotal}}" stepKey="seeGrandTotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml new file mode 100644 index 0000000000000..ce6848c3e9d66 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml @@ -0,0 +1,52 @@ +<?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="AdminCheckNewCreditMemoTotalsForFranceTest" extends="AdminCreateCreditMemoWithCashOnDeliveryTest"> + <annotations> + <stories value="Credit memo entity for France locale"/> + <title value="Credit memo entity for France locale"/> + <description value="Create Credit Memo with cash on delivery payment and assert 0 shipping refund for France locale"/> + <group value="sales"/> + </annotations> + <before> + <magentoCLI command="setup:static-content:deploy fr_FR" stepKey="deployStaticContentWithFrenchLocale" before="LoginAsAdmin"/> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToFrance" after="LoginAsAdmin"> + <argument name="InterfaceLocaleByValue" value="fr_FR"/> + </actionGroup> + </before> + <after> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToDefaultValue" before="logout"> + <argument name="InterfaceLocaleByValue" value="en_US"/> + </actionGroup> + </after> + + <actionGroup ref="AdminOpenAndFillCreditMemoRefundActionGroup" stepKey="fillCreditMemoRefund"> + <argument name="itemQtyToRefund" value="1"/> + <argument name="shippingRefund" value="0"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + </actionGroup> + + <actionGroup ref="AssertAdminCreditMemoNewPageTotalsActionGroup" stepKey="assertCreditMemoRefundTotals" after="fillCreditMemoRefund"> + <argument name="refundShipping" value="0,00"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + <argument name="subtotalRow" value="560,00"/> + <argument name="grandTotal" value="555,00"/> + </actionGroup> + + <actionGroup ref="AssertAdminCreditMemoViewPageTotalsActionGroup" stepKey="assertCreditMemoViewPageTotals"> + <argument name="subtotal" value="560,00"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + <argument name="grandTotal" value="555,00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index e9954de55afbc..45f3f76f7c520 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -98,10 +98,12 @@ <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> - <see selector="{{AdminCreditMemoViewTotalSection.subtotal}}" userInput="$560.00" stepKey="seeSubtotal"/> - <see selector="{{AdminCreditMemoViewTotalSection.adjustmentRefund}}" userInput="$5.00" stepKey="seeAdjustmentRefund"/> - <see selector="{{AdminCreditMemoViewTotalSection.adjustmentFee}}" userInput="$10.00" stepKey="seeAdjustmentFee"/> - <see selector="{{AdminCreditMemoViewTotalSection.grandTotal}}" userInput="$555.00" stepKey="assertRefundOnCreditMemoTab"/> + <actionGroup ref="AssertAdminCreditMemoViewPageTotalsActionGroup" stepKey="assertCreditMemoViewPageTotals"> + <argument name="subtotal" value="$560.00"/> + <argument name="adjustmentRefund" value="$5.00"/> + <argument name="adjustmentFee" value="$10.00"/> + <argument name="grandTotal" value="$555.00"/> + </actionGroup> <!--Login to storefront as previously created customer--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml index f46b8c11cc240..fc624bfd803b6 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml @@ -5,33 +5,42 @@ */ ?> <?php $_source = $block->getSource() ?> -<?php if ($_source) : ?> +<?php if ($_source): ?> <tr> - <td class="label"><?= $block->escapeHtml($block->getShippingLabel()) ?><div id="shipping_amount_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml($block->getShippingLabel()) ?> + <div id="shipping_amount_adv"></div> + </td> <td> <input type="text" name="creditmemo[shipping_amount]" - value="<?= $block->escapeHtmlAttr($block->getShippingAmount()) ?>" + value="<?= /* @noEscape */ $block->formatValue($block->getShippingAmount()) ?>" class="input-text admin__control-text not-negative-amount" id="shipping_amount" /> </td> </tr> <tr> - <td class="label"><?= $block->escapeHtml(__('Adjustment Refund')) ?><div id="adjustment_positive_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml(__('Adjustment Refund')) ?> + <div id="adjustment_positive_adv"></div> + </td> <td> <input type="text" name="creditmemo[adjustment_positive]" - value="<?= $block->escapeHtmlAttr($_source->getBaseAdjustmentPositive()) ?>" + value="<?= /* @noEscape */ $block->formatValue($_source->getBaseAdjustmentPositive()) ?>" class="input-text admin__control-text not-negative-amount" id="adjustment_positive" /> </td> </tr> <tr> - <td class="label"><?= $block->escapeHtml(__('Adjustment Fee')) ?><div id="adjustment_negative_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml(__('Adjustment Fee')) ?> + <div id="adjustment_negative_adv"></div> + </td> <td> <input type="text" name="creditmemo[adjustment_negative]" - value="<?= $block->escapeHtmlAttr($_source->getBaseAdjustmentNegative()) ?>" + value="<?= /* @noEscape */ $block->formatValue($_source->getBaseAdjustmentNegative()) ?>" class="input-text admin__control-text not-negative-amount" id="adjustment_negative"/> <script> @@ -39,12 +48,16 @@ //<![CDATA[ Validation.addAllThese([ - ['not-negative-amount', '<?= $block->escapeJs(__('Please enter a positive number in this field.')) ?>', function(v) { - if(v.length) - return /^\s*\d+([,.]\d+)*\s*%?\s*$/.test(v); - else - return true; - }] + [ + 'not-negative-amount', + '<?= $block->escapeJs(__('Please enter a positive number in this field.')) ?>', + function (v) { + if (v.length) + return /^\s*\d+([,.]\d+)*\s*%?\s*$/.test(v); + else + return true; + } + ] ]); if ($('shipping_amount')) { From b20f9ebfae1d49668e1901a4ed75b5d906dc4464 Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi <a.beziazychnyi@atwix.com> Date: Mon, 30 Mar 2020 11:00:26 +0300 Subject: [PATCH 606/734] Fixed tests for Magento\Framework\Stdlib\DateTime\DateTime --- .../Test/Unit/DateTime/DateTimeTest.php | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php index 3c7f49671d74a..210e36ee05ef1 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php @@ -5,13 +5,18 @@ */ namespace Magento\Framework\Stdlib\Test\Unit\DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use Exception; use Magento\Framework\Stdlib\DateTime\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Magento\Framework\Stdlib\DateTimeTest test case + * Tests for @see DateTime */ -class DateTimeTest extends \PHPUnit\Framework\TestCase +class DateTimeTest extends TestCase { /** * @var string @@ -19,12 +24,14 @@ class DateTimeTest extends \PHPUnit\Framework\TestCase private $testDate = '2015-04-02 21:03:00'; /** - * @param int|string|\DateTimeInterface $input + * @param int|string|DateTimeInterface $input + * @throws Exception + * * @dataProvider dateTimeInputDataProvider */ public function testGmtTimestamp($input) { - /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + /** @var TimezoneInterface|MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); $timezone->method('date')->willReturn(new \DateTime($this->testDate)); @@ -33,12 +40,14 @@ public function testGmtTimestamp($input) } /** - * @param int|string|\DateTimeInterface $input + * @param int|string|DateTimeInterface $input + * @throws Exception + * * @dataProvider dateTimeInputDataProvider */ public function testTimestamp($input) { - /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + /** @var TimezoneInterface|MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); $timezone->method('date')->willReturn(new \DateTime($this->testDate)); @@ -48,28 +57,50 @@ public function testTimestamp($input) public function testGtmOffset() { - /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + /** @var TimezoneInterface|MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); $timezone->method('getConfigTimezone')->willReturn('Europe/Amsterdam'); - /** @var DateTime|\PHPUnit_Framework_MockObject_MockObject $dateTime */ + /** @var DateTime|MockObject $dateTime */ $dateTime = $this->getMockBuilder(DateTime::class) ->setConstructorArgs([$timezone]) ->setMethods(null) ->getMock(); - $this->assertEquals(3600, $dateTime->getGmtOffset()); + $this->assertEquals( + $this->getExpectedGtmOffset($timezone->getConfigTimezone()), + $dateTime->getGmtOffset() + ); } /** + * Returns expected offset according to Daylight Saving Time in timezone + * + * @param string $timezoneIdentifier + * @return int + */ + private function getExpectedGtmOffset(string $timezoneIdentifier): int + { + $timeZoneToReturn = date_default_timezone_get(); + date_default_timezone_set($timezoneIdentifier); + $expectedOffset = (date('I', time()) + 1) * 3600; + date_default_timezone_set($timeZoneToReturn); + + return (int) $expectedOffset; + } + + /** + * Data provider + * * @return array + * @throws Exception */ public function dateTimeInputDataProvider() { return [ 'string' => [$this->testDate], 'int' => [strtotime($this->testDate)], - \DateTimeInterface::class => [new \DateTimeImmutable($this->testDate)], + DateTimeInterface::class => [new DateTimeImmutable($this->testDate)], ]; } } From 566604806a399d8a550b5d7b1f2595940e1de9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 30 Mar 2020 09:45:19 +0200 Subject: [PATCH 607/734] Cleanup ObjectManager usage - Magento_Developer --- .../Console/Command/GeneratePatchCommand.php | 28 ++-- .../FileGenerator/PublicationDecorator.php | 31 ++-- .../TemplateEngine/Plugin/DebugHints.php | 29 ++-- .../PreProcessor/PreprocessorStrategy.php | 22 +-- .../Model/XmlCatalog/Format/PhpStorm.php | 12 +- .../TemplateEngine/Plugin/DebugHintsTest.php | 156 +++++++++++------- .../PreProcessor/PreprocessorStrategyTest.php | 39 +++-- 7 files changed, 172 insertions(+), 145 deletions(-) diff --git a/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php b/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php index 78531c7e6c22c..5800f7162b7f0 100644 --- a/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php +++ b/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php @@ -7,13 +7,12 @@ namespace Magento\Developer\Console\Command; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\FileSystemException; -use Magento\Framework\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\Filesystem\Directory\WriteFactory; +use Magento\Framework\Filesystem\DirectoryList; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -58,20 +57,20 @@ class GeneratePatchCommand extends Command * GeneratePatchCommand constructor. * * @param ComponentRegistrar $componentRegistrar - * @param DirectoryList|null $directoryList - * @param ReadFactory|null $readFactory - * @param WriteFactory|null $writeFactory + * @param DirectoryList $directoryList + * @param ReadFactory $readFactory + * @param WriteFactory $writeFactory */ public function __construct( ComponentRegistrar $componentRegistrar, - DirectoryList $directoryList = null, - ReadFactory $readFactory = null, - WriteFactory $writeFactory = null + DirectoryList $directoryList, + ReadFactory $readFactory, + WriteFactory $writeFactory ) { $this->componentRegistrar = $componentRegistrar; - $this->directoryList = $directoryList ?: ObjectManager::getInstance()->get(DirectoryList::class); - $this->readFactory = $readFactory ?: ObjectManager::getInstance()->get(ReadFactory::class); - $this->writeFactory = $writeFactory ?: ObjectManager::getInstance()->get(WriteFactory::class); + $this->directoryList = $directoryList; + $this->readFactory = $readFactory; + $this->writeFactory = $writeFactory; parent::__construct(); } @@ -120,6 +119,7 @@ protected function configure() * * @param InputInterface $input * @param OutputInterface $output + * * @return int * @throws FileSystemException */ @@ -194,8 +194,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function getPatchTemplate(): string { $read = $this->readFactory->create(__DIR__ . '/'); - $content = $read->readFile('patch_template.php.dist'); - return $content; + return $read->readFile('patch_template.php.dist'); } /** @@ -207,7 +206,6 @@ private function getPatchTemplate(): string private function getRevertMethodTemplate(): string { $read = $this->readFactory->create(__DIR__ . '/'); - $content = $read->readFile('template_revert_function.php.dist'); - return $content; + return $read->readFile('template_revert_function.php.dist'); } } diff --git a/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php b/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php index 1b2a0986fa0ad..10474d8455b1b 100644 --- a/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php +++ b/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Model\Css\PreProcessor\FileGenerator; use Magento\Developer\Model\Config\Source\WorkflowType; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\State; use Magento\Framework\App\View\Asset\Publisher; use Magento\Framework\Css\PreProcessor\File\Temporary; @@ -31,22 +32,21 @@ class PublicationDecorator extends RelatedGenerator private $scopeConfig; /** - * @var bool + * @var State */ - private $hasRelatedPublishing; + private $state; /** - * @var State + * @var bool */ - private $state; + private $hasRelatedPublishing; /** - * Constructor - * * @param Repository $assetRepository * @param Temporary $temporaryFile * @param Publisher $assetPublisher * @param ScopeConfigInterface $scopeConfig + * @param State $state * @param bool $hasRelatedPublishing */ public function __construct( @@ -54,11 +54,13 @@ public function __construct( Temporary $temporaryFile, Publisher $assetPublisher, ScopeConfigInterface $scopeConfig, + State $state, $hasRelatedPublishing = false ) { parent::__construct($assetRepository, $temporaryFile); $this->assetPublisher = $assetPublisher; $this->scopeConfig = $scopeConfig; + $this->state = $state; $this->hasRelatedPublishing = $hasRelatedPublishing; } @@ -69,7 +71,7 @@ protected function generateRelatedFile($relatedFileId, LocalInterface $asset) { $relatedAsset = parent::generateRelatedFile($relatedFileId, $asset); $isClientSideCompilation = - $this->getState()->getMode() !== State::MODE_PRODUCTION + $this->state->getMode() !== State::MODE_PRODUCTION && WorkflowType::CLIENT_SIDE_COMPILATION === $this->scopeConfig->getValue(WorkflowType::CONFIG_NAME_PATH); if ($this->hasRelatedPublishing || $isClientSideCompilation) { @@ -78,17 +80,4 @@ protected function generateRelatedFile($relatedFileId, LocalInterface $asset) return $relatedAsset; } - - /** - * @return State - * @deprecated 100.2.0 - */ - private function getState() - { - if (null === $this->state) { - $this->state = ObjectManager::getInstance()->get(State::class); - } - - return $this->state; - } } diff --git a/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php b/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php index a10d7bca1b89e..07eb1257f9561 100644 --- a/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php +++ b/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php @@ -1,7 +1,5 @@ <?php /** - * Plugin for the template engine factory that makes a decision of whether to activate debugging hints or not - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -16,6 +14,9 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\Request\Http; +/** + * Plugin for the template engine factory that makes a decision of whether to activate debugging hints or not + */ class DebugHints { /** @@ -26,22 +27,27 @@ class DebugHints /** * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @var StoreManagerInterface */ - protected $storeManager; + private $storeManager; /** * @var DevHelper */ - protected $devHelper; + private $devHelper; /** * @var DebugHintsFactory */ - protected $debugHintsFactory; + private $debugHintsFactory; + + /** + * @var Http + */ + private $http; /** * XPath of configuration of the debug hints @@ -52,7 +58,7 @@ class DebugHints * * @var string */ - protected $debugHintsPath; + private $debugHintsPath; /** * XPath of configuration of the debug hints show with parameter @@ -77,8 +83,8 @@ class DebugHints * @param StoreManagerInterface $storeManager * @param DevHelper $devHelper * @param DebugHintsFactory $debugHintsFactory - * @param string $debugHintsPath * @param Http $http + * @param string $debugHintsPath * @param string $debugHintsWithParam * @param string $debugHintsParameter */ @@ -87,8 +93,8 @@ public function __construct( StoreManagerInterface $storeManager, DevHelper $devHelper, DebugHintsFactory $debugHintsFactory, + Http $http, $debugHintsPath, - Http $http = null, $debugHintsWithParam = null, $debugHintsParameter = null ) { @@ -96,10 +102,8 @@ public function __construct( $this->storeManager = $storeManager; $this->devHelper = $devHelper; $this->debugHintsFactory = $debugHintsFactory; + $this->http = $http; $this->debugHintsPath = $debugHintsPath; - $this->http = $http ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\App\Request\Http::class - ); $this->debugHintsWithParam = $debugHintsWithParam; $this->debugHintsParameter = $debugHintsParameter; } @@ -152,6 +156,7 @@ public function afterCreate( ]); } } + return $invocationResult; } } diff --git a/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php b/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php index 6e230d351a25f..fac74a9e2d2bd 100644 --- a/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php +++ b/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php @@ -42,32 +42,34 @@ class PreprocessorStrategy implements PreProcessorInterface private $state; /** - * Constructor - * * @param AlternativeSourceInterface $alternativeSource * @param FrontendCompilation $frontendCompilation * @param ScopeConfigInterface $scopeConfig + * @param State|null $state */ public function __construct( AlternativeSourceInterface $alternativeSource, FrontendCompilation $frontendCompilation, - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + ?State $state = null ) { $this->frontendCompilation = $frontendCompilation; $this->alternativeSource = $alternativeSource; $this->scopeConfig = $scopeConfig; + $this->state = $state ?? ObjectManager::getInstance()->get(State::class); } /** * Transform content and/or content type for the specified pre-processing chain object * * @param PreProcessor\Chain $chain + * * @return void */ public function process(PreProcessor\Chain $chain) { $isClientSideCompilation = - $this->getState()->getMode() !== State::MODE_PRODUCTION + $this->state->getMode() !== State::MODE_PRODUCTION && WorkflowType::CLIENT_SIDE_COMPILATION === $this->scopeConfig->getValue(WorkflowType::CONFIG_NAME_PATH); if ($isClientSideCompilation) { @@ -76,16 +78,4 @@ public function process(PreProcessor\Chain $chain) $this->alternativeSource->process($chain); } } - - /** - * @return State - * @deprecated 100.2.0 - */ - private function getState() - { - if (null === $this->state) { - $this->state = ObjectManager::getInstance()->get(State::class); - } - return $this->state; - } } diff --git a/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php b/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php index aef58a9a37e89..2e780d37bdf85 100644 --- a/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php +++ b/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php @@ -3,14 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Developer\Model\XmlCatalog\Format; -use Magento\Framework\App\ObjectManager; use Magento\Framework\DomDocument\DomDocumentFactory; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; use Magento\Framework\Filesystem\File\WriteFactory; /** @@ -43,11 +44,11 @@ class PhpStorm implements FormatInterface public function __construct( ReadFactory $readFactory, WriteFactory $fileWriteFactory, - DomDocumentFactory $domDocumentFactory = null + DomDocumentFactory $domDocumentFactory ) { $this->currentDirRead = $readFactory->create(getcwd()); $this->fileWriteFactory = $fileWriteFactory; - $this->domDocumentFactory = $domDocumentFactory ?: ObjectManager::getInstance()->get(DomDocumentFactory::class); + $this->domDocumentFactory = $domDocumentFactory; } /** @@ -55,6 +56,7 @@ public function __construct( * * @param string[] $dictionary * @param string $configFilePath relative path to the PhpStorm misc.xml + * * @return void */ public function generateCatalog(array $dictionary, $configFilePath) @@ -65,7 +67,7 @@ public function generateCatalog(array $dictionary, $configFilePath) try { $file = $this->fileWriteFactory->create( $configFilePath, - \Magento\Framework\Filesystem\DriverPool::FILE, + DriverPool::FILE, 'r' ); $dom = $this->domDocumentFactory->create(); @@ -103,7 +105,7 @@ public function generateCatalog(array $dictionary, $configFilePath) $dom->formatOutput = true; $file = $this->fileWriteFactory->create( $configFilePath, - \Magento\Framework\Filesystem\DriverPool::FILE, + DriverPool::FILE, 'w' ); $file->write($dom->saveXML()); diff --git a/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php b/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php index a918a562a926d..d7ab4fdea2c37 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php @@ -3,60 +3,103 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Test\Unit\Model\TemplateEngine\Plugin; +use Magento\Developer\Helper\Data; +use Magento\Developer\Model\TemplateEngine\Decorator\DebugHints as DebugHintsDecorator; use Magento\Developer\Model\TemplateEngine\Decorator\DebugHintsFactory; use Magento\Developer\Model\TemplateEngine\Plugin\DebugHints; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\TemplateEngineFactory; +use Magento\Framework\View\TemplateEngineInterface; +use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DebugHintsTest extends \PHPUnit\Framework\TestCase +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DebugHintsTest extends TestCase { + private const STORE_CODE = 'default'; + + /** + * @var ObjectManager + */ + private $objectManager; + /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $scopeConfigMock; + private $scopeConfigMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManager; + private $storeManagerMock; /** - * @var \Magento\Developer\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $devHelperMock; + private $devHelperMock; /** - * @var DebugHintsFactory | \PHPUnit_Framework_MockObject_MockObject + * @var DebugHintsFactory|MockObject */ - protected $debugHintsFactory; + private $debugHintsFactoryMock; + + /** + * @var Http|MockObject + */ + private $httpMock; /** * @return void */ protected function setUp() { - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->getMockForAbstractClass(); - $this->devHelperMock = $this->getMockBuilder(\Magento\Developer\Helper\Data::class) + $this->devHelperMock = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); - $this->debugHintsFactory = $this->getMockBuilder( - \Magento\Developer\Model\TemplateEngine\Decorator\DebugHintsFactory::class + $this->debugHintsFactoryMock = $this->getMockBuilder( + DebugHintsFactory::class ) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); + + $this->httpMock = $this->createMock(Http::class); + + $storeMock = $this->createMock(StoreInterface::class); + $storeMock->expects($this->once()) + ->method('getCode') + ->willReturn(static::STORE_CODE); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + + $this->objectManager = new ObjectManager($this); } /** * @param string $debugHintsPath * @param bool $showBlockHints + * @param bool $debugHintsWithParam + * @param bool $debugHintsParameter + * * @return void * @dataProvider afterCreateActiveDataProvider */ @@ -72,40 +115,41 @@ public function testAfterCreateActive( $this->setupConfigFixture($debugHintsPath, true, $showBlockHints); - $engine = $this->createMock(\Magento\Framework\View\TemplateEngineInterface::class); + $engine = $this->createMock(TemplateEngineInterface::class); - $debugHintsDecorator = $this->getMockBuilder( - \Magento\Developer\Model\TemplateEngine\Decorator\DebugHints::class + $debugHintsDecoratorMock = $this->getMockBuilder( + DebugHintsDecorator::class ) ->disableOriginalConstructor() ->getMock(); - $this->debugHintsFactory->expects($this->once()) + $this->debugHintsFactoryMock->expects($this->once()) ->method('create') ->with([ 'subject' => $engine, 'showBlockHints' => $showBlockHints, ]) - ->willReturn($debugHintsDecorator); + ->willReturn($debugHintsDecoratorMock); - $subjectMock = $this->getMockBuilder(\Magento\Framework\View\TemplateEngineFactory::class) + $subjectMock = $this->getMockBuilder(TemplateEngineFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->httpMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $debugHints = new DebugHints( - $this->scopeConfigMock, - $this->storeManager, - $this->devHelperMock, - $this->debugHintsFactory, - $debugHintsPath, - $this->httpMock, - $debugHintsWithParam, - $debugHintsParameter + $debugHints = $this->objectManager->getObject( + DebugHints::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'devHelper' => $this->devHelperMock, + 'debugHintsFactory' => $this->debugHintsFactoryMock, + 'http' => $this->httpMock, + 'debugHintsPath' => $debugHintsPath, + 'debugHintsWithParam' => $debugHintsWithParam, + 'debugHintsParameter' => $debugHintsParameter + ] ); - $this->assertEquals($debugHintsDecorator, $debugHints->afterCreate($subjectMock, $engine)); + $this->assertEquals($debugHintsDecoratorMock, $debugHints->afterCreate($subjectMock, $engine)); } /** @@ -125,6 +169,9 @@ public function afterCreateActiveDataProvider() * @param string $debugHintsPath * @param bool $isDevAllowed * @param bool $showTemplateHints + * @param bool $debugHintsWithParam + * @param bool $debugHintsParameter + * * @return void * @dataProvider afterCreateInactiveDataProvider */ @@ -135,29 +182,30 @@ public function testAfterCreateInactive( $debugHintsWithParam, $debugHintsParameter ) { - $this->devHelperMock->expects($this->any()) + $this->devHelperMock ->method('isDevAllowed') ->willReturn($isDevAllowed); $this->setupConfigFixture($debugHintsPath, $showTemplateHints, true); - $engine = $this->createMock(\Magento\Framework\View\TemplateEngineInterface::class); + $engine = $this->createMock(TemplateEngineInterface::class); - $subjectMock = $this->getMockBuilder(\Magento\Framework\View\TemplateEngineFactory::class) + $subjectMock = $this->getMockBuilder(TemplateEngineFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->httpMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $debugHints = new DebugHints( - $this->scopeConfigMock, - $this->storeManager, - $this->devHelperMock, - $this->debugHintsFactory, - $debugHintsPath, - $this->httpMock, - $debugHintsWithParam, - $debugHintsParameter + $debugHints = $this->objectManager->getObject( + DebugHints::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'devHelper' => $this->devHelperMock, + 'debugHintsFactory' => $this->debugHintsFactoryMock, + 'http' => $this->httpMock, + 'debugHintsPath' => $debugHintsPath, + 'debugHintsWithParam' => $debugHintsWithParam, + 'debugHintsParameter' => $debugHintsParameter + ] ); $this->assertSame($engine, $debugHints->afterCreate($subjectMock, $engine)); @@ -184,32 +232,24 @@ public function afterCreateInactiveDataProvider() * @param string $debugHintsPath * @param bool $showTemplateHints * @param bool $showBlockHints + * * @return void */ - protected function setupConfigFixture($debugHintsPath, $showTemplateHints, $showBlockHints) + private function setupConfigFixture($debugHintsPath, $showTemplateHints, $showBlockHints): void { - $storeCode = 'default'; - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - $storeMock->expects($this->once()) - ->method('getCode') - ->willReturn($storeCode); - $this->storeManager->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); - $this->scopeConfigMock->expects($this->atLeastOnce()) ->method('getValue') ->willReturnMap([ [ $debugHintsPath, ScopeInterface::SCOPE_STORE, - $storeCode, + static::STORE_CODE, $showTemplateHints, ], [ DebugHints::XML_PATH_DEBUG_TEMPLATE_HINTS_BLOCKS, ScopeInterface::SCOPE_STORE, - $storeCode, + static::STORE_CODE, $showBlockHints ] ]); diff --git a/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php b/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php index 8372092c0e763..4b1b3110b4b9b 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Test\Unit\Model\View\Asset\PreProcessor; use Magento\Developer\Model\Config\Source\WorkflowType; @@ -13,14 +15,14 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Asset\PreProcessor\AlternativeSourceInterface; use Magento\Framework\View\Asset\PreProcessor\Chain; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class PreprocessorStrategyTest - * * @see \Magento\Developer\Model\View\Asset\PreProcessor\PreprocessorStrategy * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class PreprocessorStrategyTest extends \PHPUnit\Framework\TestCase +class PreprocessorStrategyTest extends TestCase { /** * @var PreprocessorStrategy @@ -28,27 +30,27 @@ class PreprocessorStrategyTest extends \PHPUnit\Framework\TestCase private $preprocessorStrategy; /** - * @var FrontendCompilation|\PHPUnit_Framework_MockObject_MockObject + * @var FrontendCompilation|MockObject */ private $frontendCompilationMock; /** - * @var AlternativeSourceInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AlternativeSourceInterface|MockObject */ private $alternativeSourceMock; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $scopeConfigMock; /** - * @var State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ private $stateMock; /** - * @var \Magento\Framework\App\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ObjectManager|MockObject */ private $objectMangerMock; @@ -71,12 +73,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->preprocessorStrategy = (new ObjectManager($this))->getObject(PreprocessorStrategy::class, [ - 'alternativeSource' => $this->alternativeSourceMock, - 'frontendCompilation' => $this->frontendCompilationMock, - 'scopeConfig' => $this->scopeConfigMock, - 'state' => $this->stateMock, - ]); + $this->preprocessorStrategy = (new ObjectManager($this))->getObject( + PreprocessorStrategy::class, + [ + 'alternativeSource' => $this->alternativeSourceMock, + 'frontendCompilation' => $this->frontendCompilationMock, + 'scopeConfig' => $this->scopeConfigMock, + 'state' => $this->stateMock, + ] + ); } /** @@ -148,14 +153,12 @@ public function testProcessAlternativeSource() } /** - * @return Chain|\PHPUnit_Framework_MockObject_MockObject + * @return Chain|MockObject */ private function getChainMock() { - $chainMock = $this->getMockBuilder(Chain::class) + return $this->getMockBuilder(Chain::class) ->disableOriginalConstructor() ->getMock(); - - return $chainMock; } } From bbc6abca6ed62419c3e8759672f32541c1dfa476 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 30 Mar 2020 11:43:31 +0300 Subject: [PATCH 608/734] Fix the minicart items actions alignment for tablet and desktop devices --- .../blank/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index c9b1d41857eee..9ed7e3a1ba839 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -135,7 +135,7 @@ .product { .actions { float: right; - margin: -24px 0 0; + margin: -28px 0 0; text-align: right; > .primary, diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 14c754623cf03..a97cc041b1c42 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -145,7 +145,7 @@ .product { .actions { float: right; - margin: -24px 0 0; + margin: -28px 0 0; > .primary, > .secondary { From 94bdc67ead27078db85c70b295789e30ea263172 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 30 Mar 2020 13:24:09 +0300 Subject: [PATCH 609/734] make the switcher styles in the header more specific desktop styles must not apply for mobile devices as well --- .../Magento/blank/Magento_Theme/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index 3faa8ca965410..765cf789f18b5 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -200,7 +200,7 @@ } } - .page-header, + .page-header .panel.wrapper, .page-footer { .switcher { margin-right: 10px; diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 438fb55d32e5c..ac2a2f249ac11 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -289,7 +289,7 @@ text-align: center; } - .page-header, + .page-header .panel.wrapper, .page-footer { .switcher { .options { @@ -331,7 +331,7 @@ } } } - .page-header { + .page-header .panel.wrapper { .switcher { .options { ul.dropdown { From d0f356e9fce5b876fe7708f1c5a0b2dd6c017728 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Mon, 30 Mar 2020 13:02:23 +0300 Subject: [PATCH 610/734] Fix unit tests --- .../Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php index 3c7f49671d74a..c6b9c6f80dc52 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php @@ -58,7 +58,7 @@ public function testGtmOffset() ->setMethods(null) ->getMock(); - $this->assertEquals(3600, $dateTime->getGmtOffset()); + $this->assertEquals(7200, $dateTime->getGmtOffset()); } /** From 100d6f148c7dcf2f3fffdaa3991febbb4454a611 Mon Sep 17 00:00:00 2001 From: Burlacu Vasilii <v.burlacu@atwix.com> Date: Mon, 30 Mar 2020 14:12:31 +0300 Subject: [PATCH 611/734] Make the logo visible on the storefront --- .../blank/Magento_Theme/layout/default.xml | 16 ++++++++++++++++ .../Magento_Theme/web/css/source/_module.less | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml diff --git a/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml b/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml new file mode 100644 index 0000000000000..76d9d319f955c --- /dev/null +++ b/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="logo"> + <arguments> + <argument name="logo_width" xsi:type="number">170</argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index 3faa8ca965410..47d4846c0b2be 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -368,7 +368,7 @@ } .logo { - margin: -8px auto 25px 0; + margin: 0 auto 25px 0; img { max-height: inherit; From 0a3224fae4d4817b72cf3fb5d15e99c7863160e6 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Mon, 30 Mar 2020 12:29:03 -0500 Subject: [PATCH 612/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Static failure fixes --- .../Model/Cart/SetBillingAddressOnCart.php | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index cd960dc001a4e..8ba9cd7242811 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -67,29 +67,43 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $sameAsShipping = isset($billingAddressInput['same_as_shipping']) ? (bool)$billingAddressInput['same_as_shipping'] : $sameAsShipping; + $this->checkForInputExceptions($customerAddressId, $addressInput); - if (null === $customerAddressId && null === $addressInput) { + $addresses = $cart->getAllShippingAddresses(); + if ($sameAsShipping && count($addresses) > 1) { throw new GraphQlInputException( - __('The billing address must contain either "customer_address_id" or "address".') + __('Using the "same_as_shipping" option with multishipping is not possible.') ); } - if ($customerAddressId && $addressInput) { + $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); + + $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); + } + + + /** + * Check for the input exceptions + * + * @param int|null $customerAddressId + * @param array|null $addressInput + * @throws GraphQlInputException + */ + private function checkForInputExceptions( + ?int $customerAddressId, + ?array $addressInput + ) { + if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( - __('The billing address cannot contain "customer_address_id" and "address" at the same time.') + __('The billing address must contain either "customer_address_id" or "address".') ); } - $addresses = $cart->getAllShippingAddresses(); - if ($sameAsShipping && count($addresses) > 1) { + if ($customerAddressId && $addressInput) { throw new GraphQlInputException( - __('Using the "same_as_shipping" option with multishipping is not possible.') + __('The billing address cannot contain "customer_address_id" and "address" at the same time.') ); } - - $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); - - $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } /** From a073071bc7146c6ca56bd87d8c4cf0acd8786990 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Mon, 30 Mar 2020 12:31:38 -0500 Subject: [PATCH 613/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Static failure fixes on spacing --- .../Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 8ba9cd7242811..9b6a97a68326f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -81,7 +81,6 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } - /** * Check for the input exceptions * From de229c2c3c8b34470e9b6b7e49e54ad2782c67a5 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Mon, 30 Mar 2020 14:52:40 -0500 Subject: [PATCH 614/734] MC-32655: (GraphQL) Set default value of save_in_address_book to true when no value is specified - Refactored code to fix jenkin builds --- .../Model/Cart/SetBillingAddressOnCart.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 9b6a97a68326f..497819cbeec3b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -67,7 +67,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $sameAsShipping = isset($billingAddressInput['same_as_shipping']) ? (bool)$billingAddressInput['same_as_shipping'] : $sameAsShipping; - $this->checkForInputExceptions($customerAddressId, $addressInput); + $this->checkForInputExceptions($billingAddressInput); $addresses = $cart->getAllShippingAddresses(); if ($sameAsShipping && count($addresses) > 1) { @@ -84,14 +84,15 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b /** * Check for the input exceptions * - * @param int|null $customerAddressId - * @param array|null $addressInput + * @param array $billingAddressInput * @throws GraphQlInputException */ private function checkForInputExceptions( - ?int $customerAddressId, - ?array $addressInput + ?array $billingAddressInput ) { + $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; + $addressInput = $billingAddressInput['address'] ?? null; + if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( __('The billing address must contain either "customer_address_id" or "address".') From 4336cf1528ab8cc217d5da2573c2a9a237a90542 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Tue, 31 Mar 2020 09:50:32 +0300 Subject: [PATCH 615/734] Small fix --- setup/src/Magento/Setup/Controller/Navigation.php | 1 + 1 file changed, 1 insertion(+) diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 3e81a12a742e9..e3f2091accbbd 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -33,6 +33,7 @@ class Navigation extends AbstractActionController * @var ViewModel */ protected $view; + /** * @var ObjectManagerInterface */ From a7254b5d061ef20ab317a97c1e0c71e4785828fe Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Tue, 31 Mar 2020 11:00:15 +0300 Subject: [PATCH 616/734] fixed test `AdminLoginWithRestrictPermissionTest` --- .../Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml index a9706d902ff34..b26947d934865 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml @@ -24,12 +24,12 @@ <!--Create user role--> <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> + <argument name="restrictedRole" value="Global Search"/> </actionGroup> <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="addRestrictedRoleStores"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> + <argument name="restrictedRole" value="Global Search"/> </actionGroup> <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> <!--Create user and assign role to it--> From 07896edb6f95e3f3569fce833112f35f18020ed1 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 31 Mar 2020 11:42:59 +0300 Subject: [PATCH 617/734] MC-24253: [MFTF Test] Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media --- ...tWithSeveralAttributesPrependMediaTest.xml | 104 ++++++-------- ...hVisualSwatchAttributePrependMediaTest.xml | 135 +++++++++++------- 2 files changed, 130 insertions(+), 109 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index be5b9fb219598..9d48546e1ad3a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -32,12 +32,6 @@ <createData entity="productAttributeOption2" stepKey="dropdownAttributeSecondOption"> <requiredEntity createDataKey="createDropdownAttribute"/> </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getDropdownAttributeFirsOption"> - <requiredEntity createDataKey="createDropdownAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getDropdownAttributeSecondOption"> - <requiredEntity createDataKey="createDropdownAttribute"/> - </getData> <!-- Create product swatch attribute with 2 variations --> <createData entity="VisualSwatchProductAttributeForm" stepKey="createVisualSwatchAttribute"/> @@ -47,12 +41,6 @@ <createData entity="SwatchProductAttributeOption2" stepKey="swatchAttributeSecondOption"> <requiredEntity createDataKey="createVisualSwatchAttribute"/> </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSwatchAttributeFirsOption"> - <requiredEntity createDataKey="createVisualSwatchAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSwatchAttributeSecondOption"> - <requiredEntity createDataKey="createVisualSwatchAttribute"/> - </getData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -183,15 +171,15 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0"> - <actualResult type="string">$getListOfThumbnailsCase0[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase0[0]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case0"> - <actualResult type="string">$getListOfThumbnailsCase0[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase0[1]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case0"> - <actualResult type="string">$getListOfThumbnailsCase0[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase0[2]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> @@ -207,27 +195,27 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[0]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[1]</actualResult> <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[2]</actualResult> <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[3]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage5Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[4]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[4]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage6Case1"> - <actualResult type="string">$getListOfThumbnailsCase1[5]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase1[5]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> @@ -243,27 +231,27 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[0]</actualResult> <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[1]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[2]</actualResult> <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[3]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage5Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[4]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[4]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage6Case2"> - <actualResult type="string">$getListOfThumbnailsCase2[5]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase2[5]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> @@ -279,15 +267,15 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3"> - <actualResult type="string">$getListOfThumbnailsCase3[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase3[0]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case3"> - <actualResult type="string">$getListOfThumbnailsCase3[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase3[1]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case3"> - <actualResult type="string">$getListOfThumbnailsCase3[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase3[2]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> @@ -303,19 +291,19 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4"> - <actualResult type="string">$getListOfThumbnailsCase4[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase4[0]</actualResult> <expectedResult type="string">|{{ProductImage.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case4"> - <actualResult type="string">$getListOfThumbnailsCase4[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase4[1]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case4"> - <actualResult type="string">$getListOfThumbnailsCase4[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase4[2]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case4"> - <actualResult type="string">$getListOfThumbnailsCase4[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase4[3]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> @@ -331,15 +319,15 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase5"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase5"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case5"> - <actualResult type="string">$getListOfThumbnailsCase5[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase5[0]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case5"> - <actualResult type="string">$getListOfThumbnailsCase5[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase5[1]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case5"> - <actualResult type="string">$getListOfThumbnailsCase5[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase5[2]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase5"/> @@ -355,27 +343,27 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase6"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase6"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[0]</actualResult> <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[1]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[2]</actualResult> <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[3]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage5Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[4]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[4]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage6Case6"> - <actualResult type="string">$getListOfThumbnailsCase6[5]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase6[5]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase6"/> @@ -391,27 +379,27 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase7"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase7"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[0]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[1]</actualResult> <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[2]</actualResult> <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[3]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage5Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[4]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[4]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage6Case7"> - <actualResult type="string">$getListOfThumbnailsCase7[5]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase7[5]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase7"/> @@ -427,27 +415,27 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase8"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase8"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[0]</actualResult> <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[1]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[2]</actualResult> <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage4Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[3]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[3]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage5Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[4]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[4]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage6Case8"> - <actualResult type="string">$getListOfThumbnailsCase8[5]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase8[5]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase8"/> @@ -464,15 +452,15 @@ <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase9"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase9"/> <assertRegExp stepKey="checkPositionInThumbnailForImage1Case9"> - <actualResult type="string">$getListOfThumbnailsCase9[0]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase9[0]</actualResult> <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage2Case9"> - <actualResult type="string">$getListOfThumbnailsCase9[1]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase9[1]</actualResult> <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> </assertRegExp> <assertRegExp stepKey="checkPositionInThumbnailForImage3Case9"> - <actualResult type="string">$getListOfThumbnailsCase9[2]</actualResult> + <actualResult type="variable">getListOfThumbnailsCase9[2]</actualResult> <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> </assertRegExp> <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase9"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index 14b3be714dc68..ac3c2497d9dfb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -35,15 +35,6 @@ <createData entity="SwatchProductAttributeOption3" stepKey="swatchAttributeThirdOption"> <requiredEntity createDataKey="createVisualSwatchAttribute"/> </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSwatchAttributeFirsOption"> - <requiredEntity createDataKey="createVisualSwatchAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSwatchAttributeSecondOption"> - <requiredEntity createDataKey="createVisualSwatchAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getSwatchAttributeThirdOption"> - <requiredEntity createDataKey="createVisualSwatchAttribute"/> - </getData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -141,12 +132,18 @@ <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case0"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case0"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase0[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case0"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case0"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case0"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[2]</actualResult> + </assertRegExp> <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase0"> <argument name="productImage" value="{{Magento2.filename}}"/> </actionGroup> @@ -162,18 +159,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase1"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase1[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case1"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case1"/> - <assertRegExp expected="|{{placeholderSmallImage.name}}.*.jpg|" actual="$getListOfThumbnailsCase1[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case1"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case1"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case1"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase1[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case1"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1"> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case1"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case1"> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[2]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case1"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[3]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case1"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[4]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case1"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[5]</actualResult> + </assertRegExp> <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase1"> <argument name="productImage" value="{{MagentoLogo.filename}}"/> </actionGroup> @@ -189,12 +198,18 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase2"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case2"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case2"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase2[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case2"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case2"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case2"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[2]</actualResult> + </assertRegExp> <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase2"> <argument name="productImage" value="{{Magento2.filename}}"/> </actionGroup> @@ -210,18 +225,30 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase3"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case3"/> - <assertRegExp expected="|{{TestImageAdobe.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case3"/> - <assertRegExp expected="|{{MagentoLogo.filename}}.*.png|" actual="$getListOfThumbnailsCase3[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case3"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[3]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage4Case3"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[4]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage5Case3"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase3[5]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage6Case3"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case3"> + <expectedResult type="string">|{{TestImageAdobe.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case3"> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[2]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case3"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[3]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case3"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[4]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case3"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[5]</actualResult> + </assertRegExp> <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase3"> <argument name="productImage" value="{{Magento3.filename}}"/> </actionGroup> @@ -237,12 +264,18 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="unselectThirdOptionCase4"/> <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> - <assertRegExp expected="|{{Magento2.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[0]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage1Case4"/> - <assertRegExp expected="|{{Magento3.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[1]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage2Case4"/> - <assertRegExp expected="|{{TestImageNew.filename}}.*.jpg|" actual="$getListOfThumbnailsCase4[2]" - expectedType="string" actualType="string" stepKey="checkPositionInThumbnailForImage3Case4"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case4"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case4"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[2]</actualResult> + </assertRegExp> <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase4"> <argument name="productImage" value="{{Magento2.filename}}"/> </actionGroup> From f905e5f602076006f7d5b0ae46729a7f50f5d47b Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 31 Mar 2020 13:24:08 +0300 Subject: [PATCH 618/734] MC-32697: [2.4] AbstractSimpleObjectBuilder.php throws exception when Interceptor instance comes. --- .../Framework/Api/AbstractSimpleObjectBuilder.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php index cf7f7732c116c..029a78bf0ddf7 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php @@ -67,14 +67,13 @@ protected function _set($key, $value) */ protected function _getDataObjectType() { - $currentClass = get_class($this); - $suffix = 'Builder'; - $interceptorSuffix = '\Interceptor'; - if (false !== strpos($currentClass, $interceptorSuffix, -strlen($interceptorSuffix))) { - $suffix .= $interceptorSuffix; + $dataObjectType = ''; + $pattern = '/(?<data_object>.*?)Builder(\\Interceptor)?/'; + if (preg_match($pattern, get_class($this), $match)) { + $dataObjectType = $match['data_object']; } - return substr($currentClass, 0, -strlen($suffix)); + return $dataObjectType; } /** From 65543b405af358f63c3adef07e116b1cfac6da5c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 31 Mar 2020 13:26:16 +0300 Subject: [PATCH 619/734] MC-32938: [Unit] Magento.Framework.Stdlib.Test.Unit.DateTime.DateTimeTest.testGtmOffset failed after Daylight Saving Time changed --- .../Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php index 3c7f49671d74a..4f003c4b8ba10 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php @@ -50,7 +50,8 @@ public function testGtmOffset() { /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); - $timezone->method('getConfigTimezone')->willReturn('Europe/Amsterdam'); + // Asia/Tbilisi timezone have no DST + $timezone->method('getConfigTimezone')->willReturn('Asia/Tbilisi'); /** @var DateTime|\PHPUnit_Framework_MockObject_MockObject $dateTime */ $dateTime = $this->getMockBuilder(DateTime::class) @@ -58,7 +59,7 @@ public function testGtmOffset() ->setMethods(null) ->getMock(); - $this->assertEquals(3600, $dateTime->getGmtOffset()); + $this->assertEquals(14400, $dateTime->getGmtOffset()); } /** From f39a494a55f5d1337bded02db1fa932b12f43ff6 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 31 Mar 2020 13:30:11 +0300 Subject: [PATCH 620/734] Changes covered by MFTF --- .../ActionGroup/AdminClickLogoActionGroup.xml | 15 ++++++++++ .../Test/Mftf/Page/AdminSetupWizardPage.xml | 12 ++++++++ .../Test/Mftf/Section/AdminMenuSection.xml | 1 + .../Test/AdminLogoLinkSetupWizardPageTest.xml | 29 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml new file mode 100644 index 0000000000000..1ff877b259c6f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.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="AdminClickLogoActionGroup"> + <click selector="{{AdminMenuSection.logo}}" stepKey="clickLogoInAdmin"/> + <seeInCurrentUrl url="{{AdminDashboardPage.url}}" stepKey="seeAdminDashboardUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml new file mode 100644 index 0000000000000..40076ccd42b3a --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSetupWizardPage" url="admin/backendapp/redirect/app/setup/" area="admin" module="Magento_Backend"/> +</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml index 8498ad8c52e41..e6782dca897d7 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMenuSection"> + <element name="logo" type="button" selector=".menu-wrapper a.logo"/> <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> <element name="catalogProducts" type="button" selector="#nav li[data-ui-id='menu-magento-catalog-catalog-products']"/> <element name="customers" type="button" selector="#menu-magento-customer-customer"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml new file mode 100644 index 0000000000000..058ef2de6a7f4 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml @@ -0,0 +1,29 @@ +<?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="AdminLogoLinkSetupWizardPageTest"> + <annotations> + <features value="Backend"/> + <stories value="Logo link on Setup Wizard Page"/> + <title value="Check active Logo link on Setup Wizard Page"/> + <description value="Check active Logo link on Setup Wizard Page"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <amOnPage url="{{AdminSetupWizardPage.url}}" stepKey="navigateToSetupWizardPage"/> + <waitForPageLoad stepKey="waitForSetupWizardPageLoaded"/> + <actionGroup ref="AdminClickLogoActionGroup" stepKey="navigateToDashboard"/> + </test> +</tests> From 31bbf073f9b359eb610b74a0b62c8dc55e8a3b53 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 31 Mar 2020 17:59:10 +0300 Subject: [PATCH 621/734] Refactored MFTF --- .../ActionGroup/AdminClickLogoActionGroup.xml | 2 +- ...inNavigateToSetupWizardPageActionGroup.xml | 18 ++++++++++++ .../Test/AdminLogoLinkSetupWizardPageTest.xml | 29 ------------------- ...dminPanelOnLogoFromWizardPageClickTest.xml | 29 +++++++++++++++++++ 4 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml index 1ff877b259c6f..5fe89c3b8f6f4 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminClickLogoActionGroup"> <click selector="{{AdminMenuSection.logo}}" stepKey="clickLogoInAdmin"/> - <seeInCurrentUrl url="{{AdminDashboardPage.url}}" stepKey="seeAdminDashboardUrl"/> + <waitForPageLoad stepKey="waitForAdminDashboardPageLoaded"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml new file mode 100644 index 0000000000000..5d5a233186c1f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml @@ -0,0 +1,18 @@ +<?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="AdminNavigateToSetupWizardPageActionGroup"> + <annotations> + <description>Open Setup Wizard Page.</description> + </annotations> + <amOnPage url="{{AdminSetupWizardPage.url}}" stepKey="navigateToSetupWizardPage"/> + <waitForPageLoad stepKey="waitForSetupWizardPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml deleted file mode 100644 index 058ef2de6a7f4..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLogoLinkSetupWizardPageTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?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="AdminLogoLinkSetupWizardPageTest"> - <annotations> - <features value="Backend"/> - <stories value="Logo link on Setup Wizard Page"/> - <title value="Check active Logo link on Setup Wizard Page"/> - <description value="Check active Logo link on Setup Wizard Page"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - </before> - <after> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </after> - - <amOnPage url="{{AdminSetupWizardPage.url}}" stepKey="navigateToSetupWizardPage"/> - <waitForPageLoad stepKey="waitForSetupWizardPageLoaded"/> - <actionGroup ref="AdminClickLogoActionGroup" stepKey="navigateToDashboard"/> - </test> -</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml new file mode 100644 index 0000000000000..e5a1705488589 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml @@ -0,0 +1,29 @@ +<?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="AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest"> + <annotations> + <features value="Backend"/> + <stories value="Navigate to dashboard from Setup Wizard Page"/> + <title value="Navigate to dashboard after click on logo on Setup Wizard Page"/> + <description value="Check navigate to dashboard after click on logo on Setup Wizard Page"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminNavigateToSetupWizardPageActionGroup" stepKey="onSetupWizardPage"/> + <actionGroup ref="AdminClickLogoActionGroup" stepKey="clickOnLogo"/> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="navigateToDashboard"/> + </test> +</tests> From e1b0ff085333190d013abb4ce722f0cf2fae42d6 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Tue, 31 Mar 2020 18:14:23 +0300 Subject: [PATCH 622/734] Minor fixes --- ...inRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename app/code/Magento/Backend/Test/Mftf/Test/{AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml => AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml} (87%) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml similarity index 87% rename from app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml rename to app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml index e5a1705488589..bf74674a2c9c8 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminRedirectToAdminPanelOnLogoFromWizardPageClickTest"> + <test name="AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest"> <annotations> <features value="Backend"/> <stories value="Navigate to dashboard from Setup Wizard Page"/> @@ -22,8 +22,8 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <actionGroup ref="AdminNavigateToSetupWizardPageActionGroup" stepKey="onSetupWizardPage"/> + <actionGroup ref="AdminNavigateToSetupWizardPageActionGroup" stepKey="navigateToSetupWizardPage"/> <actionGroup ref="AdminClickLogoActionGroup" stepKey="clickOnLogo"/> - <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="navigateToDashboard"/> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="checkTheDashboardPage"/> </test> </tests> From 03caeca7bbbbd9c52dede1a31dd0a1f63a0d5139 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Tue, 31 Mar 2020 11:37:14 -0500 Subject: [PATCH 623/734] Fixet static test --- .../Magento/Setup/Model/ConfigOptionsList/DriverOptions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php index 3dd2af0c4bdc8..52eeaf5b34642 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php @@ -46,6 +46,7 @@ public function getDriverOptions(array $options): array */ private function optionExists($options, $driverOptionKey): bool { - return isset($options[$driverOptionKey]) && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); + return isset($options[$driverOptionKey]) + && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); } } From bd9a345649bb15f35e370aff7f3358dbf935bf0d Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Tue, 31 Mar 2020 12:45:30 -0500 Subject: [PATCH 624/734] Fixed static test --- .../src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php index 52eeaf5b34642..6d5235cffec77 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php @@ -46,7 +46,7 @@ public function getDriverOptions(array $options): array */ private function optionExists($options, $driverOptionKey): bool { - return isset($options[$driverOptionKey]) + return isset($options[$driverOptionKey]) && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); } } From fa5dadc1b5747b52fea2e74dde8bbaa61f4662a4 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 20:46:48 +0300 Subject: [PATCH 625/734] Upgrade to phpunit 8 Fix library related to unit tests --- .../Unit/Listener/GarbageCleanup.php | 85 ++----------------- .../Unit/Listener/ReplaceObjectManager.php | 12 ++- 2 files changed, 13 insertions(+), 84 deletions(-) diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php index bee6c0d0d7d42..483bc7148a7c7 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php @@ -5,98 +5,23 @@ */ namespace Magento\Framework\TestFramework\Unit\Listener; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\Warning; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; /** * Listener of PHPUnit built-in events that enforces cleanup of cyclic object references * */ -class GarbageCleanup implements \PHPUnit\Framework\TestListener +class GarbageCleanup implements TestListener { - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) - { - } + use \PHPUnit\Framework\TestListenerDefaultImplementation; /** * {@inheritdoc} * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) + public function endTestSuite(TestSuite $suite): void { gc_collect_cycles(); } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function startTest(\PHPUnit\Framework\Test $test) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function endTest(\PHPUnit\Framework\Test $test, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addWarning(Test $test, Warning $e, $time) - { - } } diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php index 79745599e8393..1d71263a100cb 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php @@ -7,12 +7,16 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\ObjectManagerInterface; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; /** * The event listener which instantiates ObjectManager before test run */ -class ReplaceObjectManager extends \PHPUnit\Framework\BaseTestListener +class ReplaceObjectManager implements TestListener { + use \PHPUnit\Framework\TestListenerDefaultImplementation; /** * Replaces ObjectManager before run for each test * @@ -21,13 +25,13 @@ class ReplaceObjectManager extends \PHPUnit\Framework\BaseTestListener * This avoids the issue with a not initialized ObjectManager * and makes working with ObjectManager predictable as it always contains clear mock for each test * - * @param \PHPUnit\Framework\Test $test + * @param Test $test * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function startTest(\PHPUnit\Framework\Test $test) + public function startTest(Test $test): void { - if ($test instanceof \PHPUnit\Framework\TestCase) { + if ($test instanceof TestCase) { $objectManagerMock = $test->getMockBuilder(ObjectManagerInterface::class) ->getMockForAbstractClass(); $createMockCallback = function ($type) use ($test) { From f4a554680db68f15190408b52b44c2462a710754 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 20:47:42 +0300 Subject: [PATCH 626/734] Upgrade to phpunit 8 Fix fatal error because signature has changed --- .../Unit/Condition/CanViewNotificationTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php index 7819f2f017a01..8d60adadf0100 100644 --- a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -11,28 +11,30 @@ use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\App\CacheInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class CanViewNotificationTest */ -class CanViewNotificationTest extends \PHPUnit\Framework\TestCase +class CanViewNotificationTest extends TestCase { /** @var CanViewNotification */ private $canViewNotification; - /** @var Logger|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Logger|MockObject */ private $viewerLoggerMock; - /** @var ProductMetadataInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductMetadataInterface|MockObject */ private $productMetadataMock; - /** @var Log|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Log|MockObject */ private $logMock; - /** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */ + /** @var $cacheStorageMock MockObject|CacheInterface */ private $cacheStorageMock; - public function setUp() + protected function setUp(): void { $this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class) ->getMockForAbstractClass(); From 40293edf73f0156e6fe2ff18128d323e926051da Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 21:01:08 +0300 Subject: [PATCH 627/734] Upgrade to phpunit 8 Add to obsolete classes PhpUnit 6.x only classes --- .../testsuite/Magento/Test/Legacy/_files/obsolete_classes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 78bcec7abda09..45b415cdcbd12 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4254,5 +4254,7 @@ ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy'], ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper'], ['Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver'], - ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'] + ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'], + ['PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject'], + ['PHPUnit\Framework\BaseTestListener'], ]; From 0b2cf658eba09cf436e7252f6c8358bf1f7c4547 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 21:33:43 +0300 Subject: [PATCH 628/734] Upgrade to phpunit 8 Update unit tests in AdminNotification module --- .../Unit/Block/Grid/Renderer/ActionsTest.php | 9 +-- .../Unit/Block/Grid/Renderer/NoticeTest.php | 5 +- .../Unit/Block/Grid/Renderer/SeverityTest.php | 7 ++- .../Test/Unit/Block/ToolbarEntryTest.php | 27 ++++---- .../Test/Unit/Model/FeedTest.php | 61 +++++++++++-------- .../Unit/Model/NotificationServiceTest.php | 32 ++++++---- .../System/Message/CacheOutdatedTest.php | 30 +++++---- .../Media/Synchronization/ErrorTest.php | 37 ++++++----- .../Model/System/Message/SecurityTest.php | 35 +++++++---- 9 files changed, 145 insertions(+), 98 deletions(-) diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php index 781734186ce6b..7199d1f44222d 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php @@ -18,6 +18,7 @@ use Magento\Framework\Escaper; use Magento\Framework\Url\Helper\Data; use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class ActionsTest extends TestCase @@ -32,20 +33,20 @@ protected function setUp() : void { parent::setUp(); - /** @var Escaper | \PHPUnit_Framework_MockObject_MockObject $escaperMock */ + /** @var Escaper|MockObject $escaperMock */ $escaperMock = $this->getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); $escaperMock->expects($this->once())->method('escapeUrl')->willReturn('https://magento.com'); - /** @var UrlInterface | \PHPUnit_Framework_MockObject_MockObject $urlBuilder */ + /** @var UrlInterface|MockObject $urlBuilder */ $urlBuilder = $this->getMockBuilder(UrlInterface::class)->getMock(); $urlBuilder->expects($this->once())->method('getUrl')->willReturn('http://magento.com'); - /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + /** @var Context|MockObject $contextMock */ $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); $contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($urlBuilder); - /** @var Data | \PHPUnit_Framework_MockObject_MockObject $urlHelperMock */ + /** @var Data|MockObject $urlHelperMock */ $urlHelperMock = $this->getMockBuilder(Data::class)->disableOriginalConstructor()->getMock(); $urlHelperMock->expects($this->once())->method('getEncodedUrl')->willReturn('http://magento.com'); diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php index 7b4b0a0f66e96..a4fb6227ecdc7 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php @@ -15,6 +15,7 @@ use Magento\Framework\DataObject; use Magento\Framework\Escaper; use Magento\Backend\Block\Context; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class NoticeTest extends TestCase @@ -30,11 +31,11 @@ protected function setUp() : void { parent::setUp(); - /** @var Escaper | \PHPUnit_Framework_MockObject_MockObject $escaperMock */ + /** @var Escaper|MockObject $escaperMock */ $escaperMock = $this->getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); $escaperMock->expects($this->exactly(2))->method('escapeHtml')->willReturn('<div>Some random html</div>'); - /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + /** @var Context|MockObject $contextMock */ $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php index 2a30be02f173b..c2e109cff130e 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php @@ -17,6 +17,7 @@ use Magento\Backend\Block\Widget\Grid\Column; use Magento\Framework\DataObject; use Magento\Framework\Escaper; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class SeverityTest extends TestCase @@ -32,10 +33,10 @@ protected function setUp() : void { parent::setUp(); - /** @var Inbox |\PHPUnit_Framework_MockObject_MockObject $inboxMock */ + /** @var Inbox|MockObject $inboxMock */ $inboxMock = $this->getMockBuilder(Inbox::class)->disableOriginalConstructor()->getMock(); - /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + /** @var Context|MockObject $contextMock */ $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $this->sut = new Severity($contextMock, $inboxMock); @@ -43,7 +44,7 @@ protected function setUp() : void public function testShouldRenderSeverity() : void { - /** @var Column | \PHPUnit_Framework_MockObject_MockObject $columnMock */ + /** @var Column|MockObject $columnMock */ $columnMock = $this->getMockBuilder(Column::class) ->disableOriginalConstructor() ->setMethods(['getIndex']) diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php index 2afa9eced1d95..eb45a9af6beb2 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php @@ -9,26 +9,31 @@ */ namespace Magento\AdminNotification\Test\Unit\Block; -class ToolbarEntryTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Block\ToolbarEntry; +use Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class ToolbarEntryTest extends TestCase { /** * Retrieve toolbar entry block instance * * @param int $unreadNotifications number of unread notifications - * @return \Magento\AdminNotification\Block\ToolbarEntry + * @return ToolbarEntry */ protected function _getBlockInstance($unreadNotifications) { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); // mock collection of unread notifications $notificationList = $this->createPartialMock( - \Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread::class, + Unread::class, ['getSize', 'setCurPage', 'setPageSize'] ); $notificationList->expects($this->any())->method('getSize')->will($this->returnValue($unreadNotifications)); $block = $objectManagerHelper->getObject( - \Magento\AdminNotification\Block\ToolbarEntry::class, + ToolbarEntry::class, ['notificationList' => $notificationList] ); @@ -44,25 +49,23 @@ public function testGetUnreadNotificationCount() public function testGetLatestUnreadNotifications() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $helper = new ObjectManager($this); // 1. Create mocks - $notificationList = $this->getMockBuilder( - \Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread::class - ) + $notificationList = $this->getMockBuilder(Unread::class) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\AdminNotification\Block\ToolbarEntry $model */ + /** @var ToolbarEntry $model */ $model = $helper->getObject( - \Magento\AdminNotification\Block\ToolbarEntry::class, + ToolbarEntry::class, ['notificationList' => $notificationList] ); // 2. Set expectations $notificationList->expects($this->atLeastOnce()) ->method('setPageSize') - ->with(\Magento\AdminNotification\Block\ToolbarEntry::NOTIFICATIONS_NUMBER) + ->with(ToolbarEntry::NOTIFICATIONS_NUMBER) ->will($this->returnSelf()); // 3. Run tested method diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php index 2b3eb6247e899..604d33ccf9092 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php @@ -6,66 +6,79 @@ namespace Magento\AdminNotification\Test\Unit\Model; +use Magento\AdminNotification\Model\Feed; +use Magento\AdminNotification\Model\Inbox; +use Magento\AdminNotification\Model\InboxFactory; +use Magento\Backend\App\ConfigInterface; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ProductMetadata; +use Magento\Framework\App\State; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\HTTP\Adapter\Curl; +use Magento\Framework\HTTP\Adapter\CurlFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class FeedTest extends \PHPUnit\Framework\TestCase +class FeedTest extends TestCase { - /** @var \Magento\AdminNotification\Model\Feed */ + /** @var Feed */ protected $feed; /** @var ObjectManagerHelper */ protected $objectManagerHelper; - /** @var \Magento\AdminNotification\Model\InboxFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var InboxFactory|MockObject */ protected $inboxFactory; - /** @var \Magento\AdminNotification\Model\Inbox|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Inbox|MockObject */ protected $inboxModel; - /** @var \Magento\Framework\HTTP\Adapter\CurlFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CurlFactory|MockObject */ protected $curlFactory; - /** @var \Magento\Framework\HTTP\Adapter\Curl|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Curl|MockObject */ protected $curl; - /** @var \Magento\Backend\App\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ConfigInterface|MockObject */ protected $backendConfig; - /** @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CacheInterface|MockObject */ protected $cacheManager; - /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ + /** @var State|MockObject */ protected $appState; - /** @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject */ + /** @var DeploymentConfig|MockObject */ protected $deploymentConfig; - /** @var \Magento\Framework\App\ProductMetadata|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductMetadata|MockObject */ protected $productMetadata; - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlInterface|MockObject */ protected $urlBuilder; - protected function setUp() + protected function setUp(): void { $this->inboxFactory = $this->createPartialMock( - \Magento\AdminNotification\Model\InboxFactory::class, + InboxFactory::class, ['create'] ); - $this->curlFactory = $this->createPartialMock(\Magento\Framework\HTTP\Adapter\CurlFactory::class, ['create']); - $this->curl = $this->getMockBuilder(\Magento\Framework\HTTP\Adapter\Curl::class) + $this->curlFactory = $this->createPartialMock(CurlFactory::class, ['create']); + $this->curl = $this->getMockBuilder(Curl::class) ->disableOriginalConstructor()->getMock(); - $this->appState = $this->createPartialMock(\Magento\Framework\App\State::class, ['getInstallDate']); - $this->inboxModel = $this->createPartialMock(\Magento\AdminNotification\Model\Inbox::class, [ + $this->appState = $this->createPartialMock(State::class, []); + $this->inboxModel = $this->createPartialMock(Inbox::class, [ '__wakeup', 'parse' ]); $this->backendConfig = $this->createPartialMock( - \Magento\Backend\App\ConfigInterface::class, + ConfigInterface::class, [ 'getValue', 'setValue', @@ -73,7 +86,7 @@ protected function setUp() ] ); $this->cacheManager = $this->createPartialMock( - \Magento\Framework\App\CacheInterface::class, + CacheInterface::class, [ 'load', 'getFrontend', @@ -83,18 +96,18 @@ protected function setUp() ] ); - $this->deploymentConfig = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $this->deploymentConfig = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->productMetadata = $this->getMockBuilder(\Magento\Framework\App\ProductMetadata::class) + $this->productMetadata = $this->getMockBuilder(ProductMetadata::class) ->disableOriginalConstructor()->getMock(); - $this->urlBuilder = $this->createMock(\Magento\Framework\UrlInterface::class); + $this->urlBuilder = $this->createMock(UrlInterface::class); $this->feed = $this->objectManagerHelper->getObject( - \Magento\AdminNotification\Model\Feed::class, + Feed::class, [ 'backendConfig' => $this->backendConfig, 'cacheManager' => $this->cacheManager, diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php index f8485847ccae2..68070add6cd98 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php @@ -9,27 +9,33 @@ */ namespace Magento\AdminNotification\Test\Unit\Model; -class NotificationServiceTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\Inbox; +use Magento\AdminNotification\Model\InboxFactory; +use Magento\AdminNotification\Model\NotificationService; +use Magento\Framework\Exception\LocalizedException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class NotificationServiceTest extends TestCase { /** * Retrieve instance of notification service model * * @param $notificationId - * @return \Magento\AdminNotification\Model\NotificationService + * @return NotificationService */ protected function _getServiceInstanceForMarkAsReadTest($notificationId) { /** - * @var - * $notificationFactory \PHPUnit_Framework_MockObject_MockObject|\Magento\AdminNotification\Model\InboxFactory + * @var $notificationFactory MockObject|InboxFactory */ $notificationFactory = $this->createPartialMock( - \Magento\AdminNotification\Model\InboxFactory::class, + InboxFactory::class, ['create'] ); $notification = $this->createPartialMock( - \Magento\AdminNotification\Model\Inbox::class, - ['load', 'getId', 'save', 'setIsRead', '__sleep', '__wakeup'] + Inbox::class, + ['load', 'getId', 'save', 'setData', '__sleep', '__wakeup'] ); $notification->expects($this->once())->method('load')->with($notificationId)->will($this->returnSelf()); $notification->expects($this->once())->method('getId')->will($this->returnValue($notificationId)); @@ -37,11 +43,12 @@ protected function _getServiceInstanceForMarkAsReadTest($notificationId) // when notification Id is valid, add additional expectations if ($notificationId) { $notification->expects($this->once())->method('save')->will($this->returnSelf()); - $notification->expects($this->once())->method('setIsRead')->with(1)->will($this->returnSelf()); + $notification->expects($this->once())->method('setData') + ->with('is_read', 1)->will($this->returnSelf()); } $notificationFactory->expects($this->once())->method('create')->will($this->returnValue($notification)); - return new \Magento\AdminNotification\Model\NotificationService($notificationFactory); + return new NotificationService($notificationFactory); } public function testMarkAsRead() @@ -51,12 +58,11 @@ public function testMarkAsRead() $service->markAsRead($notificationId); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Wrong notification ID specified. - */ public function testMarkAsReadThrowsExceptionWhenNotificationIdIsInvalid() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('Wrong notification ID specified.'); + $notificationId = null; $service = $this->_getServiceInstanceForMarkAsReadTest($notificationId); $service->markAsRead($notificationId); diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php index f49911c3e7a93..381e3fff3d080 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php @@ -5,42 +5,50 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message; -class CacheOutdatedTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\System\Message\CacheOutdated; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CacheOutdatedTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_authorizationMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_cacheTypeListMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_urlInterfaceMock; /** - * @var \Magento\AdminNotification\Model\System\Message\CacheOutdated + * @var CacheOutdated */ protected $_messageModel; - protected function setUp() + protected function setUp(): void { - $this->_authorizationMock = $this->createMock(\Magento\Framework\AuthorizationInterface::class); - $this->_urlInterfaceMock = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->_cacheTypeListMock = $this->createMock(\Magento\Framework\App\Cache\TypeListInterface::class); + $this->_authorizationMock = $this->createMock(AuthorizationInterface::class); + $this->_urlInterfaceMock = $this->createMock(UrlInterface::class); + $this->_cacheTypeListMock = $this->createMock(TypeListInterface::class); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = [ 'authorization' => $this->_authorizationMock, 'urlBuilder' => $this->_urlInterfaceMock, 'cacheTypeList' => $this->_cacheTypeListMock, ]; $this->_messageModel = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\CacheOutdated::class, + CacheOutdated::class, $arguments ); } diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php index b490efd8e9683..91595732bdb58 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php @@ -5,37 +5,44 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message\Media\Synchronization; -class ErrorTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error; +use Magento\Framework\Notification\MessageInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaStorage\Model\File\Storage\Flag; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ErrorTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_syncFlagMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_fileStorage; /** - * @var \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error + * @var Error */ protected $_model; - protected function setUp() + protected function setUp(): void { $this->_syncFlagMock = $this->createPartialMock( - \Magento\MediaStorage\Model\File\Storage\Flag::class, - ['setState', 'save', 'getFlagData'] + Flag::class, + ['save', 'getFlagData'] ); - $this->_fileStorage = $this->createMock(\Magento\MediaStorage\Model\File\Storage\Flag::class); + $this->_fileStorage = $this->createMock(Flag::class); $this->_fileStorage->expects($this->any())->method('loadSelf')->will($this->returnValue($this->_syncFlagMock)); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = ['fileStorage' => $this->_fileStorage]; $this->_model = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error::class, + Error::class, $arguments ); } @@ -43,7 +50,6 @@ protected function setUp() public function testGetText() { $messageText = 'We were unable to synchronize one or more media files.'; - $this->assertContains($messageText, (string)$this->_model->getText()); } @@ -55,15 +61,14 @@ public function testGetText() public function testIsDisplayed($expectedFirstRun, $data) { $arguments = ['fileStorage' => $this->_fileStorage]; - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); // create new instance to ensure that it hasn't been displayed yet (var $this->_isDisplayed is unset) - /** @var $model \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error */ + /** @var $model Error */ $model = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error::class, + Error::class, $arguments ); - $this->_syncFlagMock->expects($this->any())->method('setState'); $this->_syncFlagMock->expects($this->any())->method('save'); $this->_syncFlagMock->expects($this->any())->method('getFlagData')->will($this->returnValue($data)); //check first call @@ -92,7 +97,7 @@ public function testGetIdentity() public function testGetSeverity() { - $severity = \Magento\Framework\Notification\MessageInterface::SEVERITY_MAJOR; + $severity = MessageInterface::SEVERITY_MAJOR; $this->assertEquals($severity, $this->_model->getSeverity()); } } diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php index c6f61fee862ba..3b679e442c60e 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php @@ -5,51 +5,60 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message; -class SecurityTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\System\Message\Security; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\HTTP\Adapter\Curl; +use Magento\Framework\HTTP\Adapter\CurlFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class SecurityTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_cacheMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_configMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_curlFactoryMock; /** - * @var \Magento\AdminNotification\Model\System\Message\Security + * @var Security */ protected $_messageModel; - protected function setUp() + protected function setUp(): void { //Prepare objects for constructor - $this->_cacheMock = $this->createMock(\Magento\Framework\App\CacheInterface::class); - $this->_scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->_cacheMock = $this->createMock(CacheInterface::class); + $this->_scopeConfigMock = $this->createMock(ScopeConfigInterface::class); $this->_curlFactoryMock = $this->createPartialMock( - \Magento\Framework\HTTP\Adapter\CurlFactory::class, + CurlFactory::class, ['create'] ); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = [ 'cache' => $this->_cacheMock, 'scopeConfig' => $this->_scopeConfigMock, 'curlFactory' => $this->_curlFactoryMock, ]; $this->_messageModel = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Security::class, + Security::class, $arguments ); } @@ -67,7 +76,7 @@ public function testIsDisplayed($expectedResult, $cached, $response) $this->_cacheMock->expects($this->any())->method('load')->will($this->returnValue($cached)); $this->_cacheMock->expects($this->any())->method('save')->will($this->returnValue(null)); - $httpAdapterMock = $this->createMock(\Magento\Framework\HTTP\Adapter\Curl::class); + $httpAdapterMock = $this->createMock(Curl::class); $httpAdapterMock->expects($this->any())->method('read')->will($this->returnValue($response)); $this->_curlFactoryMock->expects($this->any())->method('create')->will($this->returnValue($httpAdapterMock)); From 1ee76f0d3f9a4adfd6d64ccce92c04a969efa13f Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 21:57:46 +0300 Subject: [PATCH 629/734] Upgrade to phpunit 8 Update unit tests in Amqp module --- .../Test/Unit/Setup/ConfigOptionsListTest.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php b/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php index 5b19ba055d059..57d9372dc0f0d 100644 --- a/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php +++ b/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php @@ -6,12 +6,16 @@ namespace Magento\Amqp\Test\Unit\Setup; +use Magento\Amqp\Setup\ConnectionValidator; +use Magento\Framework\Config\Data\ConfigData; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Amqp\Setup\ConfigOptionsList; use Magento\Framework\Setup\Option\TextConfigOption; use Magento\Framework\App\DeploymentConfig; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ConfigOptionsListTest extends \PHPUnit\Framework\TestCase +class ConfigOptionsListTest extends TestCase { /** * @var ObjectManager @@ -24,12 +28,12 @@ class ConfigOptionsListTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Amqp\Setup\ConnectionValidator|\PHPUnit_Framework_MockObject_MockObject + * @var ConnectionValidator|MockObject */ private $connectionValidatorMock; /** - * @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + * @var DeploymentConfig|MockObject */ private $deploymentConfigMock; @@ -38,7 +42,7 @@ class ConfigOptionsListTest extends \PHPUnit\Framework\TestCase */ private $options; - protected function setUp() + protected function setUp(): void { $this->options = [ ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_HOST => 'host', @@ -51,18 +55,18 @@ protected function setUp() ]; $this->objectManager = new ObjectManager($this); - $this->connectionValidatorMock = $this->getMockBuilder(\Magento\Amqp\Setup\ConnectionValidator::class) + $this->connectionValidatorMock = $this->getMockBuilder(ConnectionValidator::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); $this->model = $this->objectManager->getObject( - \Magento\Amqp\Setup\ConfigOptionsList::class, + ConfigOptionsList::class, [ 'connectionValidator' => $this->connectionValidatorMock, ] @@ -135,9 +139,9 @@ public function testCreateConfig($options, $expectedConfigData) $result = $this->model->createConfig($options, $this->deploymentConfigMock); $this->assertInternalType('array', $result); $this->assertNotEmpty($result); - /** @var \Magento\Framework\Config\Data\ConfigData $configData */ + /** @var ConfigData $configData */ $configData = $result[0]; - $this->assertInstanceOf(\Magento\Framework\Config\Data\ConfigData::class, $configData); + $this->assertInstanceOf(ConfigData::class, $configData); $this->assertEquals($expectedConfigData, $configData->getData()); } From 66cfd4d27085fe276f8b9d71e1050bf5f4b20cdc Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 31 Mar 2020 22:05:13 +0300 Subject: [PATCH 630/734] Upgrade to phpunit 8 Fix static tests --- .../Test/Unit/Condition/CanViewNotificationTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php index 8d60adadf0100..19c4e7a7327d0 100644 --- a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -14,9 +14,6 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -/** - * Class CanViewNotificationTest - */ class CanViewNotificationTest extends TestCase { /** @var CanViewNotification */ From 9f5d25a32fc611b32e890b4dcb97d72c725cee75 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 31 Mar 2020 20:24:38 -0500 Subject: [PATCH 631/734] MC-32871: [MFTF] 21 Failing MFTF Tests in Page Builder Develop Branch - add secondary sort by entity to mimic behavior of Page Builder category page and products to show newest entity first --- .../ResourceModel/Product/Collection.php | 5 +++- .../CategoriesQuery/CategoriesFilterTest.php | 18 ++++++------ .../GraphQl/Catalog/CategoryListTest.php | 8 +++--- .../GraphQl/Catalog/ProductSearchTest.php | 28 ++++++++++++------- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 1a46c38d04319..41b3f9781c8a0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1717,7 +1717,10 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) // optimize if using cat index $filters = $this->_productLimitationFilters; if (isset($filters['category_id']) || isset($filters['visibility'])) { - $this->getSelect()->order(['cat_index.position ' . $dir, 'e.entity_id ' . $dir]); + $this->getSelect()->order([ + 'cat_index.position ' . $dir, + 'e.entity_id ' . \Magento\Framework\DB\Select::SQL_DESC + ]); } else { $this->getSelect()->order('e.entity_id ' . $dir); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php index b30eb24da6a2d..2bea5832126e8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php @@ -188,8 +188,8 @@ public function testQueryChildCategoriesWithProducts() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -204,8 +204,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.1', $firstChildCategory['name']); $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple', 'name' => 'Simple Product'] ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; @@ -215,8 +215,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.2', $secondChildCategory['name']); $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] ]; $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; @@ -281,8 +281,8 @@ public function testQueryCategoryWithDisabledChildren() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -297,8 +297,8 @@ public function testQueryCategoryWithDisabledChildren() $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 4831671dec450..fa4527c0d9804 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -187,8 +187,8 @@ public function testQueryChildCategoriesWithProducts() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -203,8 +203,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.1', $firstChildCategory['name']); $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple', 'name' => 'Simple Product'], ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; @@ -214,8 +214,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.2', $secondChildCategory['name']); $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] ]; $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index f367e70fa7397..d7f7a3601b7e9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -364,6 +364,8 @@ private function reIndexAndCleanCache() : void $out = ''; // phpcs:ignore Magento2.Security.InsecureFunction exec("php -f {$appDir}/bin/magento indexer:reindex catalog_category_product", $out); + // phpcs:ignore Magento2.Security.InsecureFunction + exec("php -f {$appDir}/bin/magento indexer:reindex catalogsearch_fulltext", $out); CacheCleaner::cleanAll(); } @@ -670,7 +672,7 @@ public function testFilterByCategoryIdAndCustomAttribute() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product1 = $productRepository->get('simple'); $product2 = $productRepository->get('simple-4'); - $filteredProducts = [$product1, $product2]; + $filteredProducts = [$product2, $product1]; $productItemsInResponse = array_map(null, $response['products']['items'], $filteredProducts); //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall for ($itemIndex = 0; $itemIndex < count($filteredProducts); $itemIndex++) { @@ -678,7 +680,8 @@ public function testFilterByCategoryIdAndCustomAttribute() //validate that correct products are returned $this->assertResponseFields( $productItemsInResponse[$itemIndex][0], - [ 'name' => $filteredProducts[$itemIndex]->getName(), + [ + 'name' => $filteredProducts[$itemIndex]->getName(), 'sku' => $filteredProducts[$itemIndex]->getSku() ] ); @@ -1121,6 +1124,7 @@ public function testFilterWithinSpecificPriceRangeSortedByNameDesc() */ public function testSortByPosition() { + $this->reIndexAndCleanCache(); // Get category ID for filtering /** @var Collection $categoryCollection */ $categoryCollection = Bootstrap::getObjectManager()->get(Collection::class); @@ -1141,7 +1145,8 @@ public function testSortByPosition() $resultAsc = $this->graphQlQuery($queryAsc); $this->assertArrayNotHasKey('errors', $resultAsc); $productsAsc = array_column($resultAsc['products']['items'], 'sku'); - $expectedProductsAsc = ['simple1000', 'simple1001', 'simple1002']; + $expectedProductsAsc = ['simple1002', 'simple1001', 'simple1000']; + // position equal and secondary sort by entity_id DESC $this->assertEquals($expectedProductsAsc, $productsAsc); $queryDesc = <<<QUERY @@ -1158,23 +1163,25 @@ public function testSortByPosition() $resultDesc = $this->graphQlQuery($queryDesc); $this->assertArrayNotHasKey('errors', $resultDesc); $productsDesc = array_column($resultDesc['products']['items'], 'sku'); - $expectedProductsDesc = array_reverse($expectedProductsAsc); - $this->assertEquals($expectedProductsDesc, $productsDesc); + // position equal and secondary sort by entity_id DESC + $this->assertEquals($expectedProductsAsc, $productsDesc); //revert position $productPositions = $category->getProductsPosition(); - $count = 3; + $count = 1; foreach ($productPositions as $productId => $position) { $productPositions[$productId] = $count; - $count--; + $count++; } + ksort($productPositions); $category->setPostedProducts($productPositions); $category->save(); + $this->reIndexAndCleanCache(); $queryDesc = <<<QUERY { - products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: DESC}) { + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: ASC}) { total_count items { sku @@ -1186,8 +1193,8 @@ public function testSortByPosition() $resultDesc = $this->graphQlQuery($queryDesc); $this->assertArrayNotHasKey('errors', $resultDesc); $productsDesc = array_column($resultDesc['products']['items'], 'sku'); - $expectedProductsDesc = $expectedProductsAsc; - $this->assertEquals($expectedProductsDesc, $productsDesc); + // position NOT equal and oldest entity first + $this->assertEquals(array_reverse($expectedProductsAsc), $productsDesc); } /** @@ -1532,6 +1539,7 @@ public function testFilterProductsBySingleCategoryId() $categoryRepository = ObjectManager::getInstance()->get(CategoryRepositoryInterface::class); $links = $productLinks->getAssignedProducts($queryCategoryId); + $links = array_reverse($links); foreach ($response['products']['items'] as $itemIndex => $itemData) { $this->assertNotEmpty($itemData); $this->assertEquals($response['products']['items'][$itemIndex]['sku'], $links[$itemIndex]->getSku()); From 1024838b787d25af663665bd2c9cae86f70f513e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 31 Mar 2020 22:38:14 -0500 Subject: [PATCH 632/734] MC-32871: [MFTF] 21 Failing MFTF Tests in Page Builder Develop Branch - add secondary sort by entity to mimic behavior of Page Builder category page and products to show newest entity first --- .../Magento/GraphQl/Catalog/CategoryListTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index fa4527c0d9804..82a606dad7dec 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -278,8 +278,8 @@ public function testQueryCategoryWithDisabledChildren() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -294,8 +294,8 @@ public function testQueryCategoryWithDisabledChildren() $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; From 31926d6cacffc09639635572ebd3e26fb83b8e39 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Wed, 1 Apr 2020 10:58:09 +0530 Subject: [PATCH 633/734] Sorting direction changed from descending order to ascending order --- app/code/Magento/Ui/view/base/web/js/grid/sortBy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js index cdb7ed784c150..50cbb6881a6f1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js @@ -12,7 +12,7 @@ define([ template: 'ui/grid/sortBy', options: [], applied: {}, - sorting: 'desc', + sorting: 'asc', columnsProvider: 'ns = ${ $.ns }, componentType = columns', selectedOption: '', isVisible: true, From 76c357bf181912d43e786929fde1a581cd6e36a5 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Wed, 1 Apr 2020 11:37:23 +0530 Subject: [PATCH 634/734] Fixed static test --- .../tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js index e5983f1d42e06..064329ac3bce1 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -70,7 +70,7 @@ define([ it('return applied options for sorting column', function () { var applied = { field: 'selectedOption', - direction: 'desc' + direction: 'asc' }; spyOn(sortByObj, 'selectedOption').and.returnValue('selectedOption'); From bbbca238fcb993064e5a7b82db72bbb6d1a41389 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Wed, 1 Apr 2020 09:29:37 +0300 Subject: [PATCH 635/734] MC-32873: Product stock alert - unsubscribe for the product not working (logged out user) --- app/code/Magento/ProductAlert/Controller/Add.php | 7 +++++-- .../ProductAlert/Controller/Unsubscribe/Email.php | 11 +++++++---- .../layout/productalert_unsubscribe_email.xml | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/ProductAlert/Controller/Add.php b/app/code/Magento/ProductAlert/Controller/Add.php index 9498fa0e3d802..f8291ee5ffe15 100644 --- a/app/code/Magento/ProductAlert/Controller/Add.php +++ b/app/code/Magento/ProductAlert/Controller/Add.php @@ -10,6 +10,9 @@ use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\RequestInterface; +/** + * Abstract controller for notifying. + */ abstract class Add extends Action { /** @@ -39,8 +42,8 @@ public function dispatch(RequestInterface $request) { if (!$this->customerSession->authenticate()) { $this->_actionFlag->set('', 'no-dispatch', true); - if (!$this->customerSession->getBeforeUrl()) { - $this->customerSession->setBeforeUrl($this->_redirect->getRefererUrl()); + if (!$this->customerSession->getBeforeAuthUrl()) { + $this->customerSession->setBeforeAuthUrl($this->_redirect->getRefererUrl()); } } return parent::dispatch($request); diff --git a/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php index d2f589374c225..09f3adc8e1296 100644 --- a/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php +++ b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php @@ -8,18 +8,19 @@ namespace Magento\ProductAlert\Controller\Unsubscribe; +use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\Action\Context; use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\Action; use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory; +use Magento\ProductAlert\Controller\Unsubscribe as UnsubscribeController; /** * Unsubscribing from 'Back in stock Alert'. * * Is used to transform a Get request that triggered in the email into the Post request endpoint */ -class Email extends Action implements HttpGetActionInterface +class Email extends UnsubscribeController implements HttpGetActionInterface { /** * @var PageFactory @@ -29,13 +30,15 @@ class Email extends Action implements HttpGetActionInterface /** * @param Context $context * @param PageFactory $resultPageFactory + * @param CustomerSession $customerSession */ public function __construct( Context $context, - PageFactory $resultPageFactory + PageFactory $resultPageFactory, + CustomerSession $customerSession ) { $this->resultPageFactory = $resultPageFactory; - parent::__construct($context); + parent::__construct($context, $customerSession); } /** diff --git a/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml index 8666fb83e01e3..4b759514e4b7f 100644 --- a/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml +++ b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml @@ -8,7 +8,7 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> - <block class="Magento\Framework\View\Element\Template" name="unsubscription_form" template="Magento_ProductAlert::email/email.phtml" /> + <block class="Magento\Framework\View\Element\Template" name="unsubscription_form" cacheable="false" template="Magento_ProductAlert::email/email.phtml" /> </referenceContainer> </body> </page> From 4c717f0075e56e2be2ddb9d243c5001a20e7f52b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 1 Apr 2020 12:56:34 +0300 Subject: [PATCH 636/734] MC-32979: [2.4][MFTF] Failing Tests in 2.3.5-develop --- ...tProductsDisplayUsingElasticSearchTest.xml | 119 ++++++++---------- 1 file changed, 50 insertions(+), 69 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml index 9690246bbe68c..ea7912613f7f2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml @@ -16,10 +16,10 @@ <testCaseId value="MC-30209"/> <severity value="CRITICAL"/> <group value="Catalog"/> + <group value="SearchEngineElasticsearch"/> </annotations> + <before> - <!-- Login Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create Category and Simple Products--> <createData entity="SimpleSubCategory" stepKey="createCategory1"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> @@ -64,51 +64,39 @@ <createData entity="SimpleProduct" stepKey="createSimpleProduct14"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct15"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct16"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct17"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct18"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct19"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct20"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct21"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct22"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct23"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct24"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct25"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct26"> <requiredEntity createDataKey="createCategory1"/> </createData> @@ -118,7 +106,6 @@ <createData entity="SimpleProduct" stepKey="createSimpleProduct28"> <requiredEntity createDataKey="createCategory1"/> </createData> - <createData entity="SimpleProduct" stepKey="createSimpleProduct29"> <requiredEntity createDataKey="createCategory1"/> </createData> @@ -126,71 +113,65 @@ <requiredEntity createDataKey="createCategory1"/> </createData> - <!--Enable ElasticSearch as search engine.--> - <magentoCLI command="config:set {{SearchEngineElasticsearchConfigData.path}} {{SearchEngineElasticsearchConfigData.value}}" stepKey="enableElasticSearchAsSearchEngine"/> - <magentoCLI command="indexer:reindex" stepKey="performReindexAfterElasticSearchEnable"/> - <magentoCLI command="cache:flush" stepKey="cleanCacheAfterElasticSearchEnable"/> - + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="performReindex"/> + <magentoCLI command="cache:clean" arguments="full_page" stepKey="cleanFullPageCache"/> </before> - <after> - <!--Delete created products, category --> - <deleteData createDataKey="createCategory1" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> - <deleteData createDataKey="createSimpleProduct4" stepKey="deleteSimpleProduct4"/> - <deleteData createDataKey="createSimpleProduct5" stepKey="deleteSimpleProduct5"/> - <deleteData createDataKey="createSimpleProduct6" stepKey="deleteSimpleProduct6"/> - <deleteData createDataKey="createSimpleProduct7" stepKey="deleteSimpleProduct7"/> - <deleteData createDataKey="createSimpleProduct8" stepKey="deleteSimpleProduct8"/> - <deleteData createDataKey="createSimpleProduct9" stepKey="deleteSimpleProduct9"/> - <deleteData createDataKey="createSimpleProduct10" stepKey="deleteSimpleProduct10"/> - <deleteData createDataKey="createSimpleProduct11" stepKey="deleteSimpleProduct11"/> - <deleteData createDataKey="createSimpleProduct12" stepKey="deleteSimpleProduct12"/> - <deleteData createDataKey="createSimpleProduct13" stepKey="deleteSimpleProduct13"/> - <deleteData createDataKey="createSimpleProduct14" stepKey="deleteSimpleProduct14"/> - <deleteData createDataKey="createSimpleProduct15" stepKey="deleteSimpleProduct15"/> - <deleteData createDataKey="createSimpleProduct16" stepKey="deleteSimpleProduct16"/> - <deleteData createDataKey="createSimpleProduct17" stepKey="deleteSimpleProduct17"/> - <deleteData createDataKey="createSimpleProduct18" stepKey="deleteSimpleProduct18"/> - <deleteData createDataKey="createSimpleProduct19" stepKey="deleteSimpleProduct19"/> - <deleteData createDataKey="createSimpleProduct20" stepKey="deleteSimpleProduct20"/> - <deleteData createDataKey="createSimpleProduct21" stepKey="deleteSimpleProduct21"/> - <deleteData createDataKey="createSimpleProduct22" stepKey="deleteSimpleProduct22"/> - <deleteData createDataKey="createSimpleProduct23" stepKey="deleteSimpleProduct23"/> - <deleteData createDataKey="createSimpleProduct24" stepKey="deleteSimpleProduct24"/> - <deleteData createDataKey="createSimpleProduct25" stepKey="deleteSimpleProduct25"/> - <deleteData createDataKey="createSimpleProduct26" stepKey="deleteSimpleProduct26"/> - <deleteData createDataKey="createSimpleProduct27" stepKey="deleteSimpleProduct27"/> - <deleteData createDataKey="createSimpleProduct28" stepKey="deleteSimpleProduct28"/> - <deleteData createDataKey="createSimpleProduct29" stepKey="deleteSimpleProduct29"/> - <deleteData createDataKey="createSimpleProduct30" stepKey="deleteSimpleProduct30"/> - - <!--Revert ElasticSearch as search engine.--> - <actionGroup ref="ResetSearchEngineConfigurationActionGroup" stepKey="resetCatalogSearchConfiguration"/> - <magentoCLI command="indexer:reindex" stepKey="performReindexAfterElasticSearchDisable"/> - <magentoCLI command="cache:flush" stepKey="cleanCacheAfterElasticSearchDisable"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> + <after> + <!--Delete created products, category --> + <deleteData createDataKey="createCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createSimpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="createSimpleProduct5" stepKey="deleteSimpleProduct5"/> + <deleteData createDataKey="createSimpleProduct6" stepKey="deleteSimpleProduct6"/> + <deleteData createDataKey="createSimpleProduct7" stepKey="deleteSimpleProduct7"/> + <deleteData createDataKey="createSimpleProduct8" stepKey="deleteSimpleProduct8"/> + <deleteData createDataKey="createSimpleProduct9" stepKey="deleteSimpleProduct9"/> + <deleteData createDataKey="createSimpleProduct10" stepKey="deleteSimpleProduct10"/> + <deleteData createDataKey="createSimpleProduct11" stepKey="deleteSimpleProduct11"/> + <deleteData createDataKey="createSimpleProduct12" stepKey="deleteSimpleProduct12"/> + <deleteData createDataKey="createSimpleProduct13" stepKey="deleteSimpleProduct13"/> + <deleteData createDataKey="createSimpleProduct14" stepKey="deleteSimpleProduct14"/> + <deleteData createDataKey="createSimpleProduct15" stepKey="deleteSimpleProduct15"/> + <deleteData createDataKey="createSimpleProduct16" stepKey="deleteSimpleProduct16"/> + <deleteData createDataKey="createSimpleProduct17" stepKey="deleteSimpleProduct17"/> + <deleteData createDataKey="createSimpleProduct18" stepKey="deleteSimpleProduct18"/> + <deleteData createDataKey="createSimpleProduct19" stepKey="deleteSimpleProduct19"/> + <deleteData createDataKey="createSimpleProduct20" stepKey="deleteSimpleProduct20"/> + <deleteData createDataKey="createSimpleProduct21" stepKey="deleteSimpleProduct21"/> + <deleteData createDataKey="createSimpleProduct22" stepKey="deleteSimpleProduct22"/> + <deleteData createDataKey="createSimpleProduct23" stepKey="deleteSimpleProduct23"/> + <deleteData createDataKey="createSimpleProduct24" stepKey="deleteSimpleProduct24"/> + <deleteData createDataKey="createSimpleProduct25" stepKey="deleteSimpleProduct25"/> + <deleteData createDataKey="createSimpleProduct26" stepKey="deleteSimpleProduct26"/> + <deleteData createDataKey="createSimpleProduct27" stepKey="deleteSimpleProduct27"/> + <deleteData createDataKey="createSimpleProduct28" stepKey="deleteSimpleProduct28"/> + <deleteData createDataKey="createSimpleProduct29" stepKey="deleteSimpleProduct29"/> + <deleteData createDataKey="createSimpleProduct30" stepKey="deleteSimpleProduct30"/> + </after> <!--Open Storefront on the myCategory page--> - <amOnPage url="/$$createCategory1.name$$.html" stepKey="GoToStorefrontCategory"/> - <waitForPageLoad stepKey="waitForStorefrontCategoryPageLoad"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="goToStorefrontCategory"> + <argument name="category" value="$createCategory1$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForStorefrontCategoryPageLoad"/> <!--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"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="12" stepKey="selectPerPageOption"/> <!--Verify number of products displayed in First Page --> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage"/> <!--Verify number of products displayed in Second Page --> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton"/> + <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 --> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton1"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton1"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage1"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage"/> @@ -201,11 +182,11 @@ <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 --> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageOption1"/> <waitForPageLoad stepKey="waitForPageToLoad10"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="24" stepKey="seeNumberOfProductsInFirstPage3"/> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton2"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton2"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage2"/> <waitForPageLoad stepKey="waitForPageToLoad11"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInSecondPage3"/> @@ -214,7 +195,7 @@ <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage2"/> <waitForPageLoad stepKey="waitForPageToLoad13"/> <!--Select 36 items per page and verify number of products displayed in each page --> - <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage4"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage4"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageOption2"/> <waitForPageLoad stepKey="waitForPageToLoad12"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="30" stepKey="seeNumberOfProductsInFirstPage4"/> From e51b434f51b92eb153c7c263857e797c4366f748 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Wed, 1 Apr 2020 13:13:48 +0300 Subject: [PATCH 637/734] MFTF go to home using StorefrontOpenHomePageActionGroup --- ...ntBundleProductShownInCategoryListAndGridTest.xml | 3 +-- ...ontCustomerSearchBundleProductsByKeywordsTest.xml | 2 +- ...StorefrontGoToDetailsPageWhenAddingToCartTest.xml | 3 +-- ...heckCustomAttributeValuesAfterProductSaveTest.xml | 3 +-- .../AdminCreateRootCategoryAndSubcategoriesTest.xml | 3 +-- .../AdminCreateVirtualProductWithTierPriceTest.xml | 3 +-- .../Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml | 6 ++---- .../Test/AdminMoveProductBetweenCategoriesTest.xml | 6 ++---- ...WhenAssignedToCategoryWithoutCustomURLKeyTest.xml | 2 +- .../Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml | 6 ++---- .../EndToEndB2CGuestUserMysqlTest.xml | 3 +-- .../EndToEndB2CGuestUserTest.xml | 3 +-- .../StoreFrontRecentlyViewedAtStoreLevelTest.xml | 6 ++---- .../StoreFrontRecentlyViewedAtStoreViewLevelTest.xml | 8 ++------ ...ontCategoryHighlightedAndProductDisplayedTest.xml | 3 +-- .../Mftf/Test/StorefrontForthLevelCategoryTest.xml | 2 +- ...seProductCustomOptionsDifferentStoreViewsTest.xml | 2 +- ...rifyChildCategoriesShouldNotIncludeInMenuTest.xml | 12 ++++-------- .../Mftf/Test/AdminCreateSearchTermEntityTest.xml | 3 +-- .../Test/StorefrontUpdateSearchTermEntityTest.xml | 6 ++---- .../RewriteStoreLevelUrlKeyOfChildCategoryTest.xml | 3 +-- ...hoppingCartAndMiniShoppingCartPerCustomerTest.xml | 6 ++---- ...leProductWithTwoOptionsAssignedToCategoryTest.xml | 3 +-- ...ctWithTwoOptionsWithoutAssignedToCategoryTest.xml | 3 +-- ...ntConfigurableProductWithFileCustomOptionTest.xml | 3 +-- ...dminCustomerSubscribeNewsletterPerWebsiteTest.xml | 3 +-- ...wnloadableProductAndAssignItToCustomStoreTest.xml | 3 +-- ...rchWithDecimalAttributeUsingElasticSearchTest.xml | 5 ++--- ...torefrontElasticsearch6SearchInvalidValueTest.xml | 3 +-- .../Test/Mftf/Test/ShopByButtonInMobileTest.xml | 3 +-- .../Test/Mftf/Test/AdminNameEmptyForGuestTest.xml | 3 +-- .../Mftf/Test/ShippingQuotePersistedForGuestTest.xml | 3 +-- .../Test/AdminMassDeleteSearchTermEntityTest.xml | 3 +-- ...tStorefrontStoreNotVisibleInFooterActionGroup.xml | 2 +- ...sertStorefrontStoreVisibleInFooterActionGroup.xml | 2 +- ...sertStorefrontStoreVisibleInHeaderActionGroup.xml | 2 +- .../Test/Mftf/Test/AdminDeleteStoreViewTest.xml | 3 +-- .../Test/Mftf/Test/AdminUpdateStoreViewTest.xml | 3 +-- ...angingUrlKeyForStoreViewAndMovingCategoryTest.xml | 3 +-- 39 files changed, 50 insertions(+), 94 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml index 78a039eccde2d..d6c3edd9b9a3f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml @@ -79,8 +79,7 @@ <magentoCron stepKey="runCronReindex" groups="index"/> <!--Go to category page--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToload"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="cartClickCategory"/> <!--Check in grid view--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml index ae9d01a50f46e..de6718dfd9f31 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml @@ -48,7 +48,7 @@ <deleteData createDataKey="createSimpleProductTwo" stepKey="createSimpleProductTwo"/> </after> <!-- 1. Go to storefront home page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <!-- 2. Fill quick search bar with test values unique for dynamic bundle product and click search --> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchDynamic"> <argument name="phrase" value="Dynamic"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index f0e16bbdd1b99..14b1cb8a11f8c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -66,8 +66,7 @@ <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> <!--Go to category page--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToload"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory"/> <!--Click add to cart--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml index 8eec2ed40c8cb..38735c354477e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -75,8 +75,7 @@ <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode($createMultiSelectProductAttribute.attribute_code$)}}" userInput="$addThirdOptionToAttribute.option[store_labels][0][label]$" stepKey="assertThirdOptionIsSelected"/> <!-- Search for the product on Storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchProductOnStorefront"> <argument name="phrase" value="$createSimpleProduct.name$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 2352e231e66a4..eb08132ce3d3c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -72,8 +72,7 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkOnModalDialog1"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout1"/> <!--Go to storefront and verify created subcategory on frontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad2"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="checkCreatedSubcategory1OnFrontend"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml index ea73de1cab15d..d6c8af1192387 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml @@ -102,8 +102,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Verify customer see created virtual product with tier price(from above step) on storefront page and is searchable by sku --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{virtualProductBigQty.sku}}" stepKey="fillVirtualProductName"/> <waitForPageLoad stepKey="waitForSearchTextBox"/> <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 820a578e0252f..5648b841f2e69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -55,8 +55,7 @@ <argument name="parentCategory" value="$simpleSubCategoryTwo.name$"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage1"/> - <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened1"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage1"/> <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatTopCategoryIsSubCategoryTwo"/> <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="mouseOverSubCategoryTwo"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverSubCategoryTwo"/> @@ -97,8 +96,7 @@ <argument name="parentCategory" value="Default Category"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened2"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage2"/> <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="verifyThatSubCategoryOneIsTopCategory"/> <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatSubCategoryTwoIsTopCategory"/> <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="mouseOverTopSubCategoryOne"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index d3d435b7451c3..4c26dcd035320 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -134,8 +134,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Open frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="onFrontend"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="onFrontend"/> <!-- Open <Cat2> from navigation menu --> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategory.name$$)}}" stepKey="openCat2"/> @@ -192,8 +191,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Open frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="onFrontendPage"/> - <waitForPageLoad stepKey="waitForFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="onFrontendPage"/> <!-- Open <Cat2> from navigation menu --> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategory.name$$)}}" stepKey="openSecondCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml index c62c6b4b4c6a9..f221b09b1b9d8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml @@ -83,7 +83,7 @@ <executeJS function="return '$createSimpleProductSecond.name$'.toLowerCase();" stepKey="simpleProductSecondNameLower" /> <!-- Make assertions on frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="onCategoryPage"/> <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="checkCategryUrlKey"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index 604c01f05b838..cdc439e18fc99 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -89,8 +89,7 @@ <click selector="{{AdminCategoryModalSection.ok}}" stepKey="acceptModal1"/> <waitForElementVisible selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="waitForPageReloadAfterDeleteDefaultCategory"/> <!-- Verify categories 1 and 3 their products. --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <!-- @TODO: Uncomment commented below code after MQE-903 is fixed --> <!--<click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryC.name$$)}}" stepKey="browseClickCategoryC"/>--> @@ -132,8 +131,7 @@ <argument name="categoryEntity" value="$$createCategoryB$$"/> </actionGroup> <!-- Verify categories 1 and 3 are absent --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage1"/> - <waitForPageLoad stepKey="waitHomePageLoadAfterDeletingCategories"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage1"/> <dontSee selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryB.name$$)}}" stepKey="browseClickCategoryB"/> <dontSee selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryC.name$$)}}" stepKey="browseClickCategoryC"/> <!-- Verify products 1-3 are available on storefront --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml index ee98e6e152d91..f334a6c5a66ce 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -58,8 +58,7 @@ <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml index 93b08e3c468de..94b0deaf6ce1c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -61,8 +61,7 @@ <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml index d84e2283b2dd4..43ac8a63dd1f7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -100,8 +100,7 @@ <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStoreOneProductPageTwo"/> <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct3.name$)}}" stepKey="goToStoreOneProductPageThree"/> <!-- Go to Home Page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStoreFrontHomePage"/> - <waitForPageLoad time="30" stepKey="waitForHomeContentPageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStoreFrontHomePage"/> <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStoreOneRecentlyViewedProduct2"> <argument name="productName" value="$$createSimpleProduct2.name$$"/> <argument name="productPosition" value="2"/> @@ -116,8 +115,7 @@ <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct1.name$)}}" stepKey="goToStore2ProductPage1"/> <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore2ProductPage2"/> <!-- Go to Home Page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage2"/> - <waitForPageLoad time="30" stepKey="waitForStoreHomeContentPageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage2"/> <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct1"> <argument name="productName" value="$$createSimpleProduct1.name$$"/> <argument name="productPosition" value="2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index d454a3d24e273..8b3927737b656 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -94,9 +94,7 @@ <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore1ProductPage2"/> <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct3.name$)}}" stepKey="goToStore1ProductPage3"/> <!-- Go to Home Page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad time="30" stepKey="homeWaitForPageLoad"/> - + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage"/> <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStore1RecentlyViewedProduct2"> <argument name="productName" value="$$createSimpleProduct2.name$$"/> <argument name="productPosition" value="2"/> @@ -116,9 +114,7 @@ <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore2ProductPage2"/> <!-- Go to Home Page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStoreViewHomePage"/> - <waitForPageLoad time="30" stepKey="homePageWaitForStoreView"/> - + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStoreViewHomePage"/> <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct1"> <argument name="productName" value="$$createSimpleProduct1.name$$"/> <argument name="productPosition" value="2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml index 00f9608d07c3e..bb430380d2618 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml @@ -51,8 +51,7 @@ </after> <!--Open Storefront home page--> <comment userInput="Open Storefront home page" stepKey="openStorefrontHomePage"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontHomePage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontHomePage"/> <!--Click on first category--> <comment userInput="Click on first category" stepKey="openFirstCategoryPage"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$category1.name$$)}}" stepKey="clickCategory1Name"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml index bb46f8010eaa8..74264149cf1cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml @@ -35,7 +35,7 @@ <deleteData createDataKey="category2" stepKey="deleteCategory2"/> <deleteData createDataKey="category1" stepKey="deleteCategory1"/> </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category1.name$$)}}" stepKey="hoverCategoryLevelOne"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index a8ab4cca1ac78..74341378d9752 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -209,7 +209,7 @@ <!-- Switch to FR Store View Storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnProduct4Page"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore"> <argument name="storeView" value="customStoreFR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index 2a6a05c8ffeab..aa2cbb1c7646e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -40,8 +40,7 @@ <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="seeSimpleSubCategoryOnStorefront1"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront1"/> <!--Set Include in menu to No on created category under Default Category --> @@ -53,8 +52,7 @@ <seeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="seeCheckboxEnableCategoryIsChecked"/> <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="dontSeeCheckboxIncludeInMenuIsChecked"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage2"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront1"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront2"/> <!--Set Enable category to No and Include in menu to Yes on created category under Default Category --> @@ -67,8 +65,7 @@ <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="dontSeeCheckboxEnableCategoryIsChecked"/> <seeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="seeCheckboxIncludeInMenuIsChecked"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage3"/> - <waitForPageLoad stepKey="waitForPageLoad6"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage3"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront2"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront3"/> <!--Set Enable category to No and Include in menu to No on created category under Default Category --> @@ -80,8 +77,7 @@ <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="dontSeeCheckboxEnableCategoryIsChecked2"/> <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="dontSeeCheckboxIncludeInMenuIsChecked2"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage4"/> - <waitForPageLoad stepKey="waitForPageLoad8"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage4"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront3"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront4"/> </test> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml index 2ab87b3ceb967..47a57acfbeff1 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml @@ -47,8 +47,7 @@ </actionGroup> <!-- Go to storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <!-- Assert created search term on storefront --> <actionGroup ref="AssertSearchTermOnFrontendActionGroup" stepKey="assertCreatedSearchTermOnFrontend"> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml index 502301939f71a..aa84232b495a4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml @@ -28,8 +28,7 @@ <!-- Perform reindex and flush cache --> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage1"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage1"/> </before> <after> <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> @@ -71,8 +70,7 @@ <argument name="searchQuery" value="{{UpdatedSearchTermData1.query_text}}"/> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage2"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName2"> <argument name="phrase" value="{{UpdatedSearchTermData1.query_text}}"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index d61a4bd077d9c..d454c1c1f8ae2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -46,8 +46,7 @@ <argument name="value" value="gear-global"/> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index 3a9bec5cdfcf3..741fde91f851e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -135,8 +135,7 @@ <see selector="{{StorefrontProductPageSection.orderTotal}}" userInput="{{quoteQty2Subtotal266.currency}}{{quoteQty2Subtotal266.total}}" stepKey="seeFirstCustomerOrderTotal"/> <!-- Assert products in mini cart for first customer --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFrontHomePage"/> - <waitForPageLoad stepKey="waitForHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFrontHomePage"/> <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertFirstProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -168,8 +167,7 @@ <see selector="{{StorefrontProductPageSection.orderTotal}}" userInput="{{quoteQty2Price123.currency}}{{quoteQty2Price123.total}}" stepKey="seeSecondCustomerOrderTotal"/> <!-- Assert product in mini cart --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertProductInMiniCart"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 4c39b0c2a54fb..ed65330e9bd16 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -140,8 +140,7 @@ </actionGroup> <!-- Assert child products are not displayed separately: two next step --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFront"/> - <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFront"/> <!-- Quick search the storefront for the first attribute option --> <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index 31851fc78968b..cbaa25dd883b4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -122,8 +122,7 @@ </actionGroup> <!-- Assert child products are not displayed separately: two next step --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFront"/> - <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFront"/> <!-- Quick search the storefront for the first attribute option --> <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 4de8dedefab48..5551027b898ba 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -40,8 +40,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="$$createCategory.name$$" stepKey="seeOnCategoryPage"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml index 06c0593ad00c4..3b3e46f0cb1a5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml @@ -41,8 +41,7 @@ <argument name="customStore" value="NewStoreViewData"/> </actionGroup> <!-- Switch to the new Store View on storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="waitForNavigateHomePage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> <argument name="storeView" value="NewStoreViewData"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index 9a1f1273a41fd..e56b92c863569 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -77,8 +77,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Switch default store view on store view created below --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="SwitchStoreView"> <argument name="storeView" value="customStore"/> </actionGroup> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml index c48f0d63b06ca..4f98ebb4cb8b5 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml @@ -79,8 +79,7 @@ <!--Reindex and clear cache--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:clean" stepKey="cleanCache"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <!-- Navigate to Storefront and search --> <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult0"> @@ -96,4 +95,4 @@ <argument name="product" value="$$product2.name$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 49aef41d7f31c..a40d692b93848 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -83,8 +83,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush eav" stepKey="flushCache"/> <!--Assert search results on storefront--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFirstSearchTerm"> <argument name="phrase" value="?searchable;"/> </actionGroup> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml index b5fc483e251eb..76a5f161651ec 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml @@ -52,8 +52,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Check storefront mobile view for shop by button is functioning as expected --> <comment userInput="Check storefront mobile view for shop by button is functioning as expected" stepKey="commentCheckShopByButton" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <submitForm selector="#search_mini_form" parameterArray="['q' => 'Test Simple Product']" stepKey="fillSearchBar" /> <resizeWindow width="600" height="800" stepKey="resizeWindow"/> <waitForPageLoad stepKey="waitForHomePageToLoad2"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml index a90a035b25726..5e35f5aab60cd 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml @@ -26,8 +26,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForCustomerPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <scrollTo selector="{{StorefrontCustomerFooterSection.footerBlock}}" stepKey="scrollToFooter"/> <fillField userInput="{{Simple_US_Customer.email}}" selector="{{StorefrontCustomerFooterSection.formSubscribe}}" stepKey="giveEmail"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml index eebadbeaa0eec..7c4e6948386f3 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml @@ -52,8 +52,7 @@ <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> <resetCookie userInput="persistent_shopping_cart" stepKey="resetPersistentCookie"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePageAfterResetPersistentCookie"/> - <waitForPageLoad stepKey="waitHomePageLoadAfterResetCookie"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePageAfterResetPersistentCookie"/> <!--Step 4: Add the product to shopping cart and open cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsGuestUser"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCartAsGuestUser"> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml index 2b7a4e7f5e5cb..bc0abd151fbf0 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -63,8 +63,7 @@ </actionGroup> <!-- Go to storefront page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <!-- Verify search term deletion on storefront --> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForFirstSearchTerm"> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml index eb8012b7d0635..73f687669497e 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="store" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <dontSee selector="{{StorefrontFooterSection.storeLink(store)}}" stepKey="AssertStoreNotOnStorefront"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml index 3ec1555735e99..f6ef11ad01111 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="customStore" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <click selector="{{StorefrontFooterSection.switchStoreButton}}" stepKey="clickSwitchStoreButton"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml index f9c10a09c51c2..b7a5e1a76d072 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="store" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="selectStoreSwitcher"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml index a197f88bafba2..c4e3f51e6138f 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml @@ -51,8 +51,7 @@ </actionGroup> <!--Go to storefront and verify AssertStoreNotOnFrontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <dontSee selector="{{StorefrontHeaderSection.storeViewList(storeViewData.name)}}" stepKey="dontSeeAssertStoreViewNameOnStorefront"/> </test> </tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml index 3b16767e60d55..f2e9e12821595 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml @@ -71,8 +71,7 @@ <see selector="{{AdminConfigSection.defaultConfigDropdown}}" userInput="{{SecondStoreUnique.name}}" stepKey="seeAssertUpdateStoreViewInDefaultConfigDropdown"/> <!--Go to storefront and verify AssertStoreFrontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="selectStoreSwitcher"/> <waitForPageLoad stepKey="waitForFirstStoreView"/> <see selector="{{StorefrontHeaderSection.storeViewDropdown}}" userInput="{{storeViewData.name}}" stepKey="seeAssertStoreViewOnStorefront"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index d337da37e0d2a..9ba584bd311e6 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -74,8 +74,7 @@ </actionGroup> <!-- Switch default store view on store view created below for first category --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> <argument name="storeView" value="customStore"/> </actionGroup> From 8e4a197b11cde6e4b3d60d3fff34e70c0fd28682 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Wed, 1 Apr 2020 13:35:37 +0300 Subject: [PATCH 638/734] Changes test jasmine covered --- .../Magento/Ui/base/js/modal/prompt.test.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js index f2c74729e0ee8..7a0d3ac3fef91 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js @@ -10,8 +10,20 @@ define([ 'use strict'; describe('ui/js/modal/prompt', function () { - var element = $('<div>some element</div>'), + + var element, + prompt, + widget; + + beforeEach(function () { + element = $('<div id="element">some element</div>'), prompt = element.prompt({}); + widget = element.prompt({}).data('mage-prompt'); + }); + + afterEach(function () { + $('#element').remove(); + }); it('Check for modal definition', function () { expect(prompt).toBeDefined(); @@ -23,5 +35,12 @@ define([ it('Integration: modal created on page', function () { expect(prompt.length).toEqual(1); }); + it('Check cancel action', function () { + var cancel = spyOn(widget.options.actions, 'cancel'); + + jQuery('.modals-overlay').click(); + expect(widget.options.outerClickHandler).toBeDefined(); + expect(cancel).toHaveBeenCalled(); + }); }); }); From 665b26316d377e49ef5383ca73a41c389b3d570f Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 1 Apr 2020 14:19:35 +0300 Subject: [PATCH 639/734] MC-32981: Integration test fails every 1st day of the mouth - ChartTest::testGetByPeriodWithParam --- .../testsuite/Magento/Sales/_files/order_list_with_invoice.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php index d1f74c746b64f..1460f5742c1fc 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php @@ -45,7 +45,8 @@ 'base_grand_total' => 130.00, 'grand_total' => 130.00, 'subtotal' => 130.00, - 'created_at' => $dateTime->modify('-1 day')->format(DateTime::DATETIME_PHP_FORMAT), + 'created_at' => max($dateTime->modify('-1 day'), $dateTime->modify('first day of this month')) + ->format(DateTime::DATETIME_PHP_FORMAT), ], [ 'increment_id' => '100000004', From 12452e1a15e4580aabb1be29d90aa322592d111b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 1 Apr 2020 17:05:04 +0300 Subject: [PATCH 640/734] MC-31729: Non-cacheable block added to default handle makes every page non-cacheable --- .../Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php index cf19c4a8868c1..89c588b5e32a2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -12,7 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; -use PhpUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** From dbbd8261d6b9de9c11d07df6db8cf2c1c2abfc25 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Wed, 1 Apr 2020 21:18:27 +0300 Subject: [PATCH 641/734] Update SecurityTest.php --- .../Model/System/Message/SecurityTest.php | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php index 3b679e442c60e..a0e09728cbacf 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php @@ -17,47 +17,42 @@ class SecurityTest extends TestCase { /** - * @var MockObject + * @var CacheInterface|MockObject */ - protected $_cacheMock; + private $cacheMock; /** - * @var MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfigMock; + private $scopeConfigMock; /** - * @var MockObject + * @var CurlFactory|MockObject */ - protected $_configMock; - - /** - * @var MockObject - */ - protected $_curlFactoryMock; + private $curlFactoryMock; /** * @var Security */ - protected $_messageModel; + private $messageModel; protected function setUp(): void { //Prepare objects for constructor - $this->_cacheMock = $this->createMock(CacheInterface::class); - $this->_scopeConfigMock = $this->createMock(ScopeConfigInterface::class); - $this->_curlFactoryMock = $this->createPartialMock( + $this->cacheMock = $this->createMock(CacheInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->curlFactoryMock = $this->createPartialMock( CurlFactory::class, ['create'] ); $objectManagerHelper = new ObjectManager($this); $arguments = [ - 'cache' => $this->_cacheMock, - 'scopeConfig' => $this->_scopeConfigMock, - 'curlFactory' => $this->_curlFactoryMock, + 'cache' => $this->cacheMock, + 'scopeConfig' => $this->scopeConfigMock, + 'curlFactory' => $this->curlFactoryMock, ]; - $this->_messageModel = $objectManagerHelper->getObject( + $this->messageModel = $objectManagerHelper->getObject( Security::class, $arguments ); @@ -73,16 +68,16 @@ protected function setUp(): void */ public function testIsDisplayed($expectedResult, $cached, $response) { - $this->_cacheMock->expects($this->any())->method('load')->will($this->returnValue($cached)); - $this->_cacheMock->expects($this->any())->method('save')->will($this->returnValue(null)); + $this->cacheMock->expects($this->any())->method('load')->will($this->returnValue($cached)); + $this->cacheMock->expects($this->any())->method('save')->will($this->returnValue(null)); $httpAdapterMock = $this->createMock(Curl::class); $httpAdapterMock->expects($this->any())->method('read')->will($this->returnValue($response)); - $this->_curlFactoryMock->expects($this->any())->method('create')->will($this->returnValue($httpAdapterMock)); + $this->curlFactoryMock->expects($this->any())->method('create')->will($this->returnValue($httpAdapterMock)); - $this->_scopeConfigMock->expects($this->any())->method('getValue')->will($this->returnValue(null)); + $this->scopeConfigMock->expects($this->any())->method('getValue')->will($this->returnValue(null)); - $this->assertEquals($expectedResult, $this->_messageModel->isDisplayed()); + $this->assertEquals($expectedResult, $this->messageModel->isDisplayed()); } /** @@ -101,6 +96,6 @@ public function testGetText() { $messageStart = 'Your web server is set up incorrectly'; - $this->assertStringStartsWith($messageStart, (string)$this->_messageModel->getText()); + $this->assertStringStartsWith($messageStart, (string)$this->messageModel->getText()); } } From 8c6d76e24d1d42432705f78ee00d1825db541a30 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <ladiesman9x@gmail.com> Date: Thu, 2 Apr 2020 11:42:32 +0700 Subject: [PATCH 642/734] Fix alignment elements in login container form --- .../blank/Magento_Customer/web/css/source/_module.less | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 0c2b1b4db83e6..adfcb117cc9d0 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -412,8 +412,10 @@ .block { &:extend(.abs-blocks-2columns all); - &.login { + .login { .actions-toolbar { + margin-left: 0; + > .primary { margin-bottom: 0; margin-right: @indent__l; @@ -428,6 +430,7 @@ .fieldset { &:after { + margin-left: 0; &:extend(.abs-margin-for-forms-desktop all); } @@ -435,6 +438,10 @@ > .control { width: 80%; } + + .label { + text-align: left; + } } } } From 19bc72255baec0194a95fdb493fc28b8a9e4a0fc Mon Sep 17 00:00:00 2001 From: Simon Sprankel <simon@customgento.com> Date: Thu, 2 Apr 2020 09:03:36 +0200 Subject: [PATCH 643/734] Fix XML schema location --- dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml | 4 ++-- dev/tests/acceptance/tests/_suite/suite.xml.sample | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml index 65c2bb7004503..924c712404dfe 100644 --- a/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml +++ b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="WYSIWYGDisabledSuite"> <before> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled" /> @@ -20,4 +20,4 @@ <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled enabled" /> </after> </suite> -</suites> \ No newline at end of file +</suites> diff --git a/dev/tests/acceptance/tests/_suite/suite.xml.sample b/dev/tests/acceptance/tests/_suite/suite.xml.sample index 4c6a78afd40d0..30cf2eef05682 100644 --- a/dev/tests/acceptance/tests/_suite/suite.xml.sample +++ b/dev/tests/acceptance/tests/_suite/suite.xml.sample @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="sample"> <before> <createData entity="defaultCategory" stepKey="createCategory"/> From 7e29a8986ab42c7ab8894aaa0a3e27b549470b57 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 2 Apr 2020 11:02:17 +0300 Subject: [PATCH 644/734] Fix static tests --- .../Framework/TestFramework/Unit/Listener/GarbageCleanup.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php index 483bc7148a7c7..34a8963c198cc 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php @@ -17,7 +17,8 @@ class GarbageCleanup implements TestListener use \PHPUnit\Framework\TestListenerDefaultImplementation; /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function endTestSuite(TestSuite $suite): void From 45c8564a6264c37ee7c43a7862abd0f3b5216570 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 2 Apr 2020 12:17:57 +0300 Subject: [PATCH 645/734] Fix static tests --- .../Framework/TestFramework/Unit/Listener/GarbageCleanup.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php index 483bc7148a7c7..34a8963c198cc 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php @@ -17,7 +17,8 @@ class GarbageCleanup implements TestListener use \PHPUnit\Framework\TestListenerDefaultImplementation; /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function endTestSuite(TestSuite $suite): void From c2c9237e19dc9a474795fa5cdad33d748c76c810 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 2 Apr 2020 12:18:57 +0300 Subject: [PATCH 646/734] MC-32934: Fix action groups. --- .../Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml | 1 + .../StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml index 564033f459dc9..5521cc2a7d0b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml @@ -14,6 +14,7 @@ </annotations> <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfTheCategoryPage"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveCategory"/> <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> <see userInput="You saved the category." selector="{{AdminMessagesSection.success}}" stepKey="assertSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml index 4c641b621a504..0effec5329cc0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontAssertProductAbsentOnCategoryPageActionGroup"> <annotations> - <description>Navigate to category page and verify product is absent.</description> + <description>DEPRECATED. Please use AssertStorefrontProductAbsentOnCategoryPageActionGroup instead. + Navigate to category page and verify product is absent.</description> </annotations> <arguments> <argument name="category" defaultValue="_defaultCategory"/> From 98698311b7fc28251280834022d9e19941f574ef Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 2 Apr 2020 12:19:29 +0300 Subject: [PATCH 647/734] Fix static tests --- .../Framework/TestFramework/Unit/Listener/GarbageCleanup.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php index 483bc7148a7c7..34a8963c198cc 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php @@ -17,7 +17,8 @@ class GarbageCleanup implements TestListener use \PHPUnit\Framework\TestListenerDefaultImplementation; /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function endTestSuite(TestSuite $suite): void From f30de938ab66ffd6ba7024f70c43afab2d85d3f0 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 2 Apr 2020 14:08:38 +0300 Subject: [PATCH 648/734] MC-32907: [FT] [MFTF] Add section --- .../Checkout/Test/Mftf/Page/CheckoutPage.xml | 1 + .../StorefrontCheckoutPageMessagesSection.xml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml index d6173dfa17916..a021ce6c93e63 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml @@ -9,6 +9,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> + <section name="StorefrontCheckoutPageMessagesSection"/> <section name="CheckoutShippingSection"/> <section name="CheckoutShippingMethodsSection"/> <section name="CheckoutOrderSummarySection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml new file mode 100644 index 0000000000000..4cedf837918c5 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutPageMessagesSection"> + <element name="successMessage" type="text" selector="//div[@id='checkout']//div[@data-role='checkout-messages']//div[contains(@class,'message-success')]//div[contains(.,'{{message}}')]" parameterized="true"/> + </section> +</sections> From 94a0dc5ec01a16bdbe2b7a79a26993c0aaa022bf Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 2 Apr 2020 14:25:13 +0300 Subject: [PATCH 649/734] MC-31832: Revert MC-16650: Product Attribute Type Price Not Displaying --- .../Catalog/Model/Layer/FilterList.php | 4 +- .../Test/Mftf/Data/ProductAttributeData.xml | 22 ----- .../StorefrontCategoryFilterSection.xml | 1 - .../Test/Unit/Model/Layer/FilterListTest.php | 22 ++--- .../Model/Layer/Filter/Decimal.php | 15 +-- .../Model/Layer/Filter/Price.php | 2 +- .../Model/Search/RequestGenerator.php | 5 +- .../Model/Search/RequestGenerator/Price.php | 46 ---------- .../LayerNavigationOfCatalogSearchTest.xml | 92 ------------------- .../Unit/Model/Layer/Filter/PriceTest.php | 6 -- .../Search/RequestGenerator/PriceTest.php | 82 ----------------- app/code/Magento/CatalogSearch/etc/di.xml | 1 - .../Model/Layer/Filter/DecimalTest.php | 40 -------- .../Model/Layer/Filter/PriceTest.php | 10 +- .../Navigation/Category/DecimalFilterTest.php | 7 +- .../Navigation/Search/DecimalFilterTest.php | 7 +- 16 files changed, 28 insertions(+), 334 deletions(-) delete mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php delete mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml delete mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php index a7eba474c58d8..86a7d1fb61938 100644 --- a/app/code/Magento/Catalog/Model/Layer/FilterList.php +++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php @@ -123,9 +123,9 @@ protected function getAttributeFilterClass(\Magento\Catalog\Model\ResourceModel\ { $filterClassName = $this->filterTypes[self::ATTRIBUTE_FILTER]; - if ($attribute->getFrontendInput() === 'price') { + if ($attribute->getAttributeCode() == 'price') { $filterClassName = $this->filterTypes[self::PRICE_FILTER]; - } elseif ($attribute->getBackendType() === 'decimal') { + } elseif ($attribute->getBackendType() == 'decimal') { $filterClassName = $this->filterTypes[self::DECIMAL_FILTER]; } diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index a2561e00a0e24..ffee02080503e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -282,28 +282,6 @@ <data key="used_for_sort_by">false</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> - <entity name="productAttributeTypeOfPrice" type="ProductAttribute"> - <data key="attribute_code" unique="suffix">attribute</data> - <data key="frontend_input">price</data> - <data key="scope">global</data> - <data key="is_required">false</data> - <data key="is_unique">false</data> - <data key="is_searchable">false</data> - <data key="is_visible">true</data> - <data key="is_wysiwyg_enabled">false</data> - <data key="is_visible_in_advanced_search">false</data> - <data key="is_visible_on_front">true</data> - <data key="is_filterable">true</data> - <data key="is_filterable_in_search">false</data> - <data key="used_in_product_listing">false</data> - <data key="is_used_for_promo_rules">false</data> - <data key="is_comparable">true</data> - <data key="is_used_in_grid">false</data> - <data key="is_visible_in_grid">false</data> - <data key="is_filterable_in_grid">false</data> - <data key="used_for_sort_by">false</data> - <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> - </entity> <entity name="textProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> <data key="frontend_input">text</data> <data key="default_value" unique="suffix">defaultValue</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml index faee605e77319..ddec4428f90e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml @@ -11,6 +11,5 @@ <section name="StorefrontCategoryFilterSection"> <element name="CategoryFilter" type="button" selector="//main//div[@class='filter-options']//div[contains(text(), 'Category')]"/> <element name="CategoryByName" type="button" selector="//main//div[@class='filter-options']//li[@class='item']//a[contains(text(), '{{var1}}')]" parameterized="true"/> - <element name="CustomPriceAttribute" type="button" selector="div.filter-options-title"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php index 84c433379b156..8fdd41281d807 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php @@ -94,13 +94,9 @@ public function testGetFilters($method, $value, $expectedClass) $this->objectManagerMock->expects($this->at(1)) ->method('create') - ->with( - $expectedClass, - [ - 'data' => ['attribute_model' => $this->attributeMock], - 'layer' => $this->layerMock - ] - ) + ->with($expectedClass, [ + 'data' => ['attribute_model' => $this->attributeMock], + 'layer' => $this->layerMock]) ->will($this->returnValue('filter')); $this->attributeMock->expects($this->once()) @@ -169,8 +165,8 @@ public function getFiltersDataProvider() { return [ [ - 'method' => 'getFrontendInput', - 'value' => 'price', + 'method' => 'getAttributeCode', + 'value' => FilterList::PRICE_FILTER, 'expectedClass' => 'PriceFilterClass', ], [ @@ -179,8 +175,8 @@ public function getFiltersDataProvider() 'expectedClass' => 'DecimalFilterClass', ], [ - 'method' => 'getFrontendInput', - 'value' => 'text', + 'method' => 'getAttributeCode', + 'value' => null, 'expectedClass' => 'AttributeFilterClass', ] ]; @@ -195,8 +191,8 @@ public function getFiltersWithoutCategoryDataProvider(): array { return [ 'Filters contains only price attribute' => [ - 'method' => 'getFrontendInput', - 'value' => 'price', + 'method' => 'getAttributeCode', + 'value' => FilterList::PRICE_FILTER, 'expectedClass' => 'PriceFilterClass', 'expectedResult' => [ 'filter' diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php index 3b0c4dfb6df2f..e2cb9174b5e97 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php @@ -14,9 +14,6 @@ */ class Decimal extends AbstractFilter { - /** Decimal delta for filter */ - private const DECIMAL_DELTA = 0.001; - /** * @var \Magento\Framework\Pricing\PriceCurrencyInterface */ @@ -75,17 +72,11 @@ public function apply(\Magento\Framework\App\RequestInterface $request) list($from, $to) = explode('-', $filter); - // When the range is 10-20 we only need to get products that are in the 10-19.99 range. - $toValue = $to; - if (!empty($toValue) && $from !== $toValue) { - $toValue -= self::DECIMAL_DELTA; - } - $this->getLayer() ->getProductCollection() ->addFieldToFilter( $this->getAttributeModel()->getAttributeCode(), - ['from' => $from, 'to' => $toValue] + ['from' => $from, 'to' => $to] ); $this->getLayer()->getState()->addFilter( @@ -122,7 +113,7 @@ protected function _getItemsData() $from = ''; } if ($to == '*') { - $to = ''; + $to = null; } $label = $this->renderRangeLabel(empty($from) ? 0 : $from, $to); $value = $from . '-' . $to; @@ -149,7 +140,7 @@ protected function _getItemsData() protected function renderRangeLabel($fromPrice, $toPrice) { $formattedFromPrice = $this->priceCurrency->format($fromPrice); - if ($toPrice === '') { + if ($toPrice === null) { return __('%1 and above', $formattedFromPrice); } else { if ($fromPrice != $toPrice) { diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php index 66d9281ed38e2..332bb991bf29f 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php @@ -141,7 +141,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request) list($from, $to) = $filter; $this->getLayer()->getProductCollection()->addFieldToFilter( - $this->getAttributeModel()->getAttributeCode(), + 'price', ['from' => $from, 'to' => empty($to) || $from == $to ? $to : $to - self::PRICE_DELTA] ); diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php index 5ac252677ff79..caa49d0f0c4a4 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php +++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php @@ -100,10 +100,7 @@ private function generateRequest($attributeType, $container, $useFulltext) ], ]; $bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX; - $generatorType = $attribute->getFrontendInput() === 'price' - ? $attribute->getFrontendInput() - : $attribute->getBackendType(); - $generator = $this->generatorResolver->getGeneratorForType($generatorType); + $generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType()); $request['filters'][$filterName] = $generator->getFilterData($attribute, $filterName); $request['aggregations'][$bucketName] = $generator->getAggregationData($attribute, $bucketName); } diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php deleted file mode 100644 index 949806d14f45a..0000000000000 --- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogSearch\Model\Search\RequestGenerator; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Framework\Search\Request\BucketInterface; -use Magento\Framework\Search\Request\FilterInterface; - -/** - * Catalog search range request generator. - */ -class Price implements GeneratorInterface -{ - /** - * @inheritdoc - */ - public function getFilterData(Attribute $attribute, $filterName): array - { - return [ - 'type' => FilterInterface::TYPE_RANGE, - 'name' => $filterName, - 'field' => $attribute->getAttributeCode(), - 'from' => '$' . $attribute->getAttributeCode() . '.from$', - 'to' => '$' . $attribute->getAttributeCode() . '.to$', - ]; - } - - /** - * @inheritdoc - */ - public function getAggregationData(Attribute $attribute, $bucketName): array - { - return [ - 'type' => BucketInterface::TYPE_DYNAMIC, - 'name' => $bucketName, - 'field' => $attribute->getAttributeCode(), - 'method' => '$price_dynamic_algorithm$', - 'metric' => [['type' => 'count']], - ]; - } -} diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml deleted file mode 100644 index c8055d85c98ea..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?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="LayerNavigationOfCatalogSearchTest"> - <annotations> - <stories value="Search terms"/> - <title value="Layer Navigation of Catalog Search Should Equalize Price Range As Default Configuration"/> - <description value="Make sure filter of custom attribute with type of price displays on storefront Catalog page and price range should respect the configuration in Admin site"/> - <testCaseId value="MC-16979"/> - <useCaseId value="MC-16650"/> - <severity value="MAJOR"/> - <group value="CatalogSearch"/> - </annotations> - <before> - <magentoCLI command="config:set catalog/layered_navigation/price_range_calculation auto" stepKey="setAutoPriceRange"/> - <createData stepKey="createPriceAttribute" entity="productAttributeTypeOfPrice"/> - <createData stepKey="assignPriceAttributeGroup" entity="AddToDefaultSet"> - <requiredEntity createDataKey="createPriceAttribute"/> - </createData> - <createData entity="SimpleSubCategory" stepKey="subCategory"/> - <createData entity="SimpleProduct" stepKey="simpleProduct1"> - <requiredEntity createDataKey="subCategory"/> - </createData> - <createData entity="SimpleProduct" stepKey="simpleProduct2"> - <requiredEntity createDataKey="subCategory"/> - </createData> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="subCategory"/> - <deleteData stepKey="deleteSimpleProduct1" createDataKey="simpleProduct1"/> - <deleteData stepKey="deleteSimpleProduct2" createDataKey="simpleProduct2"/> - <deleteData createDataKey="createPriceAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - <!--Update value for price attribute of Product 1--> - <comment userInput="Update value for price attribute of Product 1" stepKey="comment1"/> - <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage1"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <grabTextFrom selector="{{AdminProductFormSection.attributeLabelByText($$createPriceAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="grabAttributeLabel"/> - <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="30" stepKey="fillCustomPrice1"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton1"/> - <waitForPageLoad stepKey="waitForSimpleProductSaved1"/> - <!--Update value for price attribute of Product 2--> - <comment userInput="Update value for price attribute of Product 1" stepKey="comment2"/> - <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage2"> - <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> - <waitForPageLoad stepKey="waitForSimpleProductSaved2"/> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!--Navigate to category on Storefront--> - <comment userInput="Navigate to category on Storefront" stepKey="comment3"/> - <amOnPage url="{{StorefrontCategoryPage.url($$subCategory.name$$)}}" stepKey="goToCategoryStorefront"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="{$grabAttributeLabel}" selector="{{StorefrontCategoryFilterSection.CustomPriceAttribute}}" stepKey="seePriceLayerNavigationOnStorefront"/> - </test> -</tests> - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php index f783f75a170e3..fc78c45af07a6 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php @@ -209,12 +209,6 @@ public function testApply() $priceId = '15-50'; $requestVar = 'test_request_var'; - $this->target->setAttributeModel($this->attribute); - $attributeCode = 'price'; - $this->attribute->expects($this->any()) - ->method('getAttributeCode') - ->will($this->returnValue($attributeCode)); - $this->target->setRequestVar($requestVar); $this->request->expects($this->exactly(1)) ->method('getParam') diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php deleted file mode 100644 index 3635430197591..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogSearch\Test\Unit\Model\Search\RequestGenerator; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\CatalogSearch\Model\Search\RequestGenerator\Price; -use Magento\Framework\Search\Request\BucketInterface; -use Magento\Framework\Search\Request\FilterInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; - -/** - * Test catalog search range request generator. - */ -class PriceTest extends \PHPUnit\Framework\TestCase -{ - /** @var Price */ - private $price; - - /** @var Attribute|\PHPUnit_Framework_MockObject_MockObject */ - private $attribute; - - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $scopeConfigMock; - - protected function setUp() - { - $this->attribute = $this->getMockBuilder(Attribute::class) - ->disableOriginalConstructor() - ->setMethods(['getAttributeCode']) - ->getMockForAbstractClass(); - $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) - ->setMethods(['getValue']) - ->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->price = $objectManager->getObject( - Price::class, - ['scopeConfig' => $this->scopeConfigMock] - ); - } - - public function testGetFilterData() - { - $filterName = 'test_filter_name'; - $attributeCode = 'test_attribute_code'; - $expected = [ - 'type' => FilterInterface::TYPE_RANGE, - 'name' => $filterName, - 'field' => $attributeCode, - 'from' => '$' . $attributeCode . '.from$', - 'to' => '$' . $attributeCode . '.to$', - ]; - $this->attribute->expects($this->atLeastOnce()) - ->method('getAttributeCode') - ->willReturn($attributeCode); - $actual = $this->price->getFilterData($this->attribute, $filterName); - $this->assertEquals($expected, $actual); - } - - public function testGetAggregationData() - { - $bucketName = 'test_bucket_name'; - $attributeCode = 'test_attribute_code'; - $method = 'price_dynamic_algorithm'; - $expected = [ - 'type' => BucketInterface::TYPE_DYNAMIC, - 'name' => $bucketName, - 'field' => $attributeCode, - 'method' => '$'. $method . '$', - 'metric' => [['type' => 'count']], - ]; - $this->attribute->expects($this->atLeastOnce()) - ->method('getAttributeCode') - ->willReturn($attributeCode); - $actual = $this->price->getAggregationData($this->attribute, $bucketName); - $this->assertEquals($expected, $actual); - } -} diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 4e5b38878ee52..43ebded020ec1 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -284,7 +284,6 @@ <argument name="defaultGenerator" xsi:type="object">\Magento\CatalogSearch\Model\Search\RequestGenerator\General</argument> <argument name="generators" xsi:type="array"> <item name="decimal" xsi:type="object">Magento\CatalogSearch\Model\Search\RequestGenerator\Decimal</item> - <item name="price" xsi:type="object">Magento\CatalogSearch\Model\Search\RequestGenerator\Price</item> </argument> </arguments> </type> diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php index b75a984178f24..f0c8402c51879 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php @@ -48,46 +48,6 @@ protected function setUp() ->create(\Magento\CatalogSearch\Model\Layer\Filter\Decimal::class, ['layer' => $layer]); $this->_model->setAttributeModel($attribute); } - - /** - * Test the filter label is correct - */ - public function testApplyFilterLabel() - { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $request \Magento\TestFramework\Request */ - $request = $objectManager->get(\Magento\TestFramework\Request::class); - $request->setParam('weight', '10-20'); - $this->_model->apply($request); - - $filters = $this->_model->getLayer()->getState()->getFilters(); - $this->assertArrayHasKey(0, $filters); - $this->assertEquals( - '<span class="price">$10.00</span> - <span class="price">$19.99</span>', - (string)$filters[0]->getLabel() - ); - } - - /** - * Test the filter label is correct when there is empty To value - */ - public function testApplyFilterLabelWithEmptyToValue() - { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $request \Magento\TestFramework\Request */ - $request = $objectManager->get(\Magento\TestFramework\Request::class); - $request->setParam('weight', '10-'); - $this->_model->apply($request); - - $filters = $this->_model->getLayer()->getState()->getFilters(); - $this->assertArrayHasKey(0, $filters); - $this->assertEquals( - '<span class="price">$10.00</span> and above', - (string)$filters[0]->getLabel() - ); - } public function testApplyNothing() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php index a7944566eb8e0..232b4ec00973a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php @@ -37,16 +37,10 @@ protected function setUp() $category->load(4); $layer = $this->objectManager->get(\Magento\Catalog\Model\Layer\Category::class); $layer->setCurrentCategory($category); - /** @var $attribute \Magento\Catalog\Model\Entity\Attribute */ - $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Entity\Attribute::class - ); - $attribute->loadByCode('catalog_product', 'price'); $this->_model = $this->objectManager->create( \Magento\CatalogSearch\Model\Layer\Filter\Price::class, ['layer' => $layer] ); - $this->_model->setAttributeModel($attribute); } public function testApplyNothing() @@ -91,9 +85,9 @@ public function testApplyWithCustomCurrencyRate() $request->setParam('price', '10-20'); $this->_model->setCurrencyRate(10); - + $this->_model->apply($request); - + $filters = $this->_model->getLayer()->getState()->getFilters(); $this->assertArrayHasKey(0, $filters); $this->assertEquals( diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php index f84cd5ba08259..53425bd42fbd9 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php @@ -82,7 +82,10 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'attribute_data' => ['is_filterable' => 0], 'expectation' => [], ], - 'used_in_navigation_with_results' => [ + + /* @TODO: Should be uncommented in MC-16650 */ + + /*'used_in_navigation_with_results' => [ 'products_data' => [ 'simple1000' => 10.00, 'simple1001' => 20.00, @@ -100,7 +103,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'count' => 1, ], ], - ], + ],*/ ]; } } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php index a4d8a64add4b7..325bf1a3d50dc 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php @@ -50,12 +50,15 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'attribute_data' => ['is_filterable' => 0, 'is_filterable_in_search' => 0], ], - 'used_in_navigation_with_results' => [ + + /* @TODO: Should be uncommented in MC-16650 */ + + /*'used_in_navigation_with_results' => [ 'attribute_data' => [ 'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, 'is_filterable_in_search' => 1, ], - ], + ],*/ ] ); From cd544f4e59f744a199db07754f1e3b415b135354 Mon Sep 17 00:00:00 2001 From: Marcus Pettersen Irgens <marcus.irgens@visma.com> Date: Thu, 2 Apr 2020 14:21:17 +0200 Subject: [PATCH 650/734] Fix type hint for Select::where --- lib/internal/Magento/Framework/DB/Select.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php index ceeed40787123..075aa6b24faa7 100644 --- a/lib/internal/Magento/Framework/DB/Select.php +++ b/lib/internal/Magento/Framework/DB/Select.php @@ -91,6 +91,12 @@ public function __construct( * $select->where('id = :id'); * </code> * + * You may also construct IN statements: + * + * <code> + * $select->where('entity_id IN (?)', ['1', '2', '3']); + * </code> + * * Note that it is more correct to use named bindings in your * queries for values other than strings. When you use named * bindings, don't forget to pass the values when actually @@ -101,7 +107,7 @@ public function __construct( * </code> * * @param string $cond The WHERE condition. - * @param string $value OPTIONAL A single value to quote into the condition. + * @param string|array|null $value OPTIONAL An optional single or array value to quote into the condition. * @param string|int|null $type OPTIONAL The type of the given value * @return \Magento\Framework\DB\Select */ From 12ecf8a656837a0aa6c457250a49e8935243243a Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 2 Apr 2020 15:22:04 +0300 Subject: [PATCH 651/734] MC-32782: [2.4] Multishipping with virtual product in order leads to empty order creation --- .../web/js/action/select-payment-method-mixin.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js index 50d54d4e59789..0b8eba270b030 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js @@ -7,10 +7,10 @@ define([ 'mage/utils/wrapper', 'Magento_Checkout/js/model/quote', 'Magento_SalesRule/js/model/payment/discount-messages', - 'Magento_Checkout/js/action/set-payment-information', + 'Magento_Checkout/js/action/set-payment-information-extended', 'Magento_Checkout/js/action/get-totals', 'Magento_SalesRule/js/model/coupon' -], function ($, wrapper, quote, messageContainer, setPaymentInformationAction, getTotalsAction, coupon) { +], function ($, wrapper, quote, messageContainer, setPaymentInformationExtended, getTotalsAction, coupon) { 'use strict'; return function (selectPaymentMethodAction) { @@ -20,11 +20,12 @@ define([ originalSelectPaymentMethodAction(paymentMethod); $.when( - setPaymentInformationAction( + setPaymentInformationExtended( messageContainer, { method: paymentMethod.method - } + }, + true ) ).done( function () { From 96d56b29047990417ae2a487e8cc8866790a9d27 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 2 Apr 2020 17:21:24 +0200 Subject: [PATCH 652/734] Fix issue with loading mask still visible before choosing Shipping Methods --- .../Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml index b85dc21e0b1b5..aca3ee0fdfef6 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml @@ -14,7 +14,7 @@ </annotations> <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> - <waitForPageLoad stepKey="waitForJavascriptToFinish"/> + <waitForLoadingMaskToDisappear stepKey="waitForJavascriptToFinish"/> <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods"/> <waitForElementVisible selector="{{AdminOrderFormPaymentSection.flatRateOption}}" stepKey="waitForShippingOptions"/> <selectOption selector="{{AdminOrderFormPaymentSection.flatRateOption}}" userInput="flatrate_flatrate" stepKey="checkFlatRate"/> From 86341dba9f78af92691d376bacc3c4fd4c2f1d20 Mon Sep 17 00:00:00 2001 From: Leonid Poluianov <poluyano@addobe.com> Date: Thu, 2 Apr 2020 12:58:47 -0500 Subject: [PATCH 653/734] MC-32792: Get Categories server side API operation to improve in 2.4 --- .../Catalog/Model/ResourceModel/Category/Collection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index 8599d827a44ba..4711828d8f78d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -90,6 +90,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param Visibility|null $catalogProductVisibility * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -507,6 +508,8 @@ public function getProductTable() } /** + * Get products count using catalog_category_entity table + * * @param Category $item * @param string $websiteId * @return int @@ -547,6 +550,7 @@ private function getProductsCountFromCategoryTable(Category $item, string $websi * Get query for retrieve count of products per category * * @param array $categoryIds + * @param bool $addVisibilityFilter * @return Select */ private function getProductsCountQuery(array $categoryIds, $addVisibilityFilter = true): Select From 1ab4ddfbe64d6d88f56fa0ed47e3c7a048013959 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 18:15:05 -0500 Subject: [PATCH 654/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/706 --- ...inCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml | 5 +++-- ...minCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index 5bb2bb898f8c1..8f4ab2b4d2f96 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -11,6 +11,7 @@ <test name="AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest"> <annotations> <features value="Cms"/> + <testCaseId value="MC-14657" /> <stories value="Create a CMS block via the Admin, disable, add to category, verify on frontend"/> <title value="Create disabled CMS block entity and assign to category"/> <severity value="MAJOR"/> @@ -32,7 +33,7 @@ <argument name="block_id" value="$$newDefaultBlock.id$$"/> </actionGroup> <actionGroup ref="AdminSetCMSBlockDisabledActionGroup" stepKey="disableBlock"/> - <actionGroup ref="saveCMSBlock" stepKey="saveCMSBlock"/> + <actionGroup ref="SaveCMSBlockActionGroup" stepKey="saveCMSBlock"/> <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> <actionGroup ref="AdminCategoriesExpandAllActionGroup" stepKey="expandAll"/> @@ -51,4 +52,4 @@ <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml index 6ee3f2094f190..b7f3140a79e7d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -13,6 +13,7 @@ <features value="Cms"/> <stories value="Create a CMS block via the Admin, add to category, verify on frontend"/> <title value="Create CMS block entity and assign to category"/> + <testCaseId value="MC-14658"/> <severity value="MAJOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> @@ -45,4 +46,4 @@ <argument name="text" value="{{_defaultBlock.content}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> From 3ff76c4fdc0f445f691812412f0f2383f0e3d5d4 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 18:47:00 -0500 Subject: [PATCH 655/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/707 --- .../AdminCMSPageSetDisabledActionGroup.xml | 14 -------------- .../Test/AdminCmsPageUpdateAndDisableTest.xml | 10 ++++++---- .../Test/Mftf/Test/AdminCmsPageUpdateTest.xml | 16 +++++++++------- ...ringProductImportWithConfigTurnedOffTest.xml} | 2 +- 4 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml rename app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/{AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml => AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml} (99%) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml deleted file mode 100644 index 76189d589bd04..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageSetDisabledActionGroup.xml +++ /dev/null @@ -1,14 +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="AdminCMSPageSetDisabled"> - <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> - <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageDisabled"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml index 5f9f6691f6e72..24d61311e8951 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml @@ -11,22 +11,24 @@ <test name="AdminCmsPageUpdateAndDisableTest"> <annotations> <features value="Cms"/> + <testCaseId value="MC-14673" /> <title value="Update CMS Page via the Admin, disable"/> <description value="Admin should be able to update a CMS Page"/> <group value="backend"/> <group value="cMSContent"/> <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> </before> <after> <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Navigate to Page in Admin--> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$existingCMSPage$$"/> </actionGroup> <!--Deactivate page--> @@ -34,7 +36,7 @@ <!--Fill data using _duplicatedCMSPage--> <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> <!--Save page--> - <actionGroup ref="saveCmsPage" stepKey="saveDisabledPage"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveDisabledPage"/> <!--Check that page is not found on storefront--> <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToCMSPageOnStorefront"> <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml index 8271d4c66b18b..6e05828726e7d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml @@ -13,35 +13,37 @@ <features value="Cms"/> <title value="Update CMS Page via the Admin"/> <description value="Admin should be able to update a CMS Page"/> + <testCaseId value="MC-14674"/> <group value="backend"/> <group value="cMSContent"/> <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> </before> <after> <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Navigate to Page in Admin--> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$existingCMSPage$$"/> </actionGroup> <!--Fill data using _duplicatedCMSPage--> <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> - <actionGroup ref="saveCmsPage" stepKey="saveActivatedPage"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveActivatedPage"/> <!--Verify data in admin--> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToUpdatedCMSPage"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToUpdatedCMSPage"> <argument name="CMSPage" value="_duplicatedCMSPage"/> </actionGroup> - <actionGroup ref="AssertCMSPageContent" stepKey="verifyPageDataInAdmin"/> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="verifyPageDataInAdmin"/> <!--Verify data on frontend--> <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStorefront"> <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> </actionGroup> - <actionGroup ref="AssertStoreFrontCMSPage" stepKey="verifyPageDataOnFrontend"> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="verifyPageDataOnFrontend"> <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml similarity index 99% rename from app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml index a9a5cf4a05b66..0fadd38957a69 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.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="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOffTest"> + <test name="AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest"> <annotations> <features value="Url Rewrite"/> <stories value="Url Rewrites for Multiple Storeviews"/> From d423667d29f03f6d836f3ee8540db2250d8bae81 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 19:28:55 -0500 Subject: [PATCH 656/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/710 --- .../Test/AdminCustomersDeleteSystemCustomerGroupTest.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml index 604124f2fcbd9..124f086044f76 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml @@ -12,14 +12,16 @@ <features value="Customer"/> <title value="System Customer Groups"/> <description value="Admin should not be able to delete system customer groups"/> + <stories value="Delete System Customer Group" /> + <testCaseId value="MC-14588" /> <group value="customers"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Verify Not Logged In customer group--> From f189f886e997f6b1983f1d829373d8c324063a1b Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Mon, 23 Mar 2020 11:46:59 -0500 Subject: [PATCH 657/734] MC-32574: Recently Compared widget still visible after logout (Persistant enabled) --- .../Customer/CustomerData/SectionConfigConverter.php | 10 +--------- .../Unit/CustomerData/SectionConfigConverterTest.php | 6 +++--- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php b/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php index c9a93c708e348..51a063b5562bb 100644 --- a/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php +++ b/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php @@ -24,15 +24,7 @@ public function convert($source) foreach ($source->getElementsByTagName('action') as $action) { $actionName = strtolower($action->getAttribute('name')); foreach ($action->getElementsByTagName('section') as $section) { - $sectionName = strtolower($section->getAttribute('name')); - - if ($sectionName === self::INVALIDATE_ALL_SECTIONS_MARKER) { - $sections[$actionName] = []; - $sections[$actionName][] = self::INVALIDATE_ALL_SECTIONS_MARKER; - break; - } else { - $sections[$actionName][] = $sectionName; - } + $sections[$actionName][] = strtolower($section->getAttribute('name')); } if (!isset($sections[$actionName])) { $sections[$actionName][] = self::INVALIDATE_ALL_SECTIONS_MARKER; diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php index b78aa8609607e..44401ca5eb7bf 100644 --- a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php @@ -75,12 +75,12 @@ public function testConvert() 'sections' => [ 'sales/guest/reorder' => ['account'], 'sales/order/reorder' => ['account', 'cart'], - 'stores/store/switch' => ['*'], + 'stores/store/switch' => ['account', '*', 'cart'], 'directory/currency/switch' => ['*'], 'customer/account/logout' => ['account', 'cart'], 'customer/account/editpost' => ['account', 'acc', 'cart'], - 'checkout/cart/delete' => ['*'], - 'customer/account/createpost' => ['*'], + 'checkout/cart/delete' => ['account', 'acc', 'cart', '*'], + 'customer/account/createpost' => ['account','*'], 'catalog/product_compare/add' => ['*'], 'catalog/product_compare/remove' => ['account', 'acc'], 'catalog/product_compare/clear' => ['*'], From 090b9c7a8d3a53dfd668254ca0ce5ddbcaf8d2fc Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 13 Jun 2019 11:07:27 +0300 Subject: [PATCH 658/734] Convert SecureChangingCustomerEmailTest to MFTF --- ...merChangeEmailWithIncorrectActionGroup.xml | 23 ++++++++ .../Test/SecureChangingCustomerEmailTest.xml | 52 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml new file mode 100644 index 0000000000000..24a388e24eca8 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.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="StoreFrontCustomerChangeEmailWithIncorrectActionGroup"> + <arguments> + <argument name="email" type="string" /> + <argument name="password" type="string" /> + </arguments> + <checkOption selector="{{StorefrontCustomerAccountInformationSection.changeEmail}}" stepKey="clickChangeEmailCheckbox" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.email}}" userInput="{{email}}" stepKey="fillEmail" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="11test1" stepKey="fillWrongPassword" /> + <click selector="{{StorefrontCustomerAccountInformationSection.saveButton}}" stepKey="saveChange" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + <see userInput="The password doesn't match this account. Verify the password and try again." stepKey="SeeTheIncorrectNotificationIsDisplayed" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml b/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml new file mode 100644 index 0000000000000..aae9fa8de3494 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml @@ -0,0 +1,52 @@ +<?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="SecureChangingCustomerEmailTest"> + <annotations> + <features value="Security" /> + <stories value="Changing Customer's email" /> + <title value="Changing Customer Email Test" /> + <description value="Changing Customer's email with correct and wrong passwords" /> + <group value="security" /> + <group value="mtf_migrated" /> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer" /> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$" /> + </actionGroup> + <!-- Navigate to "Account Information" tab First Time--> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <!-- Checking Email checkbox, entering new email, saving with correct password --> + <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailCorrectAttempt"> + <argument name="email" value="$$customer.email$$" /> + <argument name="password" value="$$customer.password$$" /> + </actionGroup> + <!-- Navigate to "Account Information" tab Second Time--> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageSecondTime" /> + <!-- Checking Email checkbox, entering new email, saving with incorrect password --> + <actionGroup ref="StoreFrontCustomerChangeEmailWithIncorrectActionGroup" stepKey="changeEmailWrongAttempt"> + <argument name="email" value="$$customer.email$$" /> + <argument name="password" value="{incorrect.password}" /> + </actionGroup> + <!-- END TEST BODY --> + + </test> + </tests> From 4249d2e886d1113c049407c117465d2fedf3d8e3 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Tue, 30 Jul 2019 18:09:21 +0300 Subject: [PATCH 659/734] refactoring --- ...merChangeEmailWithIncorrectActionGroup.xml | 23 ---------- .../Test/SecureChangingCustomerEmailTest.xml | 42 ++++++++++++------- .../SecureChangingCustomerEmailTest.xml | 2 + 3 files changed, 28 insertions(+), 39 deletions(-) delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml deleted file mode 100644 index 24a388e24eca8..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontCustomerChangeEmailWithIncorrectActionGroup.xml +++ /dev/null @@ -1,23 +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="StoreFrontCustomerChangeEmailWithIncorrectActionGroup"> - <arguments> - <argument name="email" type="string" /> - <argument name="password" type="string" /> - </arguments> - <checkOption selector="{{StorefrontCustomerAccountInformationSection.changeEmail}}" stepKey="clickChangeEmailCheckbox" /> - <fillField selector="{{StorefrontCustomerAccountInformationSection.email}}" userInput="{{email}}" stepKey="fillEmail" /> - <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="11test1" stepKey="fillWrongPassword" /> - <click selector="{{StorefrontCustomerAccountInformationSection.saveButton}}" stepKey="saveChange" /> - <waitForPageLoad stepKey="waitForPageLoaded" /> - <see userInput="The password doesn't match this account. Verify the password and try again." stepKey="SeeTheIncorrectNotificationIsDisplayed" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml b/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml index aae9fa8de3494..e0069445007c2 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml @@ -10,42 +10,52 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="SecureChangingCustomerEmailTest"> <annotations> - <features value="Security" /> - <stories value="Changing Customer's email" /> - <title value="Changing Customer Email Test" /> - <description value="Changing Customer's email with correct and wrong passwords" /> - <group value="security" /> - <group value="mtf_migrated" /> + <features value="Security"/> + <stories value="Changing Customer's email"/> + <title value="Changing Customer Email Test"/> + <description value="Changing Customer's email with correct and wrong passwords"/> + <group value="security"/> + <group value="mtf_migrated"/> </annotations> <before> - <createData entity="Simple_US_Customer" stepKey="customer" /> + <createData entity="Simple_US_Customer" stepKey="customer"/> </before> <after> - <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> </after> <!-- TEST BODY --> <!-- Go to storefront home page --> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> <!-- Login as created customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$" /> + <argument name="Customer" value="$$customer$$"/> </actionGroup> <!-- Navigate to "Account Information" tab First Time--> - <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime"/> <!-- Checking Email checkbox, entering new email, saving with correct password --> <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailCorrectAttempt"> - <argument name="email" value="$$customer.email$$" /> - <argument name="password" value="$$customer.password$$" /> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="$$customer.password$$"/> + </actionGroup> + <!-- See Success Notify--> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="You saved the account information."/> </actionGroup> <!-- Navigate to "Account Information" tab Second Time--> <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageSecondTime" /> <!-- Checking Email checkbox, entering new email, saving with incorrect password --> - <actionGroup ref="StoreFrontCustomerChangeEmailWithIncorrectActionGroup" stepKey="changeEmailWrongAttempt"> - <argument name="email" value="$$customer.email$$" /> - <argument name="password" value="{incorrect.password}" /> + <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailWrongAttempt"> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="{incorrect.password}"/> + </actionGroup> + <!-- See Failure Message--> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="seeFailureMessage"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again."/> + <argument name="messageType" value="error"/> </actionGroup> + <!-- END TEST BODY --> </test> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml index bd8a417d48bf3..b513e68e2d1a9 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml @@ -14,6 +14,7 @@ <data name="customer/data/current_password" xsi:type="string">123123^q</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Security\Test\Constraint\AssertCustomerEmailChanged" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingEmailV2" summary="Change Email checkbox is checked, enter new email, wrong password"> <data name="tag" xsi:type="string">severity:S2</data> @@ -21,6 +22,7 @@ <data name="customer/data/email" xsi:type="string">JaneDoe_%isolation%@example.com</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From d6cdfeaab92327e39502e7ec59c0703e7b1cb278 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 7 Aug 2019 14:17:44 +0300 Subject: [PATCH 660/734] refactoring --- .../Test/TestCase/SecureChangingCustomerEmailTest.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml index b513e68e2d1a9..8c7f35add2a8d 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml @@ -8,21 +8,19 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\SecureChangingCustomerEmailTest" summary="Secure Changing Customer Email" ticketId="MAGETWO-49041"> <variation name="SecureChangingEmailV1" summary="Change Email checkbox is checked, enter new email, correct password"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/email" xsi:type="string">JaneDoe_%isolation%@example.com</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Security\Test\Constraint\AssertCustomerEmailChanged" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingEmailV2" summary="Change Email checkbox is checked, enter new email, wrong password"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/email" xsi:type="string">JaneDoe_%isolation%@example.com</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 7f11fe722e841ddb187cd9742a23744fd06c930e Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 20:41:19 -0500 Subject: [PATCH 661/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/712 --- ...est.xml => StorefrontSecureChangingCustomerEmailTest.xml} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename app/code/Magento/Security/Test/Mftf/Test/{SecureChangingCustomerEmailTest.xml => StorefrontSecureChangingCustomerEmailTest.xml} (94%) diff --git a/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml similarity index 94% rename from app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml index e0069445007c2..020e1965ddf71 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/SecureChangingCustomerEmailTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml @@ -8,12 +8,13 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="SecureChangingCustomerEmailTest"> + <test name="StorefrontSecureChangingCustomerEmailTest"> <annotations> <features value="Security"/> <stories value="Changing Customer's email"/> <title value="Changing Customer Email Test"/> <description value="Changing Customer's email with correct and wrong passwords"/> + <testCaseId value="MC-14385"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> @@ -48,7 +49,7 @@ <!-- Checking Email checkbox, entering new email, saving with incorrect password --> <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailWrongAttempt"> <argument name="email" value="$$customer.email$$"/> - <argument name="password" value="{incorrect.password}"/> + <argument name="password" value="WRONG_PASSWORD_123123q"/> </actionGroup> <!-- See Failure Message--> <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="seeFailureMessage"> From 4fb0a8e9bf63654eb926d3338d6fd4e3cd4b2982 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 14 Jun 2019 17:04:59 +0300 Subject: [PATCH 662/734] SecureChangingCustomerEmailTest --- ...frontCustomerChangePasswordActionGroup.xml | 25 +++++++++ ...stomerDontSeePasswordFieldsActionGroup.xml | 16 ++++++ ...StorefrontChangingCustomerPasswordTest.xml | 55 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml new file mode 100644 index 0000000000000..8742e8546063a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerChangePasswordActionGroup"> + <arguments> + <argument name="password" type="string" /> + </arguments> + + <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> + <waitForPageLoad stepKey="waitForAccountInfoTabOpened" /> + <checkOption selector="{{StorefrontCustomerAccountInformationSection.changePassword}}" stepKey="clickChangePasswordlCheckbox" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="{{password}}" stepKey="fillCurrentPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" userInput="{{password}}" stepKey="fillNewPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" userInput="{{password}}" stepKey="confirmNewPassword" /> + <click selector="{{StorefrontCustomerAccountInformationSection.saveButton}}" stepKey="saveChange" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml new file mode 100644 index 0000000000000..409f664c479da --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml @@ -0,0 +1,16 @@ +<?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="StorefrontCustomerDontSeePasswordFieldsActionGroup"> + <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" /> + <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" /> + <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml new file mode 100644 index 0000000000000..b4ee16510abc8 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml @@ -0,0 +1,55 @@ +<?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="StorefrontChangingCustomerPasswordTest"> + <annotations> + <features value="Security" /> + <stories value="Changing Customer's Password" /> + <title value="Changing Customer Password Test" /> + <description value="Changing Customer's password using correct and wrong passwords" /> + <group value="security" /> + <group value="mtf_migrated" /> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer" /> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$" /> + </actionGroup> + <!-- Navigate to "Account Information" tab --> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <!-- Change Password with Correct Data --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithCorrectData"> + <argument name="password" value="$$customer.password$$" /> + </actionGroup> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess" /> + <!-- Change Password with Incorrect Data --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> + <argument name="password" value="{{Colorado_US_Customer.password}}" /> + </actionGroup> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + <!-- Change Password With Unchecked Checkbox --> + <actionGroup ref="StorefrontCustomerDontSeePasswordFieldsActionGroup" stepKey="uncheckedCheckbox" /> + <!-- END TEST BODY --> + + </test> +</tests> From 9f55ad0313dd2b00535c772aeb59bf0e908ef8ec Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Tue, 30 Jul 2019 22:11:08 +0300 Subject: [PATCH 663/734] refactoring --- ...PasswordFieldsNotAvailableActionGroup.xml} | 8 +-- ...frontCustomerChangePasswordActionGroup.xml | 2 - ...eToAccountInformationChangeActionGroup.xml | 17 +++++++ ...tAccountPasswordFieldsNotAvailableTest.xml | 42 ++++++++++++++++ ...StorefrontChangingCustomerPasswordTest.xml | 39 ++++++--------- ...gCustomerPasswordWithIncorrectDataTest.xml | 49 +++++++++++++++++++ .../SecureChangingCustomerPasswordTest.xml | 3 ++ 7 files changed, 130 insertions(+), 30 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{StorefrontCustomerDontSeePasswordFieldsActionGroup.xml => StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml} (81%) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml similarity index 81% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml index 409f664c479da..1955e05001f7b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml @@ -8,9 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCustomerDontSeePasswordFieldsActionGroup"> - <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" /> - <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" /> - <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" /> + <actionGroup name="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup"> + <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> + <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml index 8742e8546063a..802b75213825b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml @@ -13,8 +13,6 @@ <argument name="password" type="string" /> </arguments> - <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> - <waitForPageLoad stepKey="waitForAccountInfoTabOpened" /> <checkOption selector="{{StorefrontCustomerAccountInformationSection.changePassword}}" stepKey="clickChangePasswordlCheckbox" /> <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="{{password}}" stepKey="fillCurrentPassword" /> <fillField selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" userInput="{{password}}" stepKey="fillNewPassword" /> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml new file mode 100644 index 0000000000000..68ab140ff0899 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml @@ -0,0 +1,17 @@ +<?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="StorefrontNavigateToAccountInformationChangeActionGroup"> + <amOnPage url="{{StorefrontCustomerEditPage.url}}" stepKey="goToCustomerEditPage"/> + <waitForPageLoad stepKey="waitForEditPage"/> + <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> + <waitForPageLoad stepKey="waitForAccountInfoTabOpened"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml new file mode 100644 index 0000000000000..fff3c84f4e457 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -0,0 +1,42 @@ +<?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="StorefrontAccountPasswordFieldsNotAvailableTest"> + <annotations> + <features value="Security"/> + <stories value="Password Fields not Available on Frontend Until Checkbox Change Password Unchecked"/> + <title value="Password Fields not Available"/> + <description value="User Cannot Change Password Until Checkbox Change Password Unchecked"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Assert Account Password Fields not Available --> + <actionGroup ref="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml index b4ee16510abc8..737c51d9703df 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml @@ -10,45 +10,36 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontChangingCustomerPasswordTest"> <annotations> - <features value="Security" /> - <stories value="Changing Customer's Password" /> - <title value="Changing Customer Password Test" /> - <description value="Changing Customer's password using correct and wrong passwords" /> - <group value="security" /> - <group value="mtf_migrated" /> + <features value="Security"/> + <stories value="Changing Customer's Password"/> + <title value="Changing Customer Password Test"/> + <description value="Changing Customer's password using correct value"/> + <group value="security"/> + <group value="mtf_migrated"/> </annotations> <before> - <createData entity="Simple_US_Customer" stepKey="customer" /> + <createData entity="Simple_US_Customer" stepKey="customer"/> </before> <after> - <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> </after> <!-- TEST BODY --> <!-- Go to storefront home page --> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> <!-- Login as created customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$" /> + <argument name="Customer" value="$$customer$$"/> </actionGroup> - <!-- Navigate to "Account Information" tab --> - <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> <!-- Change Password with Correct Data --> <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithCorrectData"> - <argument name="password" value="$$customer.password$$" /> + <argument name="password" value="$$customer.password$$"/> </actionGroup> - <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess" /> - <!-- Change Password with Incorrect Data --> - <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> - <argument name="password" value="{{Colorado_US_Customer.password}}" /> - </actionGroup> - <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> - <argument name="message" value="The password doesn't match this account. Verify the password and try again." /> - <argument name="messageType" value="error" /> - </actionGroup> - <!-- Change Password With Unchecked Checkbox --> - <actionGroup ref="StorefrontCustomerDontSeePasswordFieldsActionGroup" stepKey="uncheckedCheckbox" /> + <!-- See Success Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess"/> <!-- END TEST BODY --> </test> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml new file mode 100644 index 0000000000000..2127e19d412dc --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml @@ -0,0 +1,49 @@ +<?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="StorefrontChangingCustomerPasswordTestWithIncorrectDataTest"> + <annotations> + <features value="Security"/> + <stories value="Changing Customer's Password With Incorrect Current Password"/> + <title value="Changing Customer Password Test with Incorrect Data"/> + <description value="Changing Customer's password using Incorrect Current Password"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Change Password with InCorrect Current Password --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> + <argument name="password" value="{{Colorado_US_Customer.password}}"/> + </actionGroup> + <!-- See Error Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again."/> + <argument name="messageType" value="error"/> + </actionGroup> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml index da5e312cf0bc9..9b1a7551f6fd9 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml @@ -16,6 +16,7 @@ <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordChanged" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV2" summary="Change Password checkbox is checked, enter wrong password"> <data name="tag" xsi:type="string">severity:S2</data> @@ -25,12 +26,14 @@ <data name="customer/data/password_confirmation" xsi:type="string">123123Qa</data> <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV3" summary="Change Password checkbox is unchecked"> <data name="tag" xsi:type="string">severity:S3</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="check" xsi:type="string">0</data> <constraint name="Magento\Security\Test\Constraint\AssertDefaultAccountInformation" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From d173efd94ed717e799fc6725132af167d208897f Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 7 Aug 2019 14:48:54 +0300 Subject: [PATCH 664/734] renaming --- ...rontAccountPasswordFieldsNotAvailableActionGroup.xml} | 2 +- .../StorefrontAccountPasswordFieldsNotAvailableTest.xml | 2 +- ...Test.xml => StorefrontChangeCustomerPasswordTest.xml} | 2 +- ...tChangeCustomerPasswordTestWithIncorrectDataTest.xml} | 2 +- .../Test/TestCase/SecureChangingCustomerPasswordTest.xml | 9 +++------ 5 files changed, 7 insertions(+), 10 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml => AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml} (92%) rename app/code/Magento/Security/Test/Mftf/Test/{StorefrontChangingCustomerPasswordTest.xml => StorefrontChangeCustomerPasswordTest.xml} (97%) rename app/code/Magento/Security/Test/Mftf/Test/{StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml => StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml} (96%) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml similarity index 92% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml index 1955e05001f7b..1e828414935da 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup"> + <actionGroup name="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup"> <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml index fff3c84f4e457..563169f56b9fd 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -35,7 +35,7 @@ <!-- Navigate to "Change Account Information" tab --> <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> <!-- Assert Account Password Fields not Available --> - <actionGroup ref="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> + <actionGroup ref="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> <!-- END TEST BODY --> </test> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml similarity index 97% rename from app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml index 737c51d9703df..ebc5772adba23 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontChangingCustomerPasswordTest"> + <test name="StorefrontChangeCustomerPasswordTest"> <annotations> <features value="Security"/> <stories value="Changing Customer's Password"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml similarity index 96% rename from app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml index 2127e19d412dc..99ce6e82a3af4 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontChangingCustomerPasswordTestWithIncorrectDataTest"> + <test name="StorefrontChangeCustomerPasswordTestWithIncorrectDataTest"> <annotations> <features value="Security"/> <stories value="Changing Customer's Password With Incorrect Current Password"/> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml index 9b1a7551f6fd9..1c5807401ca7b 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\SecureChangingCustomerPasswordTest" summary="Secure Changing Customer Password" ticketId="MAGETWO-49043"> <variation name="SecureChangingPasswordV1" summary="Change Password checkbox is checked, enter correct password"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> @@ -16,24 +16,21 @@ <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordChanged" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV2" summary="Change Password checkbox is checked, enter wrong password"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> <data name="customer/data/password_confirmation" xsi:type="string">123123Qa</data> <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV3" summary="Change Password checkbox is unchecked"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="check" xsi:type="string">0</data> <constraint name="Magento\Security\Test\Constraint\AssertDefaultAccountInformation" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 117a04573d8e7816d3cb350c764deab07ac8aabc Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 14 Jun 2019 17:04:59 +0300 Subject: [PATCH 665/734] SecureChangingCustomerEmailTest --- ...frontCustomerChangePasswordActionGroup.xml | 25 +++++++++ ...stomerDontSeePasswordFieldsActionGroup.xml | 16 ++++++ ...StorefrontChangingCustomerPasswordTest.xml | 55 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml new file mode 100644 index 0000000000000..8742e8546063a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerChangePasswordActionGroup"> + <arguments> + <argument name="password" type="string" /> + </arguments> + + <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> + <waitForPageLoad stepKey="waitForAccountInfoTabOpened" /> + <checkOption selector="{{StorefrontCustomerAccountInformationSection.changePassword}}" stepKey="clickChangePasswordlCheckbox" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="{{password}}" stepKey="fillCurrentPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" userInput="{{password}}" stepKey="fillNewPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" userInput="{{password}}" stepKey="confirmNewPassword" /> + <click selector="{{StorefrontCustomerAccountInformationSection.saveButton}}" stepKey="saveChange" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml new file mode 100644 index 0000000000000..409f664c479da --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml @@ -0,0 +1,16 @@ +<?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="StorefrontCustomerDontSeePasswordFieldsActionGroup"> + <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" /> + <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" /> + <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml new file mode 100644 index 0000000000000..b4ee16510abc8 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml @@ -0,0 +1,55 @@ +<?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="StorefrontChangingCustomerPasswordTest"> + <annotations> + <features value="Security" /> + <stories value="Changing Customer's Password" /> + <title value="Changing Customer Password Test" /> + <description value="Changing Customer's password using correct and wrong passwords" /> + <group value="security" /> + <group value="mtf_migrated" /> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer" /> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$" /> + </actionGroup> + <!-- Navigate to "Account Information" tab --> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <!-- Change Password with Correct Data --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithCorrectData"> + <argument name="password" value="$$customer.password$$" /> + </actionGroup> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess" /> + <!-- Change Password with Incorrect Data --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> + <argument name="password" value="{{Colorado_US_Customer.password}}" /> + </actionGroup> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + <!-- Change Password With Unchecked Checkbox --> + <actionGroup ref="StorefrontCustomerDontSeePasswordFieldsActionGroup" stepKey="uncheckedCheckbox" /> + <!-- END TEST BODY --> + + </test> +</tests> From 86074d87e4e794563268b75b324926956cd19608 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Tue, 30 Jul 2019 22:11:08 +0300 Subject: [PATCH 666/734] refactoring --- ...PasswordFieldsNotAvailableActionGroup.xml} | 8 +-- ...frontCustomerChangePasswordActionGroup.xml | 2 - ...eToAccountInformationChangeActionGroup.xml | 17 +++++++ ...tAccountPasswordFieldsNotAvailableTest.xml | 42 ++++++++++++++++ ...StorefrontChangingCustomerPasswordTest.xml | 39 ++++++--------- ...gCustomerPasswordWithIncorrectDataTest.xml | 49 +++++++++++++++++++ .../SecureChangingCustomerPasswordTest.xml | 3 ++ 7 files changed, 130 insertions(+), 30 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{StorefrontCustomerDontSeePasswordFieldsActionGroup.xml => StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml} (81%) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml similarity index 81% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml index 409f664c479da..1955e05001f7b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerDontSeePasswordFieldsActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml @@ -8,9 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCustomerDontSeePasswordFieldsActionGroup"> - <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" /> - <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" /> - <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" /> + <actionGroup name="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup"> + <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> + <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml index 8742e8546063a..802b75213825b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml @@ -13,8 +13,6 @@ <argument name="password" type="string" /> </arguments> - <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> - <waitForPageLoad stepKey="waitForAccountInfoTabOpened" /> <checkOption selector="{{StorefrontCustomerAccountInformationSection.changePassword}}" stepKey="clickChangePasswordlCheckbox" /> <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="{{password}}" stepKey="fillCurrentPassword" /> <fillField selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" userInput="{{password}}" stepKey="fillNewPassword" /> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml new file mode 100644 index 0000000000000..68ab140ff0899 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml @@ -0,0 +1,17 @@ +<?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="StorefrontNavigateToAccountInformationChangeActionGroup"> + <amOnPage url="{{StorefrontCustomerEditPage.url}}" stepKey="goToCustomerEditPage"/> + <waitForPageLoad stepKey="waitForEditPage"/> + <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> + <waitForPageLoad stepKey="waitForAccountInfoTabOpened"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml new file mode 100644 index 0000000000000..fff3c84f4e457 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -0,0 +1,42 @@ +<?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="StorefrontAccountPasswordFieldsNotAvailableTest"> + <annotations> + <features value="Security"/> + <stories value="Password Fields not Available on Frontend Until Checkbox Change Password Unchecked"/> + <title value="Password Fields not Available"/> + <description value="User Cannot Change Password Until Checkbox Change Password Unchecked"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Assert Account Password Fields not Available --> + <actionGroup ref="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml index b4ee16510abc8..737c51d9703df 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml @@ -10,45 +10,36 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontChangingCustomerPasswordTest"> <annotations> - <features value="Security" /> - <stories value="Changing Customer's Password" /> - <title value="Changing Customer Password Test" /> - <description value="Changing Customer's password using correct and wrong passwords" /> - <group value="security" /> - <group value="mtf_migrated" /> + <features value="Security"/> + <stories value="Changing Customer's Password"/> + <title value="Changing Customer Password Test"/> + <description value="Changing Customer's password using correct value"/> + <group value="security"/> + <group value="mtf_migrated"/> </annotations> <before> - <createData entity="Simple_US_Customer" stepKey="customer" /> + <createData entity="Simple_US_Customer" stepKey="customer"/> </before> <after> - <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> </after> <!-- TEST BODY --> <!-- Go to storefront home page --> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage" /> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> <!-- Login as created customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$" /> + <argument name="Customer" value="$$customer$$"/> </actionGroup> - <!-- Navigate to "Account Information" tab --> - <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime" /> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> <!-- Change Password with Correct Data --> <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithCorrectData"> - <argument name="password" value="$$customer.password$$" /> + <argument name="password" value="$$customer.password$$"/> </actionGroup> - <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess" /> - <!-- Change Password with Incorrect Data --> - <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> - <argument name="password" value="{{Colorado_US_Customer.password}}" /> - </actionGroup> - <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> - <argument name="message" value="The password doesn't match this account. Verify the password and try again." /> - <argument name="messageType" value="error" /> - </actionGroup> - <!-- Change Password With Unchecked Checkbox --> - <actionGroup ref="StorefrontCustomerDontSeePasswordFieldsActionGroup" stepKey="uncheckedCheckbox" /> + <!-- See Success Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess"/> <!-- END TEST BODY --> </test> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml new file mode 100644 index 0000000000000..2127e19d412dc --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml @@ -0,0 +1,49 @@ +<?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="StorefrontChangingCustomerPasswordTestWithIncorrectDataTest"> + <annotations> + <features value="Security"/> + <stories value="Changing Customer's Password With Incorrect Current Password"/> + <title value="Changing Customer Password Test with Incorrect Data"/> + <description value="Changing Customer's password using Incorrect Current Password"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Change Password with InCorrect Current Password --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> + <argument name="password" value="{{Colorado_US_Customer.password}}"/> + </actionGroup> + <!-- See Error Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again."/> + <argument name="messageType" value="error"/> + </actionGroup> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml index da5e312cf0bc9..9b1a7551f6fd9 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml @@ -16,6 +16,7 @@ <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordChanged" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV2" summary="Change Password checkbox is checked, enter wrong password"> <data name="tag" xsi:type="string">severity:S2</data> @@ -25,12 +26,14 @@ <data name="customer/data/password_confirmation" xsi:type="string">123123Qa</data> <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV3" summary="Change Password checkbox is unchecked"> <data name="tag" xsi:type="string">severity:S3</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="check" xsi:type="string">0</data> <constraint name="Magento\Security\Test\Constraint\AssertDefaultAccountInformation" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 1ac3e3ec9c5039c2ed65e21f58c913190f1727ad Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Wed, 7 Aug 2019 14:48:54 +0300 Subject: [PATCH 667/734] renaming --- ...rontAccountPasswordFieldsNotAvailableActionGroup.xml} | 2 +- .../StorefrontAccountPasswordFieldsNotAvailableTest.xml | 2 +- ...Test.xml => StorefrontChangeCustomerPasswordTest.xml} | 2 +- ...tChangeCustomerPasswordTestWithIncorrectDataTest.xml} | 2 +- .../Test/TestCase/SecureChangingCustomerPasswordTest.xml | 9 +++------ 5 files changed, 7 insertions(+), 10 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml => AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml} (92%) rename app/code/Magento/Security/Test/Mftf/Test/{StorefrontChangingCustomerPasswordTest.xml => StorefrontChangeCustomerPasswordTest.xml} (97%) rename app/code/Magento/Security/Test/Mftf/Test/{StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml => StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml} (96%) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml similarity index 92% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml index 1955e05001f7b..1e828414935da 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup"> + <actionGroup name="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup"> <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml index fff3c84f4e457..563169f56b9fd 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -35,7 +35,7 @@ <!-- Navigate to "Change Account Information" tab --> <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> <!-- Assert Account Password Fields not Available --> - <actionGroup ref="StorefrontAssertAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> + <actionGroup ref="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> <!-- END TEST BODY --> </test> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml similarity index 97% rename from app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml index 737c51d9703df..ebc5772adba23 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontChangingCustomerPasswordTest"> + <test name="StorefrontChangeCustomerPasswordTest"> <annotations> <features value="Security"/> <stories value="Changing Customer's Password"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml similarity index 96% rename from app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml rename to app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml index 2127e19d412dc..99ce6e82a3af4 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangingCustomerPasswordWithIncorrectDataTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontChangingCustomerPasswordTestWithIncorrectDataTest"> + <test name="StorefrontChangeCustomerPasswordTestWithIncorrectDataTest"> <annotations> <features value="Security"/> <stories value="Changing Customer's Password With Incorrect Current Password"/> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml index 9b1a7551f6fd9..1c5807401ca7b 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\SecureChangingCustomerPasswordTest" summary="Secure Changing Customer Password" ticketId="MAGETWO-49043"> <variation name="SecureChangingPasswordV1" summary="Change Password checkbox is checked, enter correct password"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> @@ -16,24 +16,21 @@ <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerInfoSuccessSavedMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordChanged" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV2" summary="Change Password checkbox is checked, enter wrong password"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> <data name="customer/data/password_confirmation" xsi:type="string">123123Qa</data> <data name="check" xsi:type="string">1</data> <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="SecureChangingPasswordV3" summary="Change Password checkbox is unchecked"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="check" xsi:type="string">0</data> <constraint name="Magento\Security\Test\Constraint\AssertDefaultAccountInformation" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From fa93125a90c4e1a7d760ffa2ecce2fde1348a788 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 20:58:19 -0500 Subject: [PATCH 668/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/713 --- .../Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml | 1 + .../Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml | 1 + ...StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml index 563169f56b9fd..fd7ef0261cd26 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -14,6 +14,7 @@ <stories value="Password Fields not Available on Frontend Until Checkbox Change Password Unchecked"/> <title value="Password Fields not Available"/> <description value="User Cannot Change Password Until Checkbox Change Password Unchecked"/> + <testCaseId value="MC-14369"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml index ebc5772adba23..242bad033ed47 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml @@ -14,6 +14,7 @@ <stories value="Changing Customer's Password"/> <title value="Changing Customer Password Test"/> <description value="Changing Customer's password using correct value"/> + <testCaseId value="MC-14370"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml index 99ce6e82a3af4..f08db36e7e20c 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml @@ -14,6 +14,7 @@ <stories value="Changing Customer's Password With Incorrect Current Password"/> <title value="Changing Customer Password Test with Incorrect Data"/> <description value="Changing Customer's password using Incorrect Current Password"/> + <testCaseId value="MC-14371"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> From a1f84c02d39f8b7ec0f7f0dff65281ea8d4c2d43 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 21:16:46 -0500 Subject: [PATCH 669/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/717 --- .../User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml | 2 +- .../User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml index 19189d86aafcb..668ae550f1b3d 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -14,7 +14,7 @@ <stories value="Create Admin User"/> <title value="Admin user should be able to create active admin user"/> <description value="Admin user should be able to create active admin user"/> - <testCaseId value=""/> + <testCaseId value="MC-33044"/> <severity value="CRITICAL"/> <group value="user"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml index 820c7f922784e..23a30246bd999 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -14,7 +14,7 @@ <stories value="Create Admin User"/> <title value="Admin user should be able to create inactive admin user"/> <description value="Admin user should be able to create inactive admin user"/> - <testCaseId value=""/> + <testCaseId value="MC-33045"/> <severity value="CRITICAL"/> <group value="user"/> <group value="mtf_migrated"/> From 151b595f29ade3f0450d41aac9ce681eb6dad314 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 21:40:11 -0500 Subject: [PATCH 670/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/719 --- .../Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml | 5 +++-- .../Test/TestCase/DeleteTermEntityTest.xml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml index 9b83cde365003..0cc45df4ddf71 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml @@ -15,7 +15,7 @@ <title value="Delete active text checkout agreement"/> <description value="Admin should be able to delete active text checkout agreement"/> <severity value="CRITICAL"/> - <testCaseId value=""/> + <testCaseId value="MC-14663"/> <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> @@ -39,7 +39,8 @@ <actionGroup ref="AdminAssertTermAbsentInGridActionGroup" stepKey="assertTermAbsentInGrid"> <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToTheCart"> + + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="openProductPage"> <argument name="product" value="$$createdProduct$$"/> </actionGroup> <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml index e16d5881e2ee4..b8298ffcee3c9 100644 --- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CheckoutAgreements\Test\TestCase\DeleteTermEntityTest" summary="Delete Terms And Conditions" ticketId="MAGETWO-29687"> <variation name="DeleteTermEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="configData" xsi:type="string">checkout_term_condition</data> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="agreement/dataset" xsi:type="string">term_enabled_text</data> From eaff9ac56affd61d0aaa5d04529ed3a51926610e Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 2 Apr 2020 23:45:14 -0500 Subject: [PATCH 671/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/720 --- .../Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml index 9779bf2df2b3c..f9d60796d0424 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml @@ -15,7 +15,7 @@ <title value="Update disabled HTML checkout agreement"/> <description value="Admin should be able to update disabled HTML checkout agreement"/> <severity value="CRITICAL"/> - <testCaseId value=""/> + <testCaseId value="MC-14664"/> <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml index 30bdff5c8b24f..198a9fe3fc7b4 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml @@ -15,7 +15,7 @@ <title value="Update disabled Text checkout agreement"/> <description value="Admin should be able to update disabled Text checkout agreement"/> <severity value="CRITICAL"/> - <testCaseId value=""/> + <testCaseId value="MC-14665"/> <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml index bd1ccbc5ae29a..f82840bc07c7d 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml @@ -15,7 +15,7 @@ <title value="Update enabled Text checkout agreement"/> <description value="Admin should be able to update enabled Text checkout agreement"/> <severity value="CRITICAL"/> - <testCaseId value=""/> + <testCaseId value="MC-14666"/> <group value="checkoutAgreements"/> <group value="mtf_migrated"/> </annotations> From 58f9a5602fa7aa8be65b6f51d06ec6a5d555923a Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 3 Apr 2020 00:14:31 -0500 Subject: [PATCH 672/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/723 --- .../User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml | 4 ++-- .../Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml index a9cb1f3bcf919..a3f1d864a5723 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml @@ -14,15 +14,15 @@ <stories value="Delete Admin User"/> <title value="Admin user is able to delete a user account"/> <description value="Admin user is able to delete a user account"/> + <testCaseId value="MC-14270"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> <before> <!--Create New Admin User--> - <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> <createData entity="NewAdminUser" stepKey="user"> - <field key="current_password">{$adminPassword}</field> + <field key="current_password">{{DefaultAdminUser.password}}</field> </createData> <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml index 5b928a0e6dc9a..3998a4513c6df 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml @@ -14,6 +14,7 @@ <stories value="Delete Admin User"/> <title value="Admin user is not able to delete the own account"/> <description value="Admin user is not able to delete the own account"/> + <testCaseId value="MC-14271"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> From 83eb5f04b5c98e9e11521faf707eddac84f8f02f Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 3 Apr 2020 00:27:35 -0500 Subject: [PATCH 673/734] Merge branch 'issue-308-create-admin-user-role' of https://github.com/kate-kyzyma/magento-functional-tests-migration into 2.4-develop-community --- .../User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml index c50728bfda024..dbb0497ed8511 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -14,6 +14,7 @@ <stories value="Create User Role"/> <title value="Creating a new role with different data sets"/> <description value="Creating a new role with different data sets"/> + <testCaseId value="MC-14258"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> From b08c61d28875e03fbdd6f614ec6518c3afc48b38 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 3 Apr 2020 00:31:59 -0500 Subject: [PATCH 674/734] Deliver https://github.com/magento/magento-functional-tests-migration/pull/724 --- .../Test/Mftf/Test/TrackingScriptTest.xml | 2 +- ...dminSystemNotificationNavigateMenuTest.xml | 2 +- ...AdminAdvancedReportingNavigateMenuTest.xml | 2 +- ...minAttributeTextSwatchesCanBeFiledTest.xml | 2 +- ...eAndDeveloperConfigInDeveloperModeTest.xml | 2 +- ...AndDeveloperConfigInProductionModeTest.xml | 2 +- .../AdminContentScheduleNavigateMenuTest.xml | 2 +- .../Test/AdminDashboardNavigateMenuTest.xml | 2 +- .../Test/AdminDashboardWithChartsChart.xml | 2 +- .../Mftf/Test/AdminExpireAdminSessionTest.xml | 2 +- .../Test/AdminExpireCustomerSessionTest.xml | 2 +- .../AdminLoginAfterChangeCookieDomainTest.xml | 2 +- .../AdminLoginAfterJSMinificationTest.xml | 2 +- .../AdminLoginWithRestrictPermissionTest.xml | 6 +- .../AdminMenuNavigationWithSecretKeysTest.xml | 2 +- .../Test/Mftf/Test/AdminPrivacyPolicyTest.xml | 2 +- .../AdminStoresAllStoresNavigateMenuTest.xml | 2 +- ...minStoresConfigurationNavigateMenuTest.xml | 2 +- ...nSystemCacheManagementNavigateMenuTest.xml | 2 +- .../AdminUserLoginWithStoreCodeInUrlTest.xml | 2 +- ...eAnAdminOrderUsingBraintreePaymentTest.xml | 4 +- ...linePaymentIncludingTaxAndDiscountTest.xml | 2 +- .../Mftf/Test/AdminAddBundleItemsTest.xml | 2 +- ...undleProductToCartFromWishListPageTest.xml | 2 +- .../AdminAddDefaultImageBundleProductTest.xml | 2 +- ...inAssociateBundleProductToWebsitesTest.xml | 2 +- .../Test/AdminAttributeSetSelectionTest.xml | 2 +- .../AdminBasicBundleProductAttributesTest.xml | 2 +- ...ndEditBundleProductOptionsNegativeTest.xml | 2 +- ...CreateAndEditBundleProductSettingsTest.xml | 2 +- .../Test/AdminDeleteABundleProductTest.xml | 2 +- .../AdminDeleteBundleDynamicProductTest.xml | 2 +- .../AdminDeleteBundleFixedProductTest.xml | 2 +- .../AdminEditRelatedBundleProductTest.xml | 2 +- ...inFilterProductListByBundleProductTest.xml | 2 +- .../Test/AdminMassDeleteBundleProducts.xml | 2 +- .../Test/AdminProductBundleCreationTest.xml | 2 +- ...minRemoveDefaultImageBundleProductTest.xml | 2 +- ...sUpdateAttributesForBundleProductsTest.xml | 2 +- .../Test/BundleProductFixedPricingTest.xml | 2 +- .../BundleProductWithTierPriceInCartTest.xml | 2 +- ...urrencyChangingBundleProductInCartTest.xml | 2 +- .../EnableDisableBundleProductStatusTest.xml | 2 +- .../MassEnableDisableBundleProductsTest.xml | 2 +- .../Test/NewBundleProductSelectionTest.xml | 2 +- .../StorefrontAddBundleOptionsToCartTest.xml | 2 +- ...ProductWithZeroPriceToShoppingCartTest.xml | 2 +- .../Mftf/Test/StorefrontAdminEditDataTest.xml | 2 +- .../StorefrontBundleAddToCartSuccessTest.xml | 2 +- .../Mftf/Test/StorefrontBundleCartTest.xml | 2 +- .../StorefrontBundleProductDetailsTest.xml | 2 +- ...eProductShownInCategoryListAndGridTest.xml | 2 +- ...CheckBundleProductOptionTierPricesTest.xml | 2 +- ...tCustomerSelectAndSetBundleOptionsTest.xml | 2 +- .../Test/StorefrontEditBundleProductTest.xml | 2 +- ...ontGoToDetailsPageWhenAddingToCartTest.xml | 2 +- ...oductPricesForCombinationOfOptionsTest.xml | 2 +- .../Test/UpdateBundleProductViaImportTest.xml | 4 +- .../Mftf/Test/AdminLoginWithCaptchaTest.xml | 6 +- .../CaptchaFormsDisplayingTest.xml | 2 +- ...dminCardinalCommerceSettingsHiddenTest.xml | 2 +- .../AddOutOfStockProductToCompareListTest.xml | 2 +- .../Test/Mftf/Test/AddToCartCrossSellTest.xml | 2 +- .../AdminAddDefaultImageSimpleProductTest.xml | 2 +- ...AdminAddDefaultImageVirtualProductTest.xml | 2 +- .../AdminAddDefaultVideoSimpleProductTest.xml | 2 +- .../Test/AdminAddImageForCategoryTest.xml | 2 +- .../AdminAddInStockProductToTheCartTest.xml | 2 +- .../AdminApplyTierPriceToProductTest.xml | 2 +- ...iceToProductWithPercentageDiscountTest.xml | 2 +- ...signProductAttributeToAttributeSetTest.xml | 2 +- ...AdminCatalogCategoriesNavigateMenuTest.xml | 2 +- .../AdminCatalogProductsNavigateMenuTest.xml | 2 +- .../AdminChangeProductAttributeSetTest.xml | 2 +- ...oductPriceWithDisabledChildProductTest.xml | 2 +- ...tomAttributeValuesAfterProductSaveTest.xml | 2 +- ...ubcategoryIsNotVisibleInNavigationTest.xml | 2 +- ...tegoryIsNotVisibleInNavigationMenuTest.xml | 2 +- ...ubcategoryIsNotVisibleInNavigationTest.xml | 2 +- ...MediaRolesForFirstAddedImageViaApiTest.xml | 2 +- ...inCheckNewCategoryLevelAddedViaApiTest.xml | 2 +- ...StockProductIsNotVisibleInCategoryTest.xml | 2 +- ...tOfStockProductIsVisibleInCategoryTest.xml | 2 +- .../AdminCheckPaginationInStorefrontTest.xml | 2 +- ...minCheckProductListPriceAttributesTest.xml | 2 +- ...tegoryIsNotVisibleInNavigationMenuTest.xml | 2 +- .../AdminCloneProductWithDuplicateUrlTest.xml | 2 +- ...inConfigureProductImagePlaceholderTest.xml | 2 +- ...CreateAndEditSimpleProductSettingsTest.xml | 2 +- ...reateAndEditVirtualProductSettingsTest.xml | 2 +- ...CreateSimpleProductSwitchToVirtualTest.xml | 2 +- .../AdminCreateAttributeSetEntityTest.xml | 2 +- ...AdminCreateCategoryFromProductPageTest.xml | 2 +- ...oryFormDisplaySettingsUIValidationTest.xml | 2 +- ...goryLayoutFromConfigurationSettingTest.xml | 2 +- .../AdminCreateCategoryTest.xml | 2 +- ...AdminCreateCategoryWithAnchorFieldTest.xml | 2 +- ...eateCategoryWithCustomRootCategoryTest.xml | 2 +- ...AdminCreateCategoryWithFiveNestingTest.xml | 2 +- ...CreateCategoryWithInactiveCategoryTest.xml | 2 +- ...eCategoryWithInactiveIncludeInMenuTest.xml | 2 +- ...minCreateCategoryWithNoAnchorFieldTest.xml | 2 +- ...eateCategoryWithProductsGridFilterTest.xml | 2 +- ...inCreateCategoryWithRequiredFieldsTest.xml | 2 +- ...mProductAttributeWithDropdownFieldTest.xml | 2 +- ...dminCreateDatetimeProductAttributeTest.xml | 2 +- ...dminCreateDropdownProductAttributeTest.xml | 2 +- ...ibleInStorefrontAdvancedSearchFormTest.xml | 2 +- .../Test/AdminCreateDuplicateCategoryTest.xml | 2 +- .../Test/AdminCreateDuplicateProductTest.xml | 2 +- ...iveFlatCategoryAndUpdateAsInactiveTest.xml | 2 +- .../AdminCreateInactiveFlatCategoryTest.xml | 2 +- ...inCreateInactiveInMenuFlatCategoryTest.xml | 2 +- ...ibleInStorefrontAdvancedSearchFormTest.xml | 2 +- ...nCreateNewAttributeFromProductPageTest.xml | 2 +- ...AdminCreateNewAttributeFromProductTest.xml | 2 +- ...AdminCreateNewGroupForAttributeSetTest.xml | 2 +- ...ateProductAttributeFromProductPageTest.xml | 2 +- ...eProductAttributeRequiredTextFieldTest.xml | 2 +- ...minCreateProductCustomAttributeSetTest.xml | 2 +- ...AdminCreateProductDuplicateProductTest.xml | 2 +- .../AdminCreateProductDuplicateUrlkeyTest.xml | 2 +- ...CreateRootCategoryAndSubcategoriesTest.xml | 4 +- ...impleProductNotVisibleIndividuallyTest.xml | 2 +- ...ductLayoutFromConfigurationSettingTest.xml | 2 +- ...inCreateSimpleProductNegativePriceTest.xml | 2 +- .../AdminCreateSimpleProductTest.xml | 2 +- .../AdminCreateSimpleProductZeroPriceTest.xml | 2 +- ...untryOfManufactureAttributeSKUMaskTest.xml | 2 +- ...dminCreateSimpleProductWithUnicodeTest.xml | 2 +- ...inCreateTextEditorProductAttributeTest.xml | 2 +- ...alProductFillingRequiredFieldsOnlyTest.xml | 2 +- ...tualProductOutOfStockWithTierPriceTest.xml | 2 +- ...CustomOptionsSuiteAndImportOptionsTest.xml | 2 +- ...roductWithTierPriceForGeneralGroupTest.xml | 2 +- ...nCreateVirtualProductWithTierPriceTest.xml | 2 +- ...teVirtualProductWithoutManageStockTest.xml | 2 +- .../Mftf/Test/AdminDeleteAttributeSetTest.xml | 2 +- ...minDeleteConfigurableChildProductsTest.xml | 2 +- ...wnProductAttributeFromAttributeSetTest.xml | 2 +- .../Test/AdminDeleteProductAttributeTest.xml | 2 +- ...AdminDeleteProductWithCustomOptionTest.xml | 2 +- ...roductsImageInCaseOfMultipleStoresTest.xml | 2 +- ...nDeleteRootCategoryAssignedToStoreTest.xml | 2 +- .../Mftf/Test/AdminDeleteRootCategoryTest.xml | 2 +- .../Test/AdminDeleteRootSubCategoryTest.xml | 2 +- .../Test/AdminDeleteSimpleProductTest.xml | 2 +- .../AdminDeleteSystemProductAttributeTest.xml | 2 +- ...ldProductAttributeFromAttributeSetTest.xml | 2 +- .../Test/AdminDeleteVirtualProductTest.xml | 2 +- ...sableProductOnChangingAttributeSetTest.xml | 2 +- ...lterByNameByStoreViewOnProductGridTest.xml | 2 +- ...CategoryProductsUsingScopeSelectorTest.xml | 2 +- ...dPageNumberAfterSaveAndCloseActionTest.xml | 2 +- ...CustomizableOptionToProductWithSKUTest.xml | 2 +- .../AdminMassChangeProductsStatusTest.xml | 2 +- .../Test/AdminMassProductPriceUpdateTest.xml | 2 +- ...UpdateProductAttributesGlobalScopeTest.xml | 2 +- ...ductAttributesMissingRequiredFieldTest.xml | 2 +- ...oductAttributesStoreViewScopeMysqlTest.xml | 2 +- ...ateProductAttributesStoreViewScopeTest.xml | 2 +- .../Test/AdminMoveAnchoredCategoryTest.xml | 2 +- ...eAnchoredCategoryToDefaultCategoryTest.xml | 2 +- ...minMoveCategoryAndCheckUrlRewritesTest.xml | 2 +- ...CategoryFromParentAnchoredCategoryTest.xml | 2 +- ...oryToAnotherPositionInCategoryTreeTest.xml | 2 +- .../AdminMoveProductBetweenCategoriesTest.xml | 4 +- ...dminNavigateMultipleUpSellProductsTest.xml | 2 +- ...egoryIndexerInUpdateOnScheduleModeTest.xml | 2 +- ...ignedToCategoryWithoutCustomURLKeyTest.xml | 2 +- ...ductGridFilteringByCustomAttributeTest.xml | 2 +- ...roductGridFilteringByDateAttributeTest.xml | 2 +- ...ctImageAssignmentForMultipleStoresTest.xml | 2 +- ...ctStatusAttributeDisabledByDefaultTest.xml | 2 +- ...TypeSwitchingToDownloadableProductTest.xml | 2 +- ...dminRemoveCustomOptionsFromProductTest.xml | 2 +- ...minRemoveDefaultImageSimpleProductTest.xml | 2 +- ...inRemoveDefaultImageVirtualProductTest.xml | 2 +- ...minRemoveDefaultVideoSimpleProductTest.xml | 2 +- .../AdminRemoveImageAffectsAllScopesTest.xml | 2 +- .../Test/AdminRemoveImageFromCategoryTest.xml | 2 +- ...edFieldsHaveRequiredFieldIndicatorTest.xml | 2 +- ...ctedUserAddCategoryFromProductPageTest.xml | 6 +- ...ToAssociateSimpleProductToWebsitesTest.xml | 2 +- .../Test/AdminSimpleProductEditUiTest.xml | 2 +- .../AdminSimpleProductImagesTest.xml | 2 +- .../AdminSimpleProductRemoveImagesTest.xml | 2 +- .../AdminSimpleProductSetEditContentTest.xml | 2 +- .../AdminSimpleSetEditRelatedProductsTest.xml | 2 +- .../Mftf/Test/AdminSortingByWebsitesTest.xml | 2 +- ...dminStoresAttributeSetNavigateMenuTest.xml | 2 +- .../AdminStoresProductNavigateMenuTest.xml | 2 +- ...eForProductOptionsWithoutTierPriceTest.xml | 2 +- ...gnProductAttributeFromAttributeSetTest.xml | 2 +- ...ryAndCheckDefaultUrlKeyOnStoreViewTest.xml | 2 +- ...AdminUpdateCategoryAndMakeInactiveTest.xml | 2 +- ...minUpdateCategoryNameWithStoreViewTest.xml | 2 +- .../AdminUpdateCategoryStoreUrlKeyTest.xml | 2 +- ...nUpdateCategoryUrlKeyWithStoreViewTest.xml | 2 +- ...eCategoryWithInactiveIncludeInMenuTest.xml | 2 +- .../AdminUpdateCategoryWithProductsTest.xml | 2 +- ...inUpdateFlatCategoryAndAddProductsTest.xml | 2 +- ...ateFlatCategoryIncludeInNavigationTest.xml | 2 +- ...dateFlatCategoryNameAndDescriptionTest.xml | 2 +- ...rifyDataOverridingOnStoreViewLevelTest.xml | 2 +- ...rifyDataOverridingOnStoreViewLevelTest.xml | 2 +- ...dminUpdateSimpleProductTieredPriceTest.xml | 2 +- ...RegularPriceInStockDisabledProductTest.xml | 2 +- ...WithRegularPriceInStockEnabledFlatTest.xml | 2 +- ...PriceInStockNotVisibleIndividuallyTest.xml | 2 +- ...arPriceInStockUnassignFromCategoryTest.xml | 2 +- ...ceInStockVisibleInCatalogAndSearchTest.xml | 2 +- ...arPriceInStockVisibleInCatalogOnlyTest.xml | 2 +- ...larPriceInStockVisibleInSearchOnlyTest.xml | 2 +- ...gularPriceInStockWithCustomOptionsTest.xml | 2 +- ...eProductWithRegularPriceOutOfStockTest.xml | 2 +- ...UpdateTopCategoryUrlWithNoRedirectTest.xml | 2 +- ...inUpdateTopCategoryUrlWithRedirectTest.xml | 2 +- .../Mftf/Test/AdminVerifyProductOrderTest.xml | 2 +- ...ceCatalogSearchSimpleProductByNameTest.xml | 2 +- ...eCatalogSearchSimpleProductByPriceTest.xml | 2 +- .../Test/CheckTierPricingOfProductsTest.xml | 2 +- ...bleOptionTextInputLengthValidationHint.xml | 2 +- .../CreateProductAttributeEntityDateTest.xml | 2 +- ...eateProductAttributeEntityDropdownTest.xml | 2 +- ...ibuteEntityDropdownWithSingleQuoteTest.xml | 2 +- ...eProductAttributeEntityMultiSelectTest.xml | 2 +- .../CreateProductAttributeEntityPriceTest.xml | 2 +- ...ateProductAttributeEntityTextFieldTest.xml | 2 +- .../Test/Mftf/Test/DeleteCategoriesTest.xml | 2 +- ...UsedInConfigurableProductAttributeTest.xml | 2 +- ...cheAfterChangingCategoryPageLayoutTest.xml | 2 +- .../Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- ...AttributeWithoutValueInCompareListTest.xml | 2 +- ...vailableAfterEnablingSubCategoriesTest.xml | 2 +- ...ductWithCustomOptionsSecondWebsiteTest.xml | 2 +- .../SimpleProductTwoCustomOptionsTest.xml | 2 +- ...oreFrontRecentlyViewedAtStoreLevelTest.xml | 4 +- ...rontRecentlyViewedAtStoreViewLevelTest.xml | 4 +- ...uctWithSpecialAndTierDiscountPriceTest.xml | 2 +- ...rontCatalogNavigationMenuUIDesktopTest.xml | 2 +- ...goryHighlightedAndProductDisplayedTest.xml | 2 +- ...heckDefaultNumberProductsToDisplayTest.xml | 2 +- ...rontConfigurableOptionsThumbImagesTest.xml | 4 +- ...chorIsVisibleOnViewportOnceClickedTest.xml | 2 +- .../Test/StorefrontFotoramaArrowsTest.xml | 2 +- ...orefrontProductNameWithDoubleQuoteTest.xml | 2 +- ...torefrontProductWithEmptyAttributeTest.xml | 2 +- ...tProductsCompareWithEmptyAttributeTest.xml | 2 +- ...ctCustomOptionsDifferentStoreViewsTest.xml | 2 +- ...ntPurchaseProductWithCustomOptionsTest.xml | 2 +- ...thCustomOptionsWithLongValuesTitleTest.xml | 2 +- ...ceForDifferentTimezonesForWebsitesTest.xml | 2 +- ...IncrementsWorkWithDecimalinventoryTest.xml | 2 +- ...ctAndProductCategoryPartialReindexTest.xml | 2 +- ...ldCategoriesShouldNotIncludeInMenuTest.xml | 2 +- .../Test/AdminExportBundleProductTest.xml | 2 +- ...portGroupedProductWithSpecialPriceTest.xml | 2 +- ...mportConfigurableProductWithImagesTest.xml | 2 +- ...figurableProductsWithCustomOptionsTest.xml | 2 +- ...igurableProductsWithAssignedImagesTest.xml | 2 +- ...ableProductAssignedToCustomWebsiteTest.xml | 2 +- ...rtSimpleProductWithCustomAttributeTest.xml | 2 +- ...eroMaximumQtyAllowedInShoppingCartTest.xml | 2 +- ...tedProductToConfigurableOutOfStockTest.xml | 2 +- .../AdminApplyCatalogRuleByCategoryTest.xml | 2 +- ...nfigurableProductWithSpecialPricesTest.xml | 2 +- ...minCreateCatalogPriceRuleByPercentTest.xml | 2 +- ...teCatalogPriceRuleForCustomerGroupTest.xml | 2 +- ...ateCatalogPriceRuleWithInvalidDataTest.xml | 2 +- ...dminCreateInactiveCatalogPriceRuleTest.xml | 2 +- ...eRuleEntityFromConfigurableProductTest.xml | 2 +- ...ogPriceRuleEntityFromSimpleProductTest.xml | 2 +- .../Test/AdminDeleteCatalogPriceRuleTest.xml | 2 +- ...tributeIsUndefinedCatalogPriceRuleTest.xml | 2 +- ...ketingCatalogPriceRuleNavigateMenuTest.xml | 2 +- ...CatalogPriceRuleByProductAttributeTest.xml | 2 +- ...uleForSimpleAndConfigurableProductTest.xml | 2 +- ...RuleForSimpleProductAndFixedMethodTest.xml | 2 +- ...orSimpleProductForNewCustomerGroupTest.xml | 2 +- ...eForSimpleProductWithCustomOptionsTest.xml | 2 +- ...hipArePersistedUnderLongTermCookieTest.xml | 2 +- .../StorefrontInactiveCatalogRuleTest.xml | 2 +- ...ProductWithAssignedSimpleProducts2Test.xml | 2 +- ...eProductWithAssignedSimpleProductsTest.xml | 2 +- ...ForConfigurableProductWithOptions2Test.xml | 2 +- ...eForConfigurableProductWithOptionsTest.xml | 2 +- .../Test/AdminCreateSearchTermEntityTest.xml | 2 +- ...inMarketingSearchTermsNavigateMenuTest.xml | 2 +- ...dminReportsSearchTermsNavigateMenuTest.xml | 2 +- ...MinimalQueryLengthForCatalogSearchTest.xml | 2 +- ...ickSearchAndAddToCartBundleDynamicTest.xml | 2 +- ...QuickSearchAndAddToCartBundleFixedTest.xml | 2 +- ...uickSearchAndAddToCartConfigurableTest.xml | 2 +- .../QuickSearchAndAddToCartGroupedTest.xml | 2 +- ...ickSearchTwoProductsWithSameWeightTest.xml | 2 +- ...tAdvancedSearchEntitySimpleProductTest.xml | 2 +- ...ontQuickSearchConfigurableChildrenTest.xml | 2 +- .../StorefrontUpdateSearchTermEntityTest.xml | 2 +- ...goryWithRestrictedUrlKeyNotCreatedTest.xml | 2 +- .../AdminRewriteProductWithTwoStoreTest.xml | 4 +- ...minUrlForProductRewrittenCorrectlyTest.xml | 2 +- ...iteStoreLevelUrlKeyOfChildCategoryTest.xml | 2 +- .../CatalogProductListWidgetOperatorsTest.xml | 2 +- .../CatalogProductListWidgetOrderTest.xml | 2 +- ...frontProductGridUIUpdatesOnDesktopTest.xml | 2 +- ...sNotAffectedStartedCheckoutProcessTest.xml | 2 +- .../Test/CheckoutSpecificDestinationsTest.xml | 2 +- ...guringInstantPurchaseFunctionalityTest.xml | 2 +- ...ckoutAsCustomerUsingDefaultAddressTest.xml | 2 +- ...eCheckoutAsCustomerUsingNewAddressTest.xml | 2 +- ...utAsCustomerUsingNonDefaultAddressTest.xml | 2 +- .../OnePageCheckoutUsingSignInLinkTest.xml | 2 +- ...OnePageCheckoutWithAllProductTypesTest.xml | 2 +- ...ontCheckCustomerInfoCreatedByGuestTest.xml | 2 +- ...gRecalculationAfterCouponCodeAddedTest.xml | 2 +- ...efrontApplyPromoCodeDuringCheckoutTest.xml | 2 +- ...frontCheckoutDisabledBundleProductTest.xml | 2 +- ...ingAddressAndProductWithTierPricesTest.xml | 2 +- ...ssAndRegisterCustomerAfterCheckoutTest.xml | 2 +- ...ntCheckoutWithSpecialPriceProductsTest.xml | 2 +- ...erCheckoutDisabledProductAndCouponTest.xml | 2 +- ...OnLoginWhenGuestCheckoutIsDisabledTest.xml | 2 +- .../StorefrontCustomerCheckoutTest.xml | 2 +- ...stWithMultipleAddressesAndTaxRatesTest.xml | 2 +- ...egistrationAndDisableGuestCheckoutTest.xml | 2 +- ...frontCustomerCheckoutWithoutRegionTest.xml | 2 +- ...refrontCustomerLoginDuringCheckoutTest.xml | 2 +- ...eBundleProductFromMiniShoppingCartTest.xml | 2 +- ...gurableProductFromMiniShoppingCartTest.xml | 2 +- ...oadableProductFromMiniShoppingCartTest.xml | 2 +- ...aultLimitationFromMiniShoppingCartTest.xml | 2 +- ...VirtualProductFromMiniShoppingCartTest.xml | 2 +- ...eSimpleProductFromMiniShoppingCartTest.xml | 2 +- .../StorefrontGuestCheckoutTest.xml | 2 +- ...tCheckoutUsingFreeShippingAndTaxesTest.xml | 2 +- ...tCheckoutWithCouponAndZeroSubtotalTest.xml | 2 +- ...tOnCheckoutPageDifferentStoreViewsTest.xml | 2 +- ...ngesInBackendAfterCustomerCheckoutTest.xml | 2 +- ...rontRefreshPageDuringGuestCheckoutTest.xml | 2 +- ...efrontUKCustomerCheckoutWithCouponTest.xml | 2 +- ...uctQuantityEqualsToOrderedQuantityTest.xml | 2 +- ...CouponAndBankTransferPaymentMethodTest.xml | 2 +- ...riceInShoppingCartAfterProductSaveTest.xml | 2 +- ...SubtotalOrdersWithProcessingStatusTest.xml | 2 +- .../AdminDeleteActiveTextTermEntityTest.xml | 4 +- .../AdminAddImageToCMSPageTinyMCE3Test.xml | 2 +- .../AdminCMSPageCreateDisabledPageTest.xml | 2 +- ...inCMSPageCreatePageForDefaultStoreTest.xml | 2 +- ...CMSPageCreatePageInSingleStoreModeTest.xml | 2 +- .../Mftf/Test/AdminCMSPageCreatePageTest.xml | 2 +- .../AdminCMSPageCreatePageWithBlockTest.xml | 2 +- ...capeAndEnterHandlesForWYSIWYGBlockTest.xml | 2 +- .../Mftf/Test/AdminCmsPageMassActionTest.xml | 2 +- ...PageLayoutFromConfigurationSettingTest.xml | 2 +- .../AdminContentBlocksNavigateMenuTest.xml | 2 +- .../AdminContentPagesNavigateMenuTest.xml | 2 +- ...minCreateCmsBlockWithMarginalSpaceTest.xml | 2 +- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 4 +- ...dCmsBlockEntityAndAssignToCategoryTest.xml | 4 +- .../Test/AdminSaveAndCloseCmsBlockTest.xml | 2 +- ...CheckOrderOfProdsInWidgetOnCMSPageTest.xml | 2 +- .../Test/Mftf/Test/CheckStaticBlocksTest.xml | 2 +- .../StoreViewLanguageCorrectSwitchTest.xml | 2 +- ...untryDropDownWithOneAllowedCountryTest.xml | 2 +- .../Test/Mftf/Test/ConfigurationTest.xml | 2 +- .../AdminAddDefaultImageConfigurableTest.xml | 2 +- ...agesAndPricesToConfigurableProductTest.xml | 2 +- ...hangedWhenSavingProductWithSameSkuTest.xml | 2 +- ...bleProductAttributeValueUniquenessTest.xml | 2 +- ...CheckResultsOfColorAndOtherFiltersTest.xml | 2 +- ...nCheckValidatorConfigurableProductTest.xml | 2 +- .../AdminConfigurableProductCreateTest.xml | 2 +- ...uctAfterGettingIncorrectSKUMessageTest.xml | 2 +- ...AdminConfigurableProductBulkDeleteTest.xml | 2 +- .../AdminConfigurableProductDeleteTest.xml | 2 +- .../AdminConfigurableProductLongSkuTest.xml | 2 +- ...figurableProductChildrenOutOfStockTest.xml | 2 +- ...ductOutOfStockAndDeleteCombinationTest.xml | 2 +- ...roductOutOfStockTestDeleteChildrenTest.xml | 2 +- ...minConfigurableProductFilterByTypeTest.xml | 2 +- .../AdminConfigurableProductSearchTest.xml | 2 +- ...ConfigurableProductUpdateAttributeTest.xml | 2 +- ...gurableProductUpdateChildAttributeTest.xml | 2 +- ...onfigurableProductAddConfigurationTest.xml | 2 +- ...AdminConfigurableProductBulkUpdateTest.xml | 2 +- ...ConfigurableProductDisableAnOptionTest.xml | 2 +- ...nConfigurableProductRemoveAnOptionTest.xml | 2 +- ...igurableProductRemoveConfigurationTest.xml | 2 +- ...AndEditConfigurableProductSettingsTest.xml | 2 +- ...oadableProductSwitchToConfigurableTest.xml | 2 +- ...onfigurableProductBasedOnParentSkuTest.xml | 2 +- ...ctWithCreatingCategoryAndAttributeTest.xml | 2 +- ...roductWithDisabledChildrenProductsTest.xml | 2 +- ...reateConfigurableProductWithImagesTest.xml | 2 +- ...eeProductDisplayOutOfStockProductsTest.xml | 2 +- ...oductDontDisplayOutOfStockProductsTest.xml | 2 +- ...ableProductWithTierPriceForOneItemTest.xml | 2 +- ...ctWithTwoOptionsAssignedToCategoryTest.xml | 2 +- ...woOptionsWithoutAssignedToCategoryTest.xml | 2 +- .../AdminDeleteConfigurableProductTest.xml | 2 +- ...TypeSwitchingToConfigurableProductTest.xml | 2 +- ...TypeSwitchingToConfigurableProductTest.xml | 2 +- .../Mftf/Test/AdminRelatedProductsTest.xml | 2 +- ...dminRemoveDefaultImageConfigurableTest.xml | 2 +- ...igurableProductAttributeNameDesignTest.xml | 2 +- ...bleProductPriceAdditionalStoreViewTest.xml | 2 +- .../Test/NoErrorForMiniCartItemEditTest.xml | 2 +- ...vailableToConfigureDisabledProductTest.xml | 2 +- .../ProductsQtyReturnAfterOrderCancelTest.xml | 2 +- ...rontConfigurableProductChildSearchTest.xml | 2 +- ...efrontConfigurableProductBasicInfoTest.xml | 2 +- ...ontConfigurableProductCanAddToCartTest.xml | 2 +- ...ntConfigurableProductCantAddToCartTest.xml | 2 +- ...orefrontConfigurableProductOptionsTest.xml | 2 +- ...frontConfigurableProductVariationsTest.xml | 2 +- ...efrontConfigurableProductAddToCartTest.xml | 2 +- ...refrontConfigurableProductGridViewTest.xml | 2 +- ...refrontConfigurableProductListViewTest.xml | 2 +- ...gurableProductWithFileCustomOptionTest.xml | 2 +- ...tWithSeveralAttributesPrependMediaTest.xml | 4 +- ...hVisualSwatchAttributePrependMediaTest.xml | 4 +- ...uctChildAssignedToSeparateCategoryTest.xml | 2 +- ...ConfigurableWithCatalogRuleAppliedTest.xml | 2 +- ...nfigurableProductLayeredNavigationTest.xml | 2 +- ...efrontVisibilityOfDuplicateProductTest.xml | 2 +- ...nCurrencyConverterAPIConfigurationTest.xml | 2 +- ...nDefaultCurrencySymbolsAreDisabledTest.xml | 2 +- ...ayWhenChooseThreeAllowedCurrenciesTest.xml | 2 +- .../AdminOrderRateDisplayedInOneLineTest.xml | 2 +- ...minStoresCurrencyRatesNavigateMenuTest.xml | 2 +- ...nStoresCurrencySymbolsNavigateMenuTest.xml | 2 +- ...aultBillingShippingCustomerAddressTest.xml | 2 +- ...hangeCustomerGenderInCustomersGridTest.xml | 2 +- ...inChangeSingleCustomerGroupViaGridTest.xml | 2 +- ...ultValueDisableAutoGroupChangeIsNoTest.xml | 2 +- ...ltValueDisableAutoGroupChangeIsYesTest.xml | 2 +- ...inCreateCustomerGroupAlreadyExistsTest.xml | 2 +- ...eateCustomerRetailerWithoutAddressTest.xml | 2 +- .../Mftf/Test/AdminCreateCustomerTest.xml | 2 +- ...minCreateCustomerWithCountryPolandTest.xml | 2 +- .../AdminCreateCustomerWithCountryUSATest.xml | 2 +- ...AdminCreateCustomerWithCustomGroupTest.xml | 2 +- .../AdminCreateCustomerWithPrefixTest.xml | 2 +- .../AdminCreateCustomerWithoutAddressTest.xml | 2 +- ...stomerOnStorefrontSignupNewsletterTest.xml | 2 +- ...AdminCreateNewCustomerOnStorefrontTest.xml | 2 +- .../Mftf/Test/AdminCreateNewCustomerTest.xml | 2 +- .../AdminCreateRetailCustomerGroupTest.xml | 2 +- .../AdminCreateTaxClassCustomerGroupTest.xml | 2 +- ...tomerSubscribeNewsletterPerWebsiteTest.xml | 2 +- ...nCustomersAllCustomersNavigateMenuTest.xml | 2 +- ...ustomersCustomerGroupsNavigateMenuTest.xml | 2 +- ...dminCustomersNowOnlineNavigateMenuTest.xml | 2 +- ...DeleteCustomerAddressesFromTheGridTest.xml | 2 +- ...AddressesFromTheGridViaMassActionsTest.xml | 2 +- .../Mftf/Test/AdminDeleteCustomerTest.xml | 2 +- ...eleteDefaultBillingCustomerAddressTest.xml | 2 +- ...aultBillingShippingCustomerAddressTest.xml | 2 +- ...dminExactMatchSearchInCustomerGridTest.xml | 2 +- ...fStorefrontIsOpenedViaCustomerViewTest.xml | 2 +- ...avigateFromCustomerViewCartProductTest.xml | 2 +- .../Test/AdminResetCustomerPasswordTest.xml | 2 +- ...dminSearchCustomerAddressByKeywordTest.xml | 2 +- ...inSetCustomerDefaultBillingAddressTest.xml | 2 +- ...nSetCustomerDefaultShippingAddressTest.xml | 2 +- .../AdminDeleteCustomerAddressTest.xml | 2 +- ...ustomerInfoFromDefaultToNonDefaultTest.xml | 2 +- ...VerifyCreateCustomerRequiredFieldsTest.xml | 2 +- ...erifyCustomerAddressRequiredFieldsTest.xml | 2 +- ...tomerAddressStateContainValuesOnceTest.xml | 2 +- ...ChangingSingleCustomerGroupViaGridTest.xml | 2 +- .../Mftf/Test/DeleteCustomerGroupTest.xml | 2 +- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 4 +- .../Test/SearchByEmailInCustomerGridTest.xml | 2 +- ...StorefrontCheckTaxAddingValidVATIdTest.xml | 2 +- .../StorefrontClearAllCompareProductsTest.xml | 2 +- ...frontCreateCustomerWithDateOfBirthTest.xml | 4 +- ...AdminScheduledImportSettingsHiddenTest.xml | 2 +- ...AddDefaultImageDownloadableProductTest.xml | 2 +- ...AndEditDownloadableProductSettingsTest.xml | 2 +- ...bleProductAndAssignItToCustomStoreTest.xml | 2 +- ...wnloadableProductWithCustomOptionsTest.xml | 2 +- ...loadableProductWithDefaultSetLinksTest.xml | 2 +- ...eDownloadableProductWithGroupPriceTest.xml | 2 +- ...nCreateDownloadableProductWithLinkTest.xml | 2 +- ...DownloadableProductWithManageStockTest.xml | 2 +- ...oadableProductWithOutOfStockStatusTest.xml | 2 +- ...ownloadableProductWithSpecialPriceTest.xml | 2 +- ...ductWithoutFillingQuantityAndStockTest.xml | 2 +- ...wnloadableProductWithoutTaxClassIdTest.xml | 2 +- .../AdminDeleteDownloadableProductTest.xml | 2 +- ...TypeSwitchingToDownloadableProductTest.xml | 2 +- ...oveDefaultImageDownloadableProductTest.xml | 2 +- ...leProductWithSeparateLinksFromCartTest.xml | 2 +- ...wnloadableLinksDownloadableProductTest.xml | 2 +- ...wnloadableLinksDownloadableProductTest.xml | 2 +- ...ableProductSamplesAreNotAccessibleTest.xml | 2 +- ...oductQuickSearchUsingElasticSearchTest.xml | 4 +- ...CheckAdvancedSearchOnElasticSearchTest.xml | 2 +- ...DecimalAttributeUsingElasticSearchTest.xml | 4 +- ...frontElasticSearchForChineseLocaleTest.xml | 2 +- ...ntElasticsearch6SearchInvalidValueTest.xml | 2 +- .../Test/AdminEmailTemplatePreviewTest.xml | 2 +- ...arketingEmailTemplatesNavigateMenuTest.xml | 2 +- .../AdminEncryptionKeyAutoGenerateKeyTest.xml | 2 +- ...dminEncryptionKeyManualGenerateKeyTest.xml | 2 +- .../Test/AdminCreatingShippingLabelTest.xml | 2 +- .../AdminValidateConversionIdConfigTest.xml | 2 +- ...AdminAddDefaultImageGroupedProductTest.xml | 2 +- ...nAssociateGroupedProductToWebsitesTest.xml | 2 +- ...reateAndEditGroupedProductSettingsTest.xml | 2 +- .../Test/AdminDeleteGroupedProductTest.xml | 2 +- .../Test/AdminGroupedProductsListTest.xml | 2 +- ...inRemoveDefaultImageGroupedProductTest.xml | 2 +- .../AdminSortingAssociatedProductsTest.xml | 2 +- ...utesChangedValueToEmptyAfterImportTest.xml | 2 +- .../Test/AdminExportPageNavigateMenuTest.xml | 2 +- .../Mftf/Test/AdminExportPagerGridTest.xml | 2 +- ...gesFileDirectoryCorrectExplanationTest.xml | 2 +- ...dminImportCSVWithSpecialCharactersTest.xml | 2 +- ...mportProductsWithAddUpdateBehaviorTest.xml | 2 +- ...inImportProductsWithDeleteBehaviorTest.xml | 2 +- ...dminImportProductsWithErrorEntriesTest.xml | 2 +- ...ImportCSVFileCorrectDifferentFilesTest.xml | 2 +- ...lityDifferentStoreViewsAfterImportTest.xml | 2 +- .../AdminSystemImportNavigateMenuTest.xml | 2 +- ...UpdatingProductThroughImportingCSVTest.xml | 2 +- ...inSystemIndexManagementGridChangesTest.xml | 4 +- ...nSystemIndexManagementNavigateMenuTest.xml | 2 +- ...ntegrationEntityWithDuplicatedNameTest.xml | 2 +- .../Test/AdminDeleteIntegrationEntityTest.xml | 2 +- ...dminSystemIntegrationsNavigateMenuTest.xml | 2 +- .../Mftf/Test/ShopByButtonInMobileTest.xml | 2 +- ...hMapAssignedConfigProductIsCorrectTest.xml | 2 +- ...toreFrontCheckingWithMultishipmentTest.xml | 2 +- ...oreFrontCheckingWithSingleShipmentTest.xml | 2 +- ...toreFrontMinicartWithMultishipmentTest.xml | 2 +- ...oreFrontMyAccountWithMultishipmentTest.xml | 2 +- ...frontCheckoutWithMultipleAddressesTest.xml | 2 +- .../StorefrontOrderWithMultishippingTest.xml | 2 +- ...utWhenCartPageIsOpenedInAnotherTabTest.xml | 2 +- ...heckNewRelicSystemConfigDependencyTest.xml | 2 +- ...rketingNewsletterQueueNavigateMenuTest.xml | 2 +- ...gNewsletterSubscribersNavigateMenuTest.xml | 2 +- ...tingNewsletterTemplateNavigateMenuTest.xml | 2 +- ...wsletterProblemReportsNavigateMenuTest.xml | 2 +- ...dAreaSessionMustNotAffectAdminAreaTest.xml | 2 +- ...shStaticFilesCacheButtonVisibilityTest.xml | 2 +- ...ResolutionForPayPalInUnitedKingdomTest.xml | 2 +- .../AdminConfigPaymentsSectionStateTest.xml | 2 +- ...ayPalSolutionsEnabledAtTheSameTimeTest.xml | 2 +- ...eportsPayPalSettlementNavigateMenuTest.xml | 2 +- ...SalesBillingAgreementsNavigateMenuTest.xml | 2 +- ...CartPersistenceUnderLongTermCookieTest.xml | 2 +- ...listIsPersistedUnderLongTermCookieTest.xml | 2 +- ...inValidateUrlOnGetVideoInformationTest.xml | 2 +- .../YoutubeVideoWindowOnProductPageTest.xml | 2 +- ...efrontGuestCheckoutDisabledProductTest.xml | 2 +- ...nReportsAbandonedCartsNavigateMenuTest.xml | 2 +- ...dminReportsBestsellersNavigateMenuTest.xml | 2 +- .../AdminReportsCouponsNavigateMenuTest.xml | 2 +- .../AdminReportsDownloadsNavigateMenuTest.xml | 2 +- .../AdminReportsInvoicedNavigateMenuTest.xml | 2 +- ...AdminReportsLowStockDisableProductTest.xml | 4 +- .../AdminReportsLowStockNavigateMenuTest.xml | 2 +- .../Test/AdminReportsNewNavigateMenuTest.xml | 2 +- ...AdminReportsOrderCountNavigateMenuTest.xml | 2 +- ...AdminReportsOrderTotalNavigateMenuTest.xml | 2 +- .../AdminReportsOrderedGroupedBySkuTest.xml | 2 +- .../AdminReportsOrderedNavigateMenuTest.xml | 2 +- .../AdminReportsOrdersNavigateMenuTest.xml | 2 +- ...nReportsProductsInCartNavigateMenuTest.xml | 2 +- ...portsRefreshStatisticsNavigateMenuTest.xml | 2 +- .../Test/AdminReportsTaxNavigateMenuTest.xml | 2 +- .../AdminReportsViewsNavigateMenuTest.xml | 2 +- .../CancelOrdersInOrderSalesReportTest.xml | 2 +- ...ngPendingReviewsNavigateMenuActiveTest.xml | 4 +- .../AdminMarketingReviewsNavigateMenuTest.xml | 2 +- ...dminReportsByCustomersNavigateMenuTest.xml | 2 +- ...AdminReportsByProductsNavigateMenuTest.xml | 2 +- .../Test/AdminReviewsByProductsReportTest.xml | 4 +- .../AdminStoresRatingNavigateMenuTest.xml | 2 +- ...rifyNewRatingFormSingleStoreModeNoTest.xml | 2 +- ...ifyNewRatingFormSingleStoreModeYesTest.xml | 2 +- .../StoreFrontReviewByCustomerReportTest.xml | 4 +- ...avascriptErrorOnAddYourReviewClickTest.xml | 2 +- ...ableProductToOrderFromShoppingCartTest.xml | 2 +- ...mpleProductToOrderFromShoppingCartTest.xml | 2 +- .../AdminAddSelectedProductToOrderTest.xml | 4 +- ...vailabilityCreditMemoWithNoPaymentTest.xml | 2 +- ...OrderWithBankTransferPaymentMethodTest.xml | 2 +- ...derWithCashOnDeliveryPaymentMethodTest.xml | 2 +- ...erWithCheckMoneyOrderPaymentMethodTest.xml | 2 +- ...WithProductQtyWithoutStockDecreaseTest.xml | 2 +- ...rderWithPurchaseOrderPaymentMethodTest.xml | 2 +- ...eatedOrderWithZeroSubtotalCheckoutTest.xml | 2 +- ...AdminChangeCustomerGroupInNewOrderTest.xml | 2 +- ...dminCheckingCreditMemoUpdateTotalsTest.xml | 2 +- ...kingDateAfterChangeInterfaceLocaleTest.xml | 2 +- ...ectnessInvoicedItemInBundleProductTest.xml | 2 +- ...reateCreditMemoBankTransferPaymentTest.xml | 2 +- ...reateCreditMemoConfigurableProductTest.xml | 2 +- ...AdminCreateCreditMemoPartialRefundTest.xml | 2 +- ...CreateCreditMemoWithCashOnDeliveryTest.xml | 2 +- ...nCreateCreditMemoWithPurchaseOrderTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- ...AdminCreateOrderAddProductCheckboxTest.xml | 2 +- ...AdminCreateOrderAndCheckTheReorderTest.xml | 2 +- ...thTwoAddressesTaxableAndNonTaxableTest.xml | 2 +- ...inCreateOrderStatusDuplicatingCodeTest.xml | 2 +- ...nCreateOrderStatusDuplicatingLabelTest.xml | 2 +- .../Mftf/Test/AdminCreateOrderStatusTest.xml | 2 +- .../AdminCreateOrderWithBundleProductTest.xml | 2 +- ...reateOrderWithCustomerWithoutEmailTest.xml | 4 +- ...minCreateOrderWithDateTimeOptionUITest.xml | 2 +- ...reateOrderWithMinimumAmountEnabledTest.xml | 2 +- ...OrderWithSelectedShoppingCartItemsTest.xml | 2 +- ...rWithSimpleProductCustomOptionFileTest.xml | 2 +- .../AdminCreateOrderWithSimpleProductTest.xml | 2 +- ...nimumOrderAmountNotMatchOrderTotalTest.xml | 2 +- .../Mftf/Test/AdminHoldCreatedOrderTest.xml | 2 +- ...nMassOrdersCancelCompleteAndClosedTest.xml | 2 +- ...assOrdersCancelProcessingAndClosedTest.xml | 2 +- .../AdminMassOrdersHoldOnCompleteTest.xml | 2 +- ...ssOrdersHoldOnPendingAndProcessingTest.xml | 2 +- ...AdminMassOrdersReleasePendingOrderTest.xml | 2 +- ...MassOrdersUpdateCancelPendingOrderTest.xml | 2 +- .../AdminOrdersReleaseInUnholdStatusTest.xml | 2 +- ...rderCreationWithMultiWebsiteConfigTest.xml | 2 +- ...eorderWithCatalogPriceRuleDiscountTest.xml | 2 +- .../AdminSalesCreditMemosNavigateMenuTest.xml | 2 +- .../AdminSalesInvoicesNavigateMenuTest.xml | 2 +- .../Test/AdminSalesOrdersNavigateMenuTest.xml | 2 +- .../AdminSalesShipmentsNavigateMenuTest.xml | 2 +- ...AdminSalesTransactionsNavigateMenuTest.xml | 2 +- ...AdminStoresOrderStatusNavigateMenuTest.xml | 2 +- ...dminSubmitConfigurableProductOrderTest.xml | 2 +- ...ubmitsOrderPaymentMethodValidationTest.xml | 2 +- ...minSubmitsOrderWithAndWithoutEmailTest.xml | 2 +- ...rderWithAndWithoutFieldsValidationTest.xml | 2 +- .../AdminUnassignCustomOrderStatusTest.xml | 2 +- ...mOrderStatusNotVisibleOnStorefrontTest.xml | 2 +- ...SSVulnerabilityDuringOrderCreationTest.xml | 2 +- .../CreateInvoiceAndCheckInvoiceOrderTest.xml | 2 +- ...iceWithCashOnDeliveryPaymentMethodTest.xml | 2 +- ...eWithShipmentAndCheckInvoicedOrderTest.xml | 2 +- ...ateInvoiceWithZeroSubtotalCheckoutTest.xml | 2 +- .../CreateOrderFromEditCustomerPageTest.xml | 2 +- ...editMemoTotalAfterShippingDiscountTest.xml | 2 +- ...rableProductsInComparedOnOrderPageTest.xml | 2 +- ...eredConfigurableProductOnOrderPageTest.xml | 2 +- ...astOrderedSimpleProductOnOrderPageTest.xml | 2 +- ...iewedBundleFixedProductOnOrderPageTest.xml | 2 +- ...ewedConfigurableProductOnOrderPageTest.xml | 2 +- ...impleProductsInComparedOnOrderPageTest.xml | 2 +- .../StorefrontOrderPagerDisplayedTest.xml | 2 +- .../Test/StorefrontOrderPagerIsAbsentTest.xml | 2 +- .../Test/StorefrontPrintOrderGuestTest.xml | 2 +- ...inCartRulesAppliedForProductInCartTest.xml | 2 +- .../Mftf/Test/AdminCreateBuyXGetYFreeTest.xml | 2 +- ...AndVerifyRuleConditionIsNotAppliedTest.xml | 2 +- ...inCreateCartPriceRuleEmptyFromDateTest.xml | 2 +- ...inCreateCartPriceRuleForCouponCodeTest.xml | 2 +- ...ateCartPriceRuleForGeneratedCouponTest.xml | 2 +- .../AdminCreateFixedAmountDiscountTest.xml | 2 +- ...CreateFixedAmountWholeCartDiscountTest.xml | 2 +- .../Mftf/Test/AdminCreateInvalidRuleTest.xml | 2 +- .../AdminCreatePercentOfProductPriceTest.xml | 2 +- ...exConditionsAndVerifyDeleteMessageTest.xml | 2 +- ...PercentPriceAndVerifyDeleteMessageTest.xml | 2 +- ...iveSalesRuleAndVerifyDeleteMessageTest.xml | 2 +- ...arketingCartPriceRulesNavigateMenuTest.xml | 2 +- ...artPriceRuleForConfigurableProductTest.xml | 2 +- .../Test/PriceRuleCategoryNestingTest.xml | 2 +- .../StorefrontAutoGeneratedCouponCodeTest.xml | 2 +- .../StorefrontCartPriceRuleCountryTest.xml | 2 +- .../StorefrontCartPriceRulePostcodeTest.xml | 2 +- .../StorefrontCartPriceRuleQuantityTest.xml | 2 +- .../Test/StorefrontCartPriceRuleStateTest.xml | 2 +- .../StorefrontCartPriceRuleSubtotalTest.xml | 2 +- ...frontCartRuleCouponForFreeShippingTest.xml | 2 +- ...ValueWithFullDiscountUsingCartRuleTest.xml | 2 +- ...yRulesShouldApplyToComplexProductsTest.xml | 2 +- ...ductWithInvisibleIndividualProductTest.xml | 2 +- .../AdminGlobalSearchOnProductPageTest.xml | 2 +- .../AdminMassDeleteSearchTermEntityTest.xml | 2 +- ...ngElasticSearchWithWeightAttributeTest.xml | 2 +- ...archSuggestionByProductDescriptionTest.xml | 2 +- ...erifySearchSuggestionByProductNameTest.xml | 2 +- ...uggestionByProductShortDescriptionTest.xml | 2 +- ...VerifySearchSuggestionByProductSkuTest.xml | 2 +- ...rontVerifySearchTermEntityRedirectTest.xml | 2 +- ...CreateNewUserWithInvalidExpirationTest.xml | 4 +- ...inCreateNewUserWithValidExpirationTest.xml | 4 +- ...dminUserWhenCreatingNewIntegrationTest.xml | 4 +- ...inLockAdminUserWhenCreatingNewRoleTest.xml | 4 +- ...minLoginAdminUserWithInvalidExpiration.xml | 6 +- ...AdminLoginAdminUserWithValidExpiration.xml | 10 +- .../AdminNavigateWhileUserExpiredTest.xml | 8 +- .../AdminUserLockWhenCreatingNewUserTest.xml | 4 +- .../Test/AdminUserLockWhenEditingUserTest.xml | 6 +- ...utFieldsDisabledAfterAppConfigDumpTest.xml | 2 +- .../AdminCheckTheConfirmationPopupTest.xml | 2 +- ...ustomStoreShippingMethodTableRatesTest.xml | 2 +- .../AdminCreatePartialShipmentEntityTest.xml | 2 +- .../Test/AdminCreateShipmentEntityTest.xml | 2 +- ...dminValidateShippingTrackingNumberTest.xml | 2 +- ...splayTableRatesShippingMethodForAETest.xml | 2 +- ...esShippingMethodForDifferentStatesTest.xml | 2 +- .../AdminMarketingSiteMapCreateNewTest.xml | 2 +- .../AdminMarketingSiteMapNavigateMenuTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateWebsiteTest.xml | 2 +- ...heckColorUploadChooserVisualSwatchTest.xml | 2 +- ...heckTextSwatchAttributeAddedViaApiTest.xml | 4 +- .../Mftf/Test/AdminCreateImageSwatchTest.xml | 2 +- .../Mftf/Test/AdminCreateTextSwatchTest.xml | 2 +- .../Mftf/Test/AdminCreateVisualSwatchTest.xml | 2 +- ...ateVisualSwatchWithNonValidOptionsTest.xml | 2 +- .../Test/AdminDisablingSwatchTooltipsTest.xml | 2 +- ...uctWithAttributesImagesAndSwatchesTest.xml | 2 +- .../AdminSetUpWatermarkForSwatchImageTest.xml | 2 +- ...oductSwatchMinimumPriceProductPageTest.xml | 2 +- .../StorefrontFilterByImageSwatchTest.xml | 2 +- .../Test/StorefrontFilterByTextSwatchTest.xml | 2 +- .../StorefrontFilterByVisualSwatchTest.xml | 2 +- ...tImageColorWhenFilterByColorFilterTest.xml | 2 +- ...oductImagesMatchingProductSwatchesTest.xml | 2 +- ...SwatchAttributesDisplayInWidgetCMSTest.xml | 4 +- ...tSwatchProductWithFileCustomOptionTest.xml | 2 +- .../Test/AdminCheckCreditMemoTotalsTest.xml | 2 +- .../Test/AdminCheckingTaxReportGridTest.xml | 4 +- .../Test/AdminCreateDefaultsTaxRuleTest.xml | 2 +- .../AdminCreateTaxRateAllPostCodesTest.xml | 2 +- ...teTaxRateInvalidPostcodeTestLengthTest.xml | 2 +- .../Test/AdminCreateTaxRateLargeRateTest.xml | 2 +- ...AdminCreateTaxRateSpecificPostcodeTest.xml | 2 +- ...dminCreateTaxRateWiderZipCodeRangeTest.xml | 2 +- .../AdminCreateTaxRateZipCodeRangeTest.xml | 2 +- ...RuleWithCustomerAndProductTaxClassTest.xml | 2 +- ...xRateAndCustomerAndProductTaxClassTest.xml | 2 +- ...TaxRuleWithNewTaxClassesAndTaxRateTest.xml | 2 +- .../AdminCreateTaxRuleWithZipRangeTest.xml | 2 +- .../Test/Mftf/Test/AdminDeleteTaxRuleTest.xml | 2 +- .../AdminStoresTaxRulesNavigateMenuTest.xml | 2 +- ...StoresTaxZonesAndRatesNavigateMenuTest.xml | 2 +- ...emImportExportTaxRatesNavigateMenuTest.xml | 2 +- .../Test/AdminUpdateDefaultTaxRuleTest.xml | 2 +- ...dminUpdateTaxRuleWithCustomClassesTest.xml | 2 +- ...AdminUpdateTaxRuleWithFixedZipUtahTest.xml | 2 +- .../Mftf/Test/DeleteTaxRateEntityTest.xml | 2 +- .../StorefrontTaxQuoteCartGuestSimpleTest.xml | 2 +- ...StorefrontTaxQuoteCartGuestVirtualTest.xml | 2 +- ...orefrontTaxQuoteCartLoggedInSimpleTest.xml | 2 +- ...refrontTaxQuoteCartLoggedInVirtualTest.xml | 2 +- ...refrontTaxQuoteCheckoutGuestSimpleTest.xml | 2 +- ...efrontTaxQuoteCheckoutGuestVirtualTest.xml | 2 +- ...rontTaxQuoteCheckoutLoggedInSimpleTest.xml | 2 +- ...ontTaxQuoteCheckoutLoggedInVirtualTest.xml | 2 +- .../Mftf/Test/Update01TaxRateEntityTest.xml | 2 +- .../Mftf/Test/Update100TaxRateEntityTest.xml | 2 +- .../Mftf/Test/Update1299TaxRateEntityTest.xml | 2 +- .../Test/UpdateAnyRegionTaxRateEntityTest.xml | 2 +- .../Test/UpdateDecimalTaxRateEntityTest.xml | 2 +- .../Test/UpdateLargeTaxRateEntityTest.xml | 2 +- .../Mftf/Test/AdminContentThemeSortTest.xml | 2 +- .../AdminContentThemesNavigateMenuTest.xml | 2 +- ...esignConfigMediaGalleryImageUploadTest.xml | 2 +- .../Mftf/Test/AdminWatermarkUploadTest.xml | 2 +- .../Theme/Test/Mftf/Test/ThemeTest.xml | 2 +- .../AdminInlineTranslationOnCheckoutTest.xml | 2 +- .../Mftf/Test/DefaultConfigForUPSTypeTest.xml | 2 +- ...ateURLRewriteWhenCategoryIsDeletedTest.xml | 2 +- ...tipleStoreviewsDuringProductImportTest.xml | 2 +- ...ngProductImportWithConfigTurnedOffTest.xml | 2 +- ...lKeyForStoreViewAndMovingCategory2Test.xml | 2 +- ...rlKeyForStoreViewAndMovingCategoryTest.xml | 2 +- ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 2 +- ...yUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...eUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...yUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...eProductURLRewriteAndAddNoRedirectTest.xml | 2 +- ...ithCategoryAndAddTemporaryRedirectTest.xml | 2 +- ...tUrLRewriteAndAddPermanentRedirectTest.xml | 2 +- ...tUrLRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...nDeleteCMSPageNoRedirectUrlRewriteTest.xml | 2 +- ...CMSPagePermanentRedirectUrlRewriteTest.xml | 2 +- ...CMSPageTemporaryRedirectUrlRewriteTest.xml | 2 +- ...tegoryUrlRewriteHypenAsRequestPathTest.xml | 2 +- .../AdminDeleteCategoryUrlRewriteTest.xml | 2 +- ...eCategoryUrlRewriteWithRequestPathTest.xml | 2 +- ...teCmsPageUrlRewriteWithNoRedirectsTest.xml | 2 +- ...ageUrlRewriteWithPermanentRedirectTest.xml | 2 +- ...ageUrlRewriteWithTemporaryRedirectTest.xml | 2 +- .../Test/AdminDeleteCustomUrlRewriteTest.xml | 2 +- ...AdminDeleteProductURLRewriteEntityTest.xml | 2 +- .../Test/AdminDeleteProductUrlRewriteTest.xml | 2 +- ...tesForProductInCategoriesSwitchOffTest.xml | 2 +- ...inMarketingUrlRewritesNavigateMenuTest.xml | 2 +- ...CreateUrlRewriteForCustomStoreViewTest.xml | 2 +- ...oryUrlRewriteAndAddAspxRequestPathTest.xml | 2 +- ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 2 +- ...yUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...CmsPageRewriteEntityWithNoRedirectTest.xml | 2 +- ...eRewriteEntityWithPermanentReirectTest.xml | 2 +- ...RewriteEntityWithTemporaryRedirectTest.xml | 2 +- ...eCmsPageUrlRewriteAndAddNoRedirectTest.xml | 2 +- ...eUrlRewriteAndAddPermanentRedirectTest.xml | 2 +- ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...inUpdateCustomURLRewritesPermanentTest.xml | 2 +- ...inUpdateCustomURLRewritesTemporaryTest.xml | 2 +- ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 2 +- ...inUrlRewritesForProductAfterImportTest.xml | 2 +- ...writesForProductInAnchorCategoriesTest.xml | 2 +- ...riesTestWithConfigurationTurnedOffTest.xml | 2 +- ...ProductsWithConfigurationTurnedOffTest.xml | 2 +- ...sibleForAdminUserWithLimitedAccessTest.xml | 6 +- .../Test/AdminCreateUserRoleEntityTest.xml | 4 +- .../Test/AdminDeleteAdminUserEntityTest.xml | 4 +- .../AdminDeleteOwnAdminUserAccountTest.xml | 8 +- .../AdminSystemAllUsersNavigateMenuTest.xml | 2 +- ...AdminSystemLockedUsersNavigateMenuTest.xml | 2 +- ...temManageEncryptionKeyNavigateMenuTest.xml | 2 +- .../AdminSystemUserRolesNavigateMenuTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateUserTest.xml | 6 +- .../AdminCreateCustomVariableEntityTest.xml | 2 +- ...nSystemCustomVariablesNavigateMenuTest.xml | 2 +- ...FixedTaxValSavedForSpecificWebsiteTest.xml | 2 +- ...inRemoveProductWeeeAttributeOptionTest.xml | 2 +- ...oppingCartForCustomerPhysicalQuoteTest.xml | 2 +- ...hoppingCartForCustomerVirtualQuoteTest.xml | 2 +- ...nShoppingCartForGuestPhysicalQuoteTest.xml | 2 +- ...InShoppingCartForGuestVirtualQuoteTest.xml | 2 +- .../AdminContentWidgetsMassDeletesTest.xml | 4 +- .../AdminContentWidgetsNavigateMenuTest.xml | 2 +- .../Mftf/Test/NewProductsListWidgetTest.xml | 2 +- .../Test/Mftf/Test/ProductsListWidgetTest.xml | 2 +- ...ishListShareOptionsInputValidationTest.xml | 2 +- ...ddToCartWishListWithUnselectedAttrTest.xml | 2 +- ...tChildImageShouldBeShownOnWishListTest.xml | 2 +- ...AddMultipleStoreProductsToWishlistTest.xml | 2 +- ...teBundleDynamicProductFromWishlistTest.xml | 2 +- ...eProductFromShoppingCartToWishlistTest.xml | 2 +- .../Test/WishListWithDisabledProductTest.xml | 2 +- ...inUpdateDisabledHtmlTermEntityTestCest.php | 203 ------------------ ...inUpdateDisabledTextTermEntityTestCest.php | 203 ------------------ ...minUpdateEnabledTextTermEntityTestCest.php | 202 ----------------- .../Magento/_generated/testManifest.txt | 4 +- 852 files changed, 908 insertions(+), 1518 deletions(-) delete mode 100644 dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledHtmlTermEntityTestCest.php delete mode 100644 dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledTextTermEntityTestCest.php delete mode 100644 dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateEnabledTextTermEntityTestCest.php diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml index 58bcacc190cff..e02c34fd8868e 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml @@ -21,6 +21,6 @@ </annotations> <!-- Logging in Magento admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml b/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml index 81ad2858d5901..77c8f02bfb777 100644 --- a/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml +++ b/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml index c742248b32cc3..ee25e80fcab30 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml index 851d2447dfc15..8ac7af096da0a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml @@ -22,7 +22,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml index 47b8715b5541c..cbe0f0b5f4fe9 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml @@ -21,7 +21,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Go to the general configuration and make sure the locale dropdown is available and enabled --> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml index 16bde998b9fb3..323ae324d4950 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml @@ -21,7 +21,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Go to the general configuration and make sure the locale dropdown is disabled --> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml index 091e441559d78..8ae6d6de79c0b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml index 60118202dbef2..efcc6da30199a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index 84e6b59e01504..0a54b3f66153a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -41,7 +41,7 @@ </after> <!-- Login as admin --> <comment userInput="Login as admin" stepKey="adminLogin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Grab quantity value --> <comment userInput="Grab quantity value from dashboard" stepKey="grabQuantityFromDashboard"/> <grabTextFrom selector="{{AdminDashboardSection.dashboardTotals('Quantity')}}" stepKey="grabStartQuantity"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml index 88d26c052b59b..2469151337bfe 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml @@ -27,7 +27,7 @@ <magentoCLI command="config:set {{ChangeAdminSecuritySessionLifetimeConfigData.path}} {{ChangeAdminSecuritySessionLifetimeConfigData.value}}" stepKey="changeCookieLifetime"/> <!-- 2. Wait for session to expire. --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <wait time="60" stepKey="waitForSessionLifetime"/> <reloadPage stepKey="reloadPage"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml index d932da6ec0fad..0e3bf07d32441 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml @@ -27,7 +27,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Login to Admin. --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 2. Create customer if needed. --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml index f75f3b2e3f15e..be734205e1f5b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml @@ -27,7 +27,7 @@ <magentoCLI command="config:set {{EmptyCookieDomainForMainWebsiteConfigData.path}} --scope={{EmptyCookieDomainForMainWebsiteConfigData.scope}} --scope-code={{EmptyCookieDomainForMainWebsiteConfigData.scope_code}} {{EmptyCookieDomainForMainWebsiteConfigData.value}}" stepKey="changeDomainForMainWebsiteAfterTestComplete"/> <magentoCLI command="cache:flush config" stepKey="flushCacheAfterTestComplete"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </test> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml index 0aa31bb21b6f7..d2c628ed13701 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml @@ -22,7 +22,7 @@ <before> <magentoCLI command="config:set {{MinifyJavaScriptFilesEnableConfigData.path}} {{MinifyJavaScriptFilesEnableConfigData.value}}" stepKey="enableJsMinification"/> <magentoCLI command="cache:clean config" stepKey="cleanCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI command="config:set {{MinifyJavaScriptFilesDisableConfigData.path}} {{MinifyJavaScriptFilesDisableConfigData.value}}" stepKey="disableJsMinification"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml index b26947d934865..bf4b4dd6f2108 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> <!--Create user role--> <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> <argument name="User" value="adminRole"/> @@ -40,7 +40,7 @@ </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsSaleRoleUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Delete created data--> <actionGroup ref="AdminUserOpenAdminRolesPageActionGroup" stepKey="navigateToUserRoleGrid"/> <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> @@ -53,7 +53,7 @@ </after> <!--Log out of admin and login with newly created user--> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="adminUser" value="admin2"/> </actionGroup> <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="assertRestrictPage"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml index db81a7829160d..812158948d85f 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI command="config:set admin/security/use_form_key 1" stepKey="enableUrlSecretKeys"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI command="config:set admin/security/use_form_key 0" stepKey="disableUrlSecretKeys"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml index 4f215d20a7a36..b0fbdb8b5b596 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml @@ -21,7 +21,7 @@ </annotations> <!-- Logging in Magento admin and checking for Privacy policy footer in dashboard --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <closeAdminNotification stepKey="closeAdminNotification"/> <seeLink userInput="Privacy Policy" url="https://magento.com/sites/default/files/REVISED-MAGENTO-PRIVACY-POLICY.pdf" stepKey="seePrivacyPolicyLinkDashboard"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml index 0ff1e817ac0ea..97fdcdf9596a3 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml index 94bf5c6b8993a..39b08eeb2e42a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml index 3aae643ccc36b..81fef1afc282c 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml index df5ca6d037813..e9050fa3ccd6e 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml @@ -26,7 +26,7 @@ <magentoCLI command="config:set {{StorefrontDisableAddStoreCodeToUrls.path}} {{StorefrontDisableAddStoreCodeToUrls.value}}" stepKey="addStoreCodeToUrlDisable"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> </test> </tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml index 77dc83eec1176..9334b3beaa4c1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml @@ -23,7 +23,7 @@ </annotations> <before> <!--Login As Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--CreateNewProduct--> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -93,7 +93,7 @@ <!--SignOut--> <actionGroup ref="SignOut" stepKey="signOutFromNewUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Delete Product--> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml index 85d17a69ebae0..743eed209ebf4 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml @@ -44,7 +44,7 @@ </createData> <!--Login as Admin User--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index c67a207ebf0b1..0871a6723417f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <!--Deleting data--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml index aa06990f7af78..803beb30d677b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer on Storefront and bundle product --> <comment userInput="Create customer on Storefront and bundle product" stepKey="commentCreateData"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml index 3770e47079c98..73ced5eaf1592 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml index baf9652522909..30922839a191d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml @@ -46,7 +46,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml index c8977cbae1957..06a05e7a29cd9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index fcfd80ac8533c..7b65b2db0f160 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -18,7 +18,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml index 8a8e9dd275ee4..82da228e040dc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml @@ -32,7 +32,7 @@ <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the simple product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml index 1a7117fbf4485..26d7dddbcc378 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the simple product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml index a98d544aad3b6..8dad41ca780af 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml index a3e9a8af40a34..3ce398f9c33ba 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml @@ -21,7 +21,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiBundleProductPriceViewRange" stepKey="createDynamicBundleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml index 0c26fb1775bff..1f37468ea245c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="FixedBundleProduct" stepKey="createFixedBundleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml index fbb9dda50f0d9..2fe5981eb36c7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Admin login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml index dea39fcb45908..f34ebd4b93cdc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml index 28abd06253393..81bd46fdb5412 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 0af44f64f8df1..70fe7720f0d78 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <!-- Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index 77be5b879b1c6..c292d831fac13 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml index 173319affe53b..4172611d99b88 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml @@ -46,7 +46,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> </after> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to Catalog -> Catalog -> Products and Search created product in precondition and choose it --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct"> <argument name="product" value="$$createFixedBundleProduct$$"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index 171ed1323a6b2..c14da7e1cbfd0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml index 91a2d15287033..d08ad8970e19f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index 14753da609967..a3e5c988717a7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -18,7 +18,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index f1124e5446b58..ceea9e47af7ee 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 4760570adfa2e..6310f6497ac1e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml index 2b948ff02d38c..efef033f9d974 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index 296ba31baa589..7ded8b300f82d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 7ff88c49f0bc7..1a40489387197 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -66,7 +66,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!--Check subtotal in created order--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index 22b746ce69046..dc5344e91ec82 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml index 9fc19f5c5750f..85be636ec269c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index ed4a592b0d71e..1adad0f1a4d80 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index 15d5ff7d3560b..80681704533fc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <!-- Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml index 78a039eccde2d..65b20ca1dd7c1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Admin login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml index 30369d447ab15..753a36dc1e613 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml @@ -26,7 +26,7 @@ <!-- Add tier prices to simple products --> <!-- Simple product 1 --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPageProduct1"> <argument name="productId" value="$$simpleProduct1CreateBundleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 613187a4c3856..a5e0c0cc4658a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index d284ddf59ddcf..115b540e672f1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index f0e16bbdd1b99..93554917b6783 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index d388549745d51..ac2ab4806fd44 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -39,7 +39,7 @@ </createData> <!--Add special price to simple product--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> <argument name="productId" value="$$simpleProduct5.id$$"/> </actionGroup> diff --git a/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml index 45b4c4f5ededd..23d24369cad4a 100644 --- a/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml +++ b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml @@ -18,7 +18,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete products created via import --> @@ -28,7 +28,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSimpleProduct"> <argument name="sku" value="Simple"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- Create Bundle product via import --> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml index e5ee55910df65..b978bbf1ff55d 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml @@ -31,17 +31,17 @@ <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> <argument name="adminUser" value="AdminUserWrongCredentials" /> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeFirstLoginErrorMessage" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsSecondAttempt"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsSecondAttempt"> <argument name="adminUser" value="AdminUserWrongCredentials" /> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeSecondLoginErrorMessage" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsThirdAttempt"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsThirdAttempt"> <argument name="adminUser" value="AdminUserWrongCredentials" /> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeThirdLoginErrorMessage" /> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml index ee71a243a769d..3aa57917c43a8 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml @@ -19,7 +19,7 @@ </annotations> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Go to Captcha--> <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> <waitForPageLoad stepKey="WaitForPageLoaded"/> diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml index 16f2774e22ccd..59f1c13621982 100644 --- a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml @@ -17,7 +17,7 @@ <severity value="MINOR"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 1" stepKey="enableCardinalCommerce"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index 777bbcbcf37aa..e42dd8b8ab12e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="SimpleSubCategory" stepKey="category"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml index a7c0575e0cc96..83a05fef83607 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml @@ -30,7 +30,7 @@ <requiredEntity createDataKey="category1"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="logInAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logInAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index ce8ce5ead1153..92f24fe76502d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml index 367827f8c0c28..7cf388914207b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index adf13e4e31435..61d197d34a31d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -23,7 +23,7 @@ </annotations> <before> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml index 690f2440b2f3b..dcb900164eba7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index 9ee5e9138d764..4aa96c91eb299 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml index 016d11f8e660a..b2181164070dc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml @@ -36,7 +36,7 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Case: Group Price--> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> <argument name="product" value="$$createSimpleProduct$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml index 848fb7d1155f3..fd8c0ba29fdfa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml @@ -32,7 +32,7 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index 980760699dc71..e3fa3dada5b22 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -28,7 +28,7 @@ <requiredEntity createDataKey="attribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml index 03331b0d75cc5..72ffcad4a9dfc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml index 23c9513c7ab49..acd662401399e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml index 2e55d9fbfa4bc..3b8c2cb736721 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml @@ -33,7 +33,7 @@ </createData> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml index 9821acc4b9e5d..39351539d14a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml index 8eec2ed40c8cb..51d9e26ebd2cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -35,7 +35,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="reindexCatalogSearch"/> <!-- Login to Admin page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete created entities --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index 9b95ef726a617..f076f4fa407b4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Inactive and Not Include In Menu Category --> <createData entity="CatInactiveNotInMenu" stepKey="createCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml index 3f180939bc7ea..8a0b1977bf901 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Inactive Category --> <createData entity="CatNotActive" stepKey="createCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index 3edd82e060e2e..e832f2baf24a2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create inactive Include In Menu Parent Category --> <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml index 7d27bfb3a058b..dfde5779637a4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml @@ -25,7 +25,7 @@ </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml index 427ef6551ce9b..e707c15bccdf1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml @@ -26,7 +26,7 @@ </before> <after> <deleteData createDataKey="createCategoryWithChildrenBlank" stepKey="deleteCategoryWithChildrenBlank"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AssertAdminCategoryLevelByParentCategoryLevelActionGroup" stepKey="assertCategoryLevelByParentCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml index 9b7ef4077f906..a94610abf0918 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml index 433289f59f21b..e64707a895fd4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml @@ -21,7 +21,7 @@ <!--Set Display out of stock product--> <magentoCLI stepKey="setDisplayOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 1" /> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index c1f310575de2f..278a1fdc7ad9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -51,7 +51,7 @@ <createData entity="PaginationProduct" stepKey="simpleProduct28"/> <createData entity="PaginationProduct" stepKey="simpleProduct29"/> <createData entity="PaginationProduct" stepKey="simpleProduct30"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml index f2d6d9442fb18..c389d447feeba 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml @@ -31,7 +31,7 @@ <actionGroup ref="ResetAdminProductGridColumnsActionGroup" stepKey="resetAdminProductGridColumns"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml index 4ea294e3a3f36..ff1e65bca9acc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml index 86ab49d6f69c1..d3140dba85752 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category and product--> <comment userInput="Create category and product" stepKey="commentCreateCategoryAndProduct"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml index b5ab36729c7fe..a4414901517b9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml @@ -63,7 +63,7 @@ <deleteData createDataKey="productWithImages" stepKey="deleteProductWithImages"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Admin area: configure Product Image Placeholders--> <comment userInput="Configure product image placeholders in store config" stepKey="configurePlaceholderComment"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml index 9cb709a8c7e62..0525e7543accb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create related products --> <createData entity="SimpleProduct2" stepKey="createFirstRelatedProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml index 43fce1cfdd329..ce3dd8fa0873c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml @@ -29,7 +29,7 @@ <createData entity="secondCustomWebsite" stepKey="createWebsite"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete created virtual product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml index fde018ddc181a..65e67020e4532 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml @@ -31,7 +31,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Open Dropdown and select simple product option --> <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml index aba7bf6073117..04d032511ded0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="productAttributeWysiwyg" stepKey="createProductAttribute"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml index ef21b53c7613b..b94c12d1d7a39 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as admin --> <createData entity="SimpleTwo" stepKey="simpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml index a4fe694ea65a7..e0fa4a3c3410c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml @@ -18,7 +18,7 @@ <group value="category"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml index e6a2030e37d86..e8b13afc7138e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml @@ -18,7 +18,7 @@ <group value="category"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml index b6d1d4b81fffb..8ad76bdde7306 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml index 8900e85ca3431..7deb79c66ff6d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultBlock" stepKey="createDefaultCMSBlock"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml index ac237fbe49978..71167882d135d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="navigateToStoresIndex"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml index bc60f21b5d214..c1df630685949 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml index ca2b15658f02e..34714db67fbd6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml index f6b0a0a321c5d..a11717c04ea8c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml index 5342dec6b1ebb..ec3c7a26723c2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultBlock" stepKey="createDefaultCMSBlock"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml index 9a580df52259b..99d0f88cf0e9a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml index 110f52aaeb40a..871dd606c14db 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index 06d7d1081d058..7b2c67b205ea8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -23,7 +23,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml index e32aad76c9b28..37dc7de910917 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -17,7 +17,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml index 348c973b4de82..88b1c874caadc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml @@ -17,7 +17,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Remove attribute --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 4d2250d650da2..fef69edde23e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -31,7 +31,7 @@ <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="attribute" stepKey="deleteProductAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml index 4c91c6bac0e1a..a34712397e830 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleSubCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml index c6703a1109345..cc76304bc5ebc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -23,7 +23,7 @@ <requiredEntity createDataKey="category"/> </createData> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml index bff43cf65faf6..c3b86c99bbd67 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="CatNotActive" stepKey="createCategory"/> <!-- Create First StoreView --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml index 9ef3659cb5ab1..affdbaf7b0eb7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Create First StoreView --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml index 4623f9ad4005b..10e4d9f434be0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="category"/> <!-- Create First StoreView --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index b5b5fa3b12dc7..caacfde89d1cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -31,7 +31,7 @@ <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product attribute --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml index 4ffb81d9a1d67..e99643deed11d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml index 18fbcadf985c3..7c8ce1455fc37 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml index 6cd4ce6ac47c4..573fc1f83a5a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml @@ -23,7 +23,7 @@ <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> <!-- Login to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index ae4720e19118d..7fdab11d0a050 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml index a75cb093ec8ee..274a560d343d8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml index 2fbd1ac2cf321..d2278f3ddae1d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml index 3fb3db231c29c..91ea74f1089ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml @@ -31,7 +31,7 @@ <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct1"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml index 2ccec016b8e15..c461aa8bfcf18 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml @@ -26,7 +26,7 @@ <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 2352e231e66a4..9a0ff1d02c153 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -20,7 +20,7 @@ </annotations> <!--Delete all created data during the test execution and assign Default Root Category to Store--> <after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin2"/> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnPageAdminSystemStore"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad" /> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> @@ -38,7 +38,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout2"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml index 4188fc628064a..12cd5454ea8e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml index f1bb67bf66784..7317f2f7214f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml @@ -18,7 +18,7 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml index c3b74eb128ef6..a8cc66243d73e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml @@ -17,7 +17,7 @@ <testCaseId value="MAGETWO-89912"/> <group value="product"/> </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> <waitForPageLoad stepKey="wait1"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml index 1a95824014deb..3e5ccfd8bf3b9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml @@ -24,7 +24,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml index a0e51faf0897f..f4878f2948e9d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml @@ -17,7 +17,7 @@ <testCaseId value="MAGETWO-89910"/> <group value="product"/> </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> <waitForPageLoad stepKey="wait1"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml index 967babc617ce9..c378ca5b2c27a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI stepKey="setCountryOfManufacture" command="config:set catalog/fields_masks/sku" arguments="{{name}}-{{country_of_manufacture}}"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="setName" command="config:set catalog/fields_masks/sku" arguments="{{name}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml index 9660a46d43dba..8de84867241a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml @@ -26,7 +26,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="ProductWithUnicode"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml index bad620b3dab99..fd6db45b1716b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml @@ -26,7 +26,7 @@ <magentoCLI command="config:set {{EnableTinyMCE4.path}} {{EnableTinyMCE4.value}}" stepKey="enableTinyMCE4"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete attribute --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml index 6d39455e4a31b..9d3a47cd115aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml index df46983b361c6..842f93b49c14a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index 899f3af02c78e..8bb3391b5240b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 84cba791c6629..9305e9ed3fd49 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -22,7 +22,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> <createData entity="Simple_US_CA_Customer" stepKey="customer" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="categoryEntity" stepKey="deleteSimpleSubCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml index ea73de1cab15d..d8eaed92de4cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml index 685db6db90a10..3b5a8d8e753da 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml index bb4ff7acaa4a7..3dfeea2c33af0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <createData entity="SimpleProductWithCustomAttributeSet" stepKey="SimpleProductWithCustomAttributeSet"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml index 069789738d11f..7748d4bf4db6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml @@ -21,7 +21,7 @@ <!--Set Display Out Of Stock Product --> <magentoCLI stepKey="setDisplayOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 0 "/> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <!-- Create an attribute with two options to be used in the first child product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml index 9d52399f15a96..841b08e70fb4f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create Dropdown Product Attribute --> <createData entity="productDropDownAttribute" stepKey="attribute"/> <!-- Create Attribute set --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml index 20d2a924954cb..c0cbb44ebc681 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="productAttributeWysiwyg" stepKey="createProductAttribute"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml index 834da3f4d4f9b..22e1d7d7c5d9e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml index 386077396e4a7..0b857c6d3bbaf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website, store and store view--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{NewWebSiteData.name}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml index 77ebf77f05e58..0396ab743c745 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml index 4974b983beeb9..0a41842d5709c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml index 48422d9ba2025..6ae00d213bf77 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml index b5d7f413730aa..390002f5d9498 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml index be54262987b05..22c6bf061f274 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml index 3d6eca36ec06d..36001bd0b570a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create Product Attribute and assign to Default Product Attribute Set --> <createData entity="newProductAttribute" stepKey="attribute"/> <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml index 642fb1c1f7ba0..f49e1142315eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="defaultVirtualProduct" stepKey="createVirtualProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml index 8ce478ff48469..f2a7ffcce0eb0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml @@ -31,7 +31,7 @@ <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml index 7f973c99b39a5..843221782ebd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index a0dbb7cea5e16..89b3b2cb6fb41 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -18,7 +18,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website, Store and Store View--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml index a42fe576751f7..f6b2a74eca0f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Clear product grid--> <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index cc21740cbc59f..c319116bf075c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -35,7 +35,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created data--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml index 6896b11196cf2..0214f9141b903 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml @@ -20,7 +20,7 @@ <group value="Product Attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index f803050b7a59b..e8e0d449aee4e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct1"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct2"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 0fbbca2602e86..31b5961edaaaa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -20,7 +20,7 @@ <group value="product_attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml index a4c8f5e8cff6c..e4d69e9169613 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml @@ -20,7 +20,7 @@ <group value="Product Attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml index ffaf8501ea71f..7cdfd6dabed47 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml @@ -20,7 +20,7 @@ <group value="SearchEngineMysql"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index 308a240eda184..08b2d924e2a5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -20,7 +20,7 @@ <group value="SearchEngineElasticsearch"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 820a578e0252f..72092af331974 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -35,7 +35,7 @@ <magentoCLI command="cron:run" arguments="--group='index'" stepKey="firstRunToScheduleJobs"/> <magentoCLI command="cron:run" arguments="--group='index'" stepKey="secondRunToExecuteJobs"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml index 2122d73ca7e62..1ea1f60f52a2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml @@ -18,7 +18,7 @@ <features value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml index ac766feef1742..39040c7e88205 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="FirstLevelSubCat" stepKey="createDefaultCategory"> <field key="is_active">true</field> </createData> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml index 393d0c49c2e93..7fdc6e7b9cf8f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml @@ -18,7 +18,7 @@ <features value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml index f49b9b5b5d3e3..2909d17b1541b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index d3d435b7451c3..50c7fa38d881e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> <createData entity="_defaultCategory" stepKey="createAnchoredCategory1"/> <createData entity="_defaultCategory" stepKey="createSecondCategory"/> @@ -163,7 +163,7 @@ <dontSee userInput="$$simpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/> <!-- Log in to the backend: Admin user is logged in--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAdmin"/> <!-- Navigate to the Catalog > Products: Navigate to the Catalog>Products --> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="amOnProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml index d0e466114a1d4..659521ed9e467 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml @@ -74,7 +74,7 @@ </createData> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!--Logout as admin--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index 4a4a4a6833b33..b8e7bdae0df2b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -23,7 +23,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create category A without products --> <createData entity="_defaultCategory" stepKey="createCategoryA"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml index c62c6b4b4c6a9..ee34d8286fe32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml @@ -28,7 +28,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"/> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> <argument name="customStore" value="storeViewData"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml index 5593e8b56422f..9536ee030cdf8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Login as admin and delete all products --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!--Create dropdown product attribute--> <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml index 8c334cb84be01..d47730a99308b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -19,7 +19,7 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProductWithNewFromDate" stepKey="createSimpleProductWithDate"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml index f32845072ec02..35632f4fa6311 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Store View English --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreEN"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml index 5f089aad256b7..ae63158990b96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml index 4966e94463e98..12d654508d7d7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml @@ -21,7 +21,7 @@ <before> <!-- Add downloadable domains --> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <comment userInput="Create product" stepKey="commentCreateProduct"/> <createData entity="VirtualProduct" stepKey="createProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml index f46a3aea0ef81..96ee795998459 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml @@ -30,7 +30,7 @@ <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductFilter"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Edit Simple Product --> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createProduct.id$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 3b750c2cdb21c..00eaa623e2bca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml index 6a68928be8c70..6cc1b256e5ec9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index baa952f6bcf45..60c32004e3ca8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -23,7 +23,7 @@ </annotations> <before> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml index 5e29bf30b4bf2..6cd76c4cc06b8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Create 2 websites (with stores, store views)--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="category"/> <createData entity="_defaultProduct" stepKey="product"> <requiredEntity createDataKey="category"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml index b3e5900c9bb76..ee8636dece808 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml index d2822a53b1a73..0281fded3a8e4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForElementVisible selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="waitForAddSubCategoryVisible"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml index 5f489e337b01a..f0a8ad298f014 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -33,7 +33,7 @@ </actionGroup> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Delete created data--> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> @@ -75,7 +75,7 @@ <!--Log out of admin and login with newly created user--> <comment userInput="Log out of admin and login with newly created user" stepKey="commentLoginWithNewUser"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="adminUser" value="admin2"/> </actionGroup> <!--Go to create product page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml index 190a051c16d44..00b9e77d5fe5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml @@ -23,7 +23,7 @@ <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToYes"/> <createData entity="secondCustomWebsite" stepKey="createCustomWebsite"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminStoreGroupCreateActionGroup" stepKey="createNewStore"> <argument name="Website" value="secondCustomWebsite"/> <argument name="storeGroup" value="customStoreGroup"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml index c0f121edcf6bd..3ee8d0e3c2571 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml @@ -33,7 +33,7 @@ </after> <!--check admin for valid Enable Status label--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> <argument name="productId" value="$$createSimpleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml index 8bb65045fdde9..9819890ed3751 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml @@ -26,7 +26,7 @@ <createData entity="_defaultProduct" stepKey="secondProduct"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml index 53ccb0257aa19..ec82bdcf5bc94 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultProduct" stepKey="product"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml index 80e245818e216..51a91a17ff41a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml index 76e0bdd5d46ad..534924e0f70c9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 16bc76cb6446a..71e827a64ae2d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml index 544ab05d8783b..c1538cf8c5329 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml index 37571d7b44635..e0a2c4272372a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index 0ce506fe1918d..9838283bbfb96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 307eceddae3ef..5fe71ce0a1e5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -30,7 +30,7 @@ <requiredEntity createDataKey="attribute"/> </createData> <createData entity="ApiProductWithDescription" stepKey="product"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="product" stepKey="deleteProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml index 4b1cd1f674425..f3225bcd7fe3d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml index 4eab0ca8f0694..2865e9bc87dd3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml index 51d8b9e1eaf37..3c3baf3524e87 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml index 3c99e86dba2c9..8b57c2e371355 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml @@ -26,7 +26,7 @@ </after> <!-- Create category, change store view to default --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <actionGroup ref="CreateCategoryActionGroup" stepKey="createCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml index 6ecb7e09d5a2c..117019da38ffa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml index d6c581b18beff..9663632e6dc36 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml @@ -19,7 +19,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml index 6dde1567b68f8..ccab2f9e96ce7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> <createData entity="_defaultCategory" stepKey="createCategory"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml index 8a31145f7349d..512df79cb7c9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Simple Product --> <createData entity="SimpleSubCategory" stepKey="category"/> <!-- Create category --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml index 6575fd1f1c977..f444e741fea0e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> <!-- Create First StoreView --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml index 2c356636df56c..2b9cbf379c5e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml @@ -19,7 +19,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!-- Create First StoreView --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml index 2c45e957d801c..6edffb923d540 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml index 4e80f95bbf390..e954de90ef542 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index 7096e547c5aa7..f5b0fb8054dc1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml index d0935948e88bd..d20594461173b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml index e9c2ed1511ce3..5fa7acbeb8de9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml index 17a91ed2cf4f4..259eb01d9bc5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml index 84f2c4552ae6c..45f8918100083 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="_defaultProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 423a7d23d7d4a..58db163bed720 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 29a4c5e009d07..5e9a48f659d6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml index 5c196744f0181..3d37b54dfa439 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index 00b6a5def6169..c924c94a9aba8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml index ec19a2a496f9f..af836efcf6be6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml index f499d87c84682..db8b808e74f76 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml @@ -29,7 +29,7 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml index eabedebaeab83..c5757a6428e8d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create three level nested category --> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="Two_nested_categories" stepKey="createTwoLevelNestedCategories"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml index bd1a5aaf9ed42..9146ee4d4d579 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml @@ -20,7 +20,7 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml index a667d08b8e25f..5115399db9e3b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <createData entity="ApiProductWithDescription" stepKey="product"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml index 50ca013050ea7..cacf4f3f4c9f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <createData entity="ApiProductWithDescription" stepKey="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index 1fe42a331c80c..d89ffa0055d3b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -35,7 +35,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml index 850c0e98c45f3..e50c124f7cfd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml @@ -26,7 +26,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="AdminCreateSimpleProductWithTextOptionCharLimitActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml index 6ee37221acc93..437532b9baebf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml @@ -23,7 +23,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml index dc38062a99c53..580a5bd4939bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml index b8b2224abf5a1..e24bf0d7b1115 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml index 50606994cace8..0a84d9af3c918 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml index f1a19a9ece8d5..97eff20b2d560 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml index 3495069face61..c0cff7b0b2bc9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index 604c01f05b838..f70979285446a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -41,7 +41,7 @@ <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createProduct3" stepKey="deleteProduct3"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad time="30" stepKey="waitForPageCategoryLoadAfterNavigate"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createNewRootCategoryA.name$$)}}" stepKey="openNewRootCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml index 026f3ce7067f6..d2b9fba0895ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml @@ -59,7 +59,7 @@ </createData> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the configurable product created in the before block --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml index 5a57bd844aa8d..f6ede46578f33 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml @@ -23,7 +23,7 @@ <!-- Create category, flush cache and log in --> <comment userInput="Create category, flush cache and log in" stepKey="createCategoryAndLogIn"/> <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="logInAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logInAsAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 43208f336dbcd..151046f0474e0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -25,7 +25,7 @@ </after> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Admin creates product--> <!--Create Simple Product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml index 79fe21e0c0d77..916bcd7405ecf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="textProductAttribute" stepKey="createProductAttribute"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index da7165600d64d..ea9a4cb08bb08 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -29,7 +29,7 @@ <requiredEntity createDataKey="simpleSubCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 783054c3ffb5a..b206a33ebde88 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -19,7 +19,7 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="Second Website"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml index f5ac8b243979f..7b2e004495fea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- log in as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml index d84e2283b2dd4..16f3c6ae25436 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -18,7 +18,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product and Category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> @@ -73,7 +73,7 @@ <argument name="pageId" value="{{CmsHomePageContent.page_id}}"/> </actionGroup> <!-- Logout Admin --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCacheAfterDeletion"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index d454a3d24e273..9fbdde86bb5ee 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -18,7 +18,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product and Category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> @@ -63,7 +63,7 @@ </actionGroup> <!-- Logout Admin --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCacheAfterDeletion"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml index d642f16160c5b..c38ea2de558e1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml @@ -32,7 +32,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> <argument name="productId" value="$createProduct.id$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml index 2f4b6a90bd1e5..2a59be6306a30 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToDefault"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml index 00f9608d07c3e..b6ba5245645eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="category1"/> <createData entity="SimpleSubCategory" stepKey="category2"/> <createData entity="SimpleSubCategory" stepKey="category3"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml index aacce53819178..b13c3827c6727 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create 37 Products and Subcategory --> <comment userInput="Create 37 Products and Subcategory" stepKey="commentCreateData"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml index 059b90532f8f9..c22f91b5394ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -162,7 +162,7 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml index be063d2387b25..8acc6272d5da4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml @@ -52,7 +52,7 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete reviews --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml index 68d847907a448..df1eb0c502e18 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml @@ -21,7 +21,7 @@ <createData entity="_defaultProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml index 5c13e5a47973a..bcd5d7b851db3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml @@ -24,7 +24,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product via admin--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index 40733b120f1e8..a311b63418a69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -33,7 +33,7 @@ <!-- Reindex invalidated indices after product attribute has been created/deleted --> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml index 849de20991c62..c7817ed181ae0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -37,7 +37,7 @@ <!-- Reindex invalidated indices after product attribute has been created/deleted --> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index a8ab4cca1ac78..5659f053613c2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -32,7 +32,7 @@ <field key="price">100</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!--Create storeView 1--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index 066337bf25cb6..09b596f298e0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -128,7 +128,7 @@ <!-- Login to Admin and open Order --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterByOrderId"> <argument name="orderId" value="$grabOrderNumber"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml index b8aed7f0ac2ad..3c1dc2bc844cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml @@ -96,7 +96,7 @@ <!-- Login to Admin and open Order --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 5bc8427db022f..0dccc409a1032 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 0413018128491..26cebae318cd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -29,7 +29,7 @@ <deleteData createDataKey="createPreReqSimpleProduct" stepKey="deletePreReqSimpleProduct"/> </after> <!--Step1. Login as admin. Go to Catalog > Products page. Filtering *prod1*. Open *prod1* to edit--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> <argument name="product" value="SimpleProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index c30c750fec10c..b4514c9b53736 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -49,7 +49,7 @@ <!-- Product C in 2 categories M and N --> <createData entity="SimpleProduct2" stepKey="productC"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryNAndMToProductC"> <argument name="productId" value="$$productC.id$$"/> <argument name="categoryName" value="$$categoryN.name$$, $$categoryM.name$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index 2a6a05c8ffeab..94b16ac8bddce 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -27,7 +27,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 6dab378fe6456..2b4609f1219df 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -85,7 +85,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete products creations --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index 8f9e1bd49af0e..3449a39679666 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -53,7 +53,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Deleted created products --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 6b6ee2e0fcc58..64b1680d2e9a1 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -127,7 +127,7 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Remove downloadable domains --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 1168033172d23..ac730a6c6a7cc 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -78,7 +78,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product creation --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index cc25baee2c9f4..a0a3efdedadc8 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -94,7 +94,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product creation --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 13a0f92076a57..f9669dfbdac27 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -76,7 +76,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index cd65416460ce6..2e8e62667b314 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -33,7 +33,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product creations --> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml index edbf7451c6b6b..9db6f250dc114 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml @@ -23,7 +23,7 @@ <before> <createData entity="DefaultValueForMaxSaleQty" stepKey="setDefaultValueForMaxSaleQty"/> <createData entity="SimpleProduct2" stepKey="createdProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <createData entity="DefaultValueForMaxSaleQty" stepKey="setDefaultValueForMaxSaleQty"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index b1ab6a598eb88..b291fb46ae71c 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -113,7 +113,7 @@ <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="StorefrontSignOutActionGroup"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index b2ce64fe651c0..4d97338333be8 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -27,7 +27,7 @@ <requiredEntity createDataKey="createCategoryTwo"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategoryOne" stepKey="deleteCategoryOne"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml index f8b477e796c9d..9cc0090fef756 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="ApiCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml index d033e03bb4916..8c3d1dad1b2a3 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml @@ -29,7 +29,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml index 93291104e7fc9..65ef23de65380 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the simple product and category --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml index a4a7ba012ac98..90a0835508b06 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml @@ -17,7 +17,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml index 4211f0fc76508..1d4b21cb04a60 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminSearchCatalogRuleInGridActionGroup" stepKey="searchCreatedCatalogRule"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml index 93797bcda5411..5fb33c9482709 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml @@ -75,7 +75,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml index 91ccfb458e98f..fc37fc893f871 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml @@ -25,7 +25,7 @@ <requiredEntity createDataKey="createCategory1"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index dfb846e90b669..c6b3569f3b597 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -26,7 +26,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <!-- Login to Admin page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="_defaultProduct"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml index fdd9b4788d315..973d73da2ab6a 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml @@ -20,7 +20,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="ApiCategory" stepKey="createFirstCategory"/> <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml index 2a45de28db199..60b16276f6c17 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml index 400b03c9af21e..0747d42dc8737 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml @@ -21,7 +21,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="ApiCategory" stepKey="createFirstCategory"/> <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index d79a3805d79a4..101883f5ea9f2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Simple Product --> <createData entity="ApiCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml index 0149a2d0a6abb..552a6dc48eb7f 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml index c70a72d725489..6e8cb01f64ea2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create new customer group --> <createData entity="CustomCustomerGroup" stepKey="customerGroup" /> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml index 9ebab2d28249a..aa20a1d72d063 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml index c752ce5c07636..6ac9f713e2844 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml @@ -27,7 +27,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Catalog Rule--> <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index cf34a01498e6f..46ba6e30100b1 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -24,7 +24,7 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml index aa2b41b96979f..48f53da8e2a2e 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml @@ -144,7 +144,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <!-- Delete created data --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index ff0c9058037df..e4dd65479b784 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml @@ -145,7 +145,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <!-- Customer log out --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml index b011719495d2b..350f896606c19 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml @@ -92,7 +92,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <!-- Delete created data --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index b7cae5980239b..8ed9c6ad09dad 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml @@ -93,7 +93,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <!-- Delete created data --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml index 2ab87b3ceb967..7ac06c0342229 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create simple product --> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml index 6be63541f3c27..a0abefebcb6b4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml index e1a965bd08e0b..42c6d13784a98 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml index 6ae215f821a0b..437357ba861f2 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml @@ -25,7 +25,7 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml index f6cd95b4f5a3d..49fce41fddf05 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml @@ -37,7 +37,7 @@ <field key="qty">10</field> </createData> <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml index 844c100830bc2..4b0a5c84ac360 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml @@ -48,7 +48,7 @@ </createData> <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml index e4918536245cf..35db90363b1ae 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml index 0d062c832090c..cf30e4d06e8e7 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <createData entity="ApiProductWithDescription" stepKey="simple1"/> <createData entity="ApiGroupedProduct" stepKey="createProduct"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml index 0d2009f21aac1..00a7ffa3fc142 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml @@ -26,7 +26,7 @@ <createData entity="productAlphabeticalB" stepKey="product2"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Create and Assign Attribute to product1--> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml index f032a97ac297c..968435747bdbb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Delete all products left by prev tests because it sensitive for search--> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!-- Create Data --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml index 954f53814f3c3..6f510fa315d7d 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml @@ -31,7 +31,7 @@ </createData> <!-- Assign attribute to set --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="GoToAttributeGridPageActionGroup" stepKey="goToAttributeSetPage"/> <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="openAttributeSetByName"> <argument name="name" value="$createAttributeSet.attribute_set_name$"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml index 502301939f71a..3c0c7cf53d0a1 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml @@ -48,7 +48,7 @@ <argument name="phrase" value="$$createProduct1.name$$"/> </actionGroup> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 5d02e2075430e..7d75364cc9b50 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -20,7 +20,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created categories--> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml index db4811273a5cc..e8a1ee850cd74 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml @@ -17,7 +17,7 @@ <magentoCLI command="config:set {{EnableCategoriesPathProductUrls.path}} {{EnableCategoriesPathProductUrls.value}}" stepKey="enableUseCategoriesPath"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategoryDifferentUrlStore" stepKey="defaultCategory"/> <createData entity="SimpleSubCategoryDifferentUrlStore" stepKey="subCategory"> @@ -30,7 +30,7 @@ <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> <magentoCLI command="config:set {{DisableCategoriesPathProductUrls.path}} {{DisableCategoriesPathProductUrls.value}}" stepKey="disableUseCategoriesPath"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 62ef26145619b..456458a4fad40 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -21,7 +21,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="_defaultCategory" stepKey="category"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index d61a4bd077d9c..8764555f94357 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategory" stepKey="defaultCategory"/> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml index 8d4e97420dd0b..288134a889db0 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml @@ -37,7 +37,7 @@ <createData entity="_defaultBlock" stepKey="createPreReqBlock"/> <!--User log in on back-end as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml index ea6a3a73522e7..fd87d58e47125 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml @@ -37,7 +37,7 @@ <field key="price">30</field> </createData> <createData entity="_defaultCmsPage" stepKey="createPreReqPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <!--Open created cms page--> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml index 74e1110c95636..5590aa1cdcefa 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml @@ -36,7 +36,7 @@ </createData> <!-- 2. Create new CMS page and add "Catalog Product List" widget type via content >insert widget option --> <createData entity="_emptyCmsPage" stepKey="createCmsPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCmsPage"> <argument name="CMSPage" value="$createCmsPage$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml index a9c3d20c447a9..ab0453e1faa18 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml @@ -75,7 +75,7 @@ <!-- Open new browser's window and login as Admin --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to Store > Configuration > Sales > Shipping Methods --> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml index 294fc2c562491..58a44f5b89f90 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="defaultCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Go to configuration general page--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml index 899688c80764e..016616a27a05a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml @@ -33,7 +33,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Set configs to default --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml index c88025feba3d4..3215503b3205a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml @@ -82,7 +82,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml index 4065b3691b250..76a998fec8adc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml @@ -95,7 +95,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index 571fb8c4cf3a5..340ff4159900a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -83,7 +83,7 @@ <seeElement selector="{{CheckoutSuccessMainSection.success}}" stepKey="orderIsSuccessfullyPlaced"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml index 6b7feb485c812..1c03808ac71cf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml @@ -76,7 +76,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index d362f83043c2a..e678bb0d2a87b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -195,7 +195,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml index d0fb6babb22fa..bd81a1cfab604 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml @@ -54,7 +54,7 @@ <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CustomerEntityOne.password}}" stepKey="TypeConfirmationPassword"/> <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> <see userInput="Thank you for registering" stepKey="verifyAccountCreated"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> <amOnPage url="{{AdminOrderPage.url({$grabOrderNumber})}}" stepKey="navigateToOrderPage"/> <waitForPageLoad stepKey="waitForCreatedOrderPage"/> <see stepKey="seeCustomerName" userInput="{{CustomerEntityOne.firstname}}" selector="{{AdminShipmentOrderInformationSection.customerName}}"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index ba26b44c11ba3..57ed8e442af7c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"> <field key="group_id">1</field> </createData> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml index 58cc137efd7b3..e4eb53a1f1925 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml @@ -77,7 +77,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Verify total on order page --> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml index 7405a3100728f..444ebf653b94f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml @@ -61,7 +61,7 @@ </actionGroup> <!-- Login to admin panel --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <!-- Disabled bundle product from grid --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml index 80da3fb70f944..ce7c5c3b29353 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml @@ -19,7 +19,7 @@ <before> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> </createData> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml index 85e6a6b9c434c..16c7a0609a6ff 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> </createData> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml index dee2bb16a63d0..787d8f58891d0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> <field key="price">10.00</field> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml index f8e1f32e93f52..b962d80a4d88b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml @@ -67,7 +67,7 @@ <!-- Login to admin panel --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml index f0907e89f00da..5fbfdb5a07678 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{DisableGuestCheckoutConfigData.path}} {{DisableGuestCheckoutConfigData.value}}" stepKey="disableGuestCheckout"/> <magentoCLI command="config:set {{DisableCustomerRedirectToDashboardConfigData.path}} {{DisableCustomerRedirectToDashboardConfigData.value}}" stepKey="disableCustomerRedirect"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml index 8c58827812d99..b5f573aba7561 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml @@ -60,7 +60,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="orderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="addFilterToGridAndOpenOrder"> <argument name="orderId" value="{$orderNumber}"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml index 0edc919920103..4c3c1561a2445 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml @@ -24,7 +24,7 @@ </createData> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml index c616faf716f03..0f82302260995 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{DisableGuestCheckoutConfigData.path}} {{DisableGuestCheckoutConfigData.value}}" stepKey="disableGuestCheckout"/> <magentoCLI command="config:set {{DisableCustomerRedirectToDashboardConfigData.path}} {{DisableCustomerRedirectToDashboardConfigData.value}}" stepKey="disableCustomerRedirect"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml index c66c6371ae595..0c762519e9083 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="Simple_GB_Customer" stepKey="createCustomer"/> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="SelectCountriesWithRequiredRegionActionGroup" stepKey="setCustomCountryWithRequiredRegion"> <argument name="countries" value="CustomCountryWithRequiredRegion"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml index f43cadabfd611..9152949fbf2a1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete simple product --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml index 1690612fa3242..fe320fb276c8a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml index 92e185d1bbb00..eb8b047b57288 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index a60fe104ce14b..3c234602df17a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml index 992d3eab9b563..b460bc2bb3446 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create 10 simple products--> <createData entity="SimpleProduct2" stepKey="simpleProduct1"> <field key="price">10.00</field> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml index a6ac6d40a0ce0..643c6d8c14dd7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create simple product--> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">10.00</field> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml index 9e0f59f8a0e77..a7bb3d927f620 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create simple product--> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">10.00</field> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml index 05a136e4c312a..5c06f9ed55067 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml @@ -56,7 +56,7 @@ </actionGroup> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml index 7f4e6f0201ce5..2787627b935c1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml @@ -21,7 +21,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRate"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <createData entity="MinimumOrderAmount100" stepKey="minimumOrderAmount100"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml index c217eca5053c1..b63766e0cd374 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="VirtualProduct" stepKey="virtualProduct"> <field key="price">50.00</field> </createData> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 71c6c7863f07c..e6e5c4f1a9299 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -20,7 +20,7 @@ <group value="checkout"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create a product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml index 44e12d1ea4039..146db30aa7c86 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> <magentoCLI command="config:set {{EnableCatalogInventoryConfigData.path}} {{EnableCatalogInventoryConfigData.value}}" stepKey="enableCatalogInventoryStock"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml index 90896c3eb403e..43b2262265841 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create simple product --> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index 293abcb8197e1..849b586594955 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -109,7 +109,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="orderId"/> <!-- Login to Admin Page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Open Order Index Page --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml index 04ee2e2adbf28..ec99bb0e46722 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> <field key="price">10.00</field> </createData> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml index bb3bd50072f23..642814ded6e3e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index e3a5546311f3b..12e2820821c87 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -22,7 +22,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> <field key="price">100</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SetCustomerDataLifetimeActionGroup" stepKey="setCustomerDataLifetime"> <argument name="minutes" value="1"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 781253a707271..542c0ed6586e9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -26,7 +26,7 @@ <createData entity="PaymentMethodsSettingConfig" stepKey="paymentMethodsSettingConfig"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <!--Go to Admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml index 0cc45df4ddf71..3558f94bb78e6 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> <createData entity="SimpleTwo" stepKey="createdProduct"/> <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> <argument name="term" value="activeTextTerm"/> @@ -30,7 +30,7 @@ <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml index a577f9853ea1d..7c2aedceb9b7e 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml @@ -19,7 +19,7 @@ <testCaseId value="MAGETWO-95725"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> <!-- Choose TinyMCE3 as the default WYSIWYG editor--> <magentoCLI command="config:set cms/wysiwyg/editor Magento_Tinymce3/tinymce3Adapter" stepKey="enableTinyMCE3"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml index 3c6d70dc53418..446ef1b9f3b93 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml @@ -21,7 +21,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml index 036efab75f963..e3fe278c4449e 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml @@ -21,7 +21,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml index 1b3a7e74af08f..4bb56dddec963 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml @@ -22,7 +22,7 @@ </annotations> <before> <magentoCLI command="config:set {{StorefrontSingleStoreModeEnabledConfigData.path}} {{StorefrontSingleStoreModeEnabledConfigData.value}}" stepKey="enableSingleStoreMode" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI command="config:set {{StorefrontSingleStoreModeDisabledConfigData.path}} {{StorefrontSingleStoreModeDisabledConfigData.value}}" stepKey="disableSingleStoreMode" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml index a097a6d11403e..596503cd8faab 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml @@ -21,7 +21,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml index 9c2f1abc4d522..aac56e16a53c4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml @@ -21,7 +21,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml index 8114310e46f73..0644542ccc0f3 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml @@ -28,7 +28,7 @@ <after> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml index 4c9ef31b0d202..9a02104d8d6ef 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml @@ -21,7 +21,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCmsPage" stepKey="firstCMSPage" /> <createData entity="_duplicatedCMSPage" stepKey="secondCMSPage" /> </before> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml index 99990595fca95..0eac31c891e64 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml @@ -19,7 +19,7 @@ <group value="Cms"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml index bb15904540be4..7d3946ea86c92 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml index c7726e7e427ce..bd24e4b082edc 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml index 07f033b18ea39..f2c84dea13f97 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml @@ -19,7 +19,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> </before> <amOnPage url="{{CmsNewBlock.url}}" stepKey="amOnBlocksCreationForm"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml index 8f4ab2b4d2f96..51fbbf8020545 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -21,12 +21,12 @@ <before> <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenCmsBlockActionGroup" stepKey="openCmsBlock"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml index b7f3140a79e7d..5ccd8fa713cc0 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -21,12 +21,12 @@ <before> <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml index e2800c2ac3094..b1073c1a0f41d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml index 58adae02298b7..c69cd620b1d72 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="enableTinyMCE4"/> <waitForPageLoad stepKey="waitConfigToSave"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index 8d4326040c919..fe3e69880fc5c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> <argument name="websiteCode" value="second_website"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml index a73e41de6b861..2fd31fffa838d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml @@ -20,7 +20,7 @@ <group value="Cms"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Cms Pages --> <createData entity="_newDefaultCmsPage" stepKey="createFirstCmsPage"/> diff --git a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml index 32fa1d13023de..9e05f4819cd73 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml @@ -24,7 +24,7 @@ <after> <magentoCLI stepKey="flushCache" command="cache:flush"/> <createData entity="DisableAdminAccountAllowCountry" stepKey="setDefaultValueForAllowCountries"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> <argument name="customerEmail" value="CustomerEntityOne.email"/> </actionGroup> diff --git a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml index 9700d8024ce8f..d4fcbb06ea2d3 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml @@ -20,7 +20,7 @@ <group value="configuration"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml index 9e96b5847b8e7..0d83cc6610194 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryHandle"/> <createData entity="SimpleProduct" stepKey="simple1Handle"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml index 07d9719b24d96..72ebd7962f420 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml @@ -21,7 +21,7 @@ <before> <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml index a6c3794a2d622..d58e7cfab1350 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml index c6a277295632b..8962efbb8dd26 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml @@ -20,7 +20,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="dropdownProductAttribute" stepKey="createProductAttribute"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml index 21619ca911d8a..7d75f5d53c1f4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -69,7 +69,7 @@ <requiredEntity createDataKey="createConfigProductAttribute2"/> </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Add created attributes with options to Attribute Set --> <actionGroup ref="AdminAddUnassignedAttributeToGroupActionGroup" stepKey="createDefaultAttributeSet"> <argument name="label" value="mySet"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index 2a2ef1947fdab..dc8c09864d0ab 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -21,7 +21,7 @@ <before> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Category--> <createData entity="ApiCategory" stepKey="createCategory"/> <!--Create Configurable product--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml index 9bd4161a80cf7..59da88874f5b2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml index ab0336637c12c..274a75aedbc5f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml @@ -24,7 +24,7 @@ <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml index 7c474a135c334..a7615d5565828 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml @@ -120,7 +120,7 @@ <requiredEntity createDataKey="createConfigChildProduct6"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml index de904cf854171..807ea69bb3958 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml @@ -80,7 +80,7 @@ <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> <!-- go to admin and delete --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="wait2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml index 9d01438a3c423..10cdcea2855d6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml @@ -39,7 +39,7 @@ </getData> <!--Create Category--> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml index a5d7e97c33b66..8d2f80ef262fd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml @@ -72,7 +72,7 @@ </createData> <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml index d0f686f9cbc0a..3121725c23fe9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml @@ -72,7 +72,7 @@ </createData> <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml index e9fc3443a9d56..2e8bf8ff66933 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml @@ -72,7 +72,7 @@ </createData> <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml index 4dd73ea3ce562..a35ef058dfd80 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml @@ -63,7 +63,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml index d2d5aa969bd50..6d9015b5d1cbf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml @@ -63,7 +63,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml index 4f1ab935a84d6..4b6baf8c58493 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml @@ -91,7 +91,7 @@ </createData> <!-- login --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml index 8b7fc64d265a0..56f53519e69af 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml @@ -76,7 +76,7 @@ </createData> <!-- login --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml index a6301978dc96f..589f20d0d544c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml index b2a2c1300d00a..bd409d0e4bfde 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml @@ -29,7 +29,7 @@ <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml index bd1b17ca46099..1eb3df993dd1c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml @@ -81,7 +81,7 @@ <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> <!--go to admin and disable an option--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> <argument name="productId" value="$$createConfigProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml index 09407c40db8a1..00b17fda944f1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml @@ -81,7 +81,7 @@ <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> <!--check admin for both options--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> <argument name="productId" value="$$createConfigProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml index 68d6423b43c07..bef7d26c5007f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml index 6f5e8ddd54759..796a4628393bb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml index 7593242c4f716..db5c824341c57 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml @@ -39,7 +39,7 @@ </after> <!-- Create configurable product from downloadable product page--> <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Open Dropdown and select downloadable product option --> <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml index 41ba0fc048120..78cbca2d4c099 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product with children products --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml index cef95ec7835d0..3913139c9b7e6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml index 261b307a0718c..3bf5666d5a997 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml @@ -36,7 +36,7 @@ <requiredEntity createDataKey="getConfigAttributeOption"/> </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Don't display out of stock product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml index 7d863b299f384..fa8866fa7d91c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml @@ -42,7 +42,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml index 14304d93b3c28..e76d14f3a6aae 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml @@ -57,7 +57,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Don't display out of stock product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml index c6bb6fdf7e7e8..9516216d4a62e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml @@ -57,7 +57,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml index 96ffecaf337ae..660eb82a9eacb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml @@ -50,7 +50,7 @@ <createData entity="tierProductPrice" stepKey="addTierPrice"> <requiredEntity createDataKey="createFirstSimpleProduct" /> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 4c39b0c2a54fb..f57fc81294cbc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -22,7 +22,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index 31851fc78968b..977eb2bdba0fe 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml index 68f86a7d07890..e625a1cf6f2be 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="BaseConfigurableProduct" stepKey="createConfigurableProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml index e7006591c2a8f..c5fdbfb36490a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml @@ -19,7 +19,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <comment userInput="Create product" stepKey="commentCreateProduct"/> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml index 43f00fcef9993..90a396b970c3a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml @@ -19,7 +19,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <comment userInput="Create product" stepKey="commentCreateProduct"/> <createData entity="VirtualProduct" stepKey="createProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml index d9300ad1b290b..b6b3d21c8a626 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml @@ -78,7 +78,7 @@ <requiredEntity createDataKey="baseConfigProductHandle"/> <requiredEntity createDataKey="childProductHandle2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index a1a1bb5c8a35a..86d4070a9a2c8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryHandle"/> <createData entity="SimpleProduct" stepKey="simple1Handle"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index bb16d04dfc94a..e064e635de1cd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -21,7 +21,7 @@ <before> <!-- Log in to Dashboard page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 23c3de4675718..a34dfd06ce844 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -64,7 +64,7 @@ <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml index 18993269ab0b1..c2ac4dde21c35 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -18,7 +18,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create Configurable product --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index 99d905b6d5467..54e23470ae786 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -20,7 +20,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create category--> <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index a2cfd47eb3402..355c4fa049f01 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -26,7 +26,7 @@ <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index d52cf1978d7fe..10ea3a7e400c4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -114,7 +114,7 @@ <requiredEntity createDataKey="createConfigProductAttributeSelect"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Go to the product page for the first product --> <amOnPage stepKey="goToProductGrid" url="{{ProductCatalogPage.url}}"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml index fa635dfb94e78..317563a468d74 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml index a9e3d725bb079..9fc6dce10c21a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml index 154fcca60ed6b..01859f995b00b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml index 88171e3668119..1aef8c33785ca 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml index 4dc6030e2ce4c..e51250c76bfe7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml @@ -37,7 +37,7 @@ <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> <requiredEntity createDataKey="createSecondAttribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml index d1768783e46e6..1fee355ad5e44 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml index 009a5008875e5..ca0426f1b97d5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml index 7097641c67530..619e50acba848 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 4de8dedefab48..ac14b9b299b98 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -28,7 +28,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index 9d48546e1ad3a..a50d1e32d3614 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -42,7 +42,7 @@ <requiredEntity createDataKey="createVisualSwatchAttribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open configurable product edit page --> <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> @@ -158,7 +158,7 @@ <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> <!-- Reindex invalidated indices after product attribute has been created --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> </after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index ac3c2497d9dfb..be81b64c7ed25 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -36,7 +36,7 @@ <requiredEntity createDataKey="createVisualSwatchAttribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open configurable product edit page --> <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> @@ -120,7 +120,7 @@ <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> <!-- Reindex invalidated indices after product attribute has been created --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> </after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml index ddb8190c076ee..7662779a6955f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml @@ -84,7 +84,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> </before> <after> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index 308a56860a661..ef9f71da0ebca 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -90,7 +90,7 @@ <requiredEntity createDataKey="createConfigurableProduct"/> <requiredEntity createDataKey="createConfigChildProduct3"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--SKU Product Attribute is enabled for Promo Rule Conditions--> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute"> <argument name="ProductAttribute" value="sku"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index 618881906c47d..7acece767760d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index aa9b0ac01872a..6126338461fdd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -19,7 +19,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create configurable product--> <comment userInput="Create configurable product" stepKey="commentCreateConfigProduct"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml index eb3ca4f977c65..5d6d05ebfead4 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml @@ -32,7 +32,7 @@ <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Set currency allow previous config--> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml index 7e1cb41871d43..f4d6180bae88a 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}},{{SetAllowedCurrenciesConfigForRUB.value}}" stepKey="setAllowedCurrencyWebsites_EUR_RUB_USD"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}}" stepKey="setAllowedCurrencyWebsites_EUR_USD"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index 8f87246bcf018..ce9b05882d494 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -20,7 +20,7 @@ <group value="currency"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createNewProduct"/> <!--Set Currency options for Website--> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index 8c61bd4434fff..146671e36af68 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -20,7 +20,7 @@ <group value="currency"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> <!--Set price scope website--> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml index a5781698deed4..95f1eebee6c87 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml index 65afd4e2e744a..254b0fc6058f8 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml index 59326317e74b1..b061b6a256471 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml index 30c441796c435..423954a7d9bf7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml index 908977da25d36..a7383af2d7eea 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml @@ -23,7 +23,7 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="CustomerGroupChange" stepKey="createCustomerGroup"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created data--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml index aa23fc0670a88..ab5e332aeed64 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml index d7c4fb2d68772..0bf221d49ab74 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 1" stepKey="setConfigDefaultIsYes"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml index dd065adc7f417..c660ce031422e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml index 436d7838fc6b7..3488d2c94dd69 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index 1cf73e97f91df..64e8520323184 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -26,7 +26,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <waitForPageLoad stepKey="waitForLoad1"/> <click selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}" stepKey="clickCreateCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml index 3482f150ebaad..5f496e2c5fba3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml index 7d19f2fcf096b..da2eed2006434 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml index e16a01bc3222b..52a2483096aaf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="CustomCustomerGroup" stepKey="customerGroup" /> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml index 6b2655b5deaaf..591cb2dd2845a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml index 7889f2be57a4c..081695f7ebe1e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml index 3810da9d62427..5440339e3a95e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml index 116ba3773efff..8fe6e220c4aed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml index 49d23c7787554..da25139ee8e60 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml index e0c1c0012f5bc..e8198cb79262e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml index d89b755492cec..3416c64a7e9d7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml @@ -25,7 +25,7 @@ <getData entity="customerTaxClass" stepKey="customerTaxClassData"> <requiredEntity createDataKey="createCustomerTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml index 06c0593ad00c4..affcf6ddbe6ca 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml index d4551c0cd0af9..4850a6faf438a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml index 7c1a0722ef912..bc0c3e00d75aa 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml index 828fc60f0b77f..8d5535a48f8a3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml index b21f2a795bde7..8494a94f0c122 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml index 05926e7aefc92..340295df04da2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml index 14a89df5eb0a5..03691eb7e7b72 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml @@ -22,7 +22,7 @@ <!-- Create Customer --> <createData entity="CustomerEntityOne" stepKey="createCustomer"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml index 98a9414f29885..1630743da4922 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml index c998babacb575..6e44fe96b0d7b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml index 1822f427ec389..ea4b3645d371f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml @@ -23,7 +23,7 @@ <createData entity="Simple_US_Customer" stepKey="createSecondCustomer"> <field key="firstname">"Jane Doe"</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml index 0eeab8cb36c2e..77422c6e8da3f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createSimpleCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createSimpleCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml index 3e65c688e3474..4c4175bb32198 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml @@ -28,7 +28,7 @@ <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <!-- Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Go to storefront as customer--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml index afe8dbef99916..5721c46d5e4b9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml index 6be675140c555..10da9284d45dc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml index 96a614da0b379..5ce96a8dcab3c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml index ae26519d9618e..a9832c86562f1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml index b344458363999..c9805ebcc90ed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml @@ -21,7 +21,7 @@ <before> <createData stepKey="customer" entity="Simple_US_Customer_Multiple_Addresses"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml index a6ccfda4939f9..0d550416167aa 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml @@ -21,7 +21,7 @@ <before> <createData stepKey="customer" entity="Simple_Customer_Without_Address"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml index 72661e4505322..72064617ef33b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml index 41efec9d87b18..d78d676a822d9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml index e9b0e1723c6ba..2aa85f8c966a9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="firstCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="firstCustomer" stepKey="deleteFirstCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml index ce6ba7ce05e16..0edadb86888f5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml @@ -24,7 +24,7 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created product--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml index b03478c5be684..e32ae04495fe5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml @@ -25,7 +25,7 @@ <createData entity="UsCustomerAssignedToNewCustomerGroup" stepKey="customer"> <requiredEntity createDataKey="customerGroup"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 501baca64318f..86218647778e6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -24,10 +24,10 @@ </before> <after> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteCustomerFromAdminActionGroup" stepKey="deleteCustomerFromAdmin"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Step 0: User signs up an account --> <comment userInput="Start of signing up user account" stepKey="startOfSigningUpUserAccount" /> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml index 0d9f17096b26e..d4351c8bcdc84 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="Simple_US_Customer" stepKey="createFirstCustomer"/> <createData entity="Simple_US_Customer" stepKey="createSecondCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index d154893f70588..9b4e7716f4a25 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Log In--> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!--Create product--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index 6c6ce5c8619e8..317f2c2825ca7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -106,7 +106,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Login --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml index 8de446a59ed07..8cd35f4147636 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml @@ -20,7 +20,7 @@ <group value="create"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="showDateOfBirth"> <argument name="value" value="{{ShowDateOfBirth.required}}"/> </actionGroup> @@ -29,7 +29,7 @@ <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="hideDateOfBirth"> <argument name="value" value="{{ShowDateOfBirth.no}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="StorefrontCustomerCreateAccountWithDateOfBirthActionGroup" stepKey="SignUpNewUser"> <argument name="Customer" value="CustomerEntityOne"/> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml index 51fd2facab3b0..5f3504fe0921e 100644 --- a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml +++ b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml @@ -16,7 +16,7 @@ <severity value="MINOR"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set currency/import/enabled 1" stepKey="enableCurrencyImport"/> </before> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml index e5633707824dd..c634a8426eac0 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml index 8a0c6d478ecfc..650cfd5ba8198 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete created downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index 9a1f1273a41fd..c4d1f57e10aea 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml index 8e08ae813faed..f1ea344d4e45c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index 865f392f7c841..850a73cd354a5 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -27,7 +27,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml index 7fcb70b169ab7..ba2e5e89005cf 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml index 6ec42335e60e3..9ae046210181b 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml index e9a6efc49b635..0ff7c9bab26ca 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml index 16d88c16073cb..5615c66762c52 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml index 307eb43273dbd..f1d00d83b6666 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml index 092129dc1ba1e..fb0532d9d1fbe 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml index 63796a197e586..50a2215d441ad 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml index 3d01168613ecc..7062b15aeedbf 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="DownloadableProductWithTwoLink" stepKey="createDownloadableProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml index a9ceb5e057c3f..aa94de681de1d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml @@ -21,7 +21,7 @@ <before> <!-- Add downloadable domains --> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <comment userInput="Create product" stepKey="commentCreateProduct"/> <createData entity="SimpleProduct2" stepKey="createProduct"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index e4a3be9494268..27d3d3d10a0b7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml index 3d9229a4b0854..30e31be6c8ec4 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml index 7eea3926f450c..4a7f1dde227da 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml index 2ce0272852711..0ed826e944a4f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml index 919fe4b3c645e..1529750cbb293 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml @@ -84,7 +84,7 @@ <closeTab stepKey="closeLinkSampleTab"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Downloadable product from precondition --> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml index 237715ffe76d7..03999a70322b1 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml @@ -36,7 +36,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Change Catalog search engine option to Elastic Search 5.0+" stepKey="chooseElasticSearch5"/> <comment userInput="The test was moved to elasticsearch suite" stepKey="chooseES5"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> @@ -56,6 +56,6 @@ <seeInTitle userInput="Search results for: 'Simple'" stepKey="assertQuickSearchTitle"/> <see userInput="Search results for: 'Simple'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> <comment userInput="End of searching products" stepKey="endOfSearchingProducts"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin2"/> </test> </tests> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml index 532975eddabbe..a94a6a2e3d133 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml @@ -23,7 +23,7 @@ <before> <!--Delete all product if exists--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml index c48f0d63b06ca..fc66db636bb17 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml @@ -46,7 +46,7 @@ <requiredEntity createDataKey="newCategory"/> <requiredEntity createDataKey="customAttribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="product1" stepKey="deleteProduct1"/> @@ -57,7 +57,7 @@ <!--Reindex and clear cache--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:clean" stepKey="cleanCache"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Navigate to backend and update value for custom attribute --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml index 9ffd69b65ea19..2bdbe58ecd97e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -22,7 +22,7 @@ </annotations> <before> <!-- Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForChina.scope}} --scope-code={{GeneralLocalCodeConfigsForChina.scope_code}} {{GeneralLocalCodeConfigsForChina.path}} {{GeneralLocalCodeConfigsForChina.value}}" stepKey="setLocaleToChina"/> <comment userInput="Moved to appropriate test suite" stepKey="enableElasticsearch6"/> <comment userInput="Moved to appropriate test suite" stepKey="checkConnection"/> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 49aef41d7f31c..93a2938d86f26 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -20,7 +20,7 @@ <group value="elasticsearch"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Set Minimal Query Length--> diff --git a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml index 92f4b79b09be2..0d9ca6a2c195a 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml @@ -22,7 +22,7 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> diff --git a/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml b/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml index 28e77ee399737..2dace58274e29 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml index 04430661a62a4..0d73802268ad1 100644 --- a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml @@ -20,7 +20,7 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml index 0674fd12ebead..427be2edd1184 100644 --- a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml @@ -20,7 +20,7 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index dd4512c969453..3850bfabc0c34 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -23,7 +23,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct"> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml index bc1983344ce88..3010c32cd3ed8 100644 --- a/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml @@ -16,7 +16,7 @@ <severity value="MINOR"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml index d71ee505f8356..04b704b9193ca 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml index d23013a6157c9..bd6785eb5e41b 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml @@ -33,7 +33,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml index c6228e674aa34..b842dad9c8c4e 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete grouped product --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml index ebcdc0623cd75..b88f909d977ab 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="ApiProductWithDescription" stepKey="createSimpleProduct"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiGroupedProduct2" stepKey="createGroupedProduct"> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml index 7657c9a86a62b..6514b5ddc5f78 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml @@ -27,7 +27,7 @@ <createData entity="SimpleProduct4" stepKey="simpleProduct2"> <requiredEntity createDataKey="category1"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml index 452b55b835739..053949fa20fb2 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml index dd4619c5c2ce1..7f03765720069 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="_defaultCategory" stepKey="category"/> <!-- Create 23 products so that grid can have more than one page --> <createData entity="ApiSimpleProduct" stepKey="product1"> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 2d891982e8306..a45783767e6a2 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -20,7 +20,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml index eba744e551037..98df7b8d8900e 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml index b52d8ec729fc0..0c5bf2d3fcee3 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml @@ -18,7 +18,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml index 92f93736f237a..58ff334ee9f93 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml @@ -18,7 +18,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index 91d1209f1f1b8..111ba187c6f26 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -20,7 +20,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 0bd447905fb47..583ceb0b2496f 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -29,7 +29,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml index 800e8203d19ce..503037401b9f7 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml @@ -34,7 +34,7 @@ <field key="name">Api Downloadable Product for Test</field> <field key="sku">Api Downloadable Product for Test</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml index 1d3a45b79dc74..3c208eabc9558 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login to Admin Page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Clear products grid filters--> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml index c5926428daaa7..209bb3b48834f 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Login as Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Logout from Admin--> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml index e176052d7a280..b9b61a2ae28ee 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -20,7 +20,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create English and Chinese store views--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnglishStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml index 249f3b28f7a56..69fb7598848aa 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml index b23e3703b5cfd..99622caf0697e 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProductBeforeUpdate" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created data--> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml index 30347ec1a64d0..c60f976538028 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml @@ -24,14 +24,14 @@ <magentoCLI command="indexer:set-mode" arguments="schedule" stepKey="setIndexerModeSchedule"/> <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/></before> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/></before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setIndexerModeRealTime"/> <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPageFirst"> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml index cbe5161e40ee8..4c0c4940ec130 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml index 60598fdd27612..f55e4e37585ba 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml index d1850fdc989fb..eb6c946a03bb5 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Navigate To Integrations Page --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml index 483afc62c9808..41f7f3225255d 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml index b5fc483e251eb..6f2de31548ce0 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml @@ -29,7 +29,7 @@ </createData> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index 104850dea4403..2bfb1239cba60 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -20,7 +20,7 @@ <group value="Msrp"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml index 811af2baef56f..7ae23e8f871eb 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml index de52d20542ce8..27876df8caefe 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml index 90cc8b3952dde..f21a8d32d8841 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml index 8f27fa35bde02..312fa95bf77af 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml @@ -36,7 +36,7 @@ <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml index 5890ab49b2587..a49a37e475409 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create simple products --> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml index 9a3d622b2e264..80407a219a841 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml @@ -31,7 +31,7 @@ <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml index f37b45c639263..2925f2c5b1690 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create two simple products --> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml index 5b4b92559f355..9b210db497e9a 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml @@ -18,7 +18,7 @@ <group value="NewRelicReporting"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGeneralSection"> <argument name="sectionName" value="General"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml index c094117870712..81fb69f557dfd 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml index a5046b6fa4b71..9908e4a115820 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml index 4c12765ebc2a0..bac6023b80446 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml index 9765a65cd60db..441deff9a5cf9 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml index c94eed9dc6a57..d2c738398aae1 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml @@ -58,7 +58,7 @@ </after> <!-- 1. Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 2. Navigate Go to "Catalog"->"Products" --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="onCatalogProductPage"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml index bd6f7ba362bf4..3b93ec1796838 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -22,7 +22,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Open Cache Management page --> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml index 1d2e8ce4ae156..ebdfb9e91ecf1 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml @@ -18,7 +18,7 @@ <group value="paypal"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> <argument name="credentials" value="SamplePaypalExpressConfig"/> </actionGroup> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml index 62d77d8aae6f8..11b2c1e97b02f 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <seeElement selector="{{OtherPaymentsConfigSection.expandedTab}}" stepKey="seeSectionExpanded"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml index ede251ddee647..c35e5dbe05e76 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml @@ -20,7 +20,7 @@ <group value="paypal"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set PayPal Payments Standard Configs--> <comment userInput="Set PayPal Payments Standard Configs" stepKey="commentsetConfigs"/> <magentoCLI command="config:set paypal/wpp/api_authentication 0" stepKey="setApiAuthentication"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml index d2cdec73551d0..a18717344c7a3 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml index 2f0013c43fd9e..140d2bcef28ae 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml index 2aa43e9ef828d..dd24c6ae4279d 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml @@ -38,7 +38,7 @@ <deleteData createDataKey="createSimple1" stepKey="deleteSimple1"/> <deleteData createDataKey="createSimple2" stepKey="deleteSimple2"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteJohnSmithCustomer"> <argument name="customerEmail" value="John_Smith_Customer.email"/> </actionGroup> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml index 1f84200f856d9..461304ef7aeaa 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml @@ -36,7 +36,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget"> <argument name="widget" value="RecentlyComparedProductsWidget"/> </actionGroup> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml index 440846e073f1b..96aa49a53081b 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml @@ -20,7 +20,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setStoreConfig"/> </before> <after> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml index dc9b74a8be36b..92ea0aa86000f 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml @@ -24,7 +24,7 @@ <before> <!--Log In--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!--Create product--> diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 5f5138d6b9495..12427c2caec25 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -103,7 +103,7 @@ <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$createConfigProduct.name$$)}}" time="30" stepKey="assertMessage"/> <!--Disabled via admin panel--> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml index b044b7a9b1f79..6daf322dd5b10 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml index 2c78a9568325b..546b7ad167c25 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml index ff3372285b211..14db012e76888 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml index 05ee0a8032fb5..fe578210d13a7 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml index f28ffc700c34a..e9ed4caa7ef03 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml index 841ba89b643bf..c74e4651346d0 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml @@ -15,14 +15,14 @@ <description value="A product must don't presents on 'Low Stock' report if the product is disabled."/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Created disabled simple product with stock quantity zero --> <createData entity="SimpleProductDisabledStockQuantityZero" stepKey="createProduct"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsLowStockPage"> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml index 986101a8c0db8..ce2c3f5084a21 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml index dd97d092041ca..3b308edffeb2b 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml index 638cc30279966..9a3f7ae5cee33 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml index c1c8256e3d331..be8fc67317be0 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml index 52c0e49ee9291..6d0b335ac7094 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml @@ -15,7 +15,7 @@ </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml index 9053fb35150b6..40e1005ec7c8c 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml index 77fd6c46196af..ed7eff4430030 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml index 00b93b0a93180..0ae7b79f2458d 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml index 94290b918969d..d8bc9d1a4444a 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml index 6b34f51c39d88..f8091d4f63101 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml index ff259cc5870ff..0002050bff957 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index 41b917c4c1371..e572febec5a5c 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -21,7 +21,7 @@ <before> <!-- log in as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- create new product --> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml index f1bba674ac5eb..5ff1ed0a1062b 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml @@ -16,10 +16,10 @@ <group value="menu"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsViewsPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml index 5f0bf53012126..5198949a61024 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml index bc8b5655ad537..c6ef740f69e8f 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml index cab50343efb54..97d0498c5fd22 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml index a93e89059dbf1..0809ad0fa8541 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create product and Category--> <createData stepKey="category" entity="SimpleSubCategory"/> <createData stepKey="createProduct1" entity="SimpleProduct"> @@ -38,7 +38,7 @@ <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> <!--Logout--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Navigate to Marketing > User Content> All Review --> <amOnPage url="{{AdminReviewsPage.url}}" stepKey="openReviewsPage"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml index ad211a2ee0ec8..ea03f557962ad 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml index f6e80119610c0..e9a08a3e196f5 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml index 5c4a455dde9ae..55e709f08d77d 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI command="config:set general/single_store_mode/enabled 1" stepKey="enabledSingleStoreMode"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml index e0c85f06ec712..b70000ed3f3b0 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create product and Category--> <createData stepKey="category" entity="SimpleSubCategory"/> <createData stepKey="createProduct1" entity="SimpleProduct"> @@ -43,7 +43,7 @@ <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> <!--Logout--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to frontend and make a user account and login with it --> <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml index c76c30b47ac51..81f5db6d2fd10 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml @@ -18,7 +18,7 @@ <group value="review"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="textProductAttribute" stepKey="createProductAttribute"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml index 1c6c2d1494e2c..6f4073bf70f46 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml @@ -91,7 +91,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search and open customer --> <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml index e6bcbc3b08028..d8a9effa56dac 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml @@ -52,7 +52,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search and open customer --> <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml index 159ba0c2b8e06..452fd511f2ce2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml @@ -23,12 +23,12 @@ <before> <createData entity="Simple_US_Customer" stepKey="simpleCustomer"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- Initiate create new order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml index 9b12f1c951991..11a9957fe0041 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml @@ -26,7 +26,7 @@ <!-- Enable *Free Shipping* --> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Disable *Free Shipping* --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml index 4646f6cf2e5a0..bb1940357a7f4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Bank Transfer payment --> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml index 7f162ca4e2a6a..dafd00ff60b29 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Cash On Delivery payment method --> <magentoCLI command="config:set {{EnableCashOnDeliveryConfigData.path}} {{EnableCashOnDeliveryConfigData.value}}" stepKey="enableCashOnDeliveryPayment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml index 7d6ce40659b1f..c0ebbe450119e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Set default flat rate shipping method settings --> <comment userInput="Set default flat rate shipping method settings" stepKey="setDefaultFlatRateShippingMethodComment"/> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml index ee69d9fd1ca46..256417c0d0d10 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set {{DisableCatalogInventoryConfigData.path}} {{DisableCatalogInventoryConfigData.value}}" stepKey="disableDecreaseInQuantityAfterOrder"/> <!--Set default flat rate shipping method settings--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml index ce653b11f854b..477676085cf2e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Purchase Order payment method --> <magentoCLI command="config:set {{EnablePurchaseOrderConfigData.path}} {{EnablePurchaseOrderConfigData.value}}" stepKey="enableCPurchaseOrderPayment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 095bd9af2c1b1..d22e11bca3d0e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -22,7 +22,7 @@ <before> <!-- Enable Zero Subtotal Checkout --> <magentoCLI command="config:set {{EnableZeroSubtotalCheckoutConfigData.path}} {{EnableZeroSubtotalCheckoutConfigData.value}}" stepKey="enableZeroSubtotalCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set Free shipping method settings--> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml index c8b2b66a758eb..b2bfa93678dfd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml index 34a415ccf03ad..37a4782ce2e73 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml @@ -25,7 +25,7 @@ <!--Create customer--> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> <!--Login to admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete simple product--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml index 979e2cef3ff28..ca3733d5f0b96 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml @@ -27,7 +27,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <!--Login to Admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete entities--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index c9f878a716ae8..69e3ade9231d0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -20,7 +20,7 @@ <group value="sales"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category and simple product--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index a20e9efd614fd..2935a56a6c0a1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml index 5839ff4b3dfe2..ab3a2cc647740 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml @@ -83,7 +83,7 @@ <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Flat Rate" --> <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete data --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index df1a541eeb94c..564cde1151660 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index e0e7234fc9d24..45eefbcc3f97d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index b2e0d788a24e2..7974d594eb99c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 9a635df80f108..1411f7b292757 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -59,7 +59,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!-- end todo --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml index 072522452e7b9..baef605ad52af 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml @@ -26,7 +26,7 @@ <createData entity="ApiProductWithDescription" stepKey="createSimpleProduct"/> <!-- Login to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Initiate create new order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index a7112776bf157..ab5c089dfcc30 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct_25" stepKey="simpleProduct"> <field key="price">5</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml index 72080c3170c48..2f89573b00101 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml @@ -33,7 +33,7 @@ <!--Create tax rule for US-CA--> <createData entity="defaultTaxRule" stepKey="createTaxRule"/> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Step 1: Create new order for customer--> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml index bbb0489b6c9a4..23dca916781f1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml index 23bf293f67a81..d3cd3e8b8549c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml index 03acf2194c64d..a30040045a4ca 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml index f439d792c9330..1401930131b13 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml @@ -76,7 +76,7 @@ <requiredEntity createDataKey="simple2"/> <field key="qty">2</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml index bb1f5840ed6bf..0be7e20be5aea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct" stepKey="simpleProduct"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> @@ -32,7 +32,7 @@ <deleteData createDataKey="category" stepKey="deleteCategory"/> <!--Enable required 'email' field on create order page.--> <magentoCLI command="config:set {{EnableEmailRequiredForOrder.path}} {{EnableEmailRequiredForOrder.value}}" stepKey="enableRequiredFieldEmailForAdminOrderCreation"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml index c689869d35c30..b0926948f2bf6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml @@ -26,7 +26,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <updateData createDataKey="createProduct" entity="productWithDateTimeOption" stepKey="updateProductWithOption"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index da427c9085084..ade1f783c1309 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -24,7 +24,7 @@ <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="ClearCacheActionGroup" stepKey="clearCacheBefore"/> </before> <after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml index 841db82adefb7..6e8ec14fc67cb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml @@ -37,7 +37,7 @@ </createData> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Step 1: Go to Storefront as Customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml index d1417ba1bc267..82e9606175764 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml @@ -22,7 +22,7 @@ <requiredEntity createDataKey="category"/> </createData> <createData entity="Simple_US_Customer_CA" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Clean up created test data.--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml index cc709dc6e16eb..1c8cf2219f13b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct_25" stepKey="simpleProduct"> <field key="price">5</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml index 01a27a6191d74..1c59f6f936cef 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml @@ -27,7 +27,7 @@ <createData entity="DisableFlatRateShippingMethodConfig" stepKey="disableFlatRate"/> <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml index a5cbb15faf2d7..8d328beab1adc 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set default flat rate shipping method settings--> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml index ca2a35eef5ae6..a89e9f7ce6ebe 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml index c11ea49893734..45cbe23042e03 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml index f583f46f19c7c..22b2d69a73090 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml index f334a77da6550..4b690a00ee9ed 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml index 437487e3d7e40..e1d934f794142 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml index 87d3a864e0820..86a3e381cb237 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml index 9eaa3a87e873a..bfd75a69b81d6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml index eb9d48cbbd567..338975d3c1f25 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml @@ -23,7 +23,7 @@ <before> <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="addStoreCodeToUrlEnable"/> <createData entity="SimpleProduct2" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml index 2eb7e8ec33d92..0a4445f338960 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml @@ -23,7 +23,7 @@ <before> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createSimpleProductApi"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create the catalog price rule --> <createData entity="CatalogRuleToPercent" stepKey="createCatalogRule"/> <!--Create order via API--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml index 424b931314b01..39b5cabdadd72 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml index 89d4cc4c7371f..567a0466d12e0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml index 6d765537494ed..4023be10f390e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml index 05f15ad76b07c..07d75664f1fda 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml index 17f0bad87037e..544bb5adf57cd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml index 7884926946178..d521c3dc647e2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml index 8ab26c04d5d6d..54ae549967a3b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml @@ -87,7 +87,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml index 5981c49345aa0..85665dfc1b00e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="allowSpecificValue" command="config:set payment/cashondelivery/active 0" /> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index d28b636770856..7615cc219d430 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -29,7 +29,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create order via Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml index d44cb829bc205..fd26ca1ca601e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml @@ -28,7 +28,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create order via Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml index bd43937e6f24e..692f293ef3a75 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml index af07d50bcc8c7..f0f4cf9d1a468 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml @@ -27,7 +27,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Disable created order status --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml index dbe012f17176d..df6a797372e62 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml @@ -49,7 +49,7 @@ <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="assertErrorMessageStorefront"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Try to create order in admin with provided email --> <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="navigateToNewOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml index 73f3b5310731e..d8a3db76da05e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml index 3ae4007ac211d..c58b95a41b157 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml index 540af6958d54c..1c92c2dae3712 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml index 8fd751f96914e..b562073a1276f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml index e8eea2019c941..776d84ac230b8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <!--Create simple customer--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 97d9ad9dbeb59..09197963434eb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -92,7 +92,7 @@ <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Search for Order in the order grid --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml index 1f655f319076b..c3058ca6ede87 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml @@ -130,7 +130,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Customers -> All Customers --> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml index f9215ff5019ff..c635e6b0ad6b2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml index 4596faedbbe6c..eb28ebfd068da 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml index bf78012926a7b..f374741c247d4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml index 4fafd3d163930..0e021600ab3e3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml index 9ee96e2b64678..176fb05bc74b3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml @@ -68,7 +68,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Customers -> All Customers --> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml index f68a36b29af4f..5cc4fae330d05 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 21 products created and category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct01"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml index 9319022a96b20..20261de502ea3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 20 products created and category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct01"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 468abf8fb8b81..00117c56de439 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create downloadable Product --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index 49dcc47e14779..1433d660d3535 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -28,7 +28,7 @@ <field key="quantity">500</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index 3f063b5869129..ed2dd16b7df9d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml index 0e1a4d7c186aa..9ac73ceae586e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml @@ -22,7 +22,7 @@ <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="initialSimpleProduct"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml index 64443c717ac33..67e75d63e016e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -24,7 +24,7 @@ <createData entity="SimpleProduct" stepKey="product"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index f33eb187e4cc8..2bf96cf0377d4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 4b793dbf8583f..9833231f8ed84 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index f6d61f62c1f54..0d98abfba3f62 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index cb3e6c517e1ec..bc4139435ab55 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml index 3bbacf912e5d6..56c4506196d24 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index 902c118129bab..23e472518ba84 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml index aed9d71c306ae..3aacc176acdc5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create active cart price rule--> <actionGroup ref="AdminCreateCartPriceRuleRuleInfoSectionActionGroup" stepKey="createActiveCartPriceRuleRuleInfoSection"> <argument name="ruleName" value="ActiveSalesRuleWithComplexConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml index fc9a92765c2d0..8c02f401992ee 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create active cart price rule--> <actionGroup ref="AdminCreateCartPriceRuleWithCouponCodeActionGroup" stepKey="createActiveCartPriceRule"> <argument name="ruleName" value="ActiveSalesRuleWithPercentPriceDiscountCoupon"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml index 6de5f127a296c..18183085060d2 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> <!--Create inactive cart price rule--> <actionGroup ref="AdminInactiveCartPriceRuleActionGroup" stepKey="createInactiveCartPriceRule"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml index 58d7ea7c1bad8..d001ac98eefeb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index 22dff89ebe8a6..bc608c0e06086 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -77,7 +77,7 @@ <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml index f99b19f4a6289..91fe4f142d570 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml @@ -35,7 +35,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Login as admin and open page for creation new Price Rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{PriceRuleNewPage.url}}" stepKey="openCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitCatalogPriceRulePageLoad"/> <!-- Open Conditions section and select Categories condition --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 96ba6208a7518..09b45cd554056 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -37,7 +37,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search Cart Price Rule and go to edit Cart Price Rule --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml index 1406d4dfbde67..4cc331eebc4ee 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml @@ -24,7 +24,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml index aade41b30284c..4d987c00884e1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml @@ -24,7 +24,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml index 283d22351b1f1..ff0e011210785 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml @@ -25,7 +25,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml index fafede4120573..036844562af50 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml @@ -24,7 +24,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml index 7d343cd6dafd8..efd82316f0b0a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml @@ -25,7 +25,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml index 12c278d1e7b63..7d266e2cdd6f1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml @@ -86,7 +86,7 @@ <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> <!-- Login to admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to created Order --> <amOnPage url="{{AdminOrderPage.url({$grabOrderId})}}" stepKey="goToAdminViewOrder"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml index 80b0747a3bf72..172f6b6ba24c6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set carriers/tablerate/active 1" stepKey="setShippingMethodEnabled"/> <magentoCLI command="config:set carriers/tablerate/condition_name package_value" stepKey="setShippingMethodConditionName"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index b43fd095b5556..85a30b3a3a2b4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -29,7 +29,7 @@ <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct2"> <argument name="productName" value="config2"/> </actionGroup> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Assign config1 and the associated child products to CAT1 --> <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfigurableProduct1ToCategory"> <argument name="productId" value="$$createConfigProductCreateConfigurableProduct1.id$$"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml index 13c1100bd21f6..d63df5fe50a6d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml @@ -62,7 +62,7 @@ <requiredEntity createDataKey="createSecondGroupedProduct"/> <requiredEntity createDataKey="createFourthSimpleProduct"/> </updateData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml index 6ff7a34a03b8a..82ec95b24d3ca 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml index 2b7a4e7f5e5cb..9ff088fb25b82 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -26,7 +26,7 @@ <createData entity="SearchTerm" stepKey="createThirdSearchTerm"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml index 4cdd8e6992367..18f623288621d 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml @@ -22,7 +22,7 @@ <!--Create Simple Product with weight--> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete create product --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml index 5030484434925..3bfa777ac27d8 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product with description --> <createData entity="SimpleProductWithDescription" stepKey="simpleProduct"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml index 1d312959a4a00..93a3c8ca8e4a2 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product --> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml index 3ae29f60a8e86..ebe3b6c129721 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product with short description --> <createData entity="ApiProductWithDescription" stepKey="product"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml index e14cbbb85a0e7..e72f614593cfe 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product --> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml index 5f21d4364736b..6ff4b7de5559d 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml @@ -17,7 +17,7 @@ <before> <!-- Login As Admin User --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Navigate To Marketing Search Terms Grid --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSearchTermPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml index 02a8170b308d3..fc91c8caddf71 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -19,10 +19,10 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml index dc971f2044760..cd67bf4612520 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -18,10 +18,10 @@ <group value="security"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml index c3945fe0199b6..a75f65dffeca3 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml @@ -23,7 +23,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -94,7 +94,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginError" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml index bf13ab6b5c6aa..3d04f3eed4daf 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml @@ -23,7 +23,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -92,7 +92,7 @@ </actionGroup> <!-- Try to login as admin and check error message --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage"/> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index bdea845c81a56..130dacfbc2237 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -21,7 +21,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> @@ -34,7 +34,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <wait time="120" stepKey="waitForUserToExpire"/> <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> <argument name="adminUser" value="{$grabUsername}"/> @@ -43,7 +43,7 @@ <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage" /> <!-- delete user --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> <argument name="user" value="NewAdminUser" /> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index 12bba27f21269..39e4a3b56ca13 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -21,10 +21,10 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> @@ -35,15 +35,15 @@ <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> <argument name="adminUser" value="{$grabUsername}"/> <argument name="adminPassword" value="{$grabPassword}"/> </actionGroup> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> - <actionGroup ref="logout" stepKey="logoutAsUserWithValidExpiration"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsUserWithValidExpiration"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> <argument name="user" value="NewAdminUser"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml index c4603a88c56c4..1c251ea2b9ec2 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml @@ -20,10 +20,10 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create user --> @@ -37,7 +37,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Login as that user --> <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> @@ -51,7 +51,7 @@ <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> <!-- Delete created user --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> <argument name="user" value="NewAdminUser"/> </actionGroup> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml index 4ceffd676313d..c130de5d8699f 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -73,7 +73,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeLoginUserErrorMessage" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml index 47a0224a1c4db..5b4f38ae30566 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml @@ -22,12 +22,12 @@ <before> <createData entity="NewAdminUser" stepKey="user" /> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> <magentoCLI command="admin:user:unlock {{DefaultAdminUser.username}}" stepKey="unlockAdminUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteUserViaCurlActionGroup" stepKey="deleteUser"> <argument name="user" value="$$user$$" /> </actionGroup> @@ -92,7 +92,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeLoginErrorMessage" /> </test> </tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index 08064ae59d793..188b12c6a91c3 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -20,7 +20,7 @@ <group value="configuration"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml index bacfaf15f99d7..fea499954896d 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml @@ -19,7 +19,7 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index a4c155e9d3753..0e69dba36d41c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -22,7 +22,7 @@ <before> <!--Create product and customer--> <createData entity="SimpleProduct2" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website, store group and store view--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml index 64d0932e9272d..5e57224bfee48 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml index 0985aea2e502c..6b388ae31e45e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml index 7df8f08e79530..0d709e1d08006 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml @@ -19,7 +19,7 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml index 81c76ec916f7e..5fd9a6a29c0e3 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="Simple_US_Customer_ArmedForcesEurope" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml index 3a375be6533db..0f2f7ed26f1e1 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml @@ -26,7 +26,7 @@ <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product --> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml index 0ddc0640b56df..01c8ab5658186 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml @@ -18,7 +18,7 @@ <group value="sitemap"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteSiteMap"> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml index 608b284f247f3..2a9b17ad49bff 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml index 208ed316e2e51..d25e7dbb0056a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml index 65ac017072917..a4fc0bdcfd1fb 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml @@ -17,7 +17,7 @@ <group value="Swatches"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <amOnPage url="{{ProductAttributePage.url}}" stepKey="addNewProductAttribute"/> <selectOption selector="{{AttributePropertiesSection.InputType}}" diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml index 5e5515aa25a74..6ac5f2a55f3cf 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml @@ -18,7 +18,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create an attribute with two options to be used in the first child product --> <createData entity="textSwatchProductAttribute" stepKey="createTextSwatchConfigProductAttribute"/> <!-- Add the attribute just created to default attribute set --> @@ -29,7 +29,7 @@ <after> <!-- Delete Created Data --> <deleteData createDataKey="createTextSwatchConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index ff1286d8092fc..a42aadc497a7a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 5437489fd5697..6b2a29d8ec451 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -18,7 +18,7 @@ <group value="Swatches"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 7599111260980..1a6c0341c0704 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Clean up our modifications to the existing color attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml index 5660922962b47..e93a27d377a52 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml @@ -17,7 +17,7 @@ <group value="Swatches"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Remove attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml index 7d3d1aafd8d6d..8fd21acbd51d9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml @@ -22,7 +22,7 @@ <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Log in --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Clean up our modifications to the existing color attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml index 0ca0561be0a0d..06a51575e68fb 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a new product attribute --> <actionGroup ref="AdminOpenProductAttributePageActionGroup" stepKey="openProductAttributePage"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml index d5bc8dbadfb56..3e133dacc66c1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml @@ -22,7 +22,7 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml index fdc8fb631dd8a..62444fd9d30e1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml @@ -21,7 +21,7 @@ <!-- Create category --> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <!-- Delete configurable product and all child products --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index 1fe46c20a542d..47018a1b142b2 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -24,7 +24,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml index 820150b10d06a..82dbff950d62f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -23,7 +23,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index 76ec3658243bf..b6b3e58bd7c01 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -24,7 +24,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index d15d5dcb3d63c..47ca8ce44bec3 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -25,7 +25,7 @@ <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml index 22ce176da8233..43944ceef33ef 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index 9acfdf394be4b..a1d346ffa2744 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -54,7 +54,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> <!--Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductPage"/> @@ -86,7 +86,7 @@ <seeElement selector="{{StorefrontProductInfoMainSection.productSwatch(visualSwatchOption2.default_label)}}" stepKey="assertAddedWidgetM"/> <!--Login to delete CMS page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> <argument name="UrlKey" value="$grabTextFromUrlKey"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index 5c0ad3bc4ea77..e027a41cd9d2a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -29,7 +29,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml index dce8852ac5628..fc4b6dd8b84c5 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml @@ -33,7 +33,7 @@ <!--Create customer--> <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create tax rule--> <actionGroup ref="AdminCreateTaxRuleCustomProductTaxClassActionGroup" stepKey="createTaxRuleCustomProductTaxClass"> <argument name="productTaxClassName" value="$createProductTaxClass.taxClass[class_name]$"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml index 3e0998b79c3f5..0990daf1ecfbf 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -46,7 +46,7 @@ </getData> <!-- Login to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to Tax Rule page, add Tax Rate, unassign Default Tax Rate --> <amOnPage url="{{AdminEditTaxRulePage.url($createTaxRule.id$)}}" stepKey="goToTaxRulePage"/> @@ -119,7 +119,7 @@ <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterCustomer"/> <!-- Logout Admin --> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Open Created product. In Tax Class select new created Product Tax class.--> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml index e632f6265f438..3a5f905d89dd5 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml index b63ca1953157f..06160eb09b787 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml index 3befada509afa..3a6e4dfef5bac 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml index cb79de19ce23a..b55ec35fa9eb1 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml index 696d6c4c87763..379164d134448 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml index c6c5e318cc791..d276a6a276b2c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml index f75fa716e9d30..998c948d869d0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml index 03b12c8f28098..6e2ca794379f6 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml @@ -28,7 +28,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml index abff31803a165..895bb920973c8 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml index 6e9b81743ddf0..43ce4059ad84e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml index 6a9bf30811ff5..0293e04293daf 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml index 72adf7b0dae1e..770cdd1e3b2c4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml @@ -23,7 +23,7 @@ <createData entity="defaultTaxRule" stepKey="initialTaxRule"/> <createData entity="ApiSimplePrice100Qty100v2" stepKey="simpleProduct"/> <createData entity="Simple_US_Utah_Customer" stepKey="customer" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml index 219cb309820d8..fd5b07a88b52f 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml index 73c8c63be0318..824d6d1630162 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml index 2b2e0c5184d4a..a6939bf14c4a1 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml index 0390542544060..61b09eabe7d35 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml index 0e87f268f8825..b7ffe05ebf5c2 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml @@ -30,7 +30,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml index a96a57cbfec55..14df3f8987f5e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml @@ -32,7 +32,7 @@ </getData> <createData entity="ApiSimplePrice100Qty100v2" stepKey="simpleProduct"/> <createData entity="Simple_US_Utah_Customer" stepKey="customer" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml index 5f855f5bb750d..4b34121b10829 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Search the tax rate on tax grid page --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml index b4070081519d7..959aa323308be 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml index 9469f6a7a6f20..c1268638c7104 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml index 4f7e5534e85bb..609c8c7b27980 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml index bd935113fe051..724831d9409be 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml index 14615c101b9a7..d005f4b657448 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml index 248828aa94a4b..d1fc0654fc496 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml index 187f079434ddc..3a67276c42737 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml index a0396cd2793bf..793468b7f0b1e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml @@ -18,7 +18,7 @@ <group value="Tax"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml index 2ed31c2e20488..306216422adea 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml index 27a7f2c51724e..c22bab774de29 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml index aa46a5fdae0e1..6f93d07b76eed 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml index d7b25da6c14ae..c3986e6a8d0cc 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml index 3002452196904..fb1eff1d74067 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml index 6c81a6aeb3f11..1f0406244a926 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml index 4a074a484537d..056b4c3f914fe 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -19,7 +19,7 @@ <group value="menu"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml index 2d0dc4501dfa1..167191ee69a79 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index ae0ebe2ed7ef3..07db3a5a18b73 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -19,7 +19,7 @@ <group value="Content"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml index 666d01e1090c1..fbe1d5ac936d7 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml @@ -22,7 +22,7 @@ <group value="Watermark"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml index 56041c6ccc99a..0f9ff05af3d89 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml @@ -22,7 +22,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Navigate to Themes page--> <amOnPage url="{{ThemesPageIndex.url}}" stepKey="navigateToThemesIndexPage" /> <waitForPageLoad stepKey="wait1"/> diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index c6f2bb0713f48..7a107f059a84a 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -19,7 +19,7 @@ <group value="checkout"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Product and a customer is created --> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> diff --git a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml index f4f30d4317a57..58ac4ef53861c 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml @@ -20,7 +20,7 @@ <group value="ups"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Collapse UPS tab and logout--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml index 089f74411ac58..fe9f38d1b541c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml index 70ce7e2d64fb3..b9f726aec668e 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -21,7 +21,7 @@ <createData entity="ApiCategory" stepKey="createCategory"> <field key="name">category-admin</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Store View EN --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreENNotUnique"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml index 0fadd38957a69..f9003ce8c0897 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml @@ -26,7 +26,7 @@ <createData entity="ApiCategory" stepKey="createCategory"> <field key="name">category-admin</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Store View EN --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreENNotUnique"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml index 734b8bb667674..b4d87df4b4a36 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml @@ -31,7 +31,7 @@ </createData> <!-- Log in to backend --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create additional Store View in Main Website Store --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index d337da37e0d2a..f430fb8921f80 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -33,7 +33,7 @@ </createData> <!-- Log in to backend --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create additional Store View in Main Website Store --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml index 2d5f70f4e7328..f8136fbb6b59d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index cf963bea12fa6..5aedbe9ff79c2 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 401ce09f1a149..ba810817052f0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml index b6007d63290b8..aee47c5e9df95 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml index 33e1969be38ff..3986717b16b2a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 1c6c1572b3206..56b558a94960a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml index 3191de550bd52..d2fecea9234ba 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml index c34dce4ab0b91..e3e187c1cb358 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml index 26bc86bdabae7..a0efcb02361c0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index 5276cac8625ec..d4d5b163a1881 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -18,7 +18,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 2136918fa6186..540b38a9b33fb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml index e26a283861ecd..450ecb8ba5601 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml index 86f7f84f9f1a5..3051ac2764ce9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml index d04cafc3602b6..2646c3631101d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml index fe71094e49064..6dddc0f333ca5 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml index 2e753e5fca7d8..5a33e63c5e08c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml @@ -17,7 +17,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml index 06b5745c32be3..8113d4511d8da 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml index c8ef5470eef3a..65544f414140c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml index e1228c47b9030..61e9847308263 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml index 1efd063a23708..b9f20b8651320 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml index c03c21abb9e7e..293c412570742 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml index 6803816f42d85..4ad6e99b0b424 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml index 80fe34a3099f1..dff59c1ca9770 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml @@ -17,7 +17,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml index 63e76717dd6b6..9d6b267055f70 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml @@ -24,7 +24,7 @@ <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml index 0111a35508fd9..136b2fc226566 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml @@ -21,7 +21,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml index f3b0ca2237975..f03d9ae1bad67 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct" stepKey="createProductForUrlRewrite"> <requiredEntity createDataKey="createCategory" /> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create second store view --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"> <argument name="customStore" value="customStore"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml index d47b700dab790..37c5226e4cf46 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml index 5a9aa4df336e0..4606465f1b8dd 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 788d29a4d4f9f..62ed25fd47212 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 3d890854f5e09..c6bc35c227ef3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml index 95b509ac38a8a..5692323fbb9e6 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml index da97bc5988512..d36e2e9645727 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml index 5ac0330ab80b2..53d78cdf57bdb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml index b5fa9267c581b..167a11548064f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml @@ -18,7 +18,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml index f9eb150ad6113..801bc90d106c6 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -18,7 +18,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml index 34318b3dd8ac0..7d7bc753307d4 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -18,7 +18,7 @@ <group value="urlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> </before> <after> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml index 79376dfd7be35..9b739b157cddc 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml index 9253cad21f936..f450d7640d360 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite"/> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index 0c1298672db51..a7e2eb2335eef 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml index 2a0152c9da34c..a70065dc1d307 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml @@ -47,7 +47,7 @@ </after> <comment userInput="1. Log in to Admin " stepKey="commentAdminLogin" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="2. Open Marketing - SEO and Search - URL Rewrites " stepKey="commentVerifyUrlRewrite" /> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index e666a2aad1738..cce0cd11e0199 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -43,7 +43,7 @@ </after> <!-- Steps --> <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml index e3fc730a3549a..639cd2c57f7d1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml @@ -54,7 +54,7 @@ </after> <!-- Steps --> <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml index 4618e901d42e3..1d460b9b668a0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml @@ -42,7 +42,7 @@ <magentoCLI command="cache:flush" stepKey="cleanCache2"/> </after> <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml index e0db8d8b25b96..acf2571db9c14 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml @@ -20,11 +20,11 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAdmin"/> <!-- Delete User --> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteLimitedUser"> @@ -47,7 +47,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> <!-- Login as user2 --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsUser"> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsUser"> <argument name="adminUser" value="NewAdminUser"/> </actionGroup> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml index dbb0497ed8511..d373bc6676deb 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -20,10 +20,10 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> </before> <after> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!--Create a new role with custom access--> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml index a3f1d864a5723..60f6089e56a41 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml @@ -25,11 +25,11 @@ <field key="current_password">{{DefaultAdminUser.password}}</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> </before> <after> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!--Delete New Admin User--> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml index 3998a4513c6df..8df2a2192e9d9 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml @@ -25,17 +25,17 @@ <createData entity="NewAdminUser" stepKey="user"> <field key="current_password">{$adminPassword}</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="adminUser" value="$$user$$"/> </actionGroup> </before> <after> <!-- Delete New Admin User --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAdmin"/> <actionGroup ref="AdminDeleteUserViaCurlActionGroup" stepKey="deleteUser"> <argument name="user" value="$$user$$" /> </actionGroup> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!--Assert Impossible Delete Your Own Account--> @@ -48,6 +48,6 @@ <actionGroup ref="AssertAdminUserInGridActionGroup" stepKey="assertUserInGrid"> <argument name="userName" value="$$user.username$$"/> </actionGroup> - <actionGroup ref="logout" stepKey="logOutAsNewUser"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutAsNewUser"/> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml index 57e2ab0faccf6..9793387b02289 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml index 963af44ac3bc0..e32f0736f880b 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml index d6a4318c7d7be..8c7bdfc9360b5 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml index ef9cb5ebfa5fe..5abf119f18d4a 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml index 39d7954ef7729..d1034c7a5945f 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -23,7 +23,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> <!--Create New User--> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> @@ -42,7 +42,7 @@ <after> <!--Delete new User--> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsSaleRoleUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsDefaultAdminUser"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> </actionGroup> @@ -71,7 +71,7 @@ <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutFromAdminPanel"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsSaleRoleUser"> <argument name="adminUser" value="AdminUserWithUpdatedUserRoleToSales"/> </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> diff --git a/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml b/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml index ee7368a44616d..1c6e222a242d8 100644 --- a/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml +++ b/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml b/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml index c5d85907d15fc..8197f9a5b5752 100644 --- a/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml +++ b/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml index b542d6a4d0364..3e9635e2a1492 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml @@ -32,7 +32,7 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create website, store and store view --> <comment userInput="Create website, store and store view" stepKey="createWebsite"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml index d3f39e9aee664..60c39dd5058b5 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml @@ -23,7 +23,7 @@ <requiredEntity createDataKey="createProductFPTAttribute"/> </createData> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProductInitial"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index c35a6309f1f3d..74d6c2a97b089 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -41,7 +41,7 @@ </createData> <!-- Customer is created with default addresses: --> <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createSimpleProduct.id$"/> </actionGroup> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index 92936c2ed77c5..dda125835110a 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -40,7 +40,7 @@ <!-- Customer is created with default addresses: --> <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCron groups="index" stepKey="reindexBrokenIndices"/> </before> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index d2246f07dd7cb..92f526c79e926 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -39,7 +39,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> <field key="price">10.00</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createSimpleProduct.id$"/> </actionGroup> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml index 82685910bc717..495b9a990a465 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -38,7 +38,7 @@ <field key="price">40.00</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCron groups="index" stepKey="reindexBrokenIndices"/> </before> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml index 073cdabf37698..6e83687207341 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml @@ -18,10 +18,10 @@ <group value="widget"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentWidgetsPageFirst"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml index e62383d57fc93..b599feeef76db 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index 0550c1cc2ca42..04218c76a4b7b 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -22,7 +22,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml index d7be487382c56..30b4f46c791e8 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml @@ -23,7 +23,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml index 32c16ff7f5a55..aaf7980e46aac 100755 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml @@ -19,7 +19,7 @@ <testCaseId value="N/a"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <actionGroup ref="setEmailTextLengthLimitActionGroup" stepKey="rollbackEmailTextLengthLimit"> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml index 4e4d5c1a0696f..fbda4b97f6e5c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!--Create Configurable product--> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index 9d6482248df0a..738bbc6bda35c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI command="config:set checkout/cart/configurable_product_image 0" stepKey="setProductImageSettingUnderCofigurationSalesCheckout"/> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 2a84043584dbe..eed4dc8d4767e 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -29,7 +29,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> <!-- Create new store view and assign it to non default store --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="CreateCustomStoreViewActionGroup" stepKey="createCustomStoreView"> <argument name="storeGroupName" value="$$storeGroup.group[name]$$"/> </actionGroup> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml index 6238f0dcbff09..8ba45035e7b70 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml @@ -47,7 +47,7 @@ <requiredEntity createDataKey="simpleProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml index e07315e0ee0a4..6932d56b2f56a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -45,7 +45,7 @@ <requiredEntity createDataKey="createBundleOption1_1"/> <requiredEntity createDataKey="simpleProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml index 227212cd05ae8..689b76e42e6f1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml @@ -39,7 +39,7 @@ <argument name="productVar" value="$$createProduct$$"/> </actionGroup> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createProduct.id$$"/> </actionGroup> diff --git a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledHtmlTermEntityTestCest.php b/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledHtmlTermEntityTestCest.php deleted file mode 100644 index d5e04daaafff0..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledHtmlTermEntityTestCest.php +++ /dev/null @@ -1,203 +0,0 @@ -<?php -namespace Magento\AcceptanceTest\_default\Backend; - -use Magento\FunctionalTestingFramework\AcceptanceTester; -use \Codeception\Util\Locator; -use Yandex\Allure\Adapter\Annotation\Features; -use Yandex\Allure\Adapter\Annotation\Stories; -use Yandex\Allure\Adapter\Annotation\Title; -use Yandex\Allure\Adapter\Annotation\Description; -use Yandex\Allure\Adapter\Annotation\Parameter; -use Yandex\Allure\Adapter\Annotation\Severity; -use Yandex\Allure\Adapter\Model\SeverityLevel; -use Yandex\Allure\Adapter\Annotation\TestCaseId; - -/** - * @Title("MC-14664: Update disabled HTML checkout agreement") - * @Description("Admin should be able to update disabled HTML checkout agreement<h3 class='y-label y-label_status_broken'>Deprecated Notice(s):</h3><ul><li>DEPRECATED ACTION GROUP in Test: AddSimpleProductToCartActionGroup Avoid using super-ActionGroups. Use StorefrontOpenProductEntityPageActionGroup, StorefrontAddSimpleProductToCartActionGroup and StorefrontAssertProductAddedToCartResultMessageActionGroup</li></ul><h3>Test files</h3>app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml<br>") - * @TestCaseId("MC-14664") - * @group checkoutAgreements - * @group mtf_migrated - */ -class AdminUpdateDisabledHtmlTermEntityTestCest -{ - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _before(AcceptanceTester $I) - { - $setEnableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 1", 60); // stepKey: setEnableTermsOnCheckout - $I->comment($setEnableTermsOnCheckout); - $I->createEntity("createProduct", "hook", "SimpleTwo", [], []); // stepKey: createProduct - $runCronIndex = $I->magentoCron("index", 90); // stepKey: runCronIndex - $I->comment($runCronIndex); - $I->comment("Entering Action Group [adminLogin] AdminLoginActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin"); // stepKey: navigateToAdminAdminLogin - $I->fillField("#username", getenv("MAGENTO_ADMIN_USERNAME")); // stepKey: fillUsernameAdminLogin - $I->fillField("#login", getenv("MAGENTO_ADMIN_PASSWORD")); // stepKey: fillPasswordAdminLogin - $I->click(".actions .action-primary"); // stepKey: clickLoginAdminLogin - $I->waitForPageLoad(30); // stepKey: clickLoginAdminLoginWaitForPageLoad - $I->conditionalClick(".modal-popup .action-secondary", ".modal-popup .action-secondary", true); // stepKey: clickDontAllowButtonIfVisibleAdminLogin - $I->closeAdminNotification(); // stepKey: closeAdminNotificationAdminLogin - $I->comment("Exiting Action Group [adminLogin] AdminLoginActionGroup"); - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _after(AcceptanceTester $I) - { - $setDisableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 0", 60); // stepKey: setDisableTermsOnCheckout - $I->comment($setDisableTermsOnCheckout); - $I->deleteEntity("createProduct", "hook"); // stepKey: deletedProduct - $I->comment("Entering Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGridToDelete - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGridToDelete - $I->comment("Exiting Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("activeTextTerm")); // stepKey: fillTermNameFilterOpenTermToDelete - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenTermToDelete - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenTermToDelete - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenTermToDelete - $I->comment("Exiting Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->click(".page-main-actions #delete"); // stepKey: clickDeleteButtonDeleteOpenedTerm - $I->waitForElementVisible("button.action-primary.action-accept", 30); // stepKey: waitForElementDeleteOpenedTerm - $I->click("button.action-primary.action-accept"); // stepKey: clickDeleteOkButtonDeleteOpenedTerm - $I->waitForText("You deleted the condition.", 30, ".message-success"); // stepKey: seeSuccessMessageDeleteOpenedTerm - $I->comment("Exiting Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->comment("Entering Action Group [adminLogout] AdminLogoutActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin/auth/logout/"); // stepKey: amOnLogoutPageAdminLogout - $I->comment("Exiting Action Group [adminLogout] AdminLogoutActionGroup"); - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _failed(AcceptanceTester $I) - { - $I->saveScreenshot(); // stepKey: saveScreenshot - } - - /** - * @Features({"CheckoutAgreements"}) - * @Stories({"Checkout agreements"}) - * @Severity(level = SeverityLevel::CRITICAL) - * @Parameter(name = "AcceptanceTester", value="$I") - * @param AcceptanceTester $I - * @return void - * @throws \Exception - */ - public function AdminUpdateDisabledHtmlTermEntityTest(AcceptanceTester $I) - { - $I->comment("Entering Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/new"); // stepKey: amOnNewTermPageOpenNewTerm - $I->waitForPageLoad(30); // stepKey: waitForAdminNewTermPageLoadOpenNewTerm - $I->comment("Exiting Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->comment("Entering Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("disabledHtmlTerm")); // stepKey: fillFieldConditionNameFillNewTerm - $I->selectOption("#is_active", "Disabled"); // stepKey: selectOptionIsActiveFillNewTerm - $I->selectOption("#is_html", "HTML"); // stepKey: selectOptionIsHtmlFillNewTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillNewTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillNewTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("disabledHtmlTerm")); // stepKey: fillFieldCheckboxTextFillNewTerm - $I->fillField("#content", "<html>"); // stepKey: fillFieldContentFillNewTerm - $I->comment("Exiting Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveNewTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveNewTerm - $I->comment("Exiting Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGrid - $I->comment("Exiting Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("disabledHtmlTerm")); // stepKey: fillTermNameFilterOpenUpdateTerm - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenUpdateTerm - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenUpdateTerm - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenUpdateTerm - $I->comment("Exiting Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("activeTextTerm")); // stepKey: fillFieldConditionNameFillUpdateTerm - $I->selectOption("#is_active", "Enabled"); // stepKey: selectOptionIsActiveFillUpdateTerm - $I->selectOption("#is_html", "Text"); // stepKey: selectOptionIsHtmlFillUpdateTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillUpdateTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillUpdateTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("activeTextTerm")); // stepKey: fillFieldCheckboxTextFillUpdateTerm - $I->fillField("#content", "TestMessage" . msq("activeTextTerm")); // stepKey: fillFieldContentFillUpdateTerm - $I->comment("Exiting Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveUpdateTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveUpdateTerm - $I->comment("Exiting Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenNewTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenNewTermsGrid - $I->comment("Exiting Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("activeTextTerm")); // stepKey: fillTermNameFilterAssertTermInGrid - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonAssertTermInGrid - $I->see("name" . msq("activeTextTerm"), ".data-grid>tbody>tr>td.col-name"); // stepKey: assertTermInGridAssertTermInGrid - $I->comment("Exiting Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->openNewTab(); // stepKey: openStorefrontTab - $I->comment("Entering Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->amOnPage("/" . $I->retrieveEntityField('createProduct', 'custom_attributes[url_key]', 'test') . ".html"); // stepKey: goToProductPageAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForProductPageAddProductToTheCart - $I->click("button.action.tocart.primary"); // stepKey: addToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: addToCartAddProductToTheCartWaitForPageLoad - $I->waitForElementNotVisible("//button/span[text()='Adding...']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddingAddProductToTheCart - $I->waitForElementNotVisible("//button/span[text()='Added']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddedAddProductToTheCart - $I->waitForElementVisible("//button/span[text()='Add to Cart']", 30); // stepKey: waitForElementVisibleAddToCartButtonTitleIsAddToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForPageLoadAddProductToTheCart - $I->waitForElementVisible("div.message-success.success.message", 30); // stepKey: waitForProductAddedMessageAddProductToTheCart - $I->see("You added " . $I->retrieveEntityField('createProduct', 'name', 'test') . " to your shopping cart.", "div.message-success.success.message"); // stepKey: seeAddToCartSuccessMessageAddProductToTheCart - $I->comment("Exiting Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->comment("Entering Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Go to Checkout"); - $I->waitForElementNotVisible(".counter.qty.empty", 30); // stepKey: waitUpdateQuantityProcessCheckoutToThePaymentMethodsPage - $I->wait(5); // stepKey: waitMinicartRenderingProcessCheckoutToThePaymentMethodsPage - $I->click("a.showcart"); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(60); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->click("#top-cart-btn-checkout"); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->comment("Process steps"); - $I->fillField("input[id*=customer-email]", msq("CustomerEntityOne") . "test@email.com"); // stepKey: enterEmailProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=firstname]", "John"); // stepKey: enterFirstNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=lastname]", "Doe"); // stepKey: enterLastNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name='street[0]']", "7700 W Parmer Ln"); // stepKey: enterStreetProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=city]", "Austin"); // stepKey: enterCityProcessCheckoutToThePaymentMethodsPage - $I->selectOption("select[name=region_id]", "Texas"); // stepKey: selectRegionProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=postcode]", "78729"); // stepKey: enterPostcodeProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=telephone]", "1234568910"); // stepKey: enterTelephoneProcessCheckoutToThePaymentMethodsPage - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForShippingMethodsProcessCheckoutToThePaymentMethodsPage - $I->click("//div[@id='checkout-shipping-method-load']//td[contains(., '')]/..//input"); // stepKey: selectShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->waitForElement("button.button.action.continue.primary", 30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElementNotVisible(".loading-mask", 300); // stepKey: waitForProcessShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->click("button.button.action.continue.primary"); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElement("//*[@id='checkout-payment-method-load']//div[@data-role='title']", 30); // stepKey: waitForPaymentSectionLoadedProcessCheckoutToThePaymentMethodsPage - $I->seeInCurrentUrl("/checkout/#payment"); // stepKey: assertCheckoutPaymentUrlProcessCheckoutToThePaymentMethodsPage - $I->comment("Exiting Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Entering Action Group [assertTermInCheckout] AssertStorefrontTermInCheckoutActionGroup"); - $I->comment("Check if agreement is present on checkout and select it"); - $I->see("test_checkbox" . msq("activeTextTerm"), "div.checkout-agreements-block > div > div > div > label > button > span"); // stepKey: seeTermInCheckoutAssertTermInCheckout - $I->selectOption("div.checkout-agreement.field.choice.required > input", "test_checkbox" . msq("activeTextTerm")); // stepKey: checkAgreementAssertTermInCheckout - $I->comment("Checkout select Check/Money Order payment"); - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForPaymentPageRenderingAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: waitForPaymentRenderingAssertTermInCheckout - $I->conditionalClick("//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", "//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", true); // stepKey: selectCheckmoPaymentMethodAssertTermInCheckout - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForLoadingMaskAfterPaymentMethodSelectionAssertTermInCheckout - $I->comment("Click Place Order button"); - $I->click(".payment-method._active button.action.primary.checkout"); // stepKey: clickPlaceOrderAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: clickPlaceOrderAssertTermInCheckoutWaitForPageLoad - $I->comment("See success messages"); - $I->see("Thank you for your purchase!", ".page-title"); // stepKey: seeSuccessTitleAssertTermInCheckout - $I->see("Your order # is: ", ".checkout-success > p:nth-child(1)"); // stepKey: seeOrderNumberAssertTermInCheckout - $I->comment("Exiting Action Group [assertTermInCheckout] AssertStorefrontTermInCheckoutActionGroup"); - $I->closeTab(); // stepKey: closeStorefrontTab - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledTextTermEntityTestCest.php b/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledTextTermEntityTestCest.php deleted file mode 100644 index ab7aec453db92..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateDisabledTextTermEntityTestCest.php +++ /dev/null @@ -1,203 +0,0 @@ -<?php -namespace Magento\AcceptanceTest\_default\Backend; - -use Magento\FunctionalTestingFramework\AcceptanceTester; -use \Codeception\Util\Locator; -use Yandex\Allure\Adapter\Annotation\Features; -use Yandex\Allure\Adapter\Annotation\Stories; -use Yandex\Allure\Adapter\Annotation\Title; -use Yandex\Allure\Adapter\Annotation\Description; -use Yandex\Allure\Adapter\Annotation\Parameter; -use Yandex\Allure\Adapter\Annotation\Severity; -use Yandex\Allure\Adapter\Model\SeverityLevel; -use Yandex\Allure\Adapter\Annotation\TestCaseId; - -/** - * @Title("MC-14665: Update disabled Text checkout agreement") - * @Description("Admin should be able to update disabled Text checkout agreement<h3 class='y-label y-label_status_broken'>Deprecated Notice(s):</h3><ul><li>DEPRECATED ACTION GROUP in Test: AddSimpleProductToCartActionGroup Avoid using super-ActionGroups. Use StorefrontOpenProductEntityPageActionGroup, StorefrontAddSimpleProductToCartActionGroup and StorefrontAssertProductAddedToCartResultMessageActionGroup</li></ul><h3>Test files</h3>app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml<br>") - * @TestCaseId("MC-14665") - * @group checkoutAgreements - * @group mtf_migrated - */ -class AdminUpdateDisabledTextTermEntityTestCest -{ - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _after(AcceptanceTester $I) - { - $setDisableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 0", 60); // stepKey: setDisableTermsOnCheckout - $I->comment($setDisableTermsOnCheckout); - $I->deleteEntity("createProduct", "hook"); // stepKey: deletedProduct - $I->comment("Entering Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGridToDelete - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGridToDelete - $I->comment("Exiting Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("activeHtmlTerm")); // stepKey: fillTermNameFilterOpenTermToDelete - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenTermToDelete - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenTermToDelete - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenTermToDelete - $I->comment("Exiting Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->click(".page-main-actions #delete"); // stepKey: clickDeleteButtonDeleteOpenedTerm - $I->waitForElementVisible("button.action-primary.action-accept", 30); // stepKey: waitForElementDeleteOpenedTerm - $I->click("button.action-primary.action-accept"); // stepKey: clickDeleteOkButtonDeleteOpenedTerm - $I->waitForText("You deleted the condition.", 30, ".message-success"); // stepKey: seeSuccessMessageDeleteOpenedTerm - $I->comment("Exiting Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->comment("Entering Action Group [adminLogout] AdminLogoutActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin/auth/logout/"); // stepKey: amOnLogoutPageAdminLogout - $I->comment("Exiting Action Group [adminLogout] AdminLogoutActionGroup"); - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _failed(AcceptanceTester $I) - { - $I->saveScreenshot(); // stepKey: saveScreenshot - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _before(AcceptanceTester $I) - { - $setEnableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 1", 60); // stepKey: setEnableTermsOnCheckout - $I->comment($setEnableTermsOnCheckout); - $I->createEntity("createProduct", "hook", "SimpleTwo", [], []); // stepKey: createProduct - $runCronIndex = $I->magentoCron("index", 90); // stepKey: runCronIndex - $I->comment($runCronIndex); - $I->comment("Entering Action Group [adminLogin] AdminLoginActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin"); // stepKey: navigateToAdminAdminLogin - $I->fillField("#username", getenv("MAGENTO_ADMIN_USERNAME")); // stepKey: fillUsernameAdminLogin - $I->fillField("#login", getenv("MAGENTO_ADMIN_PASSWORD")); // stepKey: fillPasswordAdminLogin - $I->click(".actions .action-primary"); // stepKey: clickLoginAdminLogin - $I->waitForPageLoad(30); // stepKey: clickLoginAdminLoginWaitForPageLoad - $I->conditionalClick(".modal-popup .action-secondary", ".modal-popup .action-secondary", true); // stepKey: clickDontAllowButtonIfVisibleAdminLogin - $I->closeAdminNotification(); // stepKey: closeAdminNotificationAdminLogin - $I->comment("Exiting Action Group [adminLogin] AdminLoginActionGroup"); - } - - /** - * @Features({"CheckoutAgreements"}) - * @Stories({"Checkout agreements"}) - * @Severity(level = SeverityLevel::CRITICAL) - * @Parameter(name = "AcceptanceTester", value="$I") - * @param AcceptanceTester $I - * @return void - * @throws \Exception - */ - public function AdminUpdateDisabledTextTermEntityTest(AcceptanceTester $I) - { - $I->comment("Entering Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/new"); // stepKey: amOnNewTermPageOpenNewTerm - $I->waitForPageLoad(30); // stepKey: waitForAdminNewTermPageLoadOpenNewTerm - $I->comment("Exiting Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->comment("Entering Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("disabledTextTerm")); // stepKey: fillFieldConditionNameFillNewTerm - $I->selectOption("#is_active", "Disabled"); // stepKey: selectOptionIsActiveFillNewTerm - $I->selectOption("#is_html", "Text"); // stepKey: selectOptionIsHtmlFillNewTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillNewTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillNewTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("disabledTextTerm")); // stepKey: fillFieldCheckboxTextFillNewTerm - $I->fillField("#content", "TestMessage" . msq("disabledTextTerm")); // stepKey: fillFieldContentFillNewTerm - $I->comment("Exiting Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveNewTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveNewTerm - $I->comment("Exiting Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGrid - $I->comment("Exiting Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("disabledTextTerm")); // stepKey: fillTermNameFilterOpenUpdateTerm - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenUpdateTerm - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenUpdateTerm - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenUpdateTerm - $I->comment("Exiting Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("activeHtmlTerm")); // stepKey: fillFieldConditionNameFillUpdateTerm - $I->selectOption("#is_active", "Enabled"); // stepKey: selectOptionIsActiveFillUpdateTerm - $I->selectOption("#is_html", "HTML"); // stepKey: selectOptionIsHtmlFillUpdateTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillUpdateTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillUpdateTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("activeHtmlTerm")); // stepKey: fillFieldCheckboxTextFillUpdateTerm - $I->fillField("#content", "<html>"); // stepKey: fillFieldContentFillUpdateTerm - $I->comment("Exiting Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveUpdateTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveUpdateTerm - $I->comment("Exiting Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenNewTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenNewTermsGrid - $I->comment("Exiting Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("activeHtmlTerm")); // stepKey: fillTermNameFilterAssertTermInGrid - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonAssertTermInGrid - $I->see("name" . msq("activeHtmlTerm"), ".data-grid>tbody>tr>td.col-name"); // stepKey: assertTermInGridAssertTermInGrid - $I->comment("Exiting Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->openNewTab(); // stepKey: openStorefrontTab - $I->comment("Entering Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->amOnPage("/" . $I->retrieveEntityField('createProduct', 'custom_attributes[url_key]', 'test') . ".html"); // stepKey: goToProductPageAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForProductPageAddProductToTheCart - $I->click("button.action.tocart.primary"); // stepKey: addToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: addToCartAddProductToTheCartWaitForPageLoad - $I->waitForElementNotVisible("//button/span[text()='Adding...']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddingAddProductToTheCart - $I->waitForElementNotVisible("//button/span[text()='Added']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddedAddProductToTheCart - $I->waitForElementVisible("//button/span[text()='Add to Cart']", 30); // stepKey: waitForElementVisibleAddToCartButtonTitleIsAddToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForPageLoadAddProductToTheCart - $I->waitForElementVisible("div.message-success.success.message", 30); // stepKey: waitForProductAddedMessageAddProductToTheCart - $I->see("You added " . $I->retrieveEntityField('createProduct', 'name', 'test') . " to your shopping cart.", "div.message-success.success.message"); // stepKey: seeAddToCartSuccessMessageAddProductToTheCart - $I->comment("Exiting Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->comment("Entering Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Go to Checkout"); - $I->waitForElementNotVisible(".counter.qty.empty", 30); // stepKey: waitUpdateQuantityProcessCheckoutToThePaymentMethodsPage - $I->wait(5); // stepKey: waitMinicartRenderingProcessCheckoutToThePaymentMethodsPage - $I->click("a.showcart"); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(60); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->click("#top-cart-btn-checkout"); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->comment("Process steps"); - $I->fillField("input[id*=customer-email]", msq("CustomerEntityOne") . "test@email.com"); // stepKey: enterEmailProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=firstname]", "John"); // stepKey: enterFirstNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=lastname]", "Doe"); // stepKey: enterLastNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name='street[0]']", "7700 W Parmer Ln"); // stepKey: enterStreetProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=city]", "Austin"); // stepKey: enterCityProcessCheckoutToThePaymentMethodsPage - $I->selectOption("select[name=region_id]", "Texas"); // stepKey: selectRegionProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=postcode]", "78729"); // stepKey: enterPostcodeProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=telephone]", "1234568910"); // stepKey: enterTelephoneProcessCheckoutToThePaymentMethodsPage - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForShippingMethodsProcessCheckoutToThePaymentMethodsPage - $I->click("//div[@id='checkout-shipping-method-load']//td[contains(., '')]/..//input"); // stepKey: selectShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->waitForElement("button.button.action.continue.primary", 30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElementNotVisible(".loading-mask", 300); // stepKey: waitForProcessShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->click("button.button.action.continue.primary"); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElement("//*[@id='checkout-payment-method-load']//div[@data-role='title']", 30); // stepKey: waitForPaymentSectionLoadedProcessCheckoutToThePaymentMethodsPage - $I->seeInCurrentUrl("/checkout/#payment"); // stepKey: assertCheckoutPaymentUrlProcessCheckoutToThePaymentMethodsPage - $I->comment("Exiting Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Entering Action Group [assertTermInCheckout] AssertStorefrontTermInCheckoutActionGroup"); - $I->comment("Check if agreement is present on checkout and select it"); - $I->see("test_checkbox" . msq("activeHtmlTerm"), "div.checkout-agreements-block > div > div > div > label > button > span"); // stepKey: seeTermInCheckoutAssertTermInCheckout - $I->selectOption("div.checkout-agreement.field.choice.required > input", "test_checkbox" . msq("activeHtmlTerm")); // stepKey: checkAgreementAssertTermInCheckout - $I->comment("Checkout select Check/Money Order payment"); - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForPaymentPageRenderingAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: waitForPaymentRenderingAssertTermInCheckout - $I->conditionalClick("//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", "//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", true); // stepKey: selectCheckmoPaymentMethodAssertTermInCheckout - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForLoadingMaskAfterPaymentMethodSelectionAssertTermInCheckout - $I->comment("Click Place Order button"); - $I->click(".payment-method._active button.action.primary.checkout"); // stepKey: clickPlaceOrderAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: clickPlaceOrderAssertTermInCheckoutWaitForPageLoad - $I->comment("See success messages"); - $I->see("Thank you for your purchase!", ".page-title"); // stepKey: seeSuccessTitleAssertTermInCheckout - $I->see("Your order # is: ", ".checkout-success > p:nth-child(1)"); // stepKey: seeOrderNumberAssertTermInCheckout - $I->comment("Exiting Action Group [assertTermInCheckout] AssertStorefrontTermInCheckoutActionGroup"); - $I->closeTab(); // stepKey: closeStorefrontTab - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateEnabledTextTermEntityTestCest.php b/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateEnabledTextTermEntityTestCest.php deleted file mode 100644 index 0d0b355f1c894..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/_generated/default/AdminUpdateEnabledTextTermEntityTestCest.php +++ /dev/null @@ -1,202 +0,0 @@ -<?php -namespace Magento\AcceptanceTest\_default\Backend; - -use Magento\FunctionalTestingFramework\AcceptanceTester; -use \Codeception\Util\Locator; -use Yandex\Allure\Adapter\Annotation\Features; -use Yandex\Allure\Adapter\Annotation\Stories; -use Yandex\Allure\Adapter\Annotation\Title; -use Yandex\Allure\Adapter\Annotation\Description; -use Yandex\Allure\Adapter\Annotation\Parameter; -use Yandex\Allure\Adapter\Annotation\Severity; -use Yandex\Allure\Adapter\Model\SeverityLevel; -use Yandex\Allure\Adapter\Annotation\TestCaseId; - -/** - * @Title("MC-14666: Update enabled Text checkout agreement") - * @Description("Admin should be able to update enabled Text checkout agreement<h3 class='y-label y-label_status_broken'>Deprecated Notice(s):</h3><ul><li>DEPRECATED ACTION GROUP in Test: AddSimpleProductToCartActionGroup Avoid using super-ActionGroups. Use StorefrontOpenProductEntityPageActionGroup, StorefrontAddSimpleProductToCartActionGroup and StorefrontAssertProductAddedToCartResultMessageActionGroup</li></ul><h3>Test files</h3>app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml<br>") - * @TestCaseId("MC-14666") - * @group checkoutAgreements - * @group mtf_migrated - */ -class AdminUpdateEnabledTextTermEntityTestCest -{ - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _after(AcceptanceTester $I) - { - $setDisableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 0", 60); // stepKey: setDisableTermsOnCheckout - $I->comment($setDisableTermsOnCheckout); - $I->deleteEntity("createProduct", "hook"); // stepKey: deletedProduct - $I->comment("Entering Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGridToDelete - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGridToDelete - $I->comment("Exiting Action Group [openTermsGridToDelete] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("disabledHtmlTerm")); // stepKey: fillTermNameFilterOpenTermToDelete - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenTermToDelete - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenTermToDelete - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenTermToDelete - $I->comment("Exiting Action Group [openTermToDelete] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->click(".page-main-actions #delete"); // stepKey: clickDeleteButtonDeleteOpenedTerm - $I->waitForElementVisible("button.action-primary.action-accept", 30); // stepKey: waitForElementDeleteOpenedTerm - $I->click("button.action-primary.action-accept"); // stepKey: clickDeleteOkButtonDeleteOpenedTerm - $I->waitForText("You deleted the condition.", 30, ".message-success"); // stepKey: seeSuccessMessageDeleteOpenedTerm - $I->comment("Exiting Action Group [deleteOpenedTerm] AdminTermsConditionsDeleteTermByNameActionGroup"); - $I->comment("Entering Action Group [adminLogout] AdminLogoutActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin/auth/logout/"); // stepKey: amOnLogoutPageAdminLogout - $I->comment("Exiting Action Group [adminLogout] AdminLogoutActionGroup"); - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _failed(AcceptanceTester $I) - { - $I->saveScreenshot(); // stepKey: saveScreenshot - } - - /** - * @param AcceptanceTester $I - * @throws \Exception - */ - public function _before(AcceptanceTester $I) - { - $setEnableTermsOnCheckout = $I->magentoCLI("config:set checkout/options/enable_agreements 1", 60); // stepKey: setEnableTermsOnCheckout - $I->comment($setEnableTermsOnCheckout); - $I->createEntity("createProduct", "hook", "SimpleTwo", [], []); // stepKey: createProduct - $runCronIndex = $I->magentoCron("index", 90); // stepKey: runCronIndex - $I->comment($runCronIndex); - $I->comment("Entering Action Group [adminLogin] AdminLoginActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/admin"); // stepKey: navigateToAdminAdminLogin - $I->fillField("#username", getenv("MAGENTO_ADMIN_USERNAME")); // stepKey: fillUsernameAdminLogin - $I->fillField("#login", getenv("MAGENTO_ADMIN_PASSWORD")); // stepKey: fillPasswordAdminLogin - $I->click(".actions .action-primary"); // stepKey: clickLoginAdminLogin - $I->waitForPageLoad(30); // stepKey: clickLoginAdminLoginWaitForPageLoad - $I->conditionalClick(".modal-popup .action-secondary", ".modal-popup .action-secondary", true); // stepKey: clickDontAllowButtonIfVisibleAdminLogin - $I->closeAdminNotification(); // stepKey: closeAdminNotificationAdminLogin - $I->comment("Exiting Action Group [adminLogin] AdminLoginActionGroup"); - } - - /** - * @Features({"CheckoutAgreements"}) - * @Stories({"Checkout agreements"}) - * @Severity(level = SeverityLevel::CRITICAL) - * @Parameter(name = "AcceptanceTester", value="$I") - * @param AcceptanceTester $I - * @return void - * @throws \Exception - */ - public function AdminUpdateEnabledTextTermEntityTest(AcceptanceTester $I) - { - $I->comment("Entering Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/new"); // stepKey: amOnNewTermPageOpenNewTerm - $I->waitForPageLoad(30); // stepKey: waitForAdminNewTermPageLoadOpenNewTerm - $I->comment("Exiting Action Group [openNewTerm] AdminTermsConditionsOpenNewTermPageActionGroup"); - $I->comment("Entering Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("activeTextTerm")); // stepKey: fillFieldConditionNameFillNewTerm - $I->selectOption("#is_active", "Enabled"); // stepKey: selectOptionIsActiveFillNewTerm - $I->selectOption("#is_html", "Text"); // stepKey: selectOptionIsHtmlFillNewTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillNewTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillNewTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("activeTextTerm")); // stepKey: fillFieldCheckboxTextFillNewTerm - $I->fillField("#content", "TestMessage" . msq("activeTextTerm")); // stepKey: fillFieldContentFillNewTerm - $I->comment("Exiting Action Group [fillNewTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveNewTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveNewTerm - $I->comment("Exiting Action Group [saveNewTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenTermsGrid - $I->comment("Exiting Action Group [openTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("activeTextTerm")); // stepKey: fillTermNameFilterOpenUpdateTerm - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonOpenUpdateTerm - $I->click(".data-grid>tbody>tr>td.col-id.col-agreement_id"); // stepKey: clickFirstRowOpenUpdateTerm - $I->waitForPageLoad(30); // stepKey: waitForEditTermPageLoadOpenUpdateTerm - $I->comment("Exiting Action Group [openUpdateTerm] AdminTermsConditionsEditTermByNameActionGroup"); - $I->comment("Entering Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->fillField("#name", "name" . msq("disabledHtmlTerm")); // stepKey: fillFieldConditionNameFillUpdateTerm - $I->selectOption("#is_active", "Disabled"); // stepKey: selectOptionIsActiveFillUpdateTerm - $I->selectOption("#is_html", "HTML"); // stepKey: selectOptionIsHtmlFillUpdateTerm - $I->selectOption("#mode", "Manually"); // stepKey: selectOptionModeFillUpdateTerm - $I->selectOption("#stores", "Default Store View"); // stepKey: selectOptionStoreViewFillUpdateTerm - $I->fillField("#checkbox_text", "test_checkbox" . msq("disabledHtmlTerm")); // stepKey: fillFieldCheckboxTextFillUpdateTerm - $I->fillField("#content", "<html>"); // stepKey: fillFieldContentFillUpdateTerm - $I->comment("Exiting Action Group [fillUpdateTerm] AdminTermsConditionsFillTermEditFormActionGroup"); - $I->comment("Entering Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->click(".page-main-actions #save"); // stepKey: saveTermSaveUpdateTerm - $I->see("You saved the condition.", ".message-success"); // stepKey: seeSuccessMessageSaveUpdateTerm - $I->comment("Exiting Action Group [saveUpdateTerm] AdminTermsConditionsSaveTermActionGroup"); - $I->comment("Entering Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->amOnPage("/" . getenv("MAGENTO_BACKEND_NAME") . "/checkout/agreement/"); // stepKey: onTermGridPageOpenNewTermsGrid - $I->waitForPageLoad(30); // stepKey: waitForPageLoadOpenNewTermsGrid - $I->comment("Exiting Action Group [openNewTermsGrid] AdminTermsConditionsOpenGridActionGroup"); - $I->comment("Entering Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->fillField("#agreementGrid_filter_name", "name" . msq("disabledHtmlTerm")); // stepKey: fillTermNameFilterAssertTermInGrid - $I->click("//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"); // stepKey: clickSearchButtonAssertTermInGrid - $I->see("name" . msq("disabledHtmlTerm"), ".data-grid>tbody>tr>td.col-name"); // stepKey: assertTermInGridAssertTermInGrid - $I->comment("Exiting Action Group [assertTermInGrid] AssertAdminTermsConditionsInGridActionGroup"); - $I->openNewTab(); // stepKey: openStorefrontTab - $I->comment("Entering Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->amOnPage("/" . $I->retrieveEntityField('createProduct', 'custom_attributes[url_key]', 'test') . ".html"); // stepKey: goToProductPageAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForProductPageAddProductToTheCart - $I->click("button.action.tocart.primary"); // stepKey: addToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: addToCartAddProductToTheCartWaitForPageLoad - $I->waitForElementNotVisible("//button/span[text()='Adding...']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddingAddProductToTheCart - $I->waitForElementNotVisible("//button/span[text()='Added']", 30); // stepKey: waitForElementNotVisibleAddToCartButtonTitleIsAddedAddProductToTheCart - $I->waitForElementVisible("//button/span[text()='Add to Cart']", 30); // stepKey: waitForElementVisibleAddToCartButtonTitleIsAddToCartAddProductToTheCart - $I->waitForPageLoad(30); // stepKey: waitForPageLoadAddProductToTheCart - $I->waitForElementVisible("div.message-success.success.message", 30); // stepKey: waitForProductAddedMessageAddProductToTheCart - $I->see("You added " . $I->retrieveEntityField('createProduct', 'name', 'test') . " to your shopping cart.", "div.message-success.success.message"); // stepKey: seeAddToCartSuccessMessageAddProductToTheCart - $I->comment("Exiting Action Group [addProductToTheCart] AddSimpleProductToCartActionGroup"); - $I->comment("Entering Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Go to Checkout"); - $I->waitForElementNotVisible(".counter.qty.empty", 30); // stepKey: waitUpdateQuantityProcessCheckoutToThePaymentMethodsPage - $I->wait(5); // stepKey: waitMinicartRenderingProcessCheckoutToThePaymentMethodsPage - $I->click("a.showcart"); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(60); // stepKey: clickCartProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->click("#top-cart-btn-checkout"); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: goToCheckoutProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->comment("Process steps"); - $I->fillField("input[id*=customer-email]", msq("CustomerEntityOne") . "test@email.com"); // stepKey: enterEmailProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=firstname]", "John"); // stepKey: enterFirstNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=lastname]", "Doe"); // stepKey: enterLastNameProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name='street[0]']", "7700 W Parmer Ln"); // stepKey: enterStreetProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=city]", "Austin"); // stepKey: enterCityProcessCheckoutToThePaymentMethodsPage - $I->selectOption("select[name=region_id]", "Texas"); // stepKey: selectRegionProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=postcode]", "78729"); // stepKey: enterPostcodeProcessCheckoutToThePaymentMethodsPage - $I->fillField("input[name=telephone]", "1234568910"); // stepKey: enterTelephoneProcessCheckoutToThePaymentMethodsPage - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForShippingMethodsProcessCheckoutToThePaymentMethodsPage - $I->click("//div[@id='checkout-shipping-method-load']//td[contains(., '')]/..//input"); // stepKey: selectShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->waitForElement("button.button.action.continue.primary", 30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: waitForTheNextButtonProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElementNotVisible(".loading-mask", 300); // stepKey: waitForProcessShippingMethodProcessCheckoutToThePaymentMethodsPage - $I->click("button.button.action.continue.primary"); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPage - $I->waitForPageLoad(30); // stepKey: clickNextProcessCheckoutToThePaymentMethodsPageWaitForPageLoad - $I->waitForElement("//*[@id='checkout-payment-method-load']//div[@data-role='title']", 30); // stepKey: waitForPaymentSectionLoadedProcessCheckoutToThePaymentMethodsPage - $I->seeInCurrentUrl("/checkout/#payment"); // stepKey: assertCheckoutPaymentUrlProcessCheckoutToThePaymentMethodsPage - $I->comment("Exiting Action Group [processCheckoutToThePaymentMethodsPage] StorefrontProcessCheckoutToPaymentActionGroup"); - $I->comment("Entering Action Group [assertTermInCheckout] AssertStorefrontTermAbsentInCheckoutActionGroup"); - $I->comment("Check if agreement is absent on checkout"); - $I->dontSee("test_checkbox" . msq("disabledHtmlTerm"), "div.checkout-agreements-block > div > div > div > label > button > span"); // stepKey: seeTermInCheckoutAssertTermInCheckout - $I->comment("Checkout select Check/Money Order payment"); - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForPaymentPageRenderingAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: waitForPaymentRenderingAssertTermInCheckout - $I->conditionalClick("//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", "//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., 'Check / Money order')]/../..//input", true); // stepKey: selectCheckmoPaymentMethodAssertTermInCheckout - $I->waitForLoadingMaskToDisappear(); // stepKey: waitForLoadingMaskAfterPaymentMethodSelectionAssertTermInCheckout - $I->comment("Click Place Order button"); - $I->click(".payment-method._active button.action.primary.checkout"); // stepKey: clickPlaceOrderAssertTermInCheckout - $I->waitForPageLoad(30); // stepKey: clickPlaceOrderAssertTermInCheckoutWaitForPageLoad - $I->comment("See success messages"); - $I->see("Thank you for your purchase!", ".page-title"); // stepKey: seeSuccessTitleAssertTermInCheckout - $I->see("Your order # is: ", ".checkout-success > p:nth-child(1)"); // stepKey: seeOrderNumberAssertTermInCheckout - $I->comment("Exiting Action Group [assertTermInCheckout] AssertStorefrontTermAbsentInCheckoutActionGroup"); - $I->closeTab(); // stepKey: closeStorefrontTab - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt b/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt index 4f56e6e680b7a..dba070c50affc 100644 --- a/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt +++ b/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt @@ -1,3 +1 @@ -tests/functional/Magento/_generated/default/AdminUpdateEnabledTextTermEntityTestCest.php -tests/functional/Magento/_generated/default/AdminUpdateDisabledTextTermEntityTestCest.php -tests/functional/Magento/_generated/default/AdminUpdateDisabledHtmlTermEntityTestCest.php +tests/functional/Magento/_generated/default/AdminCreateUserRoleEntityTestCest.php From eba6b05523a2300d861392cd170c76ad28248a21 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 3 Apr 2020 00:57:10 -0500 Subject: [PATCH 675/734] Deliver community converted tests --- .../Test/AdminDeleteActiveTextTermEntityTest.xml | 12 ++++++++---- .../functional/Magento/_generated/testManifest.txt | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml index 3558f94bb78e6..1e17dcc675573 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml @@ -23,9 +23,11 @@ <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> <createData entity="SimpleTwo" stepKey="createdProduct"/> - <actionGroup ref="CreateNewTermActionGroup" stepKey="createTerm"> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="createTerm"> <argument name="term" value="activeTextTerm"/> </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> </before> <after> <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> @@ -33,9 +35,11 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="DeleteTermActionGroup" stepKey="deleteTerm"> - <argument name="term" value="activeTextTerm"/> + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> <actionGroup ref="AdminAssertTermAbsentInGridActionGroup" stepKey="assertTermAbsentInGrid"> <argument name="termName" value="{{activeTextTerm.name}}"/> </actionGroup> @@ -44,7 +48,7 @@ <argument name="product" value="$$createdProduct$$"/> </actionGroup> <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> - <actionGroup ref="StorefrontAssertTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> </actionGroup> </test> diff --git a/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt b/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt deleted file mode 100644 index dba070c50affc..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/_generated/testManifest.txt +++ /dev/null @@ -1 +0,0 @@ -tests/functional/Magento/_generated/default/AdminCreateUserRoleEntityTestCest.php From bbcbc908461e4797702c1cf1637d1a345a431497 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 3 Apr 2020 02:32:00 -0500 Subject: [PATCH 676/734] Deliver community converted tests - fix failing tests --- .../Mftf/Test/AdminLoginWithRestrictPermissionTest.xml | 3 ++- .../Test/Mftf/Test/AdminLoginWithCaptchaTest.xml | 9 ++++++--- ...minRestrictedUserAddCategoryFromProductPageTest.xml | 3 ++- ...sNotAccessibleForAdminUserWithLimitedAccessTest.xml | 3 ++- .../Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml | 3 ++- .../Test/Mftf/Test/AdminLockAdminUserEntityTest.xml | 10 ++-------- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml index bf4b4dd6f2108..b3797b0720400 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml @@ -54,7 +54,8 @@ <!--Log out of admin and login with newly created user--> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> - <argument name="adminUser" value="admin2"/> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> </actionGroup> <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="assertRestrictPage"/> </test> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml index b978bbf1ff55d..962b788aa80bb 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml @@ -32,17 +32,20 @@ </after> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeFirstLoginErrorMessage" /> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsSecondAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeSecondLoginErrorMessage" /> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsThirdAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeThirdLoginErrorMessage" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml index f0a8ad298f014..e89e89205f3f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml @@ -76,7 +76,8 @@ <comment userInput="Log out of admin and login with newly created user" stepKey="commentLoginWithNewUser"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> - <argument name="adminUser" value="admin2"/> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> </actionGroup> <!--Go to create product page--> <comment userInput="Go to create product page" stepKey="commentGoCreateProductPage"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml index acf2571db9c14..e192bea3a25e0 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml @@ -48,7 +48,8 @@ <!-- Login as user2 --> <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsUser"> - <argument name="adminUser" value="NewAdminUser"/> + <argument name="username" value="{{NewAdminUser.username}}"/> + <argument name="password" value="{{NewAdminUser.password}}"/> </actionGroup> <!-- Bulk operation menu item isn't visible( System > Action Logs > -Bulk Actions-) --> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml index 8df2a2192e9d9..1019d58edcaf2 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml @@ -26,7 +26,8 @@ <field key="current_password">{$adminPassword}</field> </createData> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> - <argument name="adminUser" value="$$user$$"/> + <argument name="username" value="$$user.username$$"/> + <argument name="password" value="$$user.password$$"/> </actionGroup> </before> <after> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index 70b521bf688bc..850fa04549e84 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -21,11 +21,13 @@ </annotations> <before> <magentoCLI command="config:set admin/captcha/enable 0" stepKey="disableAdminCaptcha"/> + <magentoCLI command="config:set admin/security/lockout_failures 2" stepKey="setDefaultMaximumLoginFailures"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set admin/captcha/enable 1" stepKey="enableAdminCaptcha"/> + <magentoCLI command="config:set admin/security/lockout_failures 6" stepKey="setDefaultMaximumLoginFailures"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> @@ -37,14 +39,6 @@ </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - <!--Set 'Maximum Login Failures to Lockout Account'--> - <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> - <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> - <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"> - <argument name="qty" value="2"/> - </actionGroup> - <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> - <!-- Log in to Admin Panel with incorrect password specified number of times--> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsDefaultUser"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserFirstAttempt"> From 2e626e7c3fd443fe03291208e0f7aeb56ea8e3ee Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Fri, 3 Apr 2020 15:08:54 +0300 Subject: [PATCH 677/734] Fixed url for CmsPageEdit --- app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml index 73db6b61343b1..35060043ad637 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="CmsPageEditPage" area="admin" url="admin/cms_page/edit/page_id/{{var}}" parameterized="true" module="Magento_Cms"> + <page name="CmsPageEditPage" area="admin" url="cms/page/edit/page_id/{{var}}" parameterized="true" module="Magento_Cms"> <section name="CmsNewPagePageActionsSection"/> <section name="CmsNewPagePageBasicFieldsSection"/> <section name="CmsNewPagePageContentSection"/> From 039e09b4a09c7a1dc88aca5a3e4d93c50a316d95 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Fri, 3 Apr 2020 16:07:42 +0300 Subject: [PATCH 678/734] Remove redundant file and add minor fixes --- ...HomePageContentWYSIWYGDisabledActionGroup.xml | 2 +- .../ClearWidgetsFromCMSContentActionGroup.xml | 2 +- .../Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml | 2 +- .../Cms/Test/Mftf/Page/CmsPageEditPage.xml | 16 ---------------- 4 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml index adaeb8c90ff0b..31aedde95d95b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml @@ -9,7 +9,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup"> - <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> + <amOnPage url="{{AdminCmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> <waitForPageLoad stepKey="waitForCmsPageEditPage"/> <conditionalClick selector="{{CmsNewPagePageActionsSection.contentSectionName}}" dependentSelector="{{CatalogWidgetSection.insertWidgetButton}}" visible="false" stepKey="clickShowHideEditorIfVisible"/> <waitForElementVisible selector="{{CmsNewPagePageContentSection.content}}" stepKey="waitForContentField"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml index 98de51574aa4b..a50f674208d6a 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin CMS Page Edit page for the Page ID number 2. Clears the Widget and replaces it with Text. PLEASE NOTE: The Page ID/Text are Hardcoded.</description> </annotations> - <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> + <amOnPage url="{{AdminCmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> <waitForPageLoad stepKey="waitEditHomePagePageToLoad"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickContentTab"/> <waitForElementNotVisible selector="{{CmsWYSIWYGSection.CheckIfTabExpand}}" stepKey="waitForTabExpand"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml index 978b6d6a6d261..3c5461ff3a031 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{id}}" parameterized="true" module="Magento_Cms"> + <page name="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{pageId}}" parameterized="true" module="Magento_Cms"> <section name="CmsNewPagePageActionsSection"/> <section name="CmsNewPagePageBasicFieldsSection"/> <section name="CmsNewPagePageContentSection"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml deleted file mode 100644 index 35060043ad637..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="CmsPageEditPage" area="admin" url="cms/page/edit/page_id/{{var}}" parameterized="true" module="Magento_Cms"> - <section name="CmsNewPagePageActionsSection"/> - <section name="CmsNewPagePageBasicFieldsSection"/> - <section name="CmsNewPagePageContentSection"/> - <section name="CmsNewPagePageSeoSection"/> - </page> -</pages> From 0d887662aa0e81a40c44a354d3e3a1abaf91fb29 Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Fri, 3 Apr 2020 16:26:04 +0300 Subject: [PATCH 679/734] Format code. --- app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php index 7430213030f96..8ab8cbfb6122b 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php @@ -133,7 +133,7 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail() $this->emailNotificationMock->expects($this->once()) ->method('sendErrors') - ->with(['Sitemap Exception']); + ->with([$exception]); $this->observer->scheduledGenerateSitemaps(); } From 6f02349fb4d1d550440cabff49bc462d0ce8c924 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 3 Apr 2020 18:31:04 +0300 Subject: [PATCH 680/734] MC-30392: De-couple Authorize.net payment methods integrations from core in 2.4.0 --- .../Test/Legacy/_files/obsolete_classes.php | 200 ------------------ .../Legacy/_files/obsolete_namespaces.php | 4 + 2 files changed, 4 insertions(+), 200 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 76d4c0eb5d4a8..78bcec7abda09 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4255,204 +4255,4 @@ ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper'], ['Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver'], ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'] - ['Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails'], - ['Magento\Authorizenet\Block\Adminhtml\Order\View\Info\PaymentDetails'], - ['Magento\Authorizenet\Block\Transparent\Iframe'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\AddConfigured'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Cancel'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ConfigureProductToAdd'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ConfigureQuoteItems'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Index'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\LoadBlock'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Place'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ProcessData'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Redirect'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Reorder'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ReturnQuote'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Save'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\ShowUpdateResult'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\Start'], - ['Magento\Authorizenet\Controller\Directpost\Payment'], - ['Magento\Authorizenet\Controller\Directpost\Payment\BackendResponse'], - ['Magento\Authorizenet\Controller\Directpost\Payment\Place'], - ['Magento\Authorizenet\Controller\Directpost\Payment\Redirect'], - ['Magento\Authorizenet\Controller\Directpost\Payment\Response'], - ['Magento\Authorizenet\Controller\Directpost\Payment\ReturnQuote'], - ['Magento\Authorizenet\Helper\Backend\Data'], - ['Magento\Authorizenet\Helper\Data'], - ['Magento\Authorizenet\Helper\DataFactory'], - ['Magento\Authorizenet\Model\Authorizenet'], - ['Magento\Authorizenet\Model\Debug'], - ['Magento\Authorizenet\Model\Directpost'], - ['Magento\Authorizenet\Model\Directpost\Request'], - ['Magento\Authorizenet\Model\Directpost\Request\Factory'], - ['Magento\Authorizenet\Model\Directpost\Response'], - ['Magento\Authorizenet\Model\Directpost\Response\Factory'], - ['Magento\Authorizenet\Model\Directpost\Session'], - ['Magento\Authorizenet\Model\Request'], - ['Magento\Authorizenet\Model\Request\Factory'], - ['Magento\Authorizenet\Model\ResourceModel\Debug'], - ['Magento\Authorizenet\Model\ResourceModel\Debug\Collection'], - ['Magento\Authorizenet\Model\Response'], - ['Magento\Authorizenet\Model\Response\Factory'], - ['Magento\Authorizenet\Model\Source\Cctype'], - ['Magento\Authorizenet\Model\Source\PaymentAction'], - ['Magento\Authorizenet\Model\TransactionService'], - ['Magento\Authorizenet\Observer\AddFieldsToResponseObserver'], - ['Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver'], - ['Magento\Authorizenet\Observer\UpdateAllEditIncrementsObserver'], - ['Magento\Authorizenet\Test\Unit\Controller\Adminhtml\Authorizenet\Directpost\Payment\RedirectTest'], - ['Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment\PlaceTest'], - ['Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment\RedirectTest'], - ['Magento\Authorizenet\Test\Unit\Helper\Backend\DataTest'], - ['Magento\Authorizenet\Test\Unit\Helper\DataTest'], - ['Magento\Authorizenet\Test\Unit\Model\Directpost\Request\FactoryTest'], - ['Magento\Authorizenet\Test\Unit\Model\Directpost\RequestTest'], - ['Magento\Authorizenet\Test\Unit\Model\Directpost\Response\FactoryTest'], - ['Magento\Authorizenet\Test\Unit\Model\Directpost\ResponseTest'], - ['Magento\Authorizenet\Test\Unit\Model\Directpost\SessionTest'], - ['Magento\Authorizenet\Test\Unit\Model\DirectpostTest'], - ['Magento\Authorizenet\Test\Unit\Model\Request\FactoryTest'], - ['Magento\Authorizenet\Test\Unit\Model\Response\FactoryTest'], - ['Magento\Authorizenet\Test\Unit\Model\TransactionServiceTest'], - ['Magento\Authorizenet\Test\Unit\Observer\AddFieldsToResponseObserverTest'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\PlaceTest'], - ['Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\PlaceTesting'], - ['Magento\Authorizenet\Controller\Directpost\Payment\BackendResponseTest'], - ['Magento\Authorizenet\Controller\Directpost\Payment\ResponseTest'], - ['Magento\Authorizenet\Controller\Directpost\PaymentTest'], - ['Magento\Authorizenet\Model\Directpost\RequestTest'], - ['Magento\Authorizenet\Model\DirectpostTest'], - ['Magento\AuthorizenetAcceptjs\Block\Form'], - ['Magento\AuthorizenetAcceptjs\Block\Info'], - ['Magento\AuthorizenetAcceptjs\Block\Payment'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\AcceptPaymentStrategyCommand'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\CaptureStrategyCommand'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\FetchTransactionInfoCommand'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\GatewayQueryCommand'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\RefundTransactionStrategyCommand'], - ['Magento\AuthorizenetAcceptjs\Gateway\Config'], - ['Magento\AuthorizenetAcceptjs\Gateway\Http\Client'], - ['Magento\AuthorizenetAcceptjs\Gateway\Http\Payload\Filter\RemoveFieldsFilter'], - ['Magento\AuthorizenetAcceptjs\Gateway\Http\Payload\FilterInterface'], - ['Magento\AuthorizenetAcceptjs\Gateway\Http\TransferFactory'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\AcceptFdsDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\CaptureDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundPaymentDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundReferenceTransactionDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\RefundTransactionTypeDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\RequestTypeBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\SaleDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\StubDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\TransactionDetailsDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Request\VoidDataBuilder'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\ClosePartialTransactionHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentResponseHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentReviewStatusHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionDetailsResponseHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\VoidResponseHandler'], - ['Magento\AuthorizenetAcceptjs\Gateway\SubjectReader'], - ['Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator'], - ['Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator'], - ['Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionResponseValidator'], - ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Cctype'], - ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Environment'], - ['Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\PaymentAction'], - ['Magento\AuthorizenetAcceptjs\Model\PassthroughDataObject'], - ['Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider'], - ['Magento\AuthorizenetAcceptjs\Observer\DataAssignObserver'], - ['Magento\AuthorizenetAcceptjs\Setup\Patch\Data\CopyCurrentConfig'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\FormTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\InfoTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Block\PaymentTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\AcceptPaymentStrategyCommandTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\CaptureStrategyCommandTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\FetchTransactionInfoCommandTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\GatewayQueryCommandTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Command\RefundTransactionStrategyCommandTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\ConfigTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\ClientTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\Payload\Filter\RemoveFieldsFilterTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Http\TransferFactoryTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AcceptFdsDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AddressDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AmountDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AuthenticationDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\AuthorizationDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CaptureDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CustomSettingsBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\CustomerDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\OrderDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PassthroughDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PaymentDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\PoDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundPaymentDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundReferenceTransactionDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RefundTransactionTypeDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\RequestTypeBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\SaleDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\ShippingDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\SolutionDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\StoreConfigBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\TransactionDetailsDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Request\VoidDataBuilderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\CloseParentTransactionHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\CloseTransactionHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\PaymentResponseHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\PaymentReviewStatusHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\TransactionDetailsResponseHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\TransactionIdHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Response\VoidResponseHandlerTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\SubjectReaderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\GeneralResponseValidatorTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\TransactionHashValidatorTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Gateway\Validator\TransactionResponseValidatorTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Model\Ui\ConfigProviderTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Observer\DataAssignObserverTest'], - ['Magento\AuthorizenetAcceptjs\Test\Unit\Setup\Patch\Data\CopyCurrentConfigTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\AbstractTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\AcceptFdsCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\AuthorizeCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\CancelCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\FetchTransactionInfoCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\RefundSettledCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\SaleCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\SettleCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\TransactionDetailsCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Command\VoidCommandTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\ConfigTest'], - ['Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandlerTest'], - ['Magento\AuthorizenetCardinal\Gateway\Request\Authorize3DSecureBuilder'], - ['Magento\AuthorizenetCardinal\Gateway\Validator\CavvResponseValidator'], - ['Magento\AuthorizenetCardinal\Model\Checkout\ConfigProvider'], - ['Magento\AuthorizenetCardinal\Model\Config'], - ['Magento\AuthorizenetCardinal\Observer\DataAssignObserver'], - ['Magento\AuthorizenetCardinal\Test\Unit\Observer\DataAssignObserverTest'], - ['Magento\AuthorizenetCardinal\Gateway\Command\AuthorizeCommandTest'], - ['Magento\AuthorizenetCardinal\Gateway\Command\SaleCommandTest'], - ['Magento\AuthorizenetGraphQl\Model\AuthorizenetDataProvider'], - ['Magento\AuthorizenetGraphQl\Model\Resolver\Customer\PlaceOrderWithAuthorizeNetTest'], - ['Magento\AuthorizenetGraphQl\Model\Resolver\Customer\SetAuthorizeNetPaymentMethodOnCartTest'], - ['Magento\AuthorizenetGraphQl\Model\Resolver\Guest\PlaceOrderWithAuthorizeNetTest'], - ['Magento\AuthorizenetGraphQl\Model\Resolver\Guest\SetAuthorizeNetPaymentMethodOnCartTest'], - ['Magento\GraphQl\AuthorizenetAcceptjs\Customer\SetPaymentMethodTest'], - ['Magento\GraphQl\AuthorizenetAcceptjs\Guest\SetPaymentMethodTest'], - ['Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http\MockClient'], - ['Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php index 168ac5cde16cb..2849e61c0208c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php @@ -202,4 +202,8 @@ ['Magento\BulkOperations', 'Magento\AsynchronousOperations'], ['Zend', 'Laminas'], ['Magento\Signifyd'], + ['Magento\Authorizenet'], + ['Magento\AuthorizenetAcceptjs'], + ['Magento\AuthorizenetCardinal'], + ['Magento\AuthorizenetGraphQl'], ]; From 5689f3f28203326c0833b388c24e40f933e68d92 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 3 Apr 2020 18:46:08 +0300 Subject: [PATCH 681/734] MC-30392: De-couple Authorize.net payment methods integrations from core in 2.4.0 --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 550de1d904aec..37849c7c21a29 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b801d92bd57ad3f2d51d2b264e9c37ac", + "content-hash": "1f11bed01d000a3d3eeda8b462e29f75", "packages": [ { "name": "braintree/braintree_php", From ed0b1b1a0eb96340063ded2445ea8516651ce137 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Fri, 3 Apr 2020 13:29:42 -0500 Subject: [PATCH 682/734] ENGCOM-7258: Revert https://github.com/magento/magento2/pull/26999 --- ...ProductProcessUrlRewriteSavingObserver.php | 103 +--- .../ProductToWebsiteChangeObserver.php | 104 ++++ ...ProductProcessUrlRewriteRemovingPlugin.php | 128 ----- ...goryWithRestrictedUrlKeyNotCreatedTest.xml | 1 - ...minUrlForProductRewrittenCorrectlyTest.xml | 1 - ...iteStoreLevelUrlKeyOfChildCategoryTest.xml | 1 - ...CategoryAccessibleWhenSuffixIsNullTest.xml | 1 - ...uctProcessUrlRewriteSavingObserverTest.php | 479 +++-------------- ...uctProcessUrlRewriteRemovingPluginTest.php | 342 ------------ .../etc/adminhtml/events.xml | 12 + app/code/Magento/CatalogUrlRewrite/etc/di.xml | 4 - .../Storage/DeleteEntitiesFromStores.php | 68 --- ...ateURLRewriteWhenCategoryIsDeletedTest.xml | 1 - ...lKeyForStoreViewAndMovingCategory2Test.xml | 2 +- ...rlKeyForStoreViewAndMovingCategoryTest.xml | 2 +- ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 1 - ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 - ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - ...eUrlRewriteAndAddPermanentRedirectTest.xml | 1 - ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 - ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - ...eProductURLRewriteAndAddNoRedirectTest.xml | 1 - ...ithCategoryAndAddTemporaryRedirectTest.xml | 1 - ...tUrLRewriteAndAddPermanentRedirectTest.xml | 1 - ...tUrLRewriteAndAddTemporaryRedirectTest.xml | 1 - ...nDeleteCMSPageNoRedirectUrlRewriteTest.xml | 1 - ...CMSPagePermanentRedirectUrlRewriteTest.xml | 1 - ...CMSPageTemporaryRedirectUrlRewriteTest.xml | 1 - .../AdminDeleteCategoryUrlRewriteTest.xml | 1 - ...teCmsPageUrlRewriteWithNoRedirectsTest.xml | 1 - ...ageUrlRewriteWithPermanentRedirectTest.xml | 1 - ...ageUrlRewriteWithTemporaryRedirectTest.xml | 1 - .../Test/AdminDeleteProductUrlRewriteTest.xml | 1 - ...inMarketingUrlRewritesNavigateMenuTest.xml | 1 - ...oryUrlRewriteAndAddAspxRequestPathTest.xml | 1 - ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 1 - ...yUrlRewriteAndAddPermanentRedirectTest.xml | 1 - ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - ...CmsPageRewriteEntityWithNoRedirectTest.xml | 1 - ...eRewriteEntityWithPermanentReirectTest.xml | 1 - ...RewriteEntityWithTemporaryRedirectTest.xml | 1 - ...eCmsPageUrlRewriteAndAddNoRedirectTest.xml | 1 - ...eUrlRewriteAndAddPermanentRedirectTest.xml | 1 - ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 1 - .../Model/ProductUrlRewriteTest.php | 3 - ...uctProcessUrlRewriteSavingObserverTest.php | 487 ++---------------- 48 files changed, 236 insertions(+), 1534 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php delete mode 100644 app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php delete mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml delete mode 100644 app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 377e19a22a31a..6eda8dd0b61ee 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -5,26 +5,15 @@ */ namespace Magento\CatalogUrlRewrite\Observer; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Framework\Event\Observer; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; -use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; +use Magento\Framework\App\ObjectManager; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; -use Magento\Store\Model\StoreManagerInterface; /** * Class ProductProcessUrlRewriteSavingObserver - * - * Observer to update the Rewrite URLs for a product. - * This observer is triggered on the save function when making changes - * to the products website on the Product Edit page. */ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { @@ -43,62 +32,30 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $productUrlPathGenerator; - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var ProductScopeRewriteGenerator - */ - private $productScopeRewriteGenerator; - - /** - * @var DeleteEntitiesFromStores - */ - private $deleteEntitiesFromStores; - - /** - * @var CollectionFactory - */ - private $collectionFactory; - /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist - * @param ProductUrlPathGenerator $productUrlPathGenerator - * @param StoreManagerInterface $storeManager - * @param ProductScopeRewriteGenerator $productScopeRewriteGenerator - * @param DeleteEntitiesFromStores $deleteEntitiesFromStores - * @param CollectionFactory $collectionFactory + * @param ProductUrlPathGenerator|null $productUrlPathGenerator */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $productUrlPathGenerator, - StoreManagerInterface $storeManager, - ProductScopeRewriteGenerator $productScopeRewriteGenerator, - DeleteEntitiesFromStores $deleteEntitiesFromStores, - CollectionFactory $collectionFactory + ProductUrlPathGenerator $productUrlPathGenerator = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; - $this->productUrlPathGenerator = $productUrlPathGenerator; - $this->storeManager = $storeManager; - $this->productScopeRewriteGenerator = $productScopeRewriteGenerator; - $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; - $this->collectionFactory = $collectionFactory; + $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() + ->get(ProductUrlPathGenerator::class); } /** * Generate urls for UrlRewrite and save it in storage * - * @param Observer $observer + * @param \Magento\Framework\Event\Observer $observer * @return void - * @throws UrlAlreadyExistsException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException */ - public function execute(Observer $observer) + public function execute(\Magento\Framework\Event\Observer $observer) { /** @var Product $product */ $product = $observer->getEvent()->getProduct(); @@ -108,49 +65,11 @@ public function execute(Observer $observer) || $product->getIsChangedWebsites() || $product->dataHasChangedFor('visibility') ) { - //Refresh rewrite urls - $product->unsUrlPath(); - $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); - if (!empty($this->productUrlRewriteGenerator->generate($product))) { + if ($product->isVisibleInSiteVisibility()) { + $product->unsUrlPath(); + $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } - - $storeIdsToRemove = []; - $productWebsiteMap = array_flip($product->getWebsiteIds()); - $storeVisibilities = $this->collectionFactory->create() - ->getAllAttributeValues(ProductInterface::VISIBILITY); - if ($this->productScopeRewriteGenerator->isGlobalScope($product->getStoreId())) { - //Remove any rewrite URLs for websites the product is not in, or is not visible in. Global Scope. - foreach ($this->storeManager->getStores() as $store) { - $websiteId = $store->getWebsiteId(); - $storeId = $store->getStoreId(); - if (!isset($productWebsiteMap[$websiteId])) { - $storeIdsToRemove[] = $storeId; - continue; - } - //Check the visibility of the product in each store. - if (isset($storeVisibilities[$product->getId()][$storeId]) - && ($storeVisibilities[$product->getId()][$storeId] === Visibility::VISIBILITY_NOT_VISIBLE)) { - $storeIdsToRemove[] = $storeId; - } - } - } else { - //Only remove rewrite for current scope - $websiteId = $product->getStore()->getWebsiteId(); - $storeId = $product->getStoreId(); - if (!isset($productWebsiteMap[$websiteId]) || - (isset($storeVisibilities[$product->getId()][$storeId]) - && ($storeVisibilities[$product->getId()][$storeId] === Visibility::VISIBILITY_NOT_VISIBLE))) { - $storeIdsToRemove[] = $storeId; - } - } - if (count($storeIdsToRemove)) { - $this->deleteEntitiesFromStores->execute( - $storeIdsToRemove, - [$product->getId()], - ProductUrlRewriteGenerator::ENTITY_TYPE - ); - } } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php new file mode 100644 index 0000000000000..44b47faf3d4b8 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogUrlRewrite\Observer; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Event\ObserverInterface; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Store\Api\StoreWebsiteRelationInterface; +use Magento\Framework\App\ObjectManager; + +/** + * Observer to assign the products to website + */ +class ProductToWebsiteChangeObserver implements ObserverInterface +{ + /** + * @var ProductUrlRewriteGenerator + */ + protected $productUrlRewriteGenerator; + + /** + * @var UrlPersistInterface + */ + protected $urlPersist; + + /** + * @var ProductRepositoryInterface + */ + protected $productRepository; + + /** + * @var RequestInterface + */ + protected $request; + + /** + * @var StoreWebsiteRelationInterface + */ + private $storeWebsiteRelation; + + /** + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param ProductRepositoryInterface $productRepository + * @param RequestInterface $request + * @param StoreWebsiteRelationInterface $storeWebsiteRelation + */ + public function __construct( + ProductUrlRewriteGenerator $productUrlRewriteGenerator, + UrlPersistInterface $urlPersist, + ProductRepositoryInterface $productRepository, + RequestInterface $request, + StoreWebsiteRelationInterface $storeWebsiteRelation = null + ) { + $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; + $this->urlPersist = $urlPersist; + $this->productRepository = $productRepository; + $this->request = $request; + $this->storeWebsiteRelation = $storeWebsiteRelation ?: + ObjectManager::getInstance()->get(StoreWebsiteRelationInterface::class); + } + + /** + * Generate urls for UrlRewrite and save it in storage + * + * @param \Magento\Framework\Event\Observer $observer + * @return void + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + foreach ($observer->getEvent()->getProducts() as $productId) { + $product = $this->productRepository->getById( + $productId, + false, + $this->request->getParam('store_id', Store::DEFAULT_STORE_ID) + ); + + if (!empty($this->productUrlRewriteGenerator->generate($product))) { + if ($this->request->getParam('remove_website_ids')) { + foreach ($this->request->getParam('remove_website_ids') as $webId) { + foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeId) { + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId + ]); + } + } + } + if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { + $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + } + } + } + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php b/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php deleted file mode 100644 index 629649897b9de..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/ProductProcessUrlRewriteRemovingPlugin.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogUrlRewrite\Plugin; - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Action; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Store\Api\StoreWebsiteRelationInterface; -use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; -use Magento\UrlRewrite\Model\UrlPersistInterface; - -/** - * Class ProductProcessUrlRewriteRemovingPlugin - * - * Plugin to update the Rewrite URLs for a product. - * This plugin is triggered by the product_action_attribute.website.update - * consumer in response to Mass Action changes in the Admin Product Grid. - */ -class ProductProcessUrlRewriteRemovingPlugin -{ - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var StoreWebsiteRelationInterface - */ - private $storeWebsiteRelation; - - /** - * @var UrlPersistInterface - */ - private $urlPersist; - - /** - * @var ProductUrlRewriteGenerator - */ - private $productUrlRewriteGenerator; - - /** - * @var DeleteEntitiesFromStores - */ - private $deleteEntitiesFromStores; - - /** - * @param ProductRepositoryInterface $productRepository - * @param StoreWebsiteRelationInterface $storeWebsiteRelation - * @param UrlPersistInterface $urlPersist - * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param DeleteEntitiesFromStores $deleteEntitiesFromStores - */ - public function __construct( - ProductRepositoryInterface $productRepository, - StoreWebsiteRelationInterface $storeWebsiteRelation, - UrlPersistInterface $urlPersist, - ProductUrlRewriteGenerator $productUrlRewriteGenerator, - DeleteEntitiesFromStores $deleteEntitiesFromStores - ) { - $this->productRepository = $productRepository; - $this->storeWebsiteRelation = $storeWebsiteRelation; - $this->urlPersist = $urlPersist; - $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; - $this->deleteEntitiesFromStores = $deleteEntitiesFromStores; - } - - /** - * Function afterUpdateWebsites - * - * @param Action $subject - * @param void $result - * @param array $productIds - * @param array $websiteIds - * @param string $type - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function afterUpdateWebsites( - Action $subject, - $result, - $productIds, - $websiteIds, - $type - ) { - foreach ($productIds as $productId) { - /* @var Product $product */ - $product = $this->productRepository->getById( - $productId, - false, - Store::DEFAULT_STORE_ID, - true - ); - - // Refresh all existing URLs for the product - if (!empty($this->productUrlRewriteGenerator->generate($product))) { - if ($product->isVisibleInSiteVisibility()) { - $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); - } - } - } - - $storeIdsToRemove = []; - // Remove the URLs for products in $productIds array - // from all stores that belong to websites in $websiteIds array - if ($type === "remove" && $websiteIds && $productIds) { - foreach ($websiteIds as $webId) { - foreach ($this->storeWebsiteRelation->getStoreByWebsiteId($webId) as $storeid) { - $storeIdsToRemove[] = $storeid; - } - } - if (count($storeIdsToRemove)) { - $this->deleteEntitiesFromStores->execute( - $storeIdsToRemove, - $productIds, - ProductUrlRewriteGenerator::ENTITY_TYPE - ); - } - } - } -} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 5d02e2075430e..cb969ac2d329e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -17,7 +17,6 @@ <testCaseId value="MC-17515"/> <useCaseId value="MAGETWO-69825"/> <group value="CatalogUrlRewrite"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 62ef26145619b..a7b18dcd8a8c9 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -17,7 +17,6 @@ <testCaseId value="MAGETWO-97224"/> <useCaseId value="MAGETWO-64191"/> <group value="CatalogUrlRewrite"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index d61a4bd077d9c..d67ff6fe0bace 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -15,7 +15,6 @@ <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94934"/> <group value="CatalogUrlRewrite"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml index d0e7828b6cc32..99037a5c89af1 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml @@ -16,7 +16,6 @@ <stories value="Url rewrites"/> <severity value="MAJOR"/> <group value="CatalogUrlRewrite"/> - <group value="urlRewrite"/> </annotations> <before> <magentoCLI command="config:set catalog/seo/category_url_suffix ''" stepKey="setCategoryUrlSuffix"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 602bfabcd45f2..39317b42af989 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -6,508 +6,169 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; -use Magento\Catalog\Model\ResourceModel\Product\Collection; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Api\StoreWebsiteRelationInterface; -use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; -use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\Catalog\Model\Product; -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Model\Website; -use Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; /** * Class ProductProcessUrlRewriteSavingObserverTest * - * Tests the ProductProcessUrlRewriteSavingObserver to ensure the - * replace method (refresh existing URLs) and deleteByData (remove - * old URLs) are called the correct number of times. - * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductProcessUrlRewriteSavingObserverTest extends TestCase +class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase { /** - * @var UrlPersistInterface|MockObject - */ - private $urlPersist; - - /** - * @var Event|MockObject + * @var \Magento\UrlRewrite\Model\UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $event; + protected $urlPersist; /** - * @var Observer|MockObject + * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject */ - private $observer; + protected $event; /** - * @var Product|MockObject + * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject */ - private $product; + protected $observer; /** - * @var ProductUrlRewriteGenerator|MockObject + * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ - private $productUrlRewriteGenerator; + protected $product; /** - * @var ProductScopeRewriteGenerator|MockObject + * @var \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject */ - private $productScopeRewriteGenerator; + protected $productUrlRewriteGenerator; /** * @var ObjectManager */ - private $objectManager; - - /** - * @var ProductProcessUrlRewriteSavingObserver - */ - private $model; - - /** - * @var StoreManagerInterface|MockObject - */ - private $storeManager; - - /** - * @var array - */ - private $websites; - - /** - * @var array - */ - private $stores; - - /** - * @var StoreWebsiteRelationInterface|MockObject - */ - private $storeWebsiteRelation; - - /** - * @var DeleteEntitiesFromStores|MockObject - */ - private $deleteEntitiesFromStores; - - /** - * @var Collection|MockObject - */ - private $productCollection; + protected $objectManager; /** - * @var CollectionFactory|MockObject + * @var \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver */ - private $collectionFactory; + protected $model; /** * Set up - * Website_ID = 0 -> Store_ID = 0 - * Website_ID = 1 -> Store_ID = 1 - * Website_ID = 2 -> Store_ID = 2 & 5 */ protected function setUp() { - $this->objectManager = new ObjectManager($this); - - $this->urlPersist = $this->createMock(UrlPersistInterface::class); - - $this->websites[0] = $this->initialiseWebsite(0); - $this->websites[1] = $this->initialiseWebsite(1); - $this->websites[2] = $this->initialiseWebsite(2); - - $this->stores[0] = $this->initialiseStore(0, 0); - $this->stores[1] = $this->initialiseStore(1, 1); - $this->stores[2] = $this->initialiseStore(2, 2); - $this->stores[5] = $this->initialiseStore(5, 2); - - $this->product = $this->initialiseProduct(); - - $this->collectionFactory = $this->createPartialMock( - CollectionFactory::class, - ['create'] - ); - $this->productCollection = $this->createPartialMock( - Collection::class, - ['getAllAttributeValues'] - ); - $this->collectionFactory->expects($this->any()) - ->method('create') - ->willReturn($this->productCollection); - - $this->deleteEntitiesFromStores = $this->createPartialMock( - DeleteEntitiesFromStores::class, - ['execute'] - ); - - $this->event = $this->createPartialMock(Event::class, ['getProduct']); - $this->event->expects($this->any()) - ->method('getProduct') - ->willReturn($this->product); - - $this->observer = $this->createPartialMock(Observer::class, ['getEvent']); + $this->urlPersist = $this->createMock(\Magento\UrlRewrite\Model\UrlPersistInterface::class); + $this->product = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ + 'getId', + 'dataHasChangedFor', + 'isVisibleInSiteVisibility', + 'getIsChangedWebsites', + 'getIsChangedCategories', + 'getStoreId' + ]); + $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); + $this->event = $this->createPartialMock(\Magento\Framework\Event::class, ['getProduct']); + $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); + $this->observer = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); $this->observer->expects($this->any())->method('getEvent')->willReturn($this->event); - $this->productUrlRewriteGenerator = $this->createPartialMock( - ProductUrlRewriteGenerator::class, + \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::class, ['generate'] ); - $this->productScopeRewriteGenerator = $this->createPartialMock( - ProductScopeRewriteGenerator::class, - ['isGlobalScope'] - ); - $this->productScopeRewriteGenerator->expects($this->any()) - ->method('isGlobalScope') - ->will($this->returnValueMap([ - [null, true], - [0, true], - [1, false], - [2, false], - [5, false], - ])); - - $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager->expects($this->any()) - ->method('getWebsites') - ->will($this->returnValue([$this->websites[1], $this->websites[2]])); - $this->storeManager->expects($this->any()) - ->method('getStores') - ->will($this->returnValue([$this->stores[1], $this->stores[2], $this->stores[5]])); - - $this->storeWebsiteRelation = $this->createPartialMock( - StoreWebsiteRelationInterface::class, - ['getStoreByWebsiteId'] - ); - $this->storeWebsiteRelation->expects($this->any()) - ->method('getStoreByWebsiteId') - ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); - + $this->productUrlRewriteGenerator->expects($this->any()) + ->method('generate') + ->will($this->returnValue([3 => 'rewrite'])); + $this->objectManager = new ObjectManager($this); $this->model = $this->objectManager->getObject( - ProductProcessUrlRewriteSavingObserver::class, + \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver::class, [ 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'urlPersist' => $this->urlPersist, - 'storeManager' => $this->storeManager, - 'storeWebsiteRelation' => $this->storeWebsiteRelation, - 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores, - 'productScopeRewriteGenerator' => $this->productScopeRewriteGenerator, - 'collectionFactory' => $this->collectionFactory + 'urlPersist' => $this->urlPersist ] ); } - /** - * Initialise product for test - * - * @return MockObject - */ - public function initialiseProduct() - { - $product = $this->createPartialMock( - Product::class, - [ - 'getId', - 'dataHasChangedFor', - 'isVisibleInSiteVisibility', - 'getIsChangedWebsites', - 'getIsChangedCategories', - 'getStoreId', - 'getWebsiteIds', - 'getStore' - ] - ); - $product->expects($this->any())->method('getId')->will($this->returnValue(1)); - return $product; - } - - /** - * Initialise website for test - * - * @param $websiteId - * @return MockObject - */ - public function initialiseWebsite($websiteId) - { - $website = $this->createPartialMock(Website::class, ['getWebsiteId']); - $website->expects($this->any())->method('getWebsiteId')->willReturn($websiteId); - return $website; - } - - /** - * Initialise store for test - * - * @param $storeId - * @return mixed - */ - public function initialiseStore($storeId, $websiteId) - { - $store = $this->createPartialMock(Store::class, ['getStoreId','getWebsiteId']); - $store->expects($this->any())->method('getStoreId')->willReturn($storeId); - $store->expects($this->any())->method('getWebsiteId')->willReturn($websiteId); - return $store; - } - /** * Data provider * * @return array - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function urlKeyDataProvider() { return [ - //url has changed, so we would expect to see a replace issued - //and the urls removed from the stores the product is not in - //i.e stores belonging to website 2 - 'global_scope_url_changed' => [ - 'productScope' => 0, + 'url changed' => [ 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_BOTH, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1], + 'visibilityResult' => true, 'expectedReplaceCount' => 1, - 'expectedRemoves' => [2, 5], ], - //Nothing has changed, so no replaces or removes - 'global_scope_no_changes' => [ - 'productScope' => 0, + 'no chnages' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_BOTH, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1], - 'expectedReplaceCount' => 0, - 'expectedRemoves' => [], + 'visibilityResult' => true, + 'expectedReplaceCount' => 0 ], - //Product passed in had global scope set, but the visibility - //at local scope for store 2 is false. Expect to see refresh - //of urls and removal from store 2 - 'global_scope_visibility_changed_local' => [ - 'productScope' => 0, + 'visibility changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => true, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 1, - 'expectedRemoves' => [2], + 'visibilityResult' => true, + 'expectedReplaceCount' => 1 ], - //Product passed in had global scope set, but the visibility - //for all stores is false. Expect to see removal from stores 1,2 and 5 - 'global_scope_visibility_changed_global' => [ - 'productScope' => 0, - 'isChangedUrlKey' => false, - 'isChangedVisibility' => true, - 'isChangedWebsites' => false, - 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 0, - 'expectedRemoves' => [1, 2, 5], - ], - //Product has changed websites. Now in websites 1 and 2 - //We would expect to see a replace but no removals as the - //product is in all stores - 'global_scope_websites_changed' => [ - 'productScope' => 0, + 'websites changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => true, 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_BOTH, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 1, - 'expectedRemoves' => [], + 'visibilityResult' => true, + 'expectedReplaceCount' => 1 ], - //Global scope, all visible, categories changed. - //Expect to see replace and no removals. - 'global_scope_categories_changed' => [ - 'productScope' => 0, + 'categories changed' => [ 'isChangedUrlKey' => false, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => true, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_BOTH, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 1, - 'expectedRemoves' => [], + 'visibilityResult' => true, + 'expectedReplaceCount' => 1 ], - //Global scope, url key has changed but products are - //invisible in all stores, therefore remove any urls if - //they exist. - 'global_scope_url_changed_invisible' => [ - 'productScope' => 0, + 'url changed invisible' => [ 'isChangedUrlKey' => true, 'isChangedVisibility' => false, 'isChangedWebsites' => false, 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 1, - 'expectedRemoves' => [1, 2, 5], - ], - //local scope tests should only adjust URLs for local scope - //Even if there are changes to the same product in other stores - //they should be ignored. Here product in store 2 has been set - //visible. Do not expect to see any removals for the other stores. - 'local_scope_visibility_changed_local_1' => [ - 'productScope' => 2, - 'isChangedUrlKey' => false, - 'isChangedVisibility' => true, - 'isChangedWebsites' => false, - 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 2 => Product\Visibility::VISIBILITY_BOTH, - 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 1, - 'expectedRemoves' => [], - ], - //Local scope, so only expecting to operate on store 2. - //Product has been set invisible, removal expected. - 'local_scope_visibility_changed_local_2' => [ - 'productScope' => 2, - 'isChangedUrlKey' => false, - 'isChangedVisibility' => true, - 'isChangedWebsites' => false, - 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_BOTH, - 1 => Product\Visibility::VISIBILITY_BOTH, - 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 5 => Product\Visibility::VISIBILITY_BOTH, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 0, - 'expectedRemoves' => [2], - ], - //Local scope, so only operate on store 5. - //Visibility is false, so see only removal from - //store 5. - 'local_scope_visibility_changed_global' => [ - 'productScope' => 5, - 'isChangedUrlKey' => false, - 'isChangedVisibility' => true, - 'isChangedWebsites' => false, - 'isChangedCategories' => false, - 'visibility' => [ - 1 => [ - 0 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 1 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 2 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - 5 => Product\Visibility::VISIBILITY_NOT_VISIBLE, - ], - ], - 'productInWebsites' => [1, 2], - 'expectedReplaceCount' => 0, - 'expectedRemoves' => [5], + 'visibilityResult' => false, + 'expectedReplaceCount' => 0 ], ]; } /** - * @param int $productScope * @param bool $isChangedUrlKey * @param bool $isChangedVisibility * @param bool $isChangedWebsites * @param bool $isChangedCategories - * @param array $visibility - * @param int $productInWebsites + * @param bool $visibilityResult * @param int $expectedReplaceCount - * @param array $expectedRemoves - * @throws UrlAlreadyExistsException * * @dataProvider urlKeyDataProvider */ public function testExecuteUrlKey( - $productScope, $isChangedUrlKey, $isChangedVisibility, $isChangedWebsites, $isChangedCategories, - $visibility, - $productInWebsites, - $expectedReplaceCount, - $expectedRemoves + $visibilityResult, + $expectedReplaceCount ) { - $this->product->expects($this->any()) - ->method('getWebsiteIds') - ->will($this->returnValue($productInWebsites)); + $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(12)); + $this->product->expects($this->any()) ->method('dataHasChangedFor') ->will($this->returnValueMap( @@ -516,6 +177,7 @@ public function testExecuteUrlKey( ['url_key', $isChangedUrlKey] ] )); + $this->product->expects($this->any()) ->method('getIsChangedWebsites') ->will($this->returnValue($isChangedWebsites)); @@ -523,31 +185,14 @@ public function testExecuteUrlKey( $this->product->expects($this->any()) ->method('getIsChangedCategories') ->will($this->returnValue($isChangedCategories)); - $this->product->expects($this->any()) - ->method('getStoreId') - ->willReturn($productScope); - $this->product->expects($this->any()) - ->method('getStore') - ->willReturn($this->stores[$productScope]); - $this->productCollection->expects($this->any()) - ->method('getAllAttributeValues') - ->will($this->returnValue($visibility)); + $this->product->expects($this->any()) + ->method('isVisibleInSiteVisibility') + ->will($this->returnValue($visibilityResult)); - $this->productUrlRewriteGenerator->expects($this->any()) - ->method('generate') - ->will($this->returnValue($expectedReplaceCount > 0 ? ['test'] : [])); $this->urlPersist->expects($this->exactly($expectedReplaceCount)) ->method('replace') - ->with($expectedReplaceCount > 0 ? ['test'] : []); - - $this->deleteEntitiesFromStores->expects($this->any()) - ->method('execute') - ->with( - $expectedRemoves, - [1], - ProductUrlRewriteGenerator::ENTITY_TYPE - ); + ->with([3 => 'rewrite']); $this->model->execute($this->observer); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php deleted file mode 100644 index 16375da5fdf72..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/ProductProcessUrlRewriteRemovingPluginTest.php +++ /dev/null @@ -1,342 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\CatalogUrlRewrite\Test\Unit\Plugin; - -use Magento\Catalog\Model\Product\Action; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\UrlRewrite\Model\Storage\DeleteEntitiesFromStores; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Model\Website; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\CatalogUrlRewrite\Plugin\ProductProcessUrlRewriteRemovingPlugin; -use Magento\Store\Api\StoreWebsiteRelationInterface; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; - -/** - * Class ProductProcessUrlRewriteRemovingPluginTest - * - * Tests the ProductProcessUrlRewriteRemovingPlugin to ensure the - * replace method (refresh existing URLs) and deleteByData (remove - * old URLs) are called the correct number of times. - * - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ProductProcessUrlRewriteRemovingPluginTest extends TestCase -{ - /** - * @var UrlPersistInterface|MockObject - */ - private $urlPersist; - - /** - * @var Product|MockObject - */ - private $product1; - - /** - * @var Product|MockObject - */ - private $product2; - - /** - * @var Product|MockObject - */ - private $product3; - - /** - * @var ProductRepositoryInterface|MockObject - */ - private $productRepository; - - /** - * @var ProductUrlRewriteGenerator|MockObject - */ - private $productUrlRewriteGenerator; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ProductProcessUrlRewriteRemovingPlugin - */ - private $plugin; - - /** - * @var Action|MockObject - */ - private $subject; - - /** - * @var StoreManagerInterface|MockObject - */ - private $storeManager; - - /** - * @var Website|MockObject - */ - private $website1; - - /** - * @var Website|MockObject - */ - private $website2; - - /** - * @var StoreWebsiteRelationInterface|MockObject - */ - private $storeWebsiteRelation; - - /** - * @var DeleteEntitiesFromStores|MockObject - */ - private $deleteEntitiesFromStores; - - /** - * Set up - * Website_ID = 1 -> Store_ID = 1 - * Website_ID = 2 -> Store_ID = 2 & 5 - */ - protected function setUp() - { - $this->urlPersist = $this->createMock(UrlPersistInterface::class); - $this->productUrlRewriteGenerator = $this->createPartialMock( - ProductUrlRewriteGenerator::class, - ['generate'] - ); - - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->website1 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website1->expects($this->any())->method('getWebsiteId')->willReturn(1); - $this->website2 = $this->createPartialMock(Website::class, ['getWebsiteId']); - $this->website2->expects($this->any())->method('getWebsiteId')->willReturn(2); - $this->storeManager->expects($this->any()) - ->method('getWebsites') - ->will($this->returnValue([$this->website1, $this->website2])); - - $this->storeWebsiteRelation = $this->createPartialMock( - StoreWebsiteRelationInterface::class, - ['getStoreByWebsiteId'] - ); - $this->storeWebsiteRelation->expects($this->any()) - ->method('getStoreByWebsiteId') - ->will($this->returnValueMap([[1, [1]], [2, [2, 5]]])); - - $this->product1 = $this->createPartialMock( - Product::class, - ['getWebsiteIds', 'isVisibleInSiteVisibility'] - ); - $this->product2 = $this->createPartialMock( - Product::class, - ['getWebsiteIds', 'isVisibleInSiteVisibility'] - ); - $this->product3 = $this->createPartialMock( - Product::class, - ['getWebsiteIds', 'isVisibleInSiteVisibility'] - ); - - $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class); - $this->productRepository->expects($this->any()) - ->method('getById') - ->will($this->returnValueMap([ - [1, false, 0, true, $this->product1], - [2, false, 0, true, $this->product2], - [3, false, 0, true, $this->product3] - ])); - - $this->deleteEntitiesFromStores = $this->createPartialMock( - DeleteEntitiesFromStores::class, - ['execute'] - ); - - $this->subject = $this->createMock( - Action::class - ); - - $this->objectManager = new ObjectManager($this); - $this->plugin = $this->objectManager->getObject( - ProductProcessUrlRewriteRemovingPlugin::class, - [ - 'productRepository' => $this->productRepository, - 'storeWebsiteRelation' => $this->storeWebsiteRelation, - 'urlPersist' => $this->urlPersist, - 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'deleteEntitiesFromStores' => $this->deleteEntitiesFromStores - - ] - ); - } - - /** - * Data provider - * - * @return array - */ - public function afterUpdateWebsitesDataProvider() - { - return [ - 'add_new_websites_1' => [ - 'products' => [ - '1' => ['visibilityResult' => true, 'websiteids' => [1]], - '2' => ['visibilityResult' => true, 'websiteids' => [1]], - '3' => ['visibilityResult' => true, 'websiteids' => [1]], - ], - 'productids' => [1,2,3], - 'type' => 'add', - 'websiteids' => [2], - 'expectedReplaceCount' => 3, - 'expectedStoreRemovals' => [], - 'expectedDeleteCount' => 0, - 'rewrites' => [true] - ], - 'add_new_websites_2' => [ - 'products' => [ - '1' => ['visibilityResult' => true, 'websiteids' => [1]], - '2' => ['visibilityResult' => true, 'websiteids' => [1]], - '3' => ['visibilityResult' => true, 'websiteids' => [1]], - ], - 'productids' => [1,2,3], - 'type' => 'add', - 'websiteids' => [2], - 'expectedReplaceCount' => 3, - 'expectedStoreRemovals' => [], - 'expectedDeleteCount' => 0, - 'rewrites' => [true] - ], - 'remove_all' => [ - 'products' => [ - '1' => ['visibilityResult' => true, 'websiteids' => [1,2]], - '2' => ['visibilityResult' => true, 'websiteids' => [1,2]], - '3' => ['visibilityResult' => true, 'websiteids' => [1,2]], - ], - 'productids' => [1,2,3], - 'type' => 'remove', - 'websiteids' => [1,2], - 'expectedReplaceCount' => 0, - 'expectedStoreRemovals' => [1,2,5], - 'expectedDeleteCount' => 1, - 'rewrites' => [] - ], - 'remove_single' => [ - 'products' => [ - '1' => ['visibilityResult' => true, 'websiteids' => [1,2]], - '2' => ['visibilityResult' => true, 'websiteids' => [1,2]], - '3' => ['visibilityResult' => true, 'websiteids' => [1,2]], - ], - 'productids' => [1,2,3], - 'type' => 'remove', - 'websiteids' => [2], - 'expectedReplaceCount' => 0, - 'expectedStoreRemovals' => [2,5], - 'expectedDeleteCount' => 1, - 'rewrites' => [] - ], - 'not_visible_add_1' => [ - 'products' => [ - '1' => ['visibilityResult' => false, 'websiteids' => [1]], - '2' => ['visibilityResult' => false, 'websiteids' => [1]], - '3' => ['visibilityResult' => false, 'websiteids' => [1]], - ], - 'productids' => [1,2,3], - 'type' => 'add', - 'websiteids' => [2], - 'expectedReplaceCount' => 0, - 'expectedStoreRemovals' => [], - 'expectedDeleteCount' => 0, - 'rewrites' => [true] - ], - 'not_visible_add_2' => [ - 'products' => [ - '1' => ['visibilityResult' => false, 'websiteids' => [1]], - '2' => ['visibilityResult' => false, 'websiteids' => [1]], - '3' => ['visibilityResult' => true, 'websiteids' => [1]], - ], - 'productids' => [1,2,3], - 'type' => 'add', - 'websiteids' => [2], - 'expectedReplaceCount' => 1, - 'expectedStoreRemovals' => [], - 'expectedDeleteCount' => 0, - 'rewrites' => [true] - ], - - ]; - } - - /** - * @param array $products - * @param array $productids - * @param string $type - * @param array $websiteids - * @param int $expectedReplaceCount - * @param array $expectedStoreRemovals - * @param int $expectedDeleteCount - * @param array $rewrites - * - * @dataProvider afterUpdateWebsitesDataProvider - */ - public function testAfterUpdateWebsites( - $products, - $productids, - $type, - $websiteids, - $expectedReplaceCount, - $expectedStoreRemovals, - $expectedDeleteCount, - $rewrites - ) { - - $this->productUrlRewriteGenerator->expects($this->any()) - ->method('generate') - ->will($this->returnValue($rewrites)); - - $this->product1->expects($this->any()) - ->method('getWebsiteIds') - ->will($this->returnValue($products['1']['websiteids'])); - $this->product2->expects($this->any()) - ->method('getWebsiteIds') - ->will($this->returnValue($products['2']['websiteids'])); - $this->product3->expects($this->any()) - ->method('getWebsiteIds') - ->will($this->returnValue($products['3']['websiteids'])); - - $this->product1->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($products['1']['visibilityResult'])); - $this->product2->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($products['2']['visibilityResult'])); - $this->product3->expects($this->any()) - ->method('isVisibleInSiteVisibility') - ->will($this->returnValue($products['3']['visibilityResult'])); - - $this->urlPersist->expects($this->exactly($expectedReplaceCount)) - ->method('replace') - ->with($rewrites); - - $this->deleteEntitiesFromStores->expects($this->exactly($expectedDeleteCount)) - ->method('execute') - ->with( - $expectedStoreRemovals, - $productids, - ProductUrlRewriteGenerator::ENTITY_TYPE - ); - - $this->plugin->afterUpdateWebsites( - $this->subject, - null, - $productids, - $websiteids, - $type - ); - } -} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml new file mode 100644 index 0000000000000..9c4a8aaf41231 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="catalog_product_to_website_change"> + <observer name="catalog_product_to_website_change" instance="Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver"/> + </event> +</config> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index 6efaa5dd8517e..5fb7d33546d60 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -70,8 +70,4 @@ </argument> </arguments> </type> - <type name="Magento\Catalog\Model\Product\Action"> - <plugin name="process_product_url_rewrites_on_website_change" - type="Magento\CatalogUrlRewrite\Plugin\ProductProcessUrlRewriteRemovingPlugin"/> - </type> </config> diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php b/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php deleted file mode 100644 index 98ea968751182..0000000000000 --- a/app/code/Magento/UrlRewrite/Model/Storage/DeleteEntitiesFromStores.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\UrlRewrite\Model\Storage; - -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; - -/** - * Class DeleteEntitiesFromStores - * - * Deletes multiple URL Rewrites from database - */ -class DeleteEntitiesFromStores -{ - /** - * @var AdapterInterface - */ - private $connection; - - /** - * @var Resource - */ - private $resource; - - /** - * @param ResourceConnection $resource - */ - public function __construct( - ResourceConnection $resource - ) { - $this->connection = $resource->getConnection(); - $this->resource = $resource; - } - - /** - * Function execute - * - * Deletes multiple URL Rewrites from database - * - * @param array $storeIds - * @param array $entityIds - * @param int $entityType - */ - public function execute($storeIds, $entityIds, $entityType) - { - $select = $this->connection->select(); - $select->from($this->resource->getTableName(DbStorage::TABLE_NAME)); - - $select->where( - $this->connection->quoteIdentifier( - UrlRewrite::STORE_ID - ) . ' IN (' . $this->connection->quote($storeIds, 'INTEGER') . ')' . - ' AND ' . $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_ID - ) . ' IN (' . $this->connection->quote($entityIds, 'INTEGER') . ')' . - ' AND ' . $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_TYPE - ) . ' = ' . $this->connection->quote($entityType) - ); - $select = $select->deleteFromSelect($this->resource->getTableName(DbStorage::TABLE_NAME)); - $this->connection->query($select); - } -} diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml index 089f74411ac58..eae8c7f0838c4 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5342"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml index 734b8bb667674..7244ed1d6b534 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml @@ -16,7 +16,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-25622"/> <group value="catalog"/> - <group value="urlRewrite"/> + <group value="url_rewrite"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index d337da37e0d2a..cd17d169804db 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -15,7 +15,7 @@ <description value="DEPRECATED. Check url rewrites in catalog categories after changing url key for store view and moving category"/> <severity value="CRITICAL"/> <testCaseId value="MC-5352"/> - <group value="urlRewrite"/> + <group value="url_rewrite"/> <group value="mtf_migrated"/> <skip> <issueId value="DEPRECATED">Use AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test instead</issueId> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml index 2d5f70f4e7328..339e078faa5a4 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5335"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index cf963bea12fa6..546637f2e548d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5334"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 401ce09f1a149..2ea04625b7d3d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5336"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml index b6007d63290b8..d0a744a168ce3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5345"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml index 33e1969be38ff..4327b02d396f9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5346"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 1c6c1572b3206..676c7bd95bbdb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5343"/> <severity value="BLOCKER"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml index 3191de550bd52..c39bae9fd2ff2 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5344"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml index c34dce4ab0b91..36c25d9a21685 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5339"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml index 26bc86bdabae7..92a2b6cac6b16 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5338"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index 5276cac8625ec..b7e0d3bb6b4dc 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5341"/> <severity value="BLOCKER"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 2136918fa6186..ce17e0de10d56 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5340"/> <severity value="BLOCKER"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml index e26a283861ecd..a65efce1a48ea 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml index 86f7f84f9f1a5..fa19b2a30c2cd 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml index d04cafc3602b6..2bed25aa3f39d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml index 2e753e5fca7d8..b781a8297499c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml @@ -14,7 +14,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml index c8ef5470eef3a..d58560e40533e 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -16,7 +16,6 @@ <severity value="CRITICAL"/> <testCaseId value="MC-14648"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml index e1228c47b9030..989aa277f7de1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -16,7 +16,6 @@ <testCaseId value="MC-14649"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml index 1efd063a23708..a81c00506c671 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -16,7 +16,6 @@ <testCaseId value="MC-14650"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml index 80fe34a3099f1..8ebad9656e7af 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml @@ -14,7 +14,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml index 0111a35508fd9..51561cfcb220f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml @@ -18,7 +18,6 @@ <testCaseId value="MC-14202"/> <group value="menu"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml index d47b700dab790..21b0486bd1aad 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5358"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml index 5a9aa4df336e0..7edeba336a7e9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5355"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 788d29a4d4f9f..b226925748f78 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5357"/> <severity value="BLOCKER"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 3d890854f5e09..3147fcc848b0b 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5356"/> <severity value="BLOCKER"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml index 95b509ac38a8a..c47419eee28e0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml @@ -15,7 +15,6 @@ <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml index da97bc5988512..510b2f5607df7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml @@ -15,7 +15,6 @@ <severity value="CRITICAL"/> <group value="cMSContent"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml index 5ac0330ab80b2..b696811cfa8c9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml index b5fa9267c581b..b04d417cf7efa 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml index f9eb150ad6113..138b5f280d5f2 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml index 34318b3dd8ac0..d996f2e3c7bb9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value=""/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index 0c1298672db51..e70896b7de04f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -15,7 +15,6 @@ <testCaseId value="MC-5351"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> - <group value="urlRewrite"/> </annotations> <before> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php index b0ecd58745444..f8fe68c2e0a2d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -85,7 +85,6 @@ public function productDataProvider(): array 'sku' => 'test-product', 'name' => 'test product', 'price' => 150, - 'website_ids' => [1] ], 'expected_data' => [ [ @@ -105,7 +104,6 @@ public function productDataProvider(): array 'name' => 'test product', 'price' => 150, 'url_key' => 'test-product-url-key', - 'website_ids' => [1] ], 'expected_data' => [ [ @@ -125,7 +123,6 @@ public function productDataProvider(): array 'name' => 'test product', 'price' => 150, 'url_key' => 'test-product-url-key', - 'website_ids' => [1] ], 'expected_data' => [], ], diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 76508f2066b54..c72a58197b1fd 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -5,46 +5,25 @@ */ namespace Magento\CatalogUrlRewrite\Observer; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Framework\ObjectManagerInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Store\Model\Store; -use PHPUnit\Framework\TestCase; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; /** * @magentoAppArea adminhtml * @magentoDbIsolation disabled */ -class ProductProcessUrlRewriteSavingObserverTest extends TestCase +class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase { - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var ProductRepositoryInterface - */ - private $productRepository; + /** @var \Magento\Framework\ObjectManagerInterface */ + protected $objectManager; /** * Set up */ protected function setUp() { - $this->objectManager = Bootstrap::getObjectManager(); - $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); - $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); } /** @@ -53,8 +32,8 @@ protected function setUp() */ private function getActualResults(array $filter) { - /** @var UrlFinderInterface $urlFinder */ - $urlFinder = $this->objectManager->get(UrlFinderInterface::class); + /** @var \Magento\UrlRewrite\Model\UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(\Magento\UrlRewrite\Model\UrlFinderInterface::class); $actualResults = []; foreach ($urlFinder->findAllByData($filter) as $url) { $actualResults[] = [ @@ -74,14 +53,16 @@ private function getActualResults(array $filter) */ public function testUrlKeyHasChangedInGlobalContext() { - $testStore1 = $this->storeManager->getStore('default'); - $testStore4 = $this->storeManager->getStore('test'); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $productRepository->get('product1'); - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeManager->setCurrentStore(0); + $testStore = $storeManager->getStore('test'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', ]; @@ -92,14 +73,14 @@ public function testUrlKeyHasChangedInGlobalContext() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), + 'store_id' => 1, ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), + 'store_id' => $testStore->getId(), ], ]; $actual = $this->getActualResults($productFilter); @@ -110,7 +91,7 @@ public function testUrlKeyHasChangedInGlobalContext() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $this->productRepository->save($product); + $product->save(); $expected = [ [ @@ -118,28 +99,28 @@ public function testUrlKeyHasChangedInGlobalContext() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), + 'store_id' => 1, ], [ 'request_path' => "new-url.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), + 'store_id' => $testStore->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => $testStore1->getId(), + 'store_id' => 1, ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => $testStore4->getId(), + 'store_id' => $testStore->getId(), ], ]; @@ -155,13 +136,16 @@ public function testUrlKeyHasChangedInGlobalContext() */ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() { - $testStore1 = $this->storeManager->getStore('default'); - $testStore4 = $this->storeManager->getStore('test'); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $productRepository->get('product1'); - $this->storeManager->setCurrentStore($testStore1); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeManager->setCurrentStore(1); - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); + $testStore = $storeManager->getStore('test'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', @@ -170,7 +154,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $this->productRepository->save($product); + $product->save(); $expected = [ [ @@ -178,21 +162,21 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), + 'store_id' => 1, ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), + 'store_id' => $testStore->getId(), ], [ 'request_path' => "product-1.html", 'target_path' => "new-url.html", 'is_auto_generated' => 0, 'redirect_type' => 301, - 'store_id' => $testStore1->getId(), + 'store_id' => 1, ], ]; @@ -208,13 +192,16 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() */ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirection() { - $testStore1 = $this->storeManager->getStore('default'); - $testStore4 = $this->storeManager->getStore('test'); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $productRepository->get('product1'); - $this->storeManager->setCurrentStore(1); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeManager->setCurrentStore(1); - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); + $testStore = $storeManager->getStore('test'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', @@ -223,7 +210,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio $product->setData('save_rewrites_history', false); $product->setUrlKey('new-url'); $product->setUrlPath('new-path'); - $this->productRepository->save($product); + $product->save(); $expected = [ [ @@ -231,401 +218,17 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ], - ]; - - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - } - - /** - * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php - * @magentoAppIsolation enabled - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testAddAndRemoveProductFromWebsite() - { - $testStore1 = $this->storeManager->getStore('default'); - $testStore2 = $this->storeManager->getStore('fixture_second_store'); - $testStore3 = $this->storeManager->getStore('fixture_third_store'); - $testStore4 = $this->storeManager->getStore('test'); - - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); - - $productFilter = [ - UrlRewrite::ENTITY_TYPE => 'product', - ]; - - //Product in 1st website. Should result in being in 1st and 4th stores. - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Add product to websites corresponding to all 4 stores. - //Rewrites should not be present as the product is hidden - //at the global scope. - $product->setWebsiteIds( - array_unique( - [ - $testStore1->getWebsiteId(), - $testStore2->getWebsiteId(), - $testStore3->getWebsiteId(), - $testStore4->getWebsiteId() - ] - ) - ); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore2->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore3->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] - ]; - - //Add product to websites corresponding to stores 2 and 3. - $product->setWebsiteIds( - array_unique( - [ - $testStore2->getWebsiteId(), - $testStore3->getWebsiteId(), - ] - ) - ); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore2->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore3->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - } - - /** - * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testChangeVisibilityGlobalScope() - { - $testStore1 = $this->storeManager->getStore('default'); - $testStore2 = $this->storeManager->getStore('fixture_second_store'); - $testStore3 = $this->storeManager->getStore('fixture_third_store'); - $testStore4 = $this->storeManager->getStore('test'); - - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); - - $productFilter = [ - UrlRewrite::ENTITY_TYPE => 'product', - ]; - - //Product in 1st website. Should result in being in 1st and 4th stores. - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Set product to be not visible at global scope - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); - $this->productRepository->save($product); - $expected = []; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Add product to websites corresponding to all 4 stores. - //Rewrites should not be present as the product is hidden - //at the global scope. - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - $product->setWebsiteIds( - array_unique( - [ - $testStore1->getWebsiteId(), - $testStore2->getWebsiteId(), - $testStore3->getWebsiteId(), - $testStore4->getWebsiteId() - ] - ) - ); - $this->productRepository->save($product); - $expected = []; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Set product to be visible at global scope - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - $product->setVisibility(Visibility::VISIBILITY_BOTH); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore2->getId(), + 'store_id' => 1, ], [ 'request_path' => "product-1.html", 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => $testStore3->getId(), + 'store_id' => $testStore->getId(), ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - } - - /** - * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testChangeVisibilityLocalScope() - { - $testStore1 = $this->storeManager->getStore('default'); - $testStore2 = $this->storeManager->getStore('fixture_second_store'); - $testStore3 = $this->storeManager->getStore('fixture_third_store'); - $testStore4 = $this->storeManager->getStore('test'); - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - - /** @var Product $product*/ - $product = $this->productRepository->get('product1'); - - $productFilter = [ - UrlRewrite::ENTITY_TYPE => 'product', - ]; - - //Product in 1st website. Should result in being in 1st and 4th stores. - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Set product to be not visible at store 4 scope - //Rewrite should only be present for store 1 - $this->storeManager->setCurrentStore($testStore4); - $product = $this->productRepository->get('product1'); - $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Add product to websites corresponding to all 4 stores. - //Rewrites should be present for stores 1,2 and 3. - //No rewrites should be present for store 4 as that is not visible - //at local scope. - $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - $product = $this->productRepository->get('product1'); - $product->setWebsiteIds( - array_unique( - [ - $testStore1->getWebsiteId(), - $testStore2->getWebsiteId(), - $testStore3->getWebsiteId(), - $testStore4->getWebsiteId() - ] - ) - ); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore2->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore3->getId(), - ] - ]; - $actual = $this->getActualResults($productFilter); - foreach ($expected as $row) { - $this->assertContains($row, $actual); - } - - //Set product to be visible at store 4 scope only - $this->storeManager->setCurrentStore($testStore4); - $product = $this->productRepository->get('product1'); - $product->setVisibility(Visibility::VISIBILITY_BOTH); - $this->productRepository->save($product); - $expected = [ - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore1->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore2->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore3->getId(), - ], - [ - 'request_path' => "product-1.html", - 'target_path' => "catalog/product/view/id/" . $product->getId(), - 'is_auto_generated' => 1, - 'redirect_type' => 0, - 'store_id' => $testStore4->getId(), - ] - ]; $actual = $this->getActualResults($productFilter); foreach ($expected as $row) { $this->assertContains($row, $actual); From 2108de2d07089096a1ff73d51c96d8f8e2a561b5 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Fri, 3 Apr 2020 23:00:13 +0300 Subject: [PATCH 683/734] Fix static tests --- .../testsuite/Magento/Test/Legacy/_files/obsolete_classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 45b415cdcbd12..99f27bec1bb57 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4255,6 +4255,6 @@ ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper'], ['Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver'], ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'], - ['PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject'], + ['PHPUnit_Framework_MockObject_MockObject', 'PHPUnit\Framework\MockObject\MockObject'], ['PHPUnit\Framework\BaseTestListener'], ]; From 4ced5daf63bbed5a77d0326d6dc0284674a72c4e Mon Sep 17 00:00:00 2001 From: Krzysztof Daniel <krzysztof.daniel@creativestyle.pl> Date: Sat, 4 Apr 2020 17:33:17 +0200 Subject: [PATCH 684/734] Adds ImageMagick.php to blacklist of phpstan ImageMagick adapter is non default one and can be configured It is not included in Test Enviroment --- .../Magento/Test/Php/_files/phpstan/blacklist/common.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt index f54defbd57604..d153593a5ed4f 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -5,6 +5,7 @@ # dev/tests/static/framework/bootstrap.php lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php lib/internal/Magento/Framework/Cache/Backend/Eaccelerator.php +lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php dev/tests/integration/framework/deployTestModules.php dev/tests/integration/testsuite/Magento/Framework/Session/ConfigTest.php dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php From 60b7d4f313eb754e1afbfc4bc7d1739f52992534 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 15:10:58 -0500 Subject: [PATCH 685/734] MQE-2066: [MTF-to-MFTF] Update Created Integration Entity #751 split files as per MFTF3.0.0 req and added test case ID --- .../Mftf/Section/AdminIntegrationsSection.xml | 30 ------------------- .../Test/AdminUpdateIntegrationEntityTest.xml | 1 + 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml deleted file mode 100644 index 4af25b9be9714..0000000000000 --- a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?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="IntegrationsGridSection"> - <element name="add" type="button" selector=".page-actions .add"/> - <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> - <element name="name" type="input" selector=".data-grid-filters #integrationGrid_filter_name"/> - <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> - <element name="remove" type="button" selector=".data-grid .delete"/> - <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> - <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> - <element name="edit" type="button" selector=".data-grid .edit"/> - </section> - - <section name="AddNewIntegrationSection"> - <element name="name" type="input" selector="#integration_properties_name"/> - <element name="password" type="input" selector="#integration_properties_current_password"/> - <element name="saveButton" type="button" selector=".page-actions #save-split-button-button"/> - <element name="endpoint" type="input" selector="#integration_properties_endpoint"/> - <element name="linkUrl" type="input" selector="#integration_properties_identity_link_url"/> - <element name="save" type="button" selector=".page-actions-buttons .save"/> - </section> -</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml index 589a765a215e5..182ec2181e5be 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -15,6 +15,7 @@ <title value="Updating System Integration Entity"/> <description value="Admin Updates Created Integration"/> <group value="integration"/> + <testCaseId value="MC-4624"/> <group value="mtf_migrated"/> </annotations> <before> From 3443766ffdf8da626e894132ed57dc5af92691c7 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 15:18:57 -0500 Subject: [PATCH 686/734] MQE-2066: [MTF-to-MFTF] Update Created Integration Entity #751 split files as per MFTF3.0.0 req and added test case ID --- .../AddNewIntegrationSection.xml | 18 +++++++++++++++++ .../IntegrationsGridSection.xml | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/AddNewIntegrationSection.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/IntegrationsGridSection.xml diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/AddNewIntegrationSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/AddNewIntegrationSection.xml new file mode 100644 index 0000000000000..2326b924bde8e --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/AddNewIntegrationSection.xml @@ -0,0 +1,18 @@ +<?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="AddNewIntegrationSection"> + <element name="name" type="input" selector="#integration_properties_name"/> + <element name="password" type="input" selector="#integration_properties_current_password"/> + <element name="saveButton" type="button" selector=".page-actions #save-split-button-button"/> + <element name="endpoint" type="input" selector="#integration_properties_endpoint"/> + <element name="linkUrl" type="input" selector="#integration_properties_identity_link_url"/> + <element name="save" type="button" selector=".page-actions-buttons .save"/> + </section> +</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/IntegrationsGridSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/IntegrationsGridSection.xml new file mode 100644 index 0000000000000..613dec8a9474e --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection/IntegrationsGridSection.xml @@ -0,0 +1,20 @@ +<?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="IntegrationsGridSection"> + <element name="add" type="button" selector=".page-actions .add"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + <element name="name" type="input" selector=".data-grid-filters #integrationGrid_filter_name"/> + <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> + <element name="remove" type="button" selector=".data-grid .delete"/> + <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> + <element name="edit" type="button" selector=".data-grid .edit"/> + </section> +</sections> From ca0e3a1b655fa0356e69d65bd163196562c72f14 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sat, 4 Apr 2020 22:31:13 +0200 Subject: [PATCH 687/734] magento/partners-magento2ee#171 Cover reported bug with MFTF tests --- .../AdminCmsPageUpdateTitleActionGroup.xml | 17 +++++++++++++++++ .../AdminAssertGridNotEmptyActionGroup.xml | 14 ++++++++++++++ ...AdminUiGridSelectFilterOptionActionGroup.xml | 12 ++++++++++++ .../Mftf/Section/AdminDataGridFilterSection.xml | 1 + 4 files changed, 44 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml new file mode 100644 index 0000000000000..0ae610a59e480 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AdminCmsPageSetTitleActionGroup"> + <arguments> + <argument name="newTitle" type="string"/> + </arguments> + + <fillField stepKey="fillNewTitle" selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{newTitle}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml new file mode 100644 index 0000000000000..7c951db99ef5e --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml @@ -0,0 +1,14 @@ +<?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="AdminAssertGridNotEmptyActionGroup"> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoaded"/> + <dontSee selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="expectNotEmptyGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml new file mode 100644 index 0000000000000..99af4ae2f42e0 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUiGridSelectFilterOptionActionGroup"> + <arguments> + <argument name="filterName" type="string"/> + <argument name="filterValue" type="string"/> + </arguments> + <selectOption selector="{{AdminDataGridFilterSection.selectByNameAttrInGrid(filterName)}}" userInput="{{filterValue}}" stepKey="selectOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml index e93df2d26ffc4..ec4554be21510 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml @@ -13,6 +13,7 @@ <element name="filterExpand" type="button" selector="//div[@class='admin__data-grid-header'][(not(ancestor::*[@class='sticky-header']) and not(contains(@style,'visibility: hidden'))) or (ancestor::*[@class='sticky-header' and not(contains(@style,'display: none'))])]//button[@data-action='grid-filter-expand']" /> <element name="inputFieldByNameAttr" type="input" selector="//*[@data-part='filter-form']//input[@name='{{inputNameAttr}}']" parameterized="true" /> <element name="inputFieldByNameAttrInGrid" type="input" selector="//*[@data-role='filter-form']//input[@name='{{inputNameAttr}}']" parameterized="true"/> + <element name="selectByNameAttrInGrid" type="input" selector="//*[@data-role='filter-form']//select[@name='{{inputNameAttr}}']" parameterized="true"/> <element name="filterSelectFieldByName" type="select" selector="//*[@data-part='filter-form']//select[@name='{{fieldName}}']" parameterized="true"/> <element name="apply" type="button" selector="//*[@data-part='filter-form']//button[@data-action='grid-filter-apply']" /> <element name="clear" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" /> From d1e749bd1688bca170bf94025a6da7e17fae5d28 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 16:04:14 -0500 Subject: [PATCH 688/734] MQE-2066: [MTF-to-MFTF] Update Created Integration Entity #751 updated test case IDs --- .../Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml | 2 +- .../AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml index 182ec2181e5be..ab01f69308715 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -15,7 +15,7 @@ <title value="Updating System Integration Entity"/> <description value="Admin Updates Created Integration"/> <group value="integration"/> - <testCaseId value="MC-4624"/> + <testCaseId value="MC-14398"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml index b8cc7bb19f7ea..59d70da4f9762 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml @@ -15,6 +15,7 @@ <title value="Updating System Integration Entity with Incorrect Password"/> <description value="Admin Updates Created Integration with Incorrect Password"/> <group value="integration"/> + <testCaseId value="MC-14399"/> <group value="mtf_migrated"/> </annotations> <before> From b24e329603abb5514c94964a15a38d509aaaf31d Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 16:34:22 -0500 Subject: [PATCH 689/734] MQE-2067: [MTF-to-MFTF] [MTF-to-MFTF] Create Sitemap Entity #759 added test case ID --- .../Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml | 1 + ...t.xml => AdminMarketingCreateSitemapFailFolderSaveTest.xml} | 3 ++- .../Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) rename app/code/Magento/Sitemap/Test/Mftf/Test/{AdminMarketingCreateSitemapFailfFolderSaveTest.xml => AdminMarketingCreateSitemapFailFolderSaveTest.xml} (95%) diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml index 7753b03716371..edd20737eb0a7 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml @@ -14,6 +14,7 @@ <stories value="Admin Creates Sitemap Entity"/> <title value="Sitemap Creation"/> <description value="Sitemap Entity Creation"/> + <testCaseId value="MC-14312"/> <group value="sitemap"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml similarity index 95% rename from app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml rename to app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml index b0669a690370e..530e813ae440a 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailfFolderSaveTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml @@ -8,12 +8,13 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminMarketingCreateSitemapFailfFolderSaveTest"> + <test name="AdminMarketingCreateSitemapFailFolderSaveTest"> <annotations> <features value="Sitemap"/> <stories value="Admin Creates Sitemap Entity Fail Folder Save"/> <title value="Sitemap Creation Fail Folder Save"/> <description value="Sitemap Entity Creation Fail Folder Save"/> + <testCaseId value="MC-14314"/> <group value="sitemap"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml index 6451080d62025..656502e09ad63 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml @@ -14,6 +14,7 @@ <stories value="Admin Creates Sitemap Entity Path Error"/> <title value="Sitemap Creation With Path Error"/> <description value="Sitemap Entity Creation With Path Error"/> + <testCaseId value="MC-14313"/> <group value="sitemap"/> <group value="mtf_migrated"/> </annotations> From 2ef17de8e92dbc2b0c3f884900058c9fb33dea3c Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 16:59:41 -0500 Subject: [PATCH 690/734] MQE-2068: [MTF-to-MFTF] [MTF-to-MFTF] Convert Delete Sitemap Entity Test #764 added test case ID and merged mainline --- .../Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml index 54e7ed211636e..44698b7a6bb56 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingDeleteSitemapEntityTest.xml @@ -14,6 +14,7 @@ <stories value="Admin Deletes Sitemap Entity Test"/> <title value="Sitemap Deleting Test"/> <description value="Admin Should Delete Sitemap Entity"/> + <testCaseId value="MC-14315"/> <group value="sitemap"/> <group value="mtf_migrated"/> </annotations> From 2ed8b4dd5bdda29aed539f2a9b1b5565ef829640 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 18:43:38 -0500 Subject: [PATCH 691/734] MQE-2069: [MTF-to-MFTF] Create Newsletter Template Entity #761 updated test case id --- .../Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml index 2e6947e51df10..deabe4d611cb7 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml @@ -16,6 +16,7 @@ <description value="Newsletter Template Successfully Created"/> <group value="newsletter"/> <group value="mtf_migrated"/> + <testCaseId value="MC-14315"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From b7467022b7e934f7cd3045dc1697331db8e3e97c Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 18:49:55 -0500 Subject: [PATCH 692/734] MQE-2069: [MTF-to-MFTF] Create Newsletter Template Entity #761 updated test case id --- .../Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml index deabe4d611cb7..525fd70d03f8e 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml @@ -16,7 +16,7 @@ <description value="Newsletter Template Successfully Created"/> <group value="newsletter"/> <group value="mtf_migrated"/> - <testCaseId value="MC-14315"/> + <testCaseId value="MC-29809"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From b35f446931cac2a1023bf188717f02dcd10d87db Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 19:35:26 -0500 Subject: [PATCH 693/734] Merge remote-tracking branch 'origin/PR-762' into migration-pr # Conflicts: # app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml # app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNavigateToCreateNewsletterTemplatePageActionGroup.xml # app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml --- ...ReAuthorizeTokensIntegrationEntityTest.xml | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml index bb61527ecab74..3387f1137adc8 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml @@ -65,9 +65,21 @@ <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <actionGroup ref="AdminIntegrationOpenExistingEntityActionGroup" stepKey="openIntegrationEntity"/> - <assertEquals stepKey="assertConsumerKey" expected="($grabConsumerKey)" actual="$grabAfterReauthorizeConsumerKey" actualType="string"/> - <assertEquals stepKey="assertConsumerSecret" expected="($grabConsumerSecret)" actual="$grabAfterReauthorizeConsumerSecret" actualType="string"/> - <assertNotEquals stepKey="assertNotEqualsToken" expected="($grabAccessToken)" actual="$grabReauthorizedAccessToken" actualType="string"/> - <assertNotEquals stepKey="assertNotEqualsTokenSecret" expected="($grabAccessTokenSecret)" actual="$grabReauthorizedAccessTokenSecret" actualType="string"/> + <assertEquals stepKey="assertConsumerKey"> + <actualResult type="string">$grabAfterReauthorizeConsumerKey</actualResult> + <expectedResult type="const">($grabConsumerKey)</expectedResult> + </assertEquals> + <assertEquals stepKey="assertConsumerSecret"> + <actualResult type="string">$grabAfterReauthorizeConsumerSecret</actualResult> + <expectedResult type="const">($grabConsumerSecret)</expectedResult> + </assertEquals> + <assertNotEquals stepKey="assertNotEqualsToken"> + <actualResult type="string">$grabReauthorizedAccessToken</actualResult> + <expectedResult type="const">($grabAccessToken)</expectedResult> + </assertNotEquals> + <assertNotEquals stepKey="assertNotEqualsTokenSecret"> + <actualResult type="string">$grabReauthorizedAccessTokenSecret</actualResult> + <expectedResult type="const">($grabAccessTokenSecret)</expectedResult> + </assertNotEquals> </test> </tests> From 787777659f75a89da019abc9dca92eb0d33b4ca4 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Sat, 4 Apr 2020 19:40:10 -0500 Subject: [PATCH 694/734] MQE-2071: [MTF-to-MFTF] Re-Authorize Tokens Integration Entity #765 Added test case Id --- .../Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml index 3387f1137adc8..480cc4b8861d3 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminReAuthorizeTokensIntegrationEntityTest.xml @@ -16,6 +16,7 @@ <description value="ReAuthorising Tokens For Created Integration"/> <group value="integration"/> <group value="mtf_migrated"/> + <testCaseId value="MC-14397"/> </annotations> <before> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> From 5f6b2728c76f4c21977ee6d999f8c130332d4c06 Mon Sep 17 00:00:00 2001 From: Cristiano Pacheco <chris.spb25@gmail.com> Date: Sun, 5 Apr 2020 11:32:08 -0300 Subject: [PATCH 695/734] magento/magento2#27500 - Replace deprecated constructs from OfflinePayments PHPUnit. --- .../Test/Unit/Block/Info/CheckmoTest.php | 26 +++++----- .../Test/Unit/Model/BanktransferTest.php | 36 +++++++++----- .../Test/Unit/Model/CashondeliveryTest.php | 36 +++++++++----- .../Unit/Model/CheckmoConfigProviderTest.php | 35 +++++++++----- .../Test/Unit/Model/CheckmoTest.php | 48 +++++++++++-------- .../Model/InstructionsConfigProviderTest.php | 39 ++++++++++----- .../Test/Unit/Model/PurchaseorderTest.php | 37 +++++++------- 7 files changed, 156 insertions(+), 101 deletions(-) diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index 27705c242a012..5edf51f7e9b4c 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -3,22 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Block\Info; use Magento\Framework\View\Element\Template\Context; use Magento\OfflinePayments\Block\Info\Checkmo; use Magento\Payment\Model\Info; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * CheckmoTest contains list of test for block methods testing */ -class CheckmoTest extends \PHPUnit\Framework\TestCase +class CheckmoTest extends TestCase { /** * @var Info|MockObject */ - private $info; + private $infoMock; /** * @var Checkmo @@ -35,7 +37,7 @@ protected function setUp() ->setMethods([]) ->getMock(); - $this->info = $this->getMockBuilder(Info::class) + $this->infoMock = $this->getMockBuilder(Info::class) ->disableOriginalConstructor() ->setMethods(['getAdditionalInformation']) ->getMock(); @@ -44,18 +46,18 @@ protected function setUp() } /** - * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo * @param array $details * @param string|null $expected * @dataProvider getPayableToDataProvider */ public function testGetPayableTo($details, $expected) { - $this->info->expects(static::at(0)) + $this->infoMock->expects(static::at(0)) ->method('getAdditionalInformation') ->with('payable_to') ->willReturn($details); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); static::assertEquals($expected, $this->block->getPayableTo()); } @@ -73,18 +75,18 @@ public function getPayableToDataProvider() } /** - * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress * @param array $details * @param string|null $expected * @dataProvider getMailingAddressDataProvider */ public function testGetMailingAddress($details, $expected) { - $this->info->expects(static::at(1)) + $this->infoMock->expects(static::at(1)) ->method('getAdditionalInformation') ->with('mailing_address') ->willReturn($details); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); static::assertEquals($expected, $this->block->getMailingAddress()); } @@ -107,11 +109,11 @@ public function getMailingAddressDataProvider() public function testConvertAdditionalDataIsNeverCalled() { $mailingAddress = 'blah@blah.com'; - $this->info->expects(static::at(1)) + $this->infoMock->expects(static::at(1)) ->method('getAdditionalInformation') ->with('mailing_address') ->willReturn($mailingAddress); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); // First we set the property $this->_mailingAddress $this->block->getMailingAddress(); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php index 873cf4f217857..31c012645f9c0 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php @@ -3,38 +3,48 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; -class BanktransferTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Banktransfer; +use Magento\Payment\Block\Info\Instructions; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class BanktransferTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Banktransfer + * @var Banktransfer */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; protected function setUp() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_object = $objectManagerHelper->getObject( - \Magento\OfflinePayments\Model\Banktransfer::class, + $objectManagerHelper = new ObjectManager($this); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->object = $objectManagerHelper->getObject( + Banktransfer::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetInfoBlockType() { - $this->assertEquals(\Magento\Payment\Block\Info\Instructions::class, $this->_object->getInfoBlockType()); + $this->assertEquals(Instructions::class, $this->object->getInfoBlockType()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php index 271bb41967255..eb7f771c0267b 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php @@ -3,40 +3,50 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; -class CashondeliveryTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Cashondelivery; +use Magento\Payment\Block\Info\Instructions; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CashondeliveryTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Cashondelivery + * @var Cashondelivery */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $helper = new ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_object = $helper->getObject( - \Magento\OfflinePayments\Model\Cashondelivery::class, + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->object = $helper->getObject( + Cashondelivery::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetInfoBlockType() { - $this->assertEquals(\Magento\Payment\Block\Info\Instructions::class, $this->_object->getInfoBlockType()); + $this->assertEquals(Instructions::class, $this->object->getInfoBlockType()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php index 8d65146ec102b..d3bd94f473036 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php @@ -3,34 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; -use Magento\OfflinePayments\Model\CheckmoConfigProvider; -use Magento\OfflinePayments\Model\Checkmo; use Magento\Framework\Escaper; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\OfflinePayments\Model\CheckmoConfigProvider; +use Magento\Payment\Helper\Data as PaymentHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class CheckmoConfigProviderTest extends \PHPUnit\Framework\TestCase +class CheckmoConfigProviderTest extends TestCase { - /** @var CheckmoConfigProvider */ - protected $model; + /** + * @var CheckmoConfigProvider + */ + private $model; - /** @var Checkmo|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodMock; + /** + * @var Checkmo|MockObject + */ + private $methodMock; - /** @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ - protected $escaperMock; + /** + * @var Escaper|MockObject + */ + private $escaperMock; protected function setUp() { - $this->methodMock = $this->createMock(\Magento\OfflinePayments\Model\Checkmo::class); + $this->methodMock = $this->createMock(Checkmo::class); - $paymentHelperMock = $this->createMock(\Magento\Payment\Helper\Data::class); + /** @var PaymentHelper|MockObject $paymentHelperMock */ + $paymentHelperMock = $this->createMock(PaymentHelper::class); $paymentHelperMock->expects($this->once()) ->method('getMethodInstance') ->with(Checkmo::PAYMENT_METHOD_CHECKMO_CODE) ->willReturn($this->methodMock); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any()) ->method('escapeHtml') ->willReturnArgument(0); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php index a636bee744b6b..219a94918fbe5 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php @@ -3,56 +3,62 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; -class CheckmoTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CheckmoTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Checkmo + * @var Checkmo */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; protected function setUp() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); - $this->_scopeConfig = $this->createPartialMock( - \Magento\Framework\App\Config\ScopeConfigInterface::class, - ['getValue', 'isSetFlag'] - ); - $this->_object = $objectManagerHelper->getObject( - \Magento\OfflinePayments\Model\Checkmo::class, + $objectManagerHelper = new ObjectManager($this); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); + $this->scopeConfigMock = $this->createPartialMock(ScopeConfigInterface::class, ['getValue', 'isSetFlag']); + $this->object = $objectManagerHelper->getObject( + Checkmo::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetPayableTo() { - $this->_object->setStore(1); - $this->_scopeConfig->expects($this->once()) + $this->object->setStore(1); + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/checkmo/payable_to', 'store', 1) ->willReturn('payable'); - $this->assertEquals('payable', $this->_object->getPayableTo()); + $this->assertEquals('payable', $this->object->getPayableTo()); } public function testGetMailingAddress() { - $this->_object->setStore(1); - $this->_scopeConfig->expects($this->once()) + $this->object->setStore(1); + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/checkmo/mailing_address', 'store', 1) ->willReturn('blah@blah.com'); - $this->assertEquals('blah@blah.com', $this->_object->getMailingAddress()); + $this->assertEquals('blah@blah.com', $this->object->getMailingAddress()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php index 97a64d8ab59b9..777e5c15d5af5 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php @@ -3,40 +3,53 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; use Magento\Framework\Escaper; use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\InstructionsConfigProvider; +use Magento\Payment\Helper\Data as PaymentHelper; use Magento\Payment\Model\Method\AbstractMethod; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class InstructionsConfigProviderTest extends \PHPUnit\Framework\TestCase +class InstructionsConfigProviderTest extends TestCase { - /** @var InstructionsConfigProvider */ - protected $model; + /** + * @var InstructionsConfigProvider + */ + private $model; - /** @var AbstractMethod|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodOneMock; + /** + * @var AbstractMethod|MockObject + */ + private $methodOneMock; - /** @var AbstractMethod|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodTwoMock; + /** + * @var AbstractMethod|MockObject + */ + private $methodTwoMock; - /** @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ - protected $escaperMock; + /** + * @var Escaper|MockObject + */ + private $escaperMock; protected function setUp() { $this->methodOneMock = $this->createPartialMock( - \Magento\Payment\Model\Method\AbstractMethod::class, + AbstractMethod::class, ['isAvailable', 'getInstructions'] ); $this->methodTwoMock = $this->createPartialMock( - \Magento\Payment\Model\Method\AbstractMethod::class, + AbstractMethod::class, ['isAvailable', 'getInstructions'] ); - $paymentHelperMock = $this->createMock(\Magento\Payment\Helper\Data::class); + /** @var PaymentHelper|MockObject $paymentHelperMock */ + $paymentHelperMock = $this->createMock(PaymentHelper::class); $paymentHelperMock->expects($this->exactly(2)) ->method('getMethodInstance') ->willReturnMap([ @@ -44,7 +57,7 @@ protected function setUp() [Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, $this->methodTwoMock], ]); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any()) ->method('escapeHtml') ->willReturnArgument(0); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php index 2eb204651fcf4..926df21542041 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php @@ -3,11 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\OfflinePayments\Test\Unit\Model; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\DataObject; use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\OfflinePayments\Model\Purchaseorder; use Magento\Payment\Helper\Data as PaymentHelper; @@ -15,34 +17,36 @@ use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\Order\Payment; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class PurchaseorderTest extends \PHPUnit\Framework\TestCase +class PurchaseorderTest extends TestCase { /** * @var Purchaseorder */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; protected function setUp() { $objectManagerHelper = new ObjectManager($this); $eventManager = $this->createMock(EventManagerInterface::class); $paymentDataMock = $this->createMock(PaymentHelper::class); - $this->_scopeConfig = $this->createPartialMock( + $this->scopeConfigMock = $this->createPartialMock( ScopeConfigInterface::class, ['getValue', 'isSetFlag'] ); - $this->_object = $objectManagerHelper->getObject( + $this->object = $objectManagerHelper->getObject( Purchaseorder::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } @@ -54,17 +58,16 @@ public function testAssignData() ]); $instance = $this->createMock(PaymentInfo::class); - $this->_object->setData('info_instance', $instance); - $result = $this->_object->assignData($data); - $this->assertEquals($result, $this->_object); + $this->object->setData('info_instance', $instance); + $result = $this->object->assignData($data); + $this->assertEquals($result, $this->object); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Purchase order number is a required field. - */ public function testValidate() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('Purchase order number is a required field.'); + $data = new DataObject([]); $addressMock = $this->createMock(OrderAddressInterface::class); @@ -77,9 +80,9 @@ public function testValidate() $instance->expects($this->once())->method('getOrder')->willReturn($orderMock); - $this->_object->setData('info_instance', $instance); - $this->_object->assignData($data); + $this->object->setData('info_instance', $instance); + $this->object->assignData($data); - $this->_object->validate(); + $this->object->validate(); } } From 16b58bc95f534aaa7631594aebf69c746a1fe1d1 Mon Sep 17 00:00:00 2001 From: Cristiano Pacheco <chris.spb25@gmail.com> Date: Sun, 5 Apr 2020 13:10:05 -0300 Subject: [PATCH 696/734] magento/magento2#27500 - Added strict types and fix identation. --- .../Block/Form/AbstractInstructionTest.php | 25 ++++++++++++------- .../Test/Unit/Block/Info/CheckmoTest.php | 5 ++-- .../Test/Unit/Model/BanktransferTest.php | 1 + .../Test/Unit/Model/CashondeliveryTest.php | 1 + .../Unit/Model/CheckmoConfigProviderTest.php | 1 + .../Test/Unit/Model/CheckmoTest.php | 1 + .../Model/InstructionsConfigProviderTest.php | 1 + .../Test/Unit/Model/PurchaseorderTest.php | 1 + .../BeforeOrderPaymentSaveObserverTest.php | 4 ++- 9 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php index bf77d1975ed53..1300800b7974e 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php @@ -3,34 +3,41 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Block\Form; -class AbstractInstructionTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\View\Element\Template\Context; +use Magento\OfflinePayments\Block\Form\AbstractInstruction; +use Magento\Payment\Model\MethodInterface; +use PHPUnit\Framework\TestCase; + +class AbstractInstructionTest extends TestCase { /** - * @var \Magento\OfflinePayments\Block\Form\AbstractInstruction + * @var AbstractInstruction */ - protected $_model; + protected $model; protected function setUp() { - $context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); - $this->_model = $this->getMockForAbstractClass( - \Magento\OfflinePayments\Block\Form\AbstractInstruction::class, + $context = $this->createMock(Context::class); + $this->model = $this->getMockForAbstractClass( + AbstractInstruction::class, ['context' => $context] ); } public function testGetInstructions() { - $method = $this->getMockBuilder(\Magento\Payment\Model\MethodInterface::class) + $method = $this->getMockBuilder(MethodInterface::class) ->getMockForAbstractClass(); $method->expects($this->once()) ->method('getConfigData') ->willReturn('instructions'); - $this->_model->setData('method', $method); + $this->model->setData('method', $method); - $this->assertEquals('instructions', $this->_model->getInstructions()); + $this->assertEquals('instructions', $this->model->getInstructions()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index 5edf51f7e9b4c..6de906983d8e3 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Block\Info; @@ -46,7 +47,7 @@ protected function setUp() } /** - * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo * @param array $details * @param string|null $expected * @dataProvider getPayableToDataProvider @@ -75,7 +76,7 @@ public function getPayableToDataProvider() } /** - * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress * @param array $details * @param string|null $expected * @dataProvider getMailingAddressDataProvider diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php index 31c012645f9c0..18a36781dfa2f 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php index eb7f771c0267b..ad59b21e8ac15 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php index d3bd94f473036..b753b7f3ceadf 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php index 219a94918fbe5..8fadecd23584a 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php index 777e5c15d5af5..303f34cd0dfdb 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php index 926df21542041..95415fbb479df 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Test\Unit\Model; diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php index 18f57269b616b..30780d24d928a 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Observer; use Magento\Framework\Event; @@ -18,7 +20,7 @@ use PHPUnit\Framework\TestCase; /** - * Test class for \Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver + * @covers \Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver */ class BeforeOrderPaymentSaveObserverTest extends TestCase { From 1e226ffa0e382c73560eea23223d0c6a8e4d6f72 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Sun, 5 Apr 2020 21:28:13 +0200 Subject: [PATCH 697/734] Add missing copyright --- .../AdminUiGridSelectFilterOptionActionGroup.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml index 99af4ae2f42e0..1c5a27ab326c3 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml @@ -1,4 +1,10 @@ <?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"> From e359b75d53d225676d160732c273d4386988492d Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 6 Apr 2020 11:41:05 +0300 Subject: [PATCH 698/734] MC-32763: Recently Compared Products widget appears on a page immediately after adding product to compare --- ...uctInRecentlyComparedWidgetActionGroup.xml | 21 ++++ ...rontRecentlyComparedAtWebsiteLevelTest.xml | 106 ++++++++++++++++++ .../web/js/product/provider-compared.js | 26 ++++- 3 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml new file mode 100644 index 0000000000000..800685861ea3b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check the product not exist in recently compared widget --> + <actionGroup name="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup"> + <annotations> + <description>Validate that the provided Product does not appear in the Recently Compared Products widget.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" stepKey="waitLoadingRecentlyComparedProductsGrid"/> + <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="{{product.name}}" stepKey="dontSeeProductInRecentlyComparedWidget"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml new file mode 100644 index 0000000000000..f675e66f42aea --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml @@ -0,0 +1,106 @@ +<?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="StoreFrontRecentlyComparedAtWebsiteLevelTest"> + <annotations> + <stories value="Recently Compared Product"/> + <title value="Recently Compared Product at website level"/> + <description value="Recently Compared Products widget appears on a page immediately after adding product to compare"/> + <useCaseId value="MC-32763"/> + <testCaseId value="MC-33099"/> + <severity value="MAJOR"/> + <group value="catalog"/> + <group value="widget"/> + </annotations> + <before> + <!--Create Simple Products and Category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProductToCompareFirst"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductToCompareSecond"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleFirst"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleSecond"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create product widget --> + <actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget"> + <argument name="widget" value="RecentlyComparedProductsWidget"/> + </actionGroup> + <!-- Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website --> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsiteGroup"/> + </before> + <after> + <!-- Customer Logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromCustomer"/> + <!-- Delete product widget --> + <actionGroup ref="AdminDeleteWidgetActionGroup" stepKey="deleteRecentlyComparedProductsWidget"> + <argument name="widget" value="RecentlyComparedProductsWidget"/> + </actionGroup> + <!-- Logout Admin --> + <actionGroup ref="logout" stepKey="logout"/> + <!-- Reset Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website--> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/> + <!-- Delete Products and Category --> + <deleteData createDataKey="createSimpleProductToCompareFirst" stepKey="deleteSimpleProductToCompareFirst"/> + <deleteData createDataKey="createSimpleProductToCompareSecond" stepKey="deleteSimpleProductToCompareSecond"/> + <deleteData createDataKey="createSimpleProductNotVisibleFirst" stepKey="deleteSimpleProductNotVisibleFirst"/> + <deleteData createDataKey="createSimpleProductNotVisibleSecond" stepKey="deleteSimpleProductNotVisibleSecond"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + <!--Login to storefront from customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterAddedProductToCart"/> + <!--Add to compare Simple Product and Simple Product 2--> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct1ToCompare" > + <argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct2ToCompare" > + <argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + <!--The Compare Products widget displays Simple Product 1 and Simple Product 2--> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct1InCompareSidebar"> + <argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct2InCompareSidebar"> + <argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + + <!--Click Clear all in the Compare Products widget--> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="clearCompareList"/> + <!--The Recently Compared widget displays Simple Product 1 and Simple Product 2--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckProductsInRecentlyComparedSidebar"/> + <actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct1ExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct2ExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + <!--The Recently Compared widget not displays Simple Product 3 and Simple Product 4--> + <actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct3NotExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductNotVisibleFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct4NotExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductNotVisibleSecond$$"/> + </actionGroup> + <amOnPage url="customer/account/logout/" stepKey="logoutCustomer"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js b/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js index 93f01682b4e48..11e092e47ae22 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js @@ -31,13 +31,27 @@ define([ */ dataFilter: function (data) { var providerData = this.idsStorage.prepareData(customerData.get(this.identifiersConfig.provider)().items), - result = {}; + result = {}, + productCurrentScope, + scopeId; - _.each(data, function (value, key) { - if (!providerData[key]) { - result[key] = value; - } - }); + if (typeof this.data.productCurrentScope !== 'undefined') { + productCurrentScope = this.data.productCurrentScope; + scopeId = productCurrentScope === 'store' ? window.checkout.storeId : + productCurrentScope === 'group' ? window.checkout.storeGroupId : + window.checkout.websiteId; + _.each(data, function (value, key) { + if (!providerData[productCurrentScope + '-' + scopeId + '-' + key]) { + result[key] = value; + } + }); + } else { + _.each(data, function (value, key) { + if (!providerData[key]) { + result[key] = value; + } + }); + } return result; }, From 32b92d7f047b8a47ac5b237935b61f16f3316718 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 6 Apr 2020 14:30:16 +0300 Subject: [PATCH 699/734] MC-32430: [Magento Cloud] - Order / Invoice / Sales Order Report Discrepancy --- .../ResourceModel/Product/Collection.php | 4 +- .../Collection/SearchResultApplier.php | 20 +++- .../Block/Product/ListProduct/SortingTest.php | 96 +++++++++++++++++-- ...t_configurable_with_out-of-stock_child.php | 43 +++++++++ ...rable_with_out-of-stock_child_rollback.php | 9 ++ 5 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 41b3f9781c8a0..e983ec062751d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1735,7 +1735,9 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) if ($attribute == 'price' && $storeId != 0) { $this->addPriceData(); if ($this->_productLimitationFilters->isUsingPriceIndex()) { - $this->getSelect()->order("price_index.min_price {$dir}"); + $this->getSelect()->order( + new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$dir}") + ); return $this; } } diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index 4693b7502c5c1..54b8c1966ee12 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -69,10 +69,22 @@ public function apply() foreach ($items as $item) { $ids[] = (int)$item->getId(); } - $this->collection->getSelect()->where('e.entity_id IN (?)', $ids); - $orderList = join(',', $ids); - $this->collection->getSelect()->reset(\Magento\Framework\DB\Select::ORDER); - $this->collection->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); + $this->collection->getSelect() + ->where('e.entity_id IN (?)', $ids) + ->reset(\Magento\Framework\DB\Select::ORDER); + $sortOrder = $this->searchResult->getSearchCriteria() + ->getSortOrders(); + if (!empty($sortOrder['price']) && $this->collection->getLimitationFilters()->isUsingPriceIndex()) { + $sortDirection = $sortOrder['price']; + $this->collection->getSelect() + ->order( + new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$sortDirection}") + ); + } else { + $orderList = join(',', $ids); + $this->collection->getSelect() + ->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); + } } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php index 7bebf9c49d14b..31da969540d30 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php @@ -110,16 +110,7 @@ public function testProductListSortOrder(string $sortBy, string $direction, arra */ public function testProductListSortOrderWithConfig(string $sortBy, string $direction, array $expectation): void { - $this->objectManager->removeSharedInstance(Config::class); - $this->scopeConfig->setValue( - Config::XML_PATH_LIST_DEFAULT_SORT_BY, - $sortBy, - ScopeInterface::SCOPE_STORE, - Store::DEFAULT_STORE_ID - ); - $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); - $this->renderBlock($category, $direction); - $this->assertBlockSorting($sortBy, $expectation); + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expectation); } /** @@ -322,4 +313,89 @@ private function updateCategorySortBy( return $category; } + + /** + * Test product list ordered by price with out-of-stock configurable product options with elasticsearch engine + * + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * @magentoConfigFixture default/catalog/search/engine elasticsearch7 + * @dataProvider productListWithOutOfStockSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + public function testProductListOutOfStockSortOrderWithElasticsearch( + string $sortBy, + string $direction, + array $expected + ): void { + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expected); + } + + /** + * Test product list ordered by price with out-of-stock configurable product options with mysql search engine + * + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * @magentoConfigFixture default/catalog/search/engine mysql + * @dataProvider productListWithOutOfStockSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + public function testProductListOutOfStockSortOrderWithMysql( + string $sortBy, + string $direction, + array $expected + ): void { + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expected); + } + + /** + * Product list with out-of-stock sort order data provider + * + * @return array + */ + public function productListWithOutOfStockSortOrderDataProvider(): array + { + return [ + 'default_order_price_asc' => [ + 'sort' => 'price', + 'direction' => Collection::SORT_ORDER_ASC, + 'expectation' => ['simple1', 'simple2', 'simple3', 'configurable'], + ], + 'default_order_price_desc' => [ + 'sort' => 'price', + 'direction' => Collection::SORT_ORDER_DESC, + 'expectation' => ['simple3', 'simple2', 'simple1', 'configurable'], + ], + ]; + } + + /** + * Assert product list order + * + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + private function assertProductListSortOrderWithConfig(string $sortBy, string $direction, array $expected): void + { + $this->objectManager->removeSharedInstance(Config::class); + $this->scopeConfig->setValue( + Config::XML_PATH_LIST_DEFAULT_SORT_BY, + $sortBy, + ScopeInterface::SCOPE_STORE, + Store::DEFAULT_STORE_ID + ); + $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expected); + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php new file mode 100644 index 0000000000000..e46228e52a117 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_configurable.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +$product = $productRepository->get('simple_1010'); +$product->setStockData( + [ + 'qty' => 0, + ] +); +$productRepository->save($product); + +$product = $productRepository->get('simple_1020'); +$product->setStockData( + [ + 'qty' => 0, + ] +); +$productRepository->save($product); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); +/** @var DefaultCategory $categoryHelper */ +$categoryHelper = $objectManager->get(DefaultCategory::class); + +foreach (['simple_1010', 'simple_1020', 'configurable'] as $sku) { + $categoryLinkManagement->assignProductToCategories($sku, [$categoryHelper->getId(), 333]); +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php new file mode 100644 index 0000000000000..05b0d8959a5d3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Catalog/_files/category_rollback.php'; +require __DIR__ . '/product_configurable_rollback.php'; From 0941297845e67161f338fc03be48932bde1bfbe6 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 6 Apr 2020 15:50:33 +0300 Subject: [PATCH 700/734] MC-24186: Inline translation doesn't work on frontend --- .../Magento/Translation/Model/FileManager.php | 39 +++++++--- .../Translation/Model/Inline/CacheManager.php | 5 +- .../Translation/Model/Inline/Renderer.php | 24 ++++++ .../AdminTranslateElementActionGroup.xml | 6 +- ...tFillCustomTranslationFieldActionGroup.xml | 22 ++++++ ...tOpenInlineTranslationPopupActionGroup.xml | 24 ++++++ ...SubmitInlineTranslationFormActionGroup.xml | 20 +++++ ...StorefrontButtonsInlineTranslationTest.xml | 53 ++++++------- app/code/Magento/Translation/etc/di.xml | 12 +++ .../Translation/Model/Js/DataProviderTest.php | 74 +++++++++++++++++++ 10 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 app/code/Magento/Translation/Model/Inline/Renderer.php create mode 100644 app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontFillCustomTranslationFieldActionGroup.xml create mode 100644 app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontOpenInlineTranslationPopupActionGroup.xml create mode 100644 app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontSubmitInlineTranslationFormActionGroup.xml create mode 100644 dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php diff --git a/app/code/Magento/Translation/Model/FileManager.php b/app/code/Magento/Translation/Model/FileManager.php index 95fb3f2a5d4e9..4690fae763bfa 100644 --- a/app/code/Magento/Translation/Model/FileManager.php +++ b/app/code/Magento/Translation/Model/FileManager.php @@ -9,10 +9,11 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\View\Asset\Repository; /** - * A service for handling Translation config files + * A service for handling Translation config files. */ class FileManager { @@ -36,23 +37,31 @@ class FileManager */ private $driverFile; + /** + * @var Json + */ + private $serializer; + /** * @param Repository $assetRepo * @param DirectoryList $directoryList * @param File $driverFile + * @param Json $serializer */ public function __construct( Repository $assetRepo, DirectoryList $directoryList, - File $driverFile + File $driverFile, + Json $serializer ) { $this->assetRepo = $assetRepo; $this->directoryList = $directoryList; $this->driverFile = $driverFile; + $this->serializer = $serializer; } /** - * Create a view asset representing the requirejs config.config property for inline translation + * Create a view asset representing the RequireJS config.config property for inline translation. * * @return \Magento\Framework\View\Asset\File */ @@ -65,7 +74,7 @@ public function createTranslateConfigAsset() } /** - * Gets current js-translation.json timestamp + * Get current js-translation.json timestamp. * * @return string|void */ @@ -81,7 +90,7 @@ public function getTranslationFileTimestamp() } /** - * Retrieve full path for translation file + * Retrieve full path for translation file. * * @return string */ @@ -95,7 +104,7 @@ protected function getTranslationFileFullPath() } /** - * Retrieve path for translation file + * Retrieve path for translation file. * * @return string */ @@ -105,10 +114,9 @@ public function getTranslationFilePath() } /** - * Update translation file with content - * - * @param string $content + * Update translation file with content. * + * @param array $content * @return void */ public function updateTranslationFileContent($content) @@ -119,9 +127,18 @@ public function updateTranslationFileContent($content) if (!$this->driverFile->isExists($this->getTranslationFileFullPath())) { $this->driverFile->createDirectory($translationDir); + $originalFileContent = ''; + } else { + $originalFileContent = $this->driverFile->fileGetContents($this->getTranslationFileFullPath()); } - - $this->driverFile->filePutContents($this->getTranslationFileFullPath(), $content); + $originalFileTranslationPhrases = !empty($originalFileContent) + ? $this->serializer->unserialize($originalFileContent) + : []; + $updatedTranslationPhrases = array_merge($originalFileTranslationPhrases, $content); + $this->driverFile->filePutContents( + $this->getTranslationFileFullPath(), + $this->serializer->serialize($updatedTranslationPhrases) + ); } /** diff --git a/app/code/Magento/Translation/Model/Inline/CacheManager.php b/app/code/Magento/Translation/Model/Inline/CacheManager.php index 712787498b96d..2002e9a73ec38 100644 --- a/app/code/Magento/Translation/Model/Inline/CacheManager.php +++ b/app/code/Magento/Translation/Model/Inline/CacheManager.php @@ -52,7 +52,7 @@ public function __construct( } /** - * Clears cache and updates translations file + * Clear cache and update translations file. * * @return array */ @@ -60,7 +60,8 @@ public function updateAndGetTranslations() { $this->eventManager->dispatch('adminhtml_cache_flush_system'); $translations = $this->translateResource->getTranslationArray(null, $this->localeResolver->getLocale()); - $this->fileManager->updateTranslationFileContent(json_encode($translations)); + $this->fileManager->updateTranslationFileContent($translations); + return $translations; } } diff --git a/app/code/Magento/Translation/Model/Inline/Renderer.php b/app/code/Magento/Translation/Model/Inline/Renderer.php new file mode 100644 index 0000000000000..b4ef2647ea352 --- /dev/null +++ b/app/code/Magento/Translation/Model/Inline/Renderer.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Translation\Model\Inline; + +use Magento\Framework\Phrase\RendererInterface; + +/** + * Inline Translate phrase renderer for DataProvider. + */ +class Renderer implements RendererInterface +{ + /** + * @inheritdoc + */ + public function render(array $source, array $arguments) + { + return end($source); + } +} diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AdminTranslateElementActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AdminTranslateElementActionGroup.xml index ed6b0c6b91b26..6db6e10dcce0d 100644 --- a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AdminTranslateElementActionGroup.xml +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AdminTranslateElementActionGroup.xml @@ -8,12 +8,12 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminTranslateElementActionGroup"> + <actionGroup name="AdminTranslateElementActionGroup" deprecated="Avoid using super-ActionGroups. Use StorefrontOpenInlineTranslationPopupActionGroup, StorefrontFillCustomTranslationFieldActionGroup and StorefrontSubmitInlineTranslationFormActionGroup"> <arguments> <argument name="elementSelector" type="string"/> <argument name="translateText" type="string"/> </arguments> - + <executeJS function="jQuery('{{elementSelector}}').mousemove()" stepKey="moveMouseOverSelector"/> <executeJS function="jQuery('{{InlineTranslationModeSection.bookIcon}}').click()" stepKey="clickBookIcon"/> <waitForPageLoad stepKey="waitForLoadWindow"/> @@ -23,4 +23,4 @@ <fillField userInput="{{translateText}}" selector="{{InlineTranslationModeSection.inputCustomTranslate}}" stepKey="addTranslate"/> <click selector="{{InlineTranslationModeSection.buttonSubmit}}" stepKey="clickSubmitButton"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontFillCustomTranslationFieldActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontFillCustomTranslationFieldActionGroup.xml new file mode 100644 index 0000000000000..e0e7072c2172a --- /dev/null +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontFillCustomTranslationFieldActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillCustomTranslationFieldActionGroup"> + <annotations> + <description>Fill the field in the Inline Translation form by provided text on Storefront.</description> + </annotations> + <arguments> + <argument name="translateText" type="string" defaultValue="Translated"/> + </arguments> + + <clearField selector="{{InlineTranslationModeSection.inputCustomTranslate}}" stepKey="clearCustomTranslateField"/> + <fillField selector="{{InlineTranslationModeSection.inputCustomTranslate}}" userInput="{{translateText}}" stepKey="fillCustomTranslateField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontOpenInlineTranslationPopupActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontOpenInlineTranslationPopupActionGroup.xml new file mode 100644 index 0000000000000..aa92fba7a8722 --- /dev/null +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontOpenInlineTranslationPopupActionGroup.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="StorefrontOpenInlineTranslationPopupActionGroup"> + <annotations> + <description>Open the Inline Translation popup for provided element on Storefront.</description> + </annotations> + <arguments> + <argument name="elementSelector" type="string" defaultValue="{{StorefrontHeaderSection.welcomeMessage}}"/> + </arguments> + + <executeJS function="jQuery('{{elementSelector}}').mousemove()" stepKey="moveMouseOverSelector"/> + <executeJS function="jQuery('{{InlineTranslationModeSection.bookIcon}}').click()" stepKey="clickBookIcon"/> + <waitForElementVisible selector="{{InlineTranslationModeSection.popUpTranslate}}" stepKey="waitForTranslationPopupAppear"/> + <seeElement selector="{{InlineTranslationModeSection.popUpTranslate}}" stepKey="seeTranslateForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontSubmitInlineTranslationFormActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontSubmitInlineTranslationFormActionGroup.xml new file mode 100644 index 0000000000000..280e356d87bcc --- /dev/null +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/StorefrontSubmitInlineTranslationFormActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSubmitInlineTranslationFormActionGroup"> + <annotations> + <description>Click on the Inline Translation form submit button on Storefront.</description> + </annotations> + + <click selector="{{InlineTranslationModeSection.buttonSubmit}}" stepKey="clickSubmitButton"/> + <waitForElementNotVisible selector="{{InlineTranslationModeSection.popUpTranslate}}" stepKey="waitForTranslationPopupDisappear"/> + <dontSeeElement selector="{{InlineTranslationModeSection.popUpTranslate}}" stepKey="dontSeeTranslateForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml index d87d3635fa07c..1ba3236185148 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml @@ -12,57 +12,52 @@ <annotations> <features value="Translation"/> <stories value="Inline Translation"/> - <title value="[Inline Translation] Buttons inline translation"/> - <description value="[Inline Translation] Buttons inline translation"/> + <title value="Buttons inline translation"/> + <description value="A merchant should be able to translate buttons by an inline translation tool"/> <severity value="CRITICAL"/> - <testCaseId value="MC-12735"/> + <testCaseId value="MC-27118"/> + <useCaseId value="MC-24186"/> <group value="translation"/> - <skip> - <issueId value="MC-20127"/> - </skip> + <group value="catalog"/> + <group value="developer_mode_only"/> </annotations> <before> + <!-- Enable Translate Inline For Storefront --> + <magentoCLI command="config:set {{EnableTranslateInlineForStorefront.path}} {{EnableTranslateInlineForStorefront.value}}" stepKey="enableTranslateInlineForStorefront"/> + <!-- Create Category --> + <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create Simple Product --> - <createData entity="SimpleProduct2" stepKey="createProduct"/> - <!-- Enable Translate Inline For Storefront--> - <magentoCLI - command="config:set {{EnableTranslateInlineForStorefront.path}} {{EnableTranslateInlineForStorefront.value}}" - stepKey="enableTranslateInlineForStorefront"/> - <!-- Set developer mode --> - <magentoCLI command="deploy:mode:set developer" stepKey="setDeveloperMode"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> </before> <after> <!-- Disable Translate Inline For Storefront --> - <magentoCLI - command="config:set {{DisableTranslateInlineForStorefront.path}} {{DisableTranslateInlineForStorefront.value}}" - stepKey="disableTranslateInlineForStorefront"/> - <!-- Set production mode --> - <magentoCLI command="deploy:mode:set production" stepKey="setProductionMode"/> - + <magentoCLI command="config:set {{DisableTranslateInlineForStorefront.path}} {{DisableTranslateInlineForStorefront.value}}" stepKey="disableTranslateInlineForStorefront"/> <!-- Delete Simple Product --> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!-- Delete Category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> <!-- Add product to cart on storefront --> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="product" value="$$createProduct$$"/> + <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> </actionGroup> - <!-- Click on cart button on the top --> - <click selector="{{StorefrontMiniCartSection.show}}" stepKey="showMiniCart"/> - - <!-- Small cart popup appeared. --> - <waitForElementVisible selector="{{StorefrontMinicartSection.productName}}" stepKey="seeProductNameAppeared"/> + <!-- Open Mini Cart --> + <actionGroup ref="StorefrontOpenMiniCartActionGroup" stepKey="openMiniCart"/> <!-- Check button "Proceed to Checkout". There must be red borders and "book" icons on labels that can be translated. --> <actionGroup ref="AssertElementInTranslateInlineModeActionGroup" stepKey="assertRedBordersAndBookIcon"> <argument name="elementSelector" value="{{StorefrontMinicartSection.goToCheckout}}"/> </actionGroup> - <actionGroup ref="AdminTranslateElementActionGroup" stepKey="translateProceedToCheckoutButtonText"> + <!-- Open Inline Translation popup --> + <actionGroup ref="StorefrontOpenInlineTranslationPopupActionGroup" stepKey="openInlineTranslationPopup"> <argument name="elementSelector" value="{{StorefrontMinicartSection.goToCheckout}}"/> - <argument name="translateText" value="Proceed to Checkout Translated"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/Translation/etc/di.xml b/app/code/Magento/Translation/etc/di.xml index 783384fe64fa9..f19eebd29ae11 100644 --- a/app/code/Magento/Translation/etc/di.xml +++ b/app/code/Magento/Translation/etc/di.xml @@ -49,6 +49,18 @@ </argument> </arguments> </type> + <virtualType name="dataProviderCompositeRenderer" type="Magento\Framework\Phrase\Renderer\Composite"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="inline" xsi:type="object">Magento\Translation\Model\Inline\Renderer</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Translation\Model\Js\DataProvider"> + <arguments> + <argument name="translate" xsi:type="object">dataProviderCompositeRenderer</argument> + </arguments> + </type> <type name="Magento\Framework\Phrase\Renderer\Translate"> <arguments> <argument name="translator" xsi:type="object">Magento\Framework\Translate</argument> diff --git a/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php new file mode 100644 index 0000000000000..305beae5b9562 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Translation\Model\Js; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\TranslateInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Translation\Model\ResourceModel\StringUtils; +use PHPUnit\Framework\TestCase; + +/** + * Integration tests for \Magento\Translation\Model\Js\DataProvider class. + */ +class DataProviderTest extends TestCase +{ + /** + * @var StringUtils + */ + private $stringUtils; + + /** + * @var TranslateInterface + */ + private $translate; + + /** + * @var DataProviderInterface + */ + private $translationDataProvider; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->stringUtils = $objectManager->get(StringUtils::class); + $this->translate = $objectManager->get(TranslateInterface::class); + $this->translationDataProvider = $objectManager->get(DataProviderInterface::class); + } + + /** + * Test translation data. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store dev/translate_inline/active 1 + */ + public function testGetData() + { + $expectedDictionary = ['Proceed to Checkout' => 'Proceed to Checkout - Translated']; + + $this->stringUtils->saveTranslate('Proceed to Checkout', 'Proceed to Checkout - Translated'); + $this->translate->setLocale('en_US')->loadData('frontend', true); + $dictionary = $this->translationDataProvider->getData('Magento/luma'); + $this->assertEquals($expectedDictionary, $dictionary); + } + + /** + * @inheritdoc + */ + public function tearDown() + { + try { + $this->stringUtils->deleteTranslate('Proceed to Checkout'); + } catch (NoSuchEntityException $exception) { + // translate already deleted + } + } +} From 1aff699d53257a3018b8f7d51ff65656cfc47638 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Mon, 6 Apr 2020 18:28:39 -0500 Subject: [PATCH 701/734] MQE-2069: [MTF-to-MFTF] Create Newsletter Template Entity #761 --- .../AdminCreatesNewIntegrationActionGroup.xml | 2 -- ...ntegrationEntityWithDuplicatedNameTest.xml | 14 ++++++++++--- .../Test/AdminDeleteIntegrationEntityTest.xml | 12 +++++------ .../Test/AdminUpdateIntegrationEntityTest.xml | 20 +++++++++++++------ ...grationEntityWithIncorrectPasswordTest.xml | 1 + ...nMarketingCreateNewsletterTemplateTest.xml | 2 ++ ...nMarketingNewsletterTemplateUpdateTest.xml | 3 ++- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml index d605c1639cc69..3623dbd70f816 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml @@ -17,8 +17,6 @@ </arguments> <fillField stepKey="fillNameField" selector="{{AdminNewIntegrationSection.name}}" userInput="{{name}}"/> <fillField stepKey="fillAdminPasswordField" selector="{{AdminNewIntegrationSection.password}}" userInput="{{password}}"/> - <!--Click the "Save" Button --> - <click stepKey="clickSaveButton" selector="{{AdminNewIntegrationSection.saveButton}}"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml index f55e4e37585ba..7bc1c9b5a274f 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml @@ -23,6 +23,14 @@ <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="returnToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="searchForIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteCreatedIntegration"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> @@ -32,18 +40,18 @@ </actionGroup> <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createNewIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButtonSecondTime"/> <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createNewIntegrationWithDuplicatedName"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheFormWithDuplicatedName"/> <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeErrorMessage"> - <argument name="message" value="The integration with name "Integration1" exists."/> + <argument name="message" value="The integration with name "{{defaultIntegrationData.name}}" exists."/> <argument value="error" name="messageType"/> </actionGroup> </test> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml index eb6c946a03bb5..0148278ac7aaa 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -31,34 +31,32 @@ <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> <!-- Create New Integration --> <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> - <!-- Submit The Form --> <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <!-- TEST BODY --> <!-- Find Created Integration In Grid --> <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <!-- Delete Created Integration Entity --> <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteIntegration"/> <!-- Assert Success Message --> <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> - <argument name="message" value="The integration 'Integration1' has been deleted."/> + <argument name="message" value="The integration '{{defaultIntegrationData.name}}' has been deleted."/> <argument value="success" name="messageType"/> </actionGroup> <!-- Assert Deleted Integration Is Not In Grid --> <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findDeletedIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <actionGroup ref="AssertDeletedIntegrationIsNotInGridActionGroup" stepKey="dontSeeIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <!-- END TEST BODY --> </test> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml index ab01f69308715..41efc72c35fb4 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -30,24 +30,32 @@ <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> <!-- Create New Integration --> <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> + <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> </before> <after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="returnToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="searchForIntegration"> + <argument name="name" value="{{defaultIntegrationData.name}} Updated"/> + </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- TEST BODY --> <!-- Find Created Integration In Grid --> <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> - <argument name="name" value="Integration1"/> + <argument name="name" value="{{defaultIntegrationData.name}}"/> </actionGroup> <!-- Open Integration Edit Page --> <actionGroup ref="AdminClickEditIntegrationEntityActionGroup" stepKey="clickEditButton"/> <!-- Update Integration Entity --> <actionGroup ref="AdminUpdateCreatedIntegrationEntityActionGroup" stepKey="updateIntegrationEntity"> - <argument name="name" value="Integration Updated"/> + <argument name="name" value="{{defaultIntegrationData.name}} Updated"/> <argument name="endpoint" value="https://endpoint-updated.com"/> <argument name="linkUrl" value="https://testlink-updated.com"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> @@ -56,15 +64,15 @@ <actionGroup ref="AdminSubmitIntegrationFormActionGroup" stepKey="submitTheForm"/> <!-- Assert Success Message --> <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> - <argument name="message" value="The integration 'Integration Updated' has been saved."/> + <argument name="message" value="The integration '{{defaultIntegrationData.name}} Updated' has been saved."/> <argument value="success" name="messageType"/> </actionGroup> <!-- Assert Updated Entity In Grid --> <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findDeletedIntegration"> - <argument name="name" value="Integration Updated"/> + <argument name="name" value="{{defaultIntegrationData.name}} Updated"/> </actionGroup> <actionGroup ref="AssertUpdatedIntegrationEntityInGridActionGroup" stepKey="seeIntegrationEntity"> - <argument name="name" value="Integration Updated"/> + <argument name="name" value="{{defaultIntegrationData.name}} Updated"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml index 59d70da4f9762..403e886ea8209 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml @@ -33,6 +33,7 @@ <argument name="name" value="Integration1"/> <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> + <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> </before> <after> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml index 525fd70d03f8e..16bd135163307 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml @@ -16,7 +16,9 @@ <description value="Newsletter Template Successfully Created"/> <group value="newsletter"/> <group value="mtf_migrated"/> + <severity value="CRITICAL"/> <testCaseId value="MC-29809"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml index cedb176cf909f..82b6e1833a753 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateUpdateTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminMarketingNewsletterTemplateUpdate"> + <test name="AdminMarketingNewsletterTemplateUpdateTest"> <annotations> <features value="Newsletter"/> <stories value="Newsletter Updating"/> @@ -16,6 +16,7 @@ <description value="Admin should be able update created Newsletter Template"/> <group value="reports"/> <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> From 84bd04a3f897b013ada730630394df339ea45b74 Mon Sep 17 00:00:00 2001 From: Cristiano Pacheco <chris.spb25@gmail.com> Date: Mon, 6 Apr 2020 22:47:41 -0300 Subject: [PATCH 702/734] magento/magento2#27500 - Added return type void to setUp method. --- .../Test/Unit/Block/Form/AbstractInstructionTest.php | 2 +- .../OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php | 2 +- .../OfflinePayments/Test/Unit/Model/BanktransferTest.php | 2 +- .../OfflinePayments/Test/Unit/Model/CashondeliveryTest.php | 2 +- .../Test/Unit/Model/CheckmoConfigProviderTest.php | 2 +- .../Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php | 2 +- .../Test/Unit/Model/InstructionsConfigProviderTest.php | 2 +- .../OfflinePayments/Test/Unit/Model/PurchaseorderTest.php | 2 +- .../Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php index 1300800b7974e..e0dc15c66b284 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php @@ -19,7 +19,7 @@ class AbstractInstructionTest extends TestCase */ protected $model; - protected function setUp() + protected function setUp(): void { $context = $this->createMock(Context::class); $this->model = $this->getMockForAbstractClass( diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index 6de906983d8e3..cf0491167bd28 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -31,7 +31,7 @@ class CheckmoTest extends TestCase /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { $context = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php index 18a36781dfa2f..f92ca76da43dd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php @@ -28,7 +28,7 @@ class BanktransferTest extends TestCase */ private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); $eventManager = $this->createMock(ManagerInterface::class); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php index ad59b21e8ac15..afe831b3e0a50 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php @@ -28,7 +28,7 @@ class CashondeliveryTest extends TestCase */ private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { $helper = new ObjectManager($this); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php index b753b7f3ceadf..dd359b979283a 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php @@ -31,7 +31,7 @@ class CheckmoConfigProviderTest extends TestCase */ private $escaperMock; - protected function setUp() + protected function setUp(): void { $this->methodMock = $this->createMock(Checkmo::class); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php index 8fadecd23584a..d7eae294511fd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php @@ -27,7 +27,7 @@ class CheckmoTest extends TestCase */ private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); $eventManager = $this->createMock(ManagerInterface::class); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php index 303f34cd0dfdb..821ad6e7555c5 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php @@ -38,7 +38,7 @@ class InstructionsConfigProviderTest extends TestCase */ private $escaperMock; - protected function setUp() + protected function setUp(): void { $this->methodOneMock = $this->createPartialMock( AbstractMethod::class, diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php index 95415fbb479df..d16721a743abd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php @@ -33,7 +33,7 @@ class PurchaseorderTest extends TestCase */ private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); $eventManager = $this->createMock(EventManagerInterface::class); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php index 30780d24d928a..c7e7006b07c91 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php @@ -54,7 +54,7 @@ class BeforeOrderPaymentSaveObserverTest extends TestCase /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); $this->paymentMock = $this->getMockBuilder(Payment::class) From 52bccaa36bd189068201543cfc5af7ae6070f89f Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Mon, 6 Apr 2020 21:07:47 -0500 Subject: [PATCH 703/734] MQE-2069: [MTF-to-MFTF] Create Newsletter Template Entity #761 removed extra line --- .../Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml index 3623dbd70f816..c039a70293269 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml @@ -17,6 +17,5 @@ </arguments> <fillField stepKey="fillNameField" selector="{{AdminNewIntegrationSection.name}}" userInput="{{name}}"/> <fillField stepKey="fillAdminPasswordField" selector="{{AdminNewIntegrationSection.password}}" userInput="{{password}}"/> - </actionGroup> </actionGroups> From bbc1f10d87925709ff1abd7f6e79de73d1260fc5 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 6 Apr 2020 20:04:35 -0500 Subject: [PATCH 704/734] MQE-2046: remove deprecated entities usages in MFTF tests --- .../LoginAdminWithCredentialsActionGroup.xml | 13 ---- .../LoginAsAdminActionGroup.xml | 13 ---- .../Test/AdminAdvancedReportingButtonTest.xml | 2 +- .../LoginActionGroup.xml | 11 ---- .../LoginAdminWithCredentialsActionGroup.xml | 25 -------- .../LoginAsAdminActionGroup.xml | 24 ------- .../LoginAsAnyUserActionGroup.xml | 20 ------ .../LogoutActionGroup.xml | 11 ---- .../Mftf/Section/AdminMessagesSection.xml | 6 -- .../Backend/Test/Mftf/Test/AdminLoginTest.xml | 27 -------- .../BraintreeCreditCardOnCheckoutTest.xml | 11 +++- .../BundleProductWithTierPriceInCartTest.xml | 10 ++- .../AdminLoginWithCaptchaActionGroup.xml | 2 +- .../Mftf/Test/AdminLoginWithCaptchaTest.xml | 14 +++-- .../AddSimpleProductToCartActionGroup.xml | 2 +- ...AssertProductOnCategoryPageActionGroup.xml | 2 +- .../SaveCategoryFormActionGroup.xml | 20 ------ .../ActionGroup/_Deprecated_ActionGroup.xml | 12 ---- .../Test/AdminAddImageForCategoryTest.xml | 2 +- ...MediaRolesForFirstAddedImageViaApiTest.xml | 2 +- ...minCheckProductListPriceAttributesTest.xml | 2 +- ...inCreateRootCategoryRequiredFieldsTest.xml | 2 +- .../Test/AdminMoveAnchoredCategoryTest.xml | 6 +- ...ignedToCategoryWithoutCustomURLKeyTest.xml | 2 +- .../Test/AdminRemoveImageFromCategoryTest.xml | 4 +- ...rPriceInStockVisibleInCategoryOnlyTest.xml | 2 +- ...thCustomOptionsVisibleInSearchOnlyTest.xml | 2 +- ...tOfStockVisibleInCategoryAndSearchTest.xml | 2 +- ...iceOutOfStockVisibleInCategoryOnlyTest.xml | 2 +- ...PriceOutOfStockVisibleInSearchOnlyTest.xml | 2 +- ...eInStockVisibleInCategoryAndSearchTest.xml | 2 +- ...tOfStockVisibleInCategoryAndSearchTest.xml | 2 +- ...eInStockVisibleInCategoryAndSearchTest.xml | 6 +- ...rPriceInStockVisibleInCategoryOnlyTest.xml | 2 +- ...tOfStockVisibleInCategoryAndSearchTest.xml | 2 +- .../EndToEndB2CGuestUserMysqlTest.xml | 4 +- .../EndToEndB2CGuestUserTest.xml | 4 +- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 4 +- ...tProductsDisplayUsingElasticSearchTest.xml | 2 +- ...rontConfigurableOptionsThumbImagesTest.xml | 2 +- ...chorIsVisibleOnViewportOnceClickedTest.xml | 10 ++- ...teCatalogPriceRuleForCustomerGroupTest.xml | 10 ++- ...eProductWithAssignedSimpleProductsTest.xml | 2 +- ...eForConfigurableProductWithOptionsTest.xml | 2 +- .../Mftf/Test/AdminDeleteSearchTermTest.xml | 2 +- ...OfDefaultBillingAndShippingAddressTest.xml | 10 ++- ...refrontCustomerLoginDuringCheckoutTest.xml | 10 ++- ...capeAndEnterHandlesForWYSIWYGBlockTest.xml | 2 +- ...untryDropDownWithOneAllowedCountryTest.xml | 10 ++- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- ...tWithSeveralAttributesPrependMediaTest.xml | 4 +- ...hVisualSwatchAttributePrependMediaTest.xml | 4 +- ...nCurrencyConverterAPIConfigurationTest.xml | 16 ++--- ...efrontDefaultWelcomeMessageActionGroup.xml | 4 +- ...ustomerAddressInfoFillStateActionGroup.xml | 2 +- ...SignUpNewUserFromStorefrontActionGroup.xml | 2 +- ...CustomerWithWebSiteAndGroupActionGroup.xml | 33 ---------- ...CustomerAddressNoZipNoStateActionGroup.xml | 20 ------ ...etDefaultShippingAndBillingActionGroup.xml | 18 ------ .../EnterCustomerAddressInfoActionGroup.xml | 32 ---------- ...ustomerAddressInfoFillStateActionGroup.xml | 17 ----- ...NavigateToCustomerGroupPageActionGroup.xml | 11 ---- ...erifyCustomerBillingAddressActionGroup.xml | 29 --------- ...omerBillingAddressWithStateActionGroup.xml | 28 --------- ...erifyCustomerNameOnFrontendActionGroup.xml | 25 -------- ...rifyCustomerShippingAddressActionGroup.xml | 28 --------- ...merShippingAddressWithStateActionGroup.xml | 28 --------- .../Section/StorefrontPanelHeaderSection.xml | 3 - ...AdminCreateNewCustomerOnStorefrontTest.xml | 8 ++- .../ChangingAllCustomerGroupViaGridTest.xml | 28 --------- ...ChangingSingleCustomerGroupViaGridTest.xml | 62 ------------------- .../Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 10 ++- ...StorefrontCheckTaxAddingValidVATIdTest.xml | 6 +- ...efrontUpdateCustomerAddressBelgiumTest.xml | 12 +++- ...orefrontUpdateCustomerAddressChinaTest.xml | 12 +++- ...refrontUpdateCustomerAddressFranceTest.xml | 12 +++- .../StorefrontUpdateCustomerAddressUKTest.xml | 12 +++- ...pdateCustomerInformationAddAddressTest.xml | 12 +++- .../AddingProductWithExpiredSessionTest.xml | 11 ---- .../StorefrontCustomerForgotPasswordTest.xml | 11 ---- .../VerifyDisabledCustomerGroupFieldTest.xml | 11 ---- ...dminImportCSVWithSpecialCharactersTest.xml | 2 +- .../CliRunReindexUsingCronJobsActionGroup.xml | 18 ------ ...chaseFunctionalityNegativeScenarioTest.xml | 4 +- ...efrontInstantPurchaseFunctionalityTest.xml | 2 +- ...oreFrontMyAccountWithMultishipmentTest.xml | 2 +- ...ingCartBehaviorAfterSessionExpiredTest.xml | 10 ++- ...listIsPersistedUnderLongTermCookieTest.xml | 8 +-- ...eConditionAndFreeShippingIsAppliedTest.xml | 2 +- ...talAndVerifyRuleConditionIsAppliedTest.xml | 2 +- ...oryAndVerifyRuleConditionIsAppliedTest.xml | 2 +- ...ghtAndVerifyRuleConditionIsAppliedTest.xml | 2 +- ...inSaveUserInvalidExpirationActionGroup.xml | 2 +- ...minLoginAdminUserWithInvalidExpiration.xml | 6 +- ...AdminLoginAdminUserWithValidExpiration.xml | 6 +- .../AdminNavigateWhileUserExpiredTest.xml | 6 +- .../GoToShipmentIntoOrderActionGroup.xml | 19 ------ .../SeeProductInShipmentItemsActionGroup.xml | 20 ------ .../SubmitShipmentIntoOrderActionGroup.xml | 19 ------ ...ifyBasicShipmentInformationActionGroup.xml | 33 ---------- ...atusDisabledVerifyErrorSaveMessageTest.xml | 2 +- ...EnabledVerifyAbsenceOfDeleteButtonTest.xml | 2 +- ...tusEnabledVerifyBackendAndFrontendTest.xml | 2 +- ...NewLocalizedStoreViewStatusEnabledTest.xml | 2 +- ...ithCustomWebsiteAndDefaultCategoryTest.xml | 2 +- ...upWithCustomWebsiteAndRootCategoryTest.xml | 2 +- ...thDefaultWebsiteAndDefaultCategoryTest.xml | 2 +- ...usDisabledVerifyBackendAndFrontendTest.xml | 2 +- ...tusEnabledVerifyBackendAndFrontendTest.xml | 2 +- .../Test/AdminDeleteDefaultStoreViewTest.xml | 2 +- .../Mftf/Test/AdminDeleteStoreGroupTest.xml | 2 +- .../Mftf/Test/AdminDeleteStoreViewTest.xml | 2 +- ...inMoveStoreToOtherGroupSameWebsiteTest.xml | 2 +- ...pAcceptAlertAndVerifyStoreViewFormTest.xml | 2 +- ...teStoreGroupAndVerifyStoreViewFormTest.xml | 2 +- .../Mftf/Test/AdminUpdateStoreViewTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateWebsiteTest.xml | 2 +- ...heckTextSwatchAttributeAddedViaApiTest.xml | 2 +- ...axRuleCustomProductTaxClassActionGroup.xml | 4 +- .../Test/AdminCheckingTaxReportGridTest.xml | 8 +-- ...orefrontTaxQuoteCartLoggedInSimpleTest.xml | 10 ++- ...refrontTaxQuoteCartLoggedInVirtualTest.xml | 10 ++- ...rontTaxQuoteCheckoutLoggedInSimpleTest.xml | 12 +++- ...ontTaxQuoteCheckoutLoggedInVirtualTest.xml | 12 +++- .../AdminFormSaveAndCloseActionGroup.xml | 19 ------ .../AdminFormSaveAndDuplicateActionGroup.xml | 20 ------ ...dFilterDeleteAndVerifyErrorMessageTest.xml | 2 +- .../AdminAddCustomUrlRewriteActionGroup.xml | 2 +- .../AdminAddUrlRewriteActionGroup.xml | 2 +- ...dminAddUrlRewriteForProductActionGroup.xml | 2 +- .../AdminDeleteUrlRewriteActionGroup.xml | 2 +- ...AdminUpdateCustomUrlRewriteActionGroup.xml | 2 +- .../AdminUpdateUrlRewriteActionGroup.xml | 2 +- ...rlKeyForStoreViewAndMovingCategoryTest.xml | 2 +- ...SeveralWebsitesAndCheckURLRewritesTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateUserTest.xml | 3 +- ...nfigurableProductInWishlistActionGroup.xml | 25 -------- ...bleProductInWishlistSidebarActionGroup.xml | 24 ------- 138 files changed, 281 insertions(+), 941 deletions(-) delete mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml delete mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml delete mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml delete mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml delete mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml delete mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml delete mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml delete mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml delete mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml delete mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml delete mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml delete mode 100644 dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml deleted file mode 100644 index 15f6852b4767c..0000000000000 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ /dev/null @@ -1,13 +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="LoginAdminWithCredentialsActionGroup" deprecated="Use AdminLoginActionGroup instead"> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml deleted file mode 100644 index a2d024bee8b74..0000000000000 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml +++ /dev/null @@ -1,13 +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="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml index 5da7ccd3c9823..cbcbb3a5dd64c 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml @@ -22,7 +22,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml deleted file mode 100644 index 4afabe2d0eff3..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginActionGroup.xml +++ /dev/null @@ -1,11 +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="LoginActionGroup" deprecated="Use AdminLoginActionGroup instead" extends="AdminLoginActionGroup"/> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml deleted file mode 100644 index cc124990c92ca..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ /dev/null @@ -1,25 +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="LoginAdminWithCredentialsActionGroup" deprecated="Use AdminLoginActionGroup"> - <annotations> - <description>Login to Backend Admin using provided Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> - </annotations> - <arguments> - <argument name="adminUser" type="string"/> - <argument name="adminPassword" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml deleted file mode 100644 index 6fe9f184f5ecf..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAdminActionGroup.xml +++ /dev/null @@ -1,24 +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="LoginAsAdmin" deprecated="Use AdminLoginActionGroup instead"> - <annotations> - <description>Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description> - </annotations> - <arguments> - <argument name="adminUser" type="entity" defaultValue="DefaultAdminUser"/> - </arguments> - - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser.username}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminUser.password}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml deleted file mode 100644 index b6cec9056bd5d..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LoginAsAnyUserActionGroup.xml +++ /dev/null @@ -1,20 +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="LoginAsAnyUser" deprecated="Use LoginAdminWithCredentialsActionGroup instead"> - <arguments> - <argument name="uname" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/> - <argument name="passwd" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - </arguments> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> - <fillField userInput="{{uname}}" selector="{{LoginFormSection.username}}" stepKey="fillUsername"/> - <fillField userInput="{{passwd}}" selector="{{LoginFormSection.password}}" stepKey="fillPassword"/> - <click selector="{{LoginFormSection.signIn}}" stepKey="clickLogin"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml deleted file mode 100644 index ec79c982e730c..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/LogoutActionGroup.xml +++ /dev/null @@ -1,11 +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="logout" deprecated="Use AdminLogoutActionGroup instead" extends="AdminLogoutActionGroup"/> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index bb1123d01c867..dbd5348d7d696 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -16,11 +16,5 @@ <element name="messageByType" type="text" selector="#messages div.message-{{messageType}}" parameterized="true" /> <element name="warning" type="text" selector="#messages div.message-warning"/> <element name="accessDenied" type="text" selector=".access-denied-page"/> - <!-- Deprecated elements, please do not use them. Use elements above--> - <!-- Elements below are too common and catch non messages blocks. Ex: system messages blocks--> - <element name="successMessage" type="text" selector=".message-success"/> - <element name="errorMessage" type="text" selector=".message.message-error.error"/> - <element name="warningMessage" type="text" selector=".message-warning"/> - <element name="noticeMessage" type="text" selector=".message-notice"/> </section> </sections> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml deleted file mode 100644 index 09893f5f51e5e..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?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="AdminLoginTest" deprecated="Replaced with AdminLoginSuccessfulTest"> - <annotations> - <features value="Backend"/> - <stories value="Login on the Admin Login page"/> - <title value="Admin should be able to log into the Magento Admin backend"/> - <description value="Admin should be able to log into the Magento Admin backend"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-71572"/> - <group value="example"/> - <group value="login"/> - </annotations> - - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> - </test> -</tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index 0747e785c4ae4..476d8a2e84800 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -43,10 +43,15 @@ <!--Go to storefront--> <amOnPage url="" stepKey="DoToStorefront"/> <!--Create account--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUserFromStorefrontActionGroup"> - <argument name="Customer" value="Simple_US_Customer"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> - <!--Add product to cart--> <amOnPage url="$$product.sku$$.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml index d08ad8970e19f..d6d06fbc21f7d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml @@ -61,8 +61,14 @@ <argument name="price" value="Discount"/> <argument name="amount" value="50"/> </actionGroup> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <amOnPage url="{{StorefrontProductPage.url(BundleProduct.urlKey)}}" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml index b84977ba4fcd8..8d09944576538 100644 --- a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml +++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminLoginWithCaptchaActionGroup" extends="LoginAsAdmin"> + <actionGroup name="AdminLoginWithCaptchaActionGroup" extends="AdminLoginActionGroup"> <annotations> <description>EXTENDS: LoginAsAdmin. Fills in the Captcha field on the Backend Admin Login page.</description> </annotations> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml index 962b788aa80bb..28253fb3c00ef 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml @@ -30,7 +30,7 @@ <magentoCLI command="config:set {{AdminCaptchaDefaultSymbolsConfigData.path}} {{AdminCaptchaDefaultSymbolsConfigData.value}}" stepKey="setDefaultCaptchaSymbols" /> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> </after> - + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> @@ -54,17 +54,19 @@ <!-- Submit form with incorrect captcha --> <actionGroup ref="AdminLoginWithCaptchaActionGroup" stepKey="loginAsAdminWithIncorrectCaptcha"> - <argument name="adminUser" value="DefaultAdminUser" /> - <argument name="captcha" value="{{WrongCaptcha.value}}" /> + <argument name="username" value="{{DefaultAdminUser.username}}"/> + <argument name="password" value="{{DefaultAdminUser.password}}"/> + <argument name="captcha" value="{{WrongCaptcha.value}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeIncorrectCaptchaErrorMessage"> - <argument name="message" value="Incorrect CAPTCHA." /> + <argument name="message" value="Incorrect CAPTCHA."/> </actionGroup> <actionGroup ref="AssertCaptchaVisibleOnAdminLoginFormActionGroup" stepKey="assertCaptchaVisibleAfterIncorrectCaptcha" /> <actionGroup ref="AdminLoginWithCaptchaActionGroup" stepKey="loginAsAdminWithCorrectCaptcha"> - <argument name="adminUser" value="DefaultAdminUser" /> - <argument name="captcha" value="{{PreconfiguredCaptcha.value}}" /> + <argument name="username" value="{{DefaultAdminUser.username}}"/> + <argument name="password" value="{{DefaultAdminUser.password}}"/> + <argument name="captcha" value="{{PreconfiguredCaptcha.value}}"/> </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="verifyAdminLoggedIn" /> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml index 68a051c232338..81e3b8c99d9d2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AddSimpleProductToCartActionGroup" deprecated="Avoid using super-ActionGroups. Use StorefrontOpenProductEntityPageActionGroup, StorefrontAddSimpleProductToCartActionGroup and StorefrontAssertProductAddedToCartResultMessageActionGroup"> + <actionGroup name="AddSimpleProductToCartActionGroup"> <annotations> <description>Navigates to the Storefront Product page. Then adds the Product to the Cart. Validates that the Success Message is present and correct.</description> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml index 4bd2c97af7afb..4225ed3a7fcc8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup"> + <actionGroup name="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup" deprecated="some"> <annotations> <description>DEPRECATED. Use AssertStorefrontProductIsPresentOnCategoryPageActionGroup. EXTENDS:StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice', 'moveMouseOverProduct', 'AssertAddToCart'</description> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml deleted file mode 100644 index 19f11a2402f56..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml +++ /dev/null @@ -1,20 +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="SaveCategoryFormActionGroup"> - <annotations> - <description>DEPRECATED. Use AdminSaveCategoryFormActionGroup instead. Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category.</description> - </annotations> - - <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminMessagesSection.success}}" stepKey="assertSuccess"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml deleted file mode 100644 index aff1de23af409..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/_Deprecated_ActionGroup.xml +++ /dev/null @@ -1,12 +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="GoToProductPageViaIDActionGroup" extends="AdminProductPageOpenByIdActionGroup" deprecated="Use AdminProductPageOpenByIdActionGroup instead"/> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml index dcb900164eba7..e4cf255a03e05 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml @@ -34,7 +34,7 @@ <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <!-- Verify category with image in admin --> <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml index dfde5779637a4..b5ccd62401c9b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml @@ -27,7 +27,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml index c389d447feeba..27b8b06510ee1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="openColumnsDropdown"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml index a5556b076fef6..624f342741502 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml @@ -20,7 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="LoginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index d302dad52d6e6..61024f1cb3d35 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -30,11 +30,7 @@ <createData entity="_defaultProduct" stepKey="productTwo"> <requiredEntity createDataKey="simpleSubCategoryOne"/> </createData> - - <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> - <magentoCLI command="cron:run" arguments="--group='index'" stepKey="firstRunToScheduleJobs"/> - <magentoCLI command="cron:run" arguments="--group='index'" stepKey="secondRunToExecuteJobs"/> - + <magentoCron groups="index" stepKey="RunToScheduleJobs"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml index 6d197d19eb924..1c536df7c2efb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml @@ -76,7 +76,7 @@ <argument name="productSku" value="$createSimpleProductSecond.sku$"/> </actionGroup> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategory"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategory"/> <executeJS function="return '$createCategory.name$'.toLowerCase();" stepKey="categoryNameLower" /> <executeJS function="return '$createSimpleProductFirst.name$'.toLowerCase();" stepKey="simpleProductFirstNameLower" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml index ee8636dece808..8033a2dffec7c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml @@ -34,12 +34,12 @@ <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> <!-- Remove image from category --> <actionGroup ref="RemoveCategoryImageActionGroup" stepKey="removeCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryFormAfterRemove"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryFormAfterRemove"/> <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="CheckCategoryOnStorefront"> <argument name="categoryEntity" value="SimpleSubCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml index cea09b0cdb4bd..595f9bcd489ec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml index 77de42bdbac21..458d02d61426d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 918cda5274216..6d6ff0b3b1b89 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml index 5f6f44110bfb4..d5ae971d87695 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml index 393c280eedf1b..314df67d43d00 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml index 9658597a04717..d0f4fc8882e3f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 9d3d315487d65..2234d6f338b62 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index f5571633c2da4..e0c5d83b4b014 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -25,10 +25,8 @@ <requiredEntity createDataKey="initialCategoryEntity"/> </createData> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> - <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext catalog_category_product" stepKey="reindexIndices"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCron groups="index" stepKey="RunToScheduleJobs"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml index 5c0b7c31756ca..8f0861fe33371 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml index eeb85e8d6fc2e..f7f5385381590 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml index f334a6c5a66ce..6630557b37bd0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -59,8 +59,8 @@ <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome"/> <!-- Open Category --> <comment userInput="Open category" stepKey="commentOpenCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml index 94b0deaf6ce1c..441c9cd5eab8b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -62,8 +62,8 @@ <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome"/> <!-- Open Category --> <comment userInput="Open category" stepKey="commentOpenCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 4eb5b843eeb35..fbb6893e92b1e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -49,8 +49,8 @@ <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" after="endOfSigningUpUserAccount"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage" after="startOfBrowsingCatalog"/> <waitForPageLoad stepKey="homeWaitForPageLoad" after="amOnHomePage"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage" after="homeWaitForPageLoad"/> - <see userInput="Welcome, John Doe!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome" after="homeWaitForWelcomeMessage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage" after="homeWaitForPageLoad"/> + <see userInput="Welcome, John Doe!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome" after="homeWaitForWelcomeMessage"/> <!-- Open Category --> <comment userInput="Open category" stepKey="commentOpenCategory" after="homeCheckWelcome"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml index ea7912613f7f2..e109dcb0deea5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml @@ -113,7 +113,7 @@ <requiredEntity createDataKey="createCategory1"/> </createData> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="performReindex"/> <magentoCLI command="cache:clean" arguments="full_page" stepKey="cleanFullPageCache"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml index c22f91b5394ea..dec36d72a50d6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml @@ -165,7 +165,7 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open ConfigProduct in Store Front Page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml index 8acc6272d5da4..020c3d85b9fdf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml @@ -90,8 +90,14 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Go to frontend and make a user account and login with it --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!-- Go to the product view page --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml index 65ef23de65380..b9318f72bee9e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml @@ -56,8 +56,14 @@ <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> <!-- Create a user account --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!-- As a logged in user, go to the storefront category page and should NOT see discount --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index e4dd65479b784..4cc9449557520 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.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="AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest"> + <test name="AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest" deprecated="some"> <annotations> <features value="CatalogRuleConfigurable"/> <stories value="Apply catalog price rule"/> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index 8ed9c6ad09dad..436fc7714ce2d 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.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="AdminApplyCatalogRuleForConfigurableProductWithOptionsTest"> + <test name="AdminApplyCatalogRuleForConfigurableProductWithOptionsTest" deprecated="some"> <annotations> <features value="CatalogRuleConfigurable"/> <stories value="Apply catalog price rule"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml index a950fb6c379cb..c376456a64ac4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="SimpleProduct" stepKey="simpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml index e2d1a1b9139c8..c3713206ed1d2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml @@ -31,8 +31,14 @@ <amOnPage url="" stepKey="DoToStorefront"/> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!-- Add simple product to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml index 9152949fbf2a1..27597a72fdb7f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml @@ -41,7 +41,15 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Storefront as Guest and create new account --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createNewCustomerAccount"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> <!-- Sign Out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml index 0644542ccc0f3..aa29f33f90664 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> diff --git a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml index 9e05f4819cd73..5327979154389 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml @@ -35,8 +35,14 @@ <!--Flush Magento Cache--> <magentoCLI stepKey="flushCache" command="cache:flush"/> <!--Create a customer account from Storefront--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <click selector="{{CheckoutPaymentSection.addressBook}}" stepKey="goToAddressBook"/> <click selector="{{StorefrontCustomerAddressSection.country}}" stepKey="clickToExpandCountryDropDown"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 85c852d09e916..e4a9755adcef4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -62,7 +62,7 @@ </createData> <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </before> <after> <!-- @TODO: Uncomment once MQE-679 is fixed --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml index a50d1e32d3614..3e74edfb68074 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -141,7 +141,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption2Option2"/> <!-- Reindex invalidated indices after product attribute has been created --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> </before> <after> @@ -160,7 +160,7 @@ <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> <!-- Reindex invalidated indices after product attribute has been created --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> </after> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index be81b64c7ed25..c76d49a76d947 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -106,7 +106,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption3"/> <!-- Reindex invalidated indices after product attribute has been created --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> </before> <after> @@ -122,7 +122,7 @@ <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> <!-- Reindex invalidated indices after product attribute has been created --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> </after> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml index 5d6d05ebfead4..f7e6e05347345 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml @@ -47,14 +47,14 @@ <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRates"> <argument name="rateService" value="Currency Converter API"/> </actionGroup> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Click "Save" to apply the rates we found.' stepKey="seeImportMessage"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHE." stepKey="seeWarningMessageForCHE"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for RHD." stepKey="seeWarningMessageForRHD"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHW." stepKey="seeWarningMessageForCHW"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Click "Save" to apply the rates we found.' stepKey="seeImportMessage"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHE." stepKey="seeWarningMessageForCHE"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for RHD." stepKey="seeWarningMessageForRHD"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHW." stepKey="seeWarningMessageForCHW"/> <actionGroup ref="AdminSaveCurrencyRatesActionGroup" stepKey="saveCurrencyRates"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHE" rate' stepKey="seeCHEMessageAfterSave"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => RHD" rate' stepKey="seeRHDMessageAfterSave"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHW" rate' stepKey="seeCHWMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => CHE" rate' stepKey="seeCHEMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => RHD" rate' stepKey="seeRHDMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => CHW" rate' stepKey="seeCHWMessageAfterSave"/> <!--Go to the Storefront and check currency rates--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> @@ -74,6 +74,6 @@ <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRatesGreaterThen10"> <argument name="rateService" value="Currency Converter API"/> </actionGroup> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="Too many pairs. Maximum of 10 is supported for the free version." stepKey="seeTooManyPairsMessage"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="Too many pairs. Maximum of 10 is supported for the free version." stepKey="seeTooManyPairsMessage"/> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml index eb66cafe43dd3..7cfbc0283d505 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml @@ -13,8 +13,8 @@ <description>Validates that the Welcome message is present and correct and not you link absent.</description> </annotations> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="waitDefaultMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="verifyDefaultMessage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="waitDefaultMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="verifyDefaultMessage"/> <dontSeeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkAbsenceLinkNotYou"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml index 891b578e54e6b..257c9d5039c61 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="EnterCustomerAddressInfoFillStateActionGroup" extends="EnterCustomerAddressInfoActionGroup"> <annotations> - <description>EXTENDS: EnterCustomerAddressInfo. Fills the State field.</description> + <description>EXTENDS: EnterCustomerAddressInfoActionGroup. Fills the State field.</description> </annotations> <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 59601a58e64c7..56afa8854ce0d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SignUpNewUserFromStorefrontActionGroup" deprecated="Avoid using super-ActionGroups. See StorefrontCreateCustomerTest for replacement."> + <actionGroup name="SignUpNewUserFromStorefrontActionGroup"> <annotations> <description>Goes to the Storefront. Clicks on 'Create Account'. Fills in the provided Customer details, excluding Newsletter Sign-Up. Clicks on 'Create Account' button. Validate that the Customer details are present and correct.</description> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml deleted file mode 100644 index 37b543f68935a..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml +++ /dev/null @@ -1,33 +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="AdminCreateCustomerWithWebSiteAndGroup" deprecated="Use `AdminCreateCustomerWithWebSiteAndGroupActionGroup` instead"> - <annotations> - <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="customerData" defaultValue="Simple_US_Customer"/> - <argument name="website" type="string" defaultValue="{{_defaultWebsite.name}}"/> - <argument name="storeView" type="string" defaultValue="{{_defaultStore.name}}"/> - </arguments> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> - <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> - <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> - <selectOption selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerData.group}}" stepKey="selectCustomerGroup"/> - <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> - <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> - <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> - <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> - <waitForElement selector="{{AdminCustomerAccountInformationSection.storeView}}" stepKey="waitForCustomerStoreViewExpand"/> - <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> - <waitForPageLoad stepKey="waitForCustomersPage"/> - <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml deleted file mode 100644 index ebfd011115459..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml +++ /dev/null @@ -1,20 +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="AdminEditCustomerAddressNoZipNoState" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressNoZipNoStateActionGroup` instead"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'. Clicks on 'Set Default' for Billing/Shipping.</description> - </annotations> - - <remove keyForRemoval="selectState"/> - <remove keyForRemoval="fillZipCode"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml deleted file mode 100644 index 326eee275f1f1..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml +++ /dev/null @@ -1,18 +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="AdminEditCustomerAddressSetDefaultShippingAndBilling" extends="AdminEditCustomerAddressesFrom" deprecated="Use `AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup` instead"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'.</description> - </annotations> - - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml deleted file mode 100644 index 0634e0564f04b..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoActionGroup.xml +++ /dev/null @@ -1,32 +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="EnterCustomerAddressInfo" deprecated="Use `EnterCustomerAddressInfoActionGroup` instead"> - <annotations> - <description>Fills in the provided Customer details (First/Last Name, Company, Phone # and Address) on the Admin Customer creation/edit page. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="Address"/> - </arguments> - - <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPage"/> - <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> - <fillField stepKey="fillLastName" selector="{{StorefrontCustomerAddressSection.lastName}}" userInput="{{Address.lastname}}"/> - <fillField stepKey="fillCompany" selector="{{StorefrontCustomerAddressSection.company}}" userInput="{{Address.company}}"/> - <fillField stepKey="fillPhoneNumber" selector="{{StorefrontCustomerAddressSection.phoneNumber}}" userInput="{{Address.telephone}}"/> - <fillField stepKey="fillStreetAddress1" selector="{{StorefrontCustomerAddressSection.streetAddress1}}" userInput="{{Address.street[0]}}"/> - <fillField stepKey="fillStreetAddress2" selector="{{StorefrontCustomerAddressSection.streetAddress2}}" userInput="{{Address.street[1]}}"/> - <fillField stepKey="fillCityName" selector="{{StorefrontCustomerAddressSection.city}}" userInput="{{Address.city}}"/> - <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> - <selectOption stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvince}}" userInput="{{Address.state}}"/> - <fillField stepKey="fillZip" selector="{{StorefrontCustomerAddressSection.zip}}" userInput="{{Address.postcode}}"/> - <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml deleted file mode 100644 index 0705d1d0c8f2a..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml +++ /dev/null @@ -1,17 +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="EnterCustomerAddressInfoFillState" extends="EnterCustomerAddressInfo" deprecated="Use `CreateSystemBackupActionGroup` instead"> - <annotations> - <description>EXTENDS: EnterCustomerAddressInfo. Fills the State field.</description> - </annotations> - - <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml deleted file mode 100644 index 606089acf15d6..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/NavigateToCustomerGroupPageActionGroup.xml +++ /dev/null @@ -1,11 +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="NavigateToCustomerGroupPage" extends="AdminNavigateToCustomerGroupPageActionGroup" deprecated="Use AdminNavigateToCustomerGroupPageActionGroup"/> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml deleted file mode 100644 index dd5bff2d5488c..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressActionGroup.xml +++ /dev/null @@ -1,29 +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="VerifyCustomerBillingAddress" deprecated="Use `VerifyCustomerBillingAddressActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address is present and correct on the Storefront Customer Dashboard Address section.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml deleted file mode 100644 index 47403b23d7026..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml +++ /dev/null @@ -1,28 +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="VerifyCustomerBillingAddressWithState" deprecated="Use `VerifyCustomerBillingAddressWithStateActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml deleted file mode 100644 index cc01f3ade85ac..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml +++ /dev/null @@ -1,25 +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="VerifyCustomerNameOnFrontend" deprecated="Use `VerifyCustomerNameOnFrontendActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard page. Validates that the Customer First/Last Name is present and correct.</description> - </annotations> - <arguments> - <argument name="customer"/> - </arguments> - - <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> - <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.firstName}}" userInput="{{customer.firstname}}" stepKey="seeAssertCustomerFirstName"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.lastName}}" userInput="{{customer.lastname}}" stepKey="seeAssertCustomerLastName"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml deleted file mode 100644 index 1d9f3fd600155..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressActionGroup.xml +++ /dev/null @@ -1,28 +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="VerifyCustomerShippingAddress" deprecated="Use `VerifyCustomerShippingAddressActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml deleted file mode 100644 index 3291f9c8a7ef2..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml +++ /dev/null @@ -1,28 +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="VerifyCustomerShippingAddressWithState" deprecated="Use `VerifyCustomerShippingAddressWithStateActionGroup` instead"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml index 3610532c5356b..39dd719ce10f4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml @@ -9,9 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontPanelHeaderSection"> - <!-- Element name="WelcomeMessage" Deprecated due to incorrect naming convention please use name="welcomeMessage" --> - <element name="WelcomeMessage" type="text" selector=".greet.welcome span"/> - <element name="welcomeMessage" type="text" selector="header>.panel .greet.welcome" /> <element name="createAnAccountLink" type="select" selector="//div[@class='panel wrapper']//li/a[contains(.,'Create an Account')]" timeout="30"/> <element name="notYouLink" type="button" selector=".greet.welcome span a"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml index 8fe6e220c4aed..444e9aa643181 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml @@ -30,9 +30,15 @@ </after> <!--Create new customer on storefront and perform the asserts--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> <argument name="customer" value="CustomerEntityOne"/> </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> <actionGroup ref="AssertSignedUpNewsletterActionGroup" stepKey="assertSignedUpNewsLetter"> <argument name="customer" value="CustomerEntityOne"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml deleted file mode 100644 index e6c114334000e..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingAllCustomerGroupViaGridTest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?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="ChangingAllCustomerGroupViaGridTest" extends="ChangingSingleCustomerGroupViaGridTest"> - <annotations> - <title value="DEPRECATED Change all customers' group via grid"/> - <description value="Select All customers to change their group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-10924"/> - <stories value="Change Customer Group"/> - <group value="customer"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminChangeAllCustomersGroupViaGridTest instead</issueId> - </skip> - </annotations> - - <remove keyForRemoval="filterCustomer"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters" before="selectCustomer"/> - <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml deleted file mode 100644 index 0edadb86888f5..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest/ChangingSingleCustomerGroupViaGridTest.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?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="ChangingSingleCustomerGroupViaGridTest"> - <annotations> - <title value="DEPRECATED Change a single customer group via grid"/> - <description value="From the selection of All Customers select a single customer to change their group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-10921"/> - <stories value="Change Customer Group"/> - <group value="customer"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminChangeSingleCustomerGroupViaGridTest instead</issueId> - </skip> - </annotations> - - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - </before> - <after> - <!--Delete created product--> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="NavigateToCustomerGroupPage" stepKey="navToCustomers"/> - <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> - <argument name="customerGroupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <actionGroup ref="AdminCreateCustomerGroupActionGroup" stepKey="createCustomerGroup"> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - <argument name="taxClass" value="{{CustomerGroupChange.tax_class_name}}"/> - </actionGroup> - <actionGroup ref="NavigateToAllCustomerPage" stepKey="navToCustomers"/> - <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomer"> - <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> - </actionGroup> - <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> - <argument name="customerEmail" value="$$createCustomer.email$$"/> - </actionGroup> - <actionGroup ref="SetCustomerGroupForSelectedCustomersViaGrid" stepKey="setCustomerGroup"> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomerAfterGroupChange"> - <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> - </actionGroup> - <actionGroup ref="VerifyCustomerGroupForCustomer" stepKey="verifyCustomerGroupSet"> - <argument name="customerEmail" value="$$createCustomer.email$$"/> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 86218647778e6..2b6b1e7d1213c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -31,8 +31,14 @@ </after> <!-- Step 0: User signs up an account --> <comment userInput="Start of signing up user account" stepKey="startOfSigningUpUserAccount" /> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <comment userInput="End of signing up user account" stepKey="endOfSigningUpUserAccount" /> </test> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index 9b4e7716f4a25..80cdeadb391da 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -67,9 +67,11 @@ </actionGroup> <!--Register customer on storefront--> - <actionGroup ref="SignUpNewCustomerStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> <!--Go to My account > Address book--> <actionGroup ref="EnterCustomerAddressInfoFillStateActionGroup" stepKey="enterAddressInfo"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml index f45935b698fb5..b800b7870b5c5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml @@ -19,9 +19,15 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml index 87390beaa50f9..41e0a5d1b7793 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml @@ -19,9 +19,15 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml index 6a4ed633fd413..1ecfb20b38e19 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml @@ -20,9 +20,15 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml index b3ad6cc96aae1..8e29452b5495e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml @@ -20,9 +20,15 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml index 67aa050907f30..11aed4a3461e1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml @@ -20,9 +20,15 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Colorado_US_Customer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Colorado_US_Customer"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml deleted file mode 100644 index 2d580ab55075c..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/AddingProductWithExpiredSessionTest.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?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="AddingProductWithExpiredSessionTest" extends="StorefrontAddProductToCartWithExpiredSessionTest" deprecated="Use StorefrontAddProductToCartWithExpiredSessionTest"/> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml deleted file mode 100644 index 7c29e00b6a3a9..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/StorefrontCustomerForgotPasswordTest.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?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="StorefrontCustomerForgotPasswordTest" extends="StorefrontResetCustomerPasswordSuccessTest" deprecated="Use StorefrontResetCustomerPasswordSuccessTest"/> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml deleted file mode 100644 index 58e815b03126d..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/_Deprecated_Test/VerifyDisabledCustomerGroupFieldTest.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?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="VerifyDisabledCustomerGroupFieldTest" extends="AdminVerifyDisabledCustomerGroupFieldTest" deprecated="Use AdminVerifyDisabledCustomerGroupFieldTest instead"/> -</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index 111ba187c6f26..0a94bf8a52aaf 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -30,6 +30,6 @@ <argument name="importFile" value="importSpecChars.csv"/> </actionGroup> <see selector="{{AdminImportHeaderSection.messageNote}}" userInput='File must be saved in UTF-8 encoding for proper import' stepKey="seeNoteMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> </test> </tests> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml deleted file mode 100644 index 50407aff70cd5..0000000000000 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml +++ /dev/null @@ -1,18 +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="CliRunReindexUsingCronJobsActionGroup" deprecated="Use magentoCron instead"> - <annotations> - <description>Run cron 'index' group which reindex all invalidated indices.</description> - </annotations> - - <magentoCLI command="cron:run" arguments="--group='index'" stepKey="firstRunToScheduleJobs"/> - <magentoCLI command="cron:run" arguments="--group='index'" stepKey="secondRunToExecuteJobs"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml index 5f806aca004d4..fc1f92dba7ad6 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml @@ -55,7 +55,7 @@ <requiredEntity createDataKey="createSimpleProduct"/> </createData> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> <!-- Log in as a customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> <argument name="Customer" value="$customerWithDefaultAddress$"/> @@ -105,7 +105,7 @@ <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndicesAfterTest"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterTest"/> </after> <!-- 1. Ensure customer is a guest --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml index b0235fe32ca27..4283f42660f1f 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml @@ -97,7 +97,7 @@ <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- 1. Browse all product page and verify that the "Instant Purchase" button appears --> <!-- Virtual product --> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml index 312fa95bf77af..fd15b344da600 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontMyAccountWithMultishipmentTest"> + <test name="StorefrontMyAccountWithMultishipmentTest" deprecated="some"> <annotations> <features value="Multishipping"/> <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml index a2488d564001c..18e19c4276548 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml @@ -27,8 +27,14 @@ <requiredEntity createDataKey="createCategory"/> </createData> <!-- Create new customer --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!--Add shipping information--> <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml index 461304ef7aeaa..159f0c295a5b9 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml @@ -67,7 +67,7 @@ <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessage"/> <!--Open the details page of Simple Product 1, Simple Product 2 and add to cart, get to the category--> <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProductProductToCart"> @@ -144,7 +144,7 @@ <!--Sign out and check that widgets persist the information about the items--> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="logoutFromCustomerToCheckThatWidgetsPersist"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterLogoutFromCustomer"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterLogoutFromCustomer"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterLogoutFromCustomer"/> <seeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkLinkNotYouAfterLogoutFromCustomer"/> <actionGroup ref="StorefrontAssertProductInRecentlyViewedWidgetActionGroup" stepKey="checkSimpleProductInRecentlyViewedWidgetAfterLogout"> @@ -164,7 +164,7 @@ <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickLinkNotYou"/> <waitForPageLoad stepKey="waitForPageLoadAfterClickLinkNotYou"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterClickNotYou"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterClickLinkNotYou"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterClickLinkNotYou"/> <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyViewedProductsGrid}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeProductInRecentlyViewedWidget"/> <dontSee selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName($$createSimpleProduct.name$$)}}" stepKey="dontSeeProductInWishlistWidget"/> <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeProductInRecentlyComparedWidget"/> @@ -175,7 +175,7 @@ <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckWidgets"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterLogin"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterLogin"/> <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProductNameInWishlistSidebarAfterLogin"> <argument name="productVar" value="$$createSimpleProduct$$"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml index 54a6f7a16bb23..34152ea06745c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml index 7365f3b7a3425..34714e9637d46 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml index 81c30d197759d..a3e6331e31cf6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createCategory$$"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml index 2685e004ba1e0..e9f7f3ec6c70a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProductWeight200" stepKey="initialSimpleProduct"/> </before> <after> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml index 02280ed809124..7cca38e68d8d1 100644 --- a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml @@ -16,7 +16,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserTab"/> <waitForPageLoad stepKey="waitForUserRoleTabOpened"/> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='"Expiration date" must be later than the current date.' + <see selector="{{AdminMessagesSection.error}}" userInput='"Expiration date" must be later than the current date.' stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml index 130dacfbc2237..3fb798521fb45 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -36,9 +36,9 @@ <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <wait time="120" stepKey="waitForUserToExpire"/> - <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> - <argument name="adminUser" value="{$grabUsername}"/> - <argument name="adminPassword" value="{$grabPassword}"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml index 39e4a3b56ca13..5d12650351bc0 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -36,9 +36,9 @@ <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> - <argument name="adminUser" value="{$grabUsername}"/> - <argument name="adminPassword" value="{$grabPassword}"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> </actionGroup> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml index 1c251ea2b9ec2..c1a951afd87ec 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml @@ -40,9 +40,9 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Login as that user --> - <actionGroup ref="LoginAdminWithCredentialsActionGroup" stepKey="loginAsNewAdmin"> - <argument name="adminUser" value="{$grabUsername}"/> - <argument name="adminPassword" value="{$grabPassword}"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> </actionGroup> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> <wait time="120" stepKey="waitForUserToExpire"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml deleted file mode 100644 index 71341593ff036..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/GoToShipmentIntoOrderActionGroup.xml +++ /dev/null @@ -1,19 +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="goToShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Ship' button on the view Admin Order page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Shipment" stepKey="seePageNameNewInvoicePage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml deleted file mode 100644 index 10ef44b090a8b..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SeeProductInShipmentItemsActionGroup.xml +++ /dev/null @@ -1,20 +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="seeProductInShipmentItems"> - <annotations> - <description>Validates that the provided Product is present and correct on the view Admin Order Shipment page under the 'Items Shipped' section.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminShipmentItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml deleted file mode 100644 index c067a39e30f6c..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/SubmitShipmentIntoOrderActionGroup.xml +++ /dev/null @@ -1,19 +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="submitShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Submit Shipment' button on the view Admin Order Shipment page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml deleted file mode 100644 index 8d0d0e273e767..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/VerifyBasicShipmentInformationActionGroup.xml +++ /dev/null @@ -1,33 +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="verifyBasicShipmentInformation"> - <annotations> - <description>Validates that the provided Customer, Shipping Address, Billing Address and Customer Group are present and correct on the view Admin Order page.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue=""/> - <argument name="shippingAddress" defaultValue=""/> - <argument name="billingAddress" defaultValue=""/> - <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> - </arguments> - - <see selector="{{AdminShipmentOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> - <see selector="{{AdminShipmentOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> - <see selector="{{AdminShipmentOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml index d4b5bc9a9b50f..0a7b7852c38d0 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml index dc24a2f635b52..aa39340013142 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index 188300acc7015..929b3aade4022 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml index faa9d38a2d6fe..abb44165abd13 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml index aaac3e5ea08b6..d9cd4c8f9f177 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml index 8091f01e1f7ec..e541204a636e2 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create root category--> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml index a161abe767010..809210f3bc436 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStoreGroup"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml index 0207faf692f14..f2bfc7f7cea76 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index 767b452544714..5f2472a0c52ee 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml index c010935233a5b..ef4590eaff741 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml @@ -17,7 +17,7 @@ <group value="store"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml index a3afddd794723..349bcfe8f31e0 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store group--> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml index c4e3f51e6138f..305b4540668e4 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store view--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml index a94c1f8f66c7c..812fe6f2d4144 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create first store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml index 8d4e095d6ed87..09a33d5eb86a6 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create root category--> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml index f8df1059fb1ef..1c5d58c13538e 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store group--> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml index f2e9e12821595..c7c846c51af4d 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store view--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml index e9f72e46e3973..1c1c0ae30f6b1 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml index 6ac5f2a55f3cf..0fcf0c0a35033 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml @@ -31,7 +31,7 @@ <deleteData createDataKey="createTextSwatchConfigProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open the new simple product page --> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml index 377985a81af37..a1a488c2e4e37 100644 --- a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml @@ -25,7 +25,7 @@ <click selector="{{AdminTaxRulesSection.selectProductTaxClass(taxableGoodsTaxClass.class_name)}}" stepKey="unSelectTaxClass"/> <click selector="{{AdminTaxRulesSection.selectProductTaxClass(productTaxClassName)}}" stepKey="selectProductTaxClass"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the tax rule." stepKey="verifyRuleSaved"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the tax rule." stepKey="verifyRuleSaved"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml index 0990daf1ecfbf..01e1677ec8d8a 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -123,7 +123,7 @@ </after> <!--Open Created product. In Tax Class select new created Product Tax class.--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductForEdit"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductForEdit"> <argument name="productId" value="$createFirstProduct.id$"/> </actionGroup> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="$productTaxClass.class_name$" stepKey="selectTexClassForProduct"/> @@ -131,7 +131,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Open Created Second Product. In Tax Class select new created Product Tax class.--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openSecondProductForEdit"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openSecondProductForEdit"> <argument name="productId" value="$createSecondProduct.id$"/> </actionGroup> @@ -181,9 +181,9 @@ <actionGroup ref="SubmitInvoiceActionGroup" stepKey="clickSubmitInvoice"/> - <actionGroup ref="goToShipmentIntoOrder" stepKey="seeShipmentOrderPage"/> + <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="seeShipmentOrderPage"/> <!--Submit Shipment--> - <actionGroup ref="submitShipmentIntoOrder" stepKey="clickSubmitShipment"/> + <actionGroup ref="SubmitShipmentIntoOrderActionGroup" stepKey="clickSubmitShipment"/> <!--Go to "Reports" -> "Sales" -> "Tax"--> <amOnPage url="{{AdminSalesTaxReportPage.url}}" stepKey="navigateToReportsTaxPage"/> <waitForPageLoad stepKey="waitForReportsTaxPageLoad"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml index 609c8c7b27980..e43511318f137 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml @@ -43,8 +43,14 @@ <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml index 724831d9409be..3b92ab3498442 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml @@ -42,8 +42,14 @@ <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml index 3a67276c42737..18a1a11d35fd2 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml @@ -75,11 +75,17 @@ </after> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> <argument name="Address" value="US_Address_NY"/> </actionGroup> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml index 793468b7f0b1e..35a483da7f690 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml @@ -75,11 +75,17 @@ </after> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> <argument name="Address" value="US_Address_NY"/> </actionGroup> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml deleted file mode 100644 index d712c1a116068..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndCloseActionGroup.xml +++ /dev/null @@ -1,19 +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="AdminFormSaveAndClose"> - <annotations> - <description>Clicks on 'Save and Close'. Validates that the Success Message is present.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml deleted file mode 100644 index 51031a7225889..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/_Deprecated_ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml +++ /dev/null @@ -1,20 +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="AdminFormSaveAndDuplicate"> - <annotations> - <description>Clicks on 'Save and Duplicate'. Validates that the Success Message is present and correct.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminProductFormActionSection.saveAndDuplicate}}" stepKey="clickOnSaveAndDuplicate"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveSuccess" userInput="You saved the product."/> - <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertDuplicateSuccess" userInput="You duplicated the product."/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml index 3cc120ad98176..d38e065914617 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="defaultSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="rootCategory" /> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml index 5dcb97692d047..dbc5e4bd48e84 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml @@ -33,6 +33,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml index 1bb3e80aa06b7..b0335607d5455 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml @@ -35,6 +35,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml index 3a72c73f3e54a..f1dcf175455f3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml @@ -29,6 +29,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml index a5519e84b4c03..4d26f60cd48eb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml @@ -32,6 +32,6 @@ <waitForElementVisible selector="{{AdminUrlRewriteEditSection.okButton}}" stepKey="waitForOkButtonToVisible"/> <click selector="{{AdminUrlRewriteEditSection.okButton}}" stepKey="clickOnOkButton"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the URL rewrite." stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the URL rewrite." stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml index 69b5906c9f6e1..ed60081160ace 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml @@ -27,6 +27,6 @@ <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml index 386836fe2da69..c3e539db34108 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml @@ -26,6 +26,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue(redirectTypeValue)}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 82849755d78fc..6a1bcb38bdb31 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.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="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest"> + <test name="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest" deprecated="some"> <annotations> <features value="Url Rewrite"/> <stories value="Update url rewrites"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml index 653995da1a3a8..036d35d9c3258 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="rootCategory"/> </createData> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStore1"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml index d1034c7a5945f..10f81c9254b40 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -72,7 +72,8 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutFromAdminPanel"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsSaleRoleUser"> - <argument name="adminUser" value="AdminUserWithUpdatedUserRoleToSales"/> + <argument name="username" value="{{AdminUserWithUpdatedUserRoleToSales.username}}"/> + <argument name="password" value="{{AdminUserWithUpdatedUserRoleToSales.password}}"/> </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml deleted file mode 100644 index d6d6819b20bfa..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml +++ /dev/null @@ -1,25 +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="StorefrontCustomerCheckConfigurableProductInWishlist"> - <annotations> - <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List.</description> - </annotations> - <arguments> - <argument name="productVar"/> - <argument name="optionProductVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistProductPrice"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productVar.name)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> - </actionGroup> -</actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml deleted file mode 100644 index 4b22145f5130b..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/_Deprecated_ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml +++ /dev/null @@ -1,24 +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="StorefrontCustomerCheckConfigurableProductInWishlistSidebar"> - <annotations> - <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List sidebar.</description> - </annotations> - <arguments> - <argument name="productVar"/> - <argument name="optionProductVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> - <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> - </actionGroup> -</actionGroups> From c6958ca6926f295212ebb279e708035989fd4563 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 7 Apr 2020 10:55:18 +0300 Subject: [PATCH 705/734] MC-23733: Wrong image URL is returned if the product has no image --- .../DataProvider/Image/Placeholder.php | 22 +++++- .../DataProvider/Image/Placeholder/Theme.php | 71 ------------------- .../GraphQl/Catalog/MediaGalleryTest.php | 27 +++++++ 3 files changed, 46 insertions(+), 74 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php index f5cf2a9ef82ff..e7ce63784addf 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php @@ -8,7 +8,9 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; +use Magento\Framework\App\Area; use Magento\Framework\View\Asset\Repository as AssetRepository; +use Magento\Framework\View\DesignInterface; /** * Image Placeholder provider @@ -25,16 +27,24 @@ class Placeholder */ private $assetRepository; + /** + * @var DesignInterface + */ + private $themeDesign; + /** * @param PlaceholderFactory $placeholderFactory * @param AssetRepository $assetRepository + * @param DesignInterface $themeDesign */ public function __construct( PlaceholderFactory $placeholderFactory, - AssetRepository $assetRepository + AssetRepository $assetRepository, + DesignInterface $themeDesign ) { $this->placeholderFactory = $placeholderFactory; $this->assetRepository = $assetRepository; + $this->themeDesign = $themeDesign; } /** @@ -52,8 +62,14 @@ public function getPlaceholder(string $imageType): string return $imageAsset->getUrl(); } - return $this->assetRepository->getUrl( - "Magento_Catalog::images/product/placeholder/{$imageType}.jpg" + $params = [ + 'area' => Area::AREA_FRONTEND, + 'themeId' => $this->themeDesign->getConfigurationDesignTheme(Area::AREA_FRONTEND), + ]; + + return $this->assetRepository->getUrlWithParams( + "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", + $params ); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php deleted file mode 100644 index dc48c5ef69346..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\View\Design\Theme\ThemeProviderInterface; -use Magento\Store\Model\StoreManagerInterface; - -/** - * Theme provider - */ -class Theme -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var ThemeProviderInterface - */ - private $themeProvider; - - /** - * @param ScopeConfigInterface $scopeConfig - * @param StoreManagerInterface $storeManager - * @param ThemeProviderInterface $themeProvider - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - StoreManagerInterface $storeManager, - ThemeProviderInterface $themeProvider - ) { - $this->scopeConfig = $scopeConfig; - $this->storeManager = $storeManager; - $this->themeProvider = $themeProvider; - } - - /** - * Get theme model - * - * @return array - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function getThemeData(): array - { - $themeId = $this->scopeConfig->getValue( - \Magento\Framework\View\DesignInterface::XML_PATH_THEME_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $this->storeManager->getStore()->getId() - ); - - /** @var $theme \Magento\Framework\View\Design\ThemeInterface */ - $theme = $this->themeProvider->getThemeById($themeId); - - $data = $theme->getData(); - $data['themeModel'] = $theme; - - return $data; - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php index 86d36c1c767f7..615438d52e764 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php @@ -38,6 +38,33 @@ public function testProductSmallImageUrlWithExistingImage() self::assertTrue($this->checkImageExists($response['products']['items'][0]['small_image']['url'])); } + /** + * Test for get product image placeholder + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testProductSmallImageUrlPlaceholder() + { + $productSku = 'simple'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) { + items { + small_image { + url + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $responseImage = $response['products']['items'][0]['small_image']; + + self::assertArrayHasKey('url', $responseImage); + self::assertContains('placeholder/small_image.jpg', $responseImage['url']); + self::assertTrue($this->checkImageExists($responseImage['url'])); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/product_with_multiple_images.php */ From 1f4166bc94bc3143e30e1f66243391ccd8434f1f Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Tue, 7 Apr 2020 13:49:50 +0300 Subject: [PATCH 706/734] MC-31544: Email Template New Pickup Order or add New Template order items are missing in Mail --- .../Block/Order/Email/Creditmemo/Items.php | 87 +++++++++++++++ .../Sales/Block/Order/Email/Invoice/Items.php | 87 +++++++++++++++ .../Magento/Sales/Block/Order/Email/Items.php | 51 +++++++++ .../Block/Order/Email/Shipment/Items.php | 87 +++++++++++++++ .../Order/Email/Sender/CreditmemoSender.php | 4 +- .../Order/Email/Sender/InvoiceSender.php | 4 +- .../Model/Order/Email/Sender/OrderSender.php | 4 +- .../Order/Email/Sender/ShipmentSender.php | 4 +- .../Order/Shipment/Sender/EmailSender.php | 2 + .../Order/Email/Sender/AbstractSenderTest.php | 2 +- .../Email/Sender/CreditmemoSenderTest.php | 13 +++ .../Order/Email/Sender/InvoiceSenderTest.php | 14 +++ .../Order/Email/Sender/OrderSenderTest.php | 7 ++ .../Order/Email/Sender/ShipmentSenderTest.php | 14 +++ .../Order/Shipment/Sender/EmailSenderTest.php | 103 ++++++++++++------ .../view/frontend/email/creditmemo_new.html | 4 +- .../frontend/email/creditmemo_new_guest.html | 6 +- .../view/frontend/email/invoice_new.html | 4 +- .../frontend/email/invoice_new_guest.html | 4 +- .../Sales/view/frontend/email/order_new.html | 3 +- .../view/frontend/email/order_new_guest.html | 3 +- .../view/frontend/email/shipment_new.html | 8 +- .../frontend/email/shipment_new_guest.html | 8 +- .../sales_email_order_shipment_track.xml | 4 +- .../Magento_Sales/email/creditmemo_new.html | 4 +- .../email/creditmemo_new_guest.html | 4 +- .../luma/Magento_Sales/email/invoice_new.html | 4 +- .../email/invoice_new_guest.html | 4 +- .../luma/Magento_Sales/email/order_new.html | 3 +- .../Magento_Sales/email/order_new_guest.html | 3 +- .../Magento_Sales/email/shipment_new.html | 8 +- .../email/shipment_new_guest.html | 6 +- 32 files changed, 496 insertions(+), 67 deletions(-) diff --git a/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php b/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php index 0b691eff5757b..0a1e87e5e0a27 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php @@ -5,6 +5,13 @@ */ namespace Magento\Sales\Block\Order\Email\Creditmemo; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\CreditmemoRepositoryInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * Sales Order Email creditmemo items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CreditmemoRepositoryInterface + */ + private $creditmemoRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param CreditmemoRepositoryInterface|null $creditmemoRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?CreditmemoRepositoryInterface $creditmemoRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->creditmemoRepository = + $creditmemoRepository ?: ObjectManager::getInstance()->get(CreditmemoRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getCreditmemo()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns creditmemo. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So creditmemo is loaded by creditmemo_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return CreditmemoInterface|null + */ + public function getCreditmemo() + { + $creditmemo = $this->getData('creditmemo'); + if ($creditmemo !== null) { + return $creditmemo; + } + + $creditmemoId = (int)$this->getData('creditmemo_id'); + if ($creditmemoId) { + $creditmemo = $this->creditmemoRepository->get($creditmemoId); + $this->setData('creditmemo', $creditmemo); + } + + return $this->getData('creditmemo'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php b/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php index bc7756816d32a..cc2b197ab0eb2 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php @@ -6,6 +6,13 @@ namespace Magento\Sales\Block\Order\Email\Invoice; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * Sales Order Email Invoice items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var InvoiceRepositoryInterface + */ + private $invoiceRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param InvoiceRepositoryInterface|null $invoiceRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?InvoiceRepositoryInterface $invoiceRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->invoiceRepository = + $invoiceRepository ?: ObjectManager::getInstance()->get(InvoiceRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getInvoice()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns invoice. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So invoice is loaded by invoice_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return InvoiceInterface|null + */ + public function getInvoice() + { + $invoice = $this->getData('invoice'); + if ($invoice !== null) { + return $invoice; + } + + $invoiceId = (int)$this->getData('invoice_id'); + if ($invoiceId) { + $invoice = $this->invoiceRepository->get($invoiceId); + $this->setData('invoice', $invoice); + } + + return $this->getData('invoice'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Items.php b/app/code/Magento/Sales/Block/Order/Email/Items.php index ddce387b91068..e11981285f04f 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Items.php @@ -11,10 +11,61 @@ */ namespace Magento\Sales\Block\Order\Email; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** + * Sales Order Email items. + * * @api * @since 100.0.2 */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null + ) { + $this->orderRepository = $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + + parent::__construct($context, $data); + } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + + if ($order !== null) { + return $order; + } + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php b/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php index a4c9a7b80a00d..1f9b353180fd9 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php @@ -6,6 +6,13 @@ namespace Magento\Sales\Block\Order\Email\Shipment; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\ShipmentRepositoryInterface; + /** * Sales Order Email Shipment items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var ShipmentRepositoryInterface + */ + private $shipmentRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param ShipmentRepositoryInterface|null $creditmemoRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?ShipmentRepositoryInterface $creditmemoRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->shipmentRepository = + $creditmemoRepository ?: ObjectManager::getInstance()->get(ShipmentRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getShipment()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns shipment. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So shipment is loaded by shipment_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return ShipmentInterface|null + */ + public function getShipment() + { + $shipment = $this->getData('shipment'); + if ($shipment !== null) { + return $shipment; + } + + $shipmentId = (int)$this->getData('shipment_id'); + if ($shipmentId) { + $shipment = $this->shipmentRepository->get($shipmentId); + $this->setData('shipment', $shipment); + } + + return $this->getData('shipment'); + } } diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php index e6d528fb93a34..c27afe9fb5b0d 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php @@ -17,7 +17,7 @@ use Magento\Framework\DataObject; /** - * Class CreditmemoSender + * Sends order creditmemo email to the customer. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -108,7 +108,9 @@ public function send(Creditmemo $creditmemo, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'creditmemo' => $creditmemo, + 'creditmemo_id' => $creditmemo->getId(), 'comment' => $creditmemo->getCustomerNoteNotify() ? $creditmemo->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php index 79133af6d6fb8..05164d1b7b5f3 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php @@ -17,7 +17,7 @@ use Magento\Framework\DataObject; /** - * Class InvoiceSender + * Sends order invoice email to the customer. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -108,7 +108,9 @@ public function send(Invoice $invoice, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'invoice' => $invoice, + 'invoice_id' => $invoice->getId(), 'comment' => $invoice->getCustomerNoteNotify() ? $invoice->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php index c67804475cd65..a2d61c3b2d31d 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php @@ -16,7 +16,8 @@ use Magento\Framework\DataObject; /** - * Class OrderSender + * Sends order email to the customer. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OrderSender extends Sender @@ -125,6 +126,7 @@ protected function prepareTemplate(Order $order) { $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), 'store' => $order->getStore(), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php index c6b40800d5160..4c8e1744ac0e0 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php @@ -17,7 +17,7 @@ use Magento\Framework\DataObject; /** - * Class for shipment email notification sender + * Sends order shipment email to the customer. * * @deprecated since this class works only with the concrete model and no data interface * @see \Magento\Sales\Model\Order\Shipment\Sender\EmailSender @@ -110,7 +110,9 @@ public function send(Shipment $shipment, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'shipment' => $shipment, + 'shipment_id' => $shipment->getId(), 'comment' => $shipment->getCustomerNoteNotify() ? $shipment->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php index 1d4418c50047d..fe68555d9f7c7 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php @@ -104,7 +104,9 @@ public function send( $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'shipment' => $shipment, + 'shipment_id' => $shipment->getId(), 'comment' => $comment ? $comment->getComment() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php index 2f4e0e927db2c..2864f846f5a1d 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php @@ -95,7 +95,7 @@ public function stepMockSetup() $this->orderMock = $this->createPartialMock( \Magento\Sales\Model\Order::class, [ - 'getStore', 'getBillingAddress', 'getPayment', + 'getId', 'getStore', 'getBillingAddress', 'getPayment', '__wakeup', 'getCustomerIsGuest', 'getCustomerName', 'getCustomerEmail', 'getShippingAddress', 'setSendEmail', 'setEmailSent', 'getCreatedAtFormatted', 'getIsNotVirtual', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php index 72a51a15db592..8162f89c3e281 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php @@ -12,6 +12,10 @@ */ class CreditmemoSenderTest extends AbstractSenderTest { + private const CREDITMEMO_ID = 1; + + private const ORDER_ID = 1; + /** * @var \Magento\Sales\Model\Order\Email\Sender\CreditmemoSender */ @@ -40,6 +44,7 @@ protected function setUp() \Magento\Sales\Model\Order\Creditmemo::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -54,6 +59,10 @@ protected function setUp() $this->creditmemoMock->expects($this->any()) ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->creditmemoMock->method('getId') + ->willReturn(self::CREDITMEMO_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); $this->identityContainerMock = $this->createPartialMock( \Magento\Sales\Model\Order\Email\Container\CreditmemoIdentity::class, @@ -142,7 +151,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'creditmemo' => $this->creditmemoMock, + 'creditmemo_id' => self::CREDITMEMO_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -285,7 +296,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'creditmemo' => $this->creditmemoMock, + 'creditmemo_id' => self::CREDITMEMO_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php index 00a1855055a84..ef196ef229c2a 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php @@ -12,6 +12,10 @@ */ class InvoiceSenderTest extends AbstractSenderTest { + private const INVOICE_ID = 1; + + private const ORDER_ID = 1; + /** * @var \Magento\Sales\Model\Order\Email\Sender\InvoiceSender */ @@ -40,6 +44,7 @@ protected function setUp() \Magento\Sales\Model\Order\Invoice::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -55,6 +60,11 @@ protected function setUp() ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->invoiceMock->method('getId') + ->willReturn(self::INVOICE_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->identityContainerMock = $this->createPartialMock( \Magento\Sales\Model\Order\Email\Container\InvoiceIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] @@ -148,7 +158,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'invoice' => $this->invoiceMock, + 'invoice_id' => self::INVOICE_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -287,7 +299,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'invoice' => $this->invoiceMock, + 'invoice_id' => self::INVOICE_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php index a033e41dd8e8b..9590602187f1f 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php @@ -9,6 +9,8 @@ class OrderSenderTest extends AbstractSenderTest { + private const ORDER_ID = 1; + /** * @var \Magento\Sales\Model\Order\Email\Sender\OrderSender */ @@ -36,6 +38,9 @@ protected function setUp() ->method('getStore') ->will($this->returnValue($this->storeMock)); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->sender = new OrderSender( $this->templateContainerMock, $this->identityContainerMock, @@ -127,6 +132,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'billing' => $addressMock, 'payment_html' => 'payment', 'store' => $this->storeMock, @@ -295,6 +301,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'billing' => $addressMock, 'payment_html' => 'payment', 'store' => $this->storeMock, diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index dcd80646b168c..64dd9285ddf88 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -15,6 +15,10 @@ */ class ShipmentSenderTest extends AbstractSenderTest { + private const SHIPMENT_ID = 1; + + private const ORDER_ID = 1; + /** * @var \Magento\Sales\Model\Order\Email\Sender\ShipmentSender */ @@ -43,6 +47,7 @@ protected function setUp() \Magento\Sales\Model\Order\Shipment::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -58,6 +63,11 @@ protected function setUp() ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->shipmentMock->method('getId') + ->willReturn(self::SHIPMENT_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->identityContainerMock = $this->createPartialMock( \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] @@ -151,7 +161,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -291,7 +303,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php index 2262fbf03c1a1..9c080d422b0d1 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php @@ -5,96 +5,119 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Shipment\Sender; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Payment\Helper\Data; +use Magento\Payment\Model\Info; +use Magento\Sales\Api\Data\ShipmentCommentCreationInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Address\Renderer; +use Magento\Sales\Model\Order\Email\Container\ShipmentIdentity; +use Magento\Sales\Model\Order\Email\Container\Template; +use Magento\Sales\Model\Order\Email\Sender; +use Magento\Sales\Model\Order\Email\SenderBuilderFactory; +use Magento\Sales\Model\Order\Shipment\Sender\EmailSender; +use Magento\Sales\Model\ResourceModel\Order\Shipment; +use Magento\Store\Model\Store; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + /** * Unit test for email notification sender for Shipment. * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class EmailSenderTest extends \PHPUnit\Framework\TestCase +class EmailSenderTest extends TestCase { + private const SHIPMENT_ID = 1; + + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Shipment\Sender\EmailSender + * @var EmailSender */ private $subject; /** - * @var \Magento\Sales\Model\Order|\PHPUnit_Framework_MockObject_MockObject + * @var Order|\PHPUnit_Framework_MockObject_MockObject */ private $orderMock; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var Store|\PHPUnit_Framework_MockObject_MockObject */ private $storeMock; /** - * @var \Magento\Sales\Model\Order\Email\Sender|\PHPUnit_Framework_MockObject_MockObject + * @var Sender|\PHPUnit_Framework_MockObject_MockObject */ private $senderMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ private $loggerMock; /** - * @var \Magento\Sales\Api\Data\ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject */ private $shipmentMock; /** - * @var \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject */ private $commentMock; /** - * @var \Magento\Sales\Model\Order\Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|\PHPUnit_Framework_MockObject_MockObject */ private $addressMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ private $globalConfigMock; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ private $eventManagerMock; /** - * @var \Magento\Payment\Model\Info|\PHPUnit_Framework_MockObject_MockObject + * @var Info|\PHPUnit_Framework_MockObject_MockObject */ private $paymentInfoMock; /** - * @var \Magento\Payment\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|\PHPUnit_Framework_MockObject_MockObject */ private $paymentHelperMock; /** - * @var \Magento\Sales\Model\ResourceModel\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject + * @var Shipment|\PHPUnit_Framework_MockObject_MockObject */ private $shipmentResourceMock; /** - * @var \Magento\Sales\Model\Order\Address\Renderer|\PHPUnit_Framework_MockObject_MockObject + * @var Renderer|\PHPUnit_Framework_MockObject_MockObject */ private $addressRendererMock; /** - * @var \Magento\Sales\Model\Order\Email\Container\Template|\PHPUnit_Framework_MockObject_MockObject + * @var Template|\PHPUnit_Framework_MockObject_MockObject */ private $templateContainerMock; /** - * @var \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentIdentity|\PHPUnit_Framework_MockObject_MockObject */ private $identityContainerMock; /** - * @var \Magento\Sales\Model\Order\Email\SenderBuilderFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SenderBuilderFactory|\PHPUnit_Framework_MockObject_MockObject */ private $senderBuilderFactoryMock; @@ -103,11 +126,11 @@ class EmailSenderTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + $this->orderMock = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $this->storeMock = $this->getMockBuilder(Store::class) ->setMethods(['getStoreId']) ->disableOriginalConstructor() ->getMock(); @@ -119,21 +142,21 @@ protected function setUp() ->method('getStore') ->willReturn($this->storeMock); - $this->senderMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Sender::class) + $this->senderMock = $this->getMockBuilder(Sender::class) ->disableOriginalConstructor() ->setMethods(['send', 'sendCopyTo']) ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment::class) + $this->shipmentMock = $this->getMockBuilder(Order\Shipment::class) ->disableOriginalConstructor() - ->setMethods(['setSendEmail', 'setEmailSent']) + ->setMethods(['setSendEmail', 'setEmailSent', 'getId']) ->getMock(); - $this->commentMock = $this->getMockBuilder(\Magento\Sales\Api\Data\ShipmentCommentCreationInterface::class) + $this->commentMock = $this->getMockBuilder(ShipmentCommentCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -141,7 +164,7 @@ protected function setUp() ->method('getComment') ->willReturn('Comment text'); - $this->addressMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address::class) + $this->addressMock = $this->getMockBuilder(Address::class) ->disableOriginalConstructor() ->getMock(); @@ -151,16 +174,19 @@ protected function setUp() $this->orderMock->expects($this->any()) ->method('getShippingAddress') ->willReturn($this->addressMock); + $this->orderMock->expects($this->any()) + ->method('getId') + ->willReturn(self::ORDER_ID); - $this->globalConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->globalConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) + $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->paymentInfoMock = $this->getMockBuilder(\Magento\Payment\Model\Info::class) + $this->paymentInfoMock = $this->getMockBuilder(Info::class) ->disableOriginalConstructor() ->getMock(); @@ -168,7 +194,7 @@ protected function setUp() ->method('getPayment') ->willReturn($this->paymentInfoMock); - $this->paymentHelperMock = $this->getMockBuilder(\Magento\Payment\Helper\Data::class) + $this->paymentHelperMock = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); @@ -177,11 +203,11 @@ protected function setUp() ->with($this->paymentInfoMock, 1) ->willReturn('Payment Info Block'); - $this->shipmentResourceMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Shipment::class) + $this->shipmentResourceMock = $this->getMockBuilder(Shipment::class) ->disableOriginalConstructor() ->getMock(); - $this->addressRendererMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address\Renderer::class) + $this->addressRendererMock = $this->getMockBuilder(Renderer::class) ->disableOriginalConstructor() ->getMock(); @@ -190,12 +216,12 @@ protected function setUp() ->with($this->addressMock, 'html') ->willReturn('Formatted address'); - $this->templateContainerMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Container\Template::class) + $this->templateContainerMock = $this->getMockBuilder(Template::class) ->disableOriginalConstructor() ->getMock(); $this->identityContainerMock = $this->getMockBuilder( - \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class + ShipmentIdentity::class ) ->disableOriginalConstructor() ->getMock(); @@ -205,13 +231,13 @@ protected function setUp() ->willReturn($this->storeMock); $this->senderBuilderFactoryMock = $this->getMockBuilder( - \Magento\Sales\Model\Order\Email\SenderBuilderFactory::class + SenderBuilderFactory::class ) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->subject = new \Magento\Sales\Model\Order\Shipment\Sender\EmailSender( + $this->subject = new EmailSender( $this->templateContainerMock, $this->identityContainerMock, $this->senderBuilderFactoryMock, @@ -247,6 +273,9 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending $this->commentMock = null; } + $this->shipmentMock->expects($this->any()) + ->method('getId') + ->willReturn(self::SHIPMENT_ID); $this->shipmentMock->expects($this->once()) ->method('setSendEmail') ->with($emailSendingResult); @@ -254,7 +283,9 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending if (!$configValue || $forceSyncMode) { $transport = [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => $isComment ? 'Comment text' : '', 'billing' => $this->addressMock, 'payment_html' => 'Payment Info Block', diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html index 5ae6f5f9d82c7..f475503528dc9 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html @@ -22,6 +22,8 @@ "var store_hours":"Store Hours", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html index 657de2aae2045..d8a8a0baeca98 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html @@ -21,7 +21,9 @@ "var store_hours":"Store Hours", "var creditmemo":"Credit Memo", "var order":"Order", -"var order_data.is_not_virtual":"Order Type" +"var order_data.is_not_virtual":"Order Type", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -80,7 +82,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new.html b/app/code/Magento/Sales/view/frontend/email/invoice_new.html index 68773ee9d7570..c4818172449a2 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new.html @@ -22,6 +22,8 @@ "var store_hours":"Store Hours", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout area="frontend" handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout area="frontend" handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html index 5053ccc2ac635..c06630fd249ab 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html @@ -21,6 +21,8 @@ "var store_hours":"Store Hours", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -80,7 +82,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/order_new.html b/app/code/Magento/Sales/view/frontend/email/order_new.html index 13c436b131b82..a411e8d5b29b3 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new.html @@ -22,6 +22,7 @@ "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", "var order_data.is_not_virtual":"Order Type", "var order":"Order", +"var order_id": "Order DB Id", "var order_data.customer_name":"Customer Name" } @--> @@ -90,7 +91,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order area="frontend"}} + {{layout handle="sales_email_order_items" order_id=$order_id area="frontend"}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html index 866a1ad87f9b1..dc3a8e9f69aca 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html @@ -21,6 +21,7 @@ "var store_email":"Store Email", "var store_hours":"Store Hours", "var order_data.is_not_virtual":"Order Type", +"var order_id": "Order DB Id", "var order":"Order" } @--> {{template config_path="design/email/header_template"}} @@ -85,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order}} + {{layout handle="sales_email_order_items" order_id=$order_id }} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new.html b/app/code/Magento/Sales/view/frontend/email/shipment_new.html index 39823a0c9d80b..39397979d2aaa 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new.html @@ -23,7 +23,9 @@ "var store_hours":"Store Hours", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -59,7 +61,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -86,7 +88,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html index ed2f52ed85066..54c7f08506497 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html @@ -22,7 +22,9 @@ "var store_hours":"Store Hours", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -57,7 +59,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -84,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml index bbc7f04ce94fd..489317cfa65c7 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml @@ -8,10 +8,10 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="sales_email_order_shipment_renderers"/> <body> - <block class="Magento\Framework\View\Element\Template" name="sales.order.email.shipment.track" template="Magento_Sales::email/shipment/track.phtml"> + <block class="Magento\Sales\Block\Order\Email\Shipment\Items" name="sales.order.email.shipment.track" template="Magento_Sales::email/shipment/track.phtml"> <arguments> <argument name="tracking_url" xsi:type="object">Magento\Sales\Block\DataProviders\Email\Shipment\TrackingUrl</argument> </arguments> </block> </body> -</page> \ No newline at end of file +</page> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html index 86e3cf01e965e..f8e192bafdf29 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html @@ -20,6 +20,8 @@ "var store_email":"Store Email", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -79,7 +81,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html index d0310a8e2c7b6..4442c172a08e5 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html @@ -19,6 +19,8 @@ "var store_email":"Store Email", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -77,7 +79,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html index 636fa9ac5f425..c46f0b03a53f7 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html @@ -20,6 +20,8 @@ "var store_email":"Store Email", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -79,7 +81,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout area="frontend" handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout area="frontend" handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html index 7df5ffe5f4ab8..6e35fd2609dff 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html @@ -19,6 +19,8 @@ "var store_email":"Store Email", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -77,7 +79,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html index 745bf5c9c2eff..373db99d87d99 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html @@ -20,6 +20,7 @@ "var order":"Order", "var order_data.is_not_virtual":"Order Type", "var order_data.customer_name":"Customer Name", +"var order_id": "Order DB Id", "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> @@ -85,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order area="frontend"}} + {{layout handle="sales_email_order_items" order_id=$order_id area="frontend"}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html index 907be4d45a6c5..024f6daf76ace 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html @@ -19,6 +19,7 @@ "var store.frontend_name":"Store Frontend Name", "var store_email":"Store Email", "var order":"Order", +"var order_id": "Order DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +83,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order}} + {{layout handle="sales_email_order_items" order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html index 4ff9da3a31b27..d1b1e1e33763c 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html @@ -21,7 +21,9 @@ "var store_email":"Store Email", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -55,7 +57,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html index ac7eaae6b7ff7..18684fb052b4e 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html @@ -21,6 +21,8 @@ "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", "var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id", "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> {{template config_path="design/email/header_template"}} @@ -54,7 +56,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -81,7 +83,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> From 4bb9242b5ef15fb53e34c8490645d9ae3e1e2857 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 7 Apr 2020 14:39:40 +0300 Subject: [PATCH 707/734] MC-32411: Linking a simple product to a configurable product does not rollback when the API request fails. --- .../Model/Plugin/ProductRepositorySave.php | 62 ++++++++---- .../Plugin/ProductRepositorySaveTest.php | 98 +++++++++---------- .../Api/ProductRepositoryTest.php | 45 ++++++--- 3 files changed, 122 insertions(+), 83 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php index 54b6e42ff7678..ed2dd9fd536b1 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php @@ -5,15 +5,18 @@ */ namespace Magento\ConfigurableProduct\Model\Plugin; -use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ProductRepository; use Magento\Framework\Exception\InputException; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Exception\CouldNotSaveException; use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\Exception\NoSuchEntityException; +/** + * Plugin to validate product links of configurable product and reset configurable attributes + */ class ProductRepositorySave { /** @@ -22,46 +25,45 @@ class ProductRepositorySave private $productAttributeRepository; /** - * @var ProductFactory + * @var ProductRepositoryInterface */ - private $productFactory; + private $productRepository; /** * @param ProductAttributeRepositoryInterface $productAttributeRepository - * @param ProductFactory $productFactory + * @param ProductRepositoryInterface $productRepository */ public function __construct( ProductAttributeRepositoryInterface $productAttributeRepository, - ProductFactory $productFactory + ProductRepositoryInterface $productRepository ) { $this->productAttributeRepository = $productAttributeRepository; - $this->productFactory = $productFactory; + $this->productRepository = $productRepository; } /** - * Validate product links and reset configurable attributes to configurable product + * Validate product links of configurable product * * @param ProductRepositoryInterface $subject - * @param ProductInterface $result * @param ProductInterface $product * @param bool $saveOptions - * @return ProductInterface - * @throws CouldNotSaveException + * @return array * @throws InputException + * @throws NoSuchEntityException * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave( + public function beforeSave( ProductRepositoryInterface $subject, - ProductInterface $result, ProductInterface $product, $saveOptions = false ) { + $result[] = $product; if ($product->getTypeId() !== Configurable::TYPE_CODE) { return $result; } - $extensionAttributes = $result->getExtensionAttributes(); + $extensionAttributes = $product->getExtensionAttributes(); if ($extensionAttributes === null) { return $result; } @@ -81,23 +83,49 @@ public function afterSave( $attributeCodes[] = $attributeCode; } $this->validateProductLinks($attributeCodes, $configurableLinks); + + return $result; + } + + /** + * Reset configurable attributes to configurable product + * + * @param ProductRepositoryInterface $subject + * @param ProductInterface $result + * @param ProductInterface $product + * @param bool $saveOptions + * @return ProductInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave( + ProductRepositoryInterface $subject, + ProductInterface $result, + ProductInterface $product, + $saveOptions = false + ) { + if ($product->getTypeId() !== Configurable::TYPE_CODE) { + return $result; + } $result->getTypeInstance()->resetConfigurableAttributes($product); return $result; } /** + * Validate product links + * * @param array $attributeCodes * @param array $linkIds - * @return $this + * @return void * @throws InputException + * @throws NoSuchEntityException */ private function validateProductLinks(array $attributeCodes, array $linkIds) { $valueMap = []; - foreach ($linkIds as $productId) { - $variation = $this->productFactory->create()->load($productId); + $variation = $this->productRepository->getById($productId); $valueKey = ''; foreach ($attributeCodes as $attributeCode) { if (!$variation->getData($attributeCode)) { diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php index 1c3e421ae924f..ea8bb065a7786 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php @@ -9,30 +9,26 @@ use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ProductFactory; use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\ConfigurableProduct\Model\Plugin\ProductRepositorySave; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class ProductRepositorySaveTest + * Test for ProductRepositorySave plugin + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductRepositorySaveTest extends \PHPUnit\Framework\TestCase +class ProductRepositorySaveTest extends TestCase { /** * @var ProductAttributeRepositoryInterface|MockObject */ private $productAttributeRepository; - /** - * @var ProductFactory|MockObject - */ - private $productFactory; - /** * @var Product|MockObject */ @@ -68,15 +64,13 @@ class ProductRepositorySaveTest extends \PHPUnit\Framework\TestCase */ private $plugin; + /** + * @inheritdoc + */ protected function setUp() { $this->productAttributeRepository = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class); - $this->productFactory = $this->getMockBuilder(ProductFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getTypeId', 'getExtensionAttributes']) @@ -102,12 +96,15 @@ protected function setUp() ProductRepositorySave::class, [ 'productAttributeRepository' => $this->productAttributeRepository, - 'productFactory' => $this->productFactory + 'productRepository' => $this->productRepository ] ); } - public function testAfterSaveWhenProductIsSimple() + /** + * Validating the result after saving a configurable product + */ + public function testBeforeSaveWhenProductIsSimple() { $this->product->expects(static::once()) ->method('getTypeId') @@ -116,18 +113,21 @@ public function testAfterSaveWhenProductIsSimple() ->method('getExtensionAttributes'); $this->assertEquals( - $this->result, - $this->plugin->afterSave($this->productRepository, $this->result, $this->product) + $this->product, + $this->plugin->beforeSave($this->productRepository, $this->product)[0] ); } - public function testAfterSaveWithoutOptions() + /** + * Test saving a configurable product without attribute options + */ + public function testBeforeSaveWithoutOptions() { $this->product->expects(static::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); @@ -142,23 +142,25 @@ public function testAfterSaveWithoutOptions() ->method('get'); $this->assertEquals( - $this->result, - $this->plugin->afterSave($this->productRepository, $this->result, $this->product) + $this->product, + $this->plugin->beforeSave($this->productRepository, $this->product)[0] ); } /** + * Test saving a configurable product with same set of attribute values + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAfterSaveWithLinks() + public function testBeforeSaveWithLinks() { $links = [4, 5]; $this->product->expects(static::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -173,27 +175,26 @@ public function testAfterSaveWithLinks() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::exactly(2)) - ->method('create') + $this->productRepository->expects(static::exactly(2)) + ->method('getById') ->willReturn($product); - $product->expects(static::exactly(2)) - ->method('load') - ->willReturnSelf(); $product->expects(static::never()) ->method('getData'); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } /** + * Test saving a configurable product with missing attribute + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Product with id "4" does not contain required attribute "color". */ - public function testAfterSaveWithLinksWithMissingAttribute() + public function testBeforeSaveWithLinksWithMissingAttribute() { $simpleProductId = 4; $links = [$simpleProductId, 5]; @@ -208,7 +209,7 @@ public function testAfterSaveWithLinksWithMissingAttribute() ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -228,29 +229,28 @@ public function testAfterSaveWithLinksWithMissingAttribute() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::once()) - ->method('create') + $this->productRepository->expects(static::once()) + ->method('getById') ->willReturn($product); - $product->expects(static::once()) - ->method('load') - ->with($simpleProductId) - ->willReturnSelf(); + $product->expects(static::once()) ->method('getData') ->with($attributeCode) ->willReturn(false); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } /** + * Test saving a configurable product with duplicate attributes + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAfterSaveWithLinksWithDuplicateAttributes() + public function testBeforeSaveWithLinksWithDuplicateAttributes() { $links = [4, 5]; $attributeCode = 'color'; @@ -264,7 +264,7 @@ public function testAfterSaveWithLinksWithDuplicateAttributes() ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -284,20 +284,18 @@ public function testAfterSaveWithLinksWithDuplicateAttributes() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::exactly(2)) - ->method('create') + $this->productRepository->expects(static::exactly(2)) + ->method('getById') ->willReturn($product); - $product->expects(static::exactly(2)) - ->method('load') - ->willReturnSelf(); + $product->expects(static::exactly(4)) ->method('getData') ->with($attributeCode) ->willReturn($attributeId); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } } diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php index 1dc7ca1ad44a6..c8ecab9ce54d8 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\ConfigurableProduct\Api; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Entity\Attribute; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection; use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Webapi\Rest\Request; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -22,31 +26,31 @@ class ProductRepositoryTest extends WebapiAbstract const CONFIGURABLE_PRODUCT_SKU = 'configurable-product-sku'; /** - * @var \Magento\Eav\Model\Config + * @var Config */ protected $eavConfig; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $objectManager; /** - * @var \Magento\Catalog\Model\Entity\Attribute + * @var Attribute */ protected $configurableAttribute; /** - * Execute per test initialization + * @inheritdoc */ public function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->eavConfig = $this->objectManager->get(\Magento\Eav\Model\Config::class); + $this->eavConfig = $this->objectManager->get(Config::class); } /** - * Execute per test cleanup + * @inheritdoc */ public function tearDown() { @@ -54,16 +58,26 @@ public function tearDown() parent::tearDown(); } + /** + * Retrieve configurable attribute options + * + * @return array + */ protected function getConfigurableAttributeOptions() { - /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $optionCollection */ + /** @var Collection $optionCollection */ $optionCollection = $this->objectManager->create( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class + Collection::class ); $options = $optionCollection->setAttributeFilter($this->configurableAttribute->getId())->getData(); return $options; } + /** + * Create configurable product by web api + * + * @return array + */ protected function createConfigurableProduct() { $productId1 = 10; @@ -254,7 +268,6 @@ public function testUpdateConfigurableProductLinks() $this->assertEquals([$productId1], $resultConfigurableProductLinks); //adding back the product links, the option value should be restored - unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1, $productId2]; //set the value for required attribute @@ -286,7 +299,7 @@ public function testUpdateConfigurableProductLinksWithNonExistingProduct() $productId1, $nonExistingId ]; - $expectedMessage = 'The product was unable to be saved. Please try again.'; + $expectedMessage = 'The product that was requested doesn\'t exist. Verify the product and try again.'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -362,7 +375,7 @@ public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes $productId1, $productId2 ]; - $expectedMessage = 'The product was unable to be saved. Please try again.'; + $expectedMessage = 'The product that was requested doesn\'t exist. Verify the product and try again.'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -389,7 +402,7 @@ protected function getProduct($productSku) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -415,7 +428,7 @@ protected function createProduct($product) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + 'httpMethod' => Request::HTTP_METHOD_POST ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -440,7 +453,7 @@ protected function deleteProductBySku($productSku) $serviceInfo = [ 'rest' => [ 'resourcePath' => $resourcePath, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + 'httpMethod' => Request::HTTP_METHOD_DELETE ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -475,7 +488,7 @@ protected function saveProduct($product) $serviceInfo = [ 'rest' => [ 'resourcePath' => $resourcePath, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT + 'httpMethod' => Request::HTTP_METHOD_PUT ], 'soap' => [ 'service' => self::SERVICE_NAME, From 236ff0ffb83a501ac8a957cf511eaf3591056f31 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 7 Apr 2020 14:40:11 +0300 Subject: [PATCH 708/734] MC-32411: Linking a simple product to a configurable product does not rollback when the API request fails. --- .../ConfigurableProduct/Model/Plugin/ProductRepositorySave.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php index ed2dd9fd536b1..8bc7f05b49e30 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php @@ -6,7 +6,6 @@ namespace Magento\ConfigurableProduct\Model\Plugin; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Model\ProductRepository; use Magento\Framework\Exception\InputException; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\ConfigurableProduct\Api\Data\OptionInterface; From 05667ed53584030e1771654585cdb9234ad834fe Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 8 Apr 2020 09:23:02 +0300 Subject: [PATCH 709/734] MC-33151: Performance issue caused by checking if DB tables exist on every call --- .../App/Config/Source/RuntimeConfigSource.php | 18 ++++++- .../Config/Source/RuntimeConfigSourceTest.php | 54 +++++++++++++++---- .../App/Config/Source/RuntimeConfigSource.php | 36 +++++++------ .../Store/Model/Config/Processor/Fallback.php | 35 ++++++++---- .../Store/Model/ResourceModel/Store.php | 14 ++--- .../Store/Model/ResourceModel/Website.php | 12 ++--- .../Config/Source/RuntimeConfigSourceTest.php | 32 ++++++++--- .../Unit/Model/ResourceModel/StoreTest.php | 40 -------------- .../Unit/Model/ResourceModel/WebsiteTest.php | 40 -------------- .../Model/ResourceModel/Db/ProfilerTest.php | 4 +- .../Magento/Setup/Module/DataSetupTest.php | 5 +- .../Framework/DB/Adapter/Pdo/Mysql.php | 13 ++++- .../DB/Adapter/TableNotFoundException.php | 15 ++++++ 13 files changed, 172 insertions(+), 146 deletions(-) create mode 100644 lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php diff --git a/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php b/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php index b33c944c73477..7926708772a9f 100644 --- a/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php +++ b/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php @@ -8,9 +8,12 @@ use Magento\Framework\App\Config\ConfigSourceInterface; use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Config\Model\ResourceModel\Config\Data\CollectionFactory; use Magento\Framework\App\Config\Scope\Converter; +use Magento\Framework\DB\Adapter\TableNotFoundException; /** * Class for retrieving runtime configuration from database. @@ -34,20 +37,27 @@ class RuntimeConfigSource implements ConfigSourceInterface * @var ScopeCodeResolver */ private $scopeCodeResolver; + /** + * @var DeploymentConfig + */ + private $deploymentConfig; /** * @param CollectionFactory $collectionFactory * @param ScopeCodeResolver $scopeCodeResolver * @param Converter $converter + * @param DeploymentConfig|null $deploymentConfig */ public function __construct( CollectionFactory $collectionFactory, ScopeCodeResolver $scopeCodeResolver, - Converter $converter + Converter $converter, + ?DeploymentConfig $deploymentConfig = null ) { $this->collectionFactory = $collectionFactory; $this->converter = $converter; $this->scopeCodeResolver = $scopeCodeResolver; + $this->deploymentConfig = $deploymentConfig ?? ObjectManager::getInstance()->get(DeploymentConfig::class); } /** @@ -59,7 +69,7 @@ public function __construct( */ public function get($path = '') { - $data = new DataObject($this->loadConfig()); + $data = new DataObject($this->deploymentConfig->isDbAvailable() ? $this->loadConfig() : []); return $data->getData($path) ?: []; } @@ -75,8 +85,12 @@ private function loadConfig() { try { $collection = $this->collectionFactory->create(); + $collection->load(); } catch (\DomainException $e) { $collection = []; + } catch (TableNotFoundException $exception) { + // database is empty or not setup + $collection = []; } $config = []; foreach ($collection as $item) { diff --git a/app/code/Magento/Config/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php b/app/code/Magento/Config/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php index ae6ee8a028868..d880d93d78da3 100644 --- a/app/code/Magento/Config/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php +++ b/app/code/Magento/Config/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php @@ -5,41 +5,46 @@ */ namespace Magento\Config\Test\Unit\App\Config\Source; +use ArrayIterator; use Magento\Config\App\Config\Source\RuntimeConfigSource; +use Magento\Config\Model\ResourceModel\Config\Data\Collection; use Magento\Config\Model\ResourceModel\Config\Data\CollectionFactory; use Magento\Framework\App\Config\Scope\Converter; use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Config\Value; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\DB\Adapter\TableNotFoundException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Test Class for retrieving runtime configuration from database. - * @package Magento\Config\Test\Unit\App\Config\Source */ -class RuntimeConfigSourceTest extends \PHPUnit\Framework\TestCase +class RuntimeConfigSourceTest extends TestCase { /** - * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ private $collectionFactory; /** - * @var ScopeCodeResolver|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeCodeResolver|MockObject */ private $scopeCodeResolver; /** - * @var Converter|\PHPUnit_Framework_MockObject_MockObject + * @var Converter|MockObject */ private $converter; /** - * @var Value|\PHPUnit_Framework_MockObject_MockObject + * @var Value|MockObject */ private $configItem; /** - * @var Value|\PHPUnit_Framework_MockObject_MockObject + * @var Value|MockObject */ private $configItemTwo; @@ -47,6 +52,10 @@ class RuntimeConfigSourceTest extends \PHPUnit\Framework\TestCase * @var RuntimeConfigSource */ private $configSource; + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfig; public function setUp() { @@ -68,20 +77,29 @@ public function setUp() ->disableOriginalConstructor() ->setMethods(['getScope', 'getPath', 'getValue', 'getScopeId']) ->getMock(); + $this->deploymentConfig = $this->createPartialMock(DeploymentConfig::class, ['isDbAvailable']); $this->configSource = new RuntimeConfigSource( $this->collectionFactory, $this->scopeCodeResolver, - $this->converter + $this->converter, + $this->deploymentConfig ); } public function testGet() { + $this->deploymentConfig->method('isDbAvailable') + ->willReturn(true); + $collection = $this->createPartialMock(Collection::class, ['load', 'getIterator']); + $collection->method('load') + ->willReturn($collection); + $collection->method('getIterator') + ->willReturn(new ArrayIterator([$this->configItem, $this->configItemTwo])); $scope = 'websites'; $scopeCode = 'myWebsites'; $this->collectionFactory->expects($this->once()) ->method('create') - ->willReturn([$this->configItem, $this->configItemTwo]); + ->willReturn($collection); $this->configItem->expects($this->exactly(2)) ->method('getScope') ->willReturn(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); @@ -133,4 +151,22 @@ public function testGet() $this->configSource->get() ); } + + public function testGetWhenDbIsNotAvailable() + { + $this->deploymentConfig->method('isDbAvailable')->willReturn(false); + $this->assertEquals([], $this->configSource->get()); + } + + public function testGetWhenDbIsEmpty() + { + $this->deploymentConfig->method('isDbAvailable') + ->willReturn(true); + $collection = $this->createPartialMock(Collection::class, ['load']); + $collection->method('load') + ->willThrowException($this->createMock(TableNotFoundException::class)); + $this->collectionFactory->method('create') + ->willReturn($collection); + $this->assertEquals([], $this->configSource->get()); + } } diff --git a/app/code/Magento/Store/App/Config/Source/RuntimeConfigSource.php b/app/code/Magento/Store/App/Config/Source/RuntimeConfigSource.php index f732871114061..b8b0ede43c70d 100644 --- a/app/code/Magento/Store/App/Config/Source/RuntimeConfigSource.php +++ b/app/code/Magento/Store/App/Config/Source/RuntimeConfigSource.php @@ -9,6 +9,7 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Adapter\TableNotFoundException; /** * Config source. Retrieve all configuration for scopes from db @@ -53,15 +54,21 @@ public function __construct( */ public function get($path = '') { - if ($this->canUseDatabase()) { - return [ - 'websites' => $this->getEntities('store_website', 'code'), - 'groups' => $this->getEntities('store_group', 'group_id'), - 'stores' => $this->getEntities('store', 'code'), - ]; + $data = []; + try { + if ($this->canUseDatabase()) { + $data = [ + 'websites' => $this->getEntities('store_website', 'code'), + 'groups' => $this->getEntities('store_group', 'group_id'), + 'stores' => $this->getEntities('store', 'code'), + ]; + } + } catch (TableNotFoundException $exception) { + // database is empty or not setup + $data = []; } - return []; + return $data; } /** @@ -89,14 +96,13 @@ private function getEntities($table, $keyField) $data = []; $tableName = $this->resourceConnection->getTableName($table); // Check if db table exists before fetch data - if ($this->resourceConnection->getConnection()->isTableExists($tableName)) { - $entities = $this->getConnection()->fetchAll( - $this->getConnection()->select()->from($tableName) - ); - foreach ($entities as $entity) { - $data[$entity[$keyField]] = $entity; - } + $entities = $this->getConnection()->fetchAll( + $this->getConnection()->select()->from($tableName) + ); + + foreach ($entities as $entity) { + $data[$entity[$keyField]] = $entity; } return $data; @@ -109,6 +115,6 @@ private function getEntities($table, $keyField) */ private function canUseDatabase() { - return $this->deploymentConfig->get('db'); + return $this->deploymentConfig->isDbAvailable(); } } diff --git a/app/code/Magento/Store/Model/Config/Processor/Fallback.php b/app/code/Magento/Store/Model/Config/Processor/Fallback.php index 57ac3459ccb48..4e8b3bca14c92 100644 --- a/app/code/Magento/Store/Model/Config/Processor/Fallback.php +++ b/app/code/Magento/Store/Model/Config/Processor/Fallback.php @@ -8,8 +8,7 @@ use Magento\Framework\App\Config\Spi\PostProcessorInterface; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ResourceConnection; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Api\Data\WebsiteInterface; +use Magento\Framework\DB\Adapter\TableNotFoundException; use Magento\Store\App\Config\Type\Scopes; use Magento\Store\Model\ResourceModel\Store; use Magento\Store\Model\ResourceModel\Store\AllStoresCollectionFactory; @@ -85,13 +84,7 @@ public function __construct( */ public function process(array $data) { - if ($this->deploymentConfig->isDbAvailable()) {//read only from db - $this->storeData = $this->storeResource->readAllStores(); - $this->websiteData = $this->websiteResource->readAllWebsites(); - } else { - $this->storeData = $this->scopes->get('stores'); - $this->websiteData = $this->scopes->get('websites'); - } + $this->loadScopes(); $defaultConfig = isset($data['default']) ? $data['default'] : []; $result = [ @@ -177,4 +170,28 @@ private function getWebsiteConfig(array $websites, $id) } return []; } + + /** + * Load config from database. + * + * @return void + */ + private function loadScopes(): void + { + $loaded = false; + try { + if ($this->deploymentConfig->isDbAvailable()) { + $this->storeData = $this->storeResource->readAllStores(); + $this->websiteData = $this->websiteResource->readAllWebsites(); + $loaded = true; + } + } catch (TableNotFoundException $exception) { + // database is empty or not setup + $loaded = false; + } + if (!$loaded) { + $this->storeData = $this->scopes->get('stores'); + $this->websiteData = $this->scopes->get('websites'); + } + } } diff --git a/app/code/Magento/Store/Model/ResourceModel/Store.php b/app/code/Magento/Store/Model/ResourceModel/Store.php index 7a2821987f9bf..cadbe1817aed2 100644 --- a/app/code/Magento/Store/Model/ResourceModel/Store.php +++ b/app/code/Magento/Store/Model/ResourceModel/Store.php @@ -166,16 +166,10 @@ protected function _changeGroup(\Magento\Framework\Model\AbstractModel $model) */ public function readAllStores() { - $stores = []; - if ($this->getConnection()->isTableExists($this->getMainTable())) { - $select = $this->getConnection() - ->select() - ->from($this->getTable($this->getMainTable())); - - $stores = $this->getConnection()->fetchAll($select); - } - - return $stores; + $select = $this->getConnection() + ->select() + ->from($this->getTable($this->getMainTable())); + return $this->getConnection()->fetchAll($select); } /** diff --git a/app/code/Magento/Store/Model/ResourceModel/Website.php b/app/code/Magento/Store/Model/ResourceModel/Website.php index 431a9d62e7c39..a59c7746bec6e 100644 --- a/app/code/Magento/Store/Model/ResourceModel/Website.php +++ b/app/code/Magento/Store/Model/ResourceModel/Website.php @@ -48,14 +48,12 @@ public function readAllWebsites() { $websites = []; $tableName = $this->getMainTable(); - if ($this->getConnection()->isTableExists($tableName)) { - $select = $this->getConnection() - ->select() - ->from($tableName); + $select = $this->getConnection() + ->select() + ->from($tableName); - foreach ($this->getConnection()->fetchAll($select) as $websiteData) { - $websites[$websiteData['code']] = $websiteData; - } + foreach ($this->getConnection()->fetchAll($select) as $websiteData) { + $websites[$websiteData['code']] = $websiteData; } return $websites; diff --git a/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php b/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php index a8f76d0a28fee..104c1bbba2614 100644 --- a/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php +++ b/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php @@ -8,6 +8,7 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Adapter\TableNotFoundException; use Magento\Framework\DB\Select; use Magento\Store\App\Config\Source\RuntimeConfigSource; @@ -54,8 +55,7 @@ public function setUp() public function testGet() { $this->deploymentConfig->expects($this->any()) - ->method('get') - ->with('db') + ->method('isDbAvailable') ->willReturn(true); $this->resourceConnection->expects($this->any())->method('getConnection')->willReturn($this->connection); @@ -63,7 +63,6 @@ public function testGet() $selectMock->expects($this->any())->method('from')->willReturnSelf(); $this->connection->expects($this->any())->method('select')->willReturn($selectMock); $this->connection->expects($this->any())->method('fetchAll')->willReturn([]); - $this->assertEquals( [ 'websites' => [], @@ -74,13 +73,32 @@ public function testGet() ); } - public function testGenWhenDbNotAvailable() + public function testGetWhenDbIsNotAvailable() { $this->deploymentConfig->expects($this->once()) - ->method('get') - ->with('db') + ->method('isDbAvailable') ->willReturn(false); - $this->resourceConnection->expects($this->never())->method('getConnection'); + $this->resourceConnection->expects($this->never()) + ->method('getConnection'); + + $this->assertEquals([], $this->configSource->get()); + } + + public function testGetWhenDbIsEmpty() + { + $this->deploymentConfig->expects($this->once()) + ->method('isDbAvailable') + ->willReturn(true); + $this->connection->method('fetchAll') + ->willThrowException($this->createMock(TableNotFoundException::class)); + $selectMock = $this->createMock(Select::class); + $selectMock->method('from') + ->willReturnSelf(); + $this->connection->method('select') + ->willReturn($selectMock); + $this->resourceConnection->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connection); $this->assertEquals([], $this->configSource->get()); } diff --git a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php index 926764b989686..b4e6c5af7c9f9 100644 --- a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php @@ -131,11 +131,6 @@ public function testReadAllStores() ->method('getTableName') ->willReturn($mainTable); - $this->connectionMock->expects($this->once()) - ->method('isTableExists') - ->with($mainTable) - ->willReturn(true); - $this->connectionMock->expects($this->once()) ->method('select') ->willReturn($this->select); @@ -152,39 +147,4 @@ public function testReadAllStores() $this->assertEquals($data, $this->model->readAllStores()); } - - public function testReadAllStoresNoDbTable() - { - $mainTable = 'no_store_table'; - $data = []; - - $this->resourceMock->expects($this->once()) - ->method('getConnection') - ->willReturn($this->connectionMock); - - $this->resourceMock->expects($this->once()) - ->method('getTableName') - ->willReturn($mainTable); - - $this->connectionMock->expects($this->once()) - ->method('isTableExists') - ->with($mainTable) - ->willReturn(false); - - $this->connectionMock->expects($this->never()) - ->method('select') - ->willReturn($this->select); - - $this->select->expects($this->never()) - ->method('from') - ->with($mainTable) - ->willReturnSelf(); - - $this->connectionMock->expects($this->never()) - ->method('fetchAll') - ->with($this->select) - ->willReturn($data); - - $this->assertEquals($data, $this->model->readAllStores()); - } } diff --git a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php index 5fd5aa09a46be..3238023cd6e09 100644 --- a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php @@ -79,11 +79,6 @@ public function testReadAllWebsites() ->method('getConnection') ->willReturn($this->connectionMock); - $this->connectionMock->expects($this->once()) - ->method('isTableExists') - ->with($mainTable) - ->willReturn(true); - $this->connectionMock->expects($this->once()) ->method('select') ->willReturn($this->select); @@ -101,41 +96,6 @@ public function testReadAllWebsites() $this->assertEquals($data, $this->model->readAllWebsites()); } - public function testReadAllWebsitesNoDbTable() - { - $data = []; - $mainTable = 'no_store_website_table'; - - $this->resourceMock->expects($this->once()) - ->method('getTableName') - ->willReturn($mainTable); - - $this->resourceMock->expects($this->atLeastOnce()) - ->method('getConnection') - ->willReturn($this->connectionMock); - - $this->connectionMock->expects($this->once()) - ->method('isTableExists') - ->with($mainTable) - ->willReturn(false); - - $this->connectionMock->expects($this->never()) - ->method('select') - ->willReturn($this->select); - - $this->select->expects($this->never()) - ->method('from') - ->with($mainTable) - ->willReturnSelf(); - - $this->connectionMock->expects($this->never()) - ->method('fetchAll') - ->with($this->select) - ->willReturn($data); - - $this->assertEquals($data, $this->model->readAllWebsites()); - } - public function testGetDefaultStoresSelect($includeDefault = false) { $storeId = 1; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Model/ResourceModel/Db/ProfilerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Model/ResourceModel/Db/ProfilerTest.php index 99305ad2d4e80..5caa94b680c88 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Model/ResourceModel/Db/ProfilerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Model/ResourceModel/Db/ProfilerTest.php @@ -10,7 +10,7 @@ use Magento\Framework\Config\ConfigOptionsListConstants; /** - * Class ProfilerTest + * Test profiler on database queries */ class ProfilerTest extends \PHPUnit\Framework\TestCase { @@ -140,7 +140,7 @@ public function testProfilerDuringSqlException() try { $connection->select()->from('unknown_table')->query()->fetch(); - } catch (\Zend_Db_Statement_Exception $exception) { + } catch (\Magento\Framework\DB\Adapter\TableNotFoundException $exception) { $this->assertNotEmpty($exception); } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php b/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php index 425840b44ba0e..4c35d9882a0ac 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php @@ -39,7 +39,7 @@ public function testUpdateTableRow() } /** - * @expectedException \Zend_Db_Statement_Exception + * @expectedException \Magento\Framework\DB\Adapter\TableNotFoundException */ public function testDeleteTableRow() { @@ -47,8 +47,7 @@ public function testDeleteTableRow() } /** - * @covers \Magento\Setup\Module\DataSetup::updateTableRow - * @expectedException \Zend_Db_Statement_Exception + * @expectedException \Magento\Framework\DB\Adapter\TableNotFoundException */ public function testUpdateTableRowNameConversion() { diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index ec2731c667ee6..80d8808ab1768 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -13,6 +13,7 @@ use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\DuplicateException; use Magento\Framework\DB\Adapter\LockWaitException; +use Magento\Framework\DB\Adapter\TableNotFoundException; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\DB\ExpressionConverter; use Magento\Framework\DB\LoggerInterface; @@ -38,6 +39,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @since 100.0.2 */ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface { @@ -257,6 +259,8 @@ public function __construct( 1213 => DeadlockException::class, // SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 1062 => DuplicateException::class, + // SQLSTATE[42S02]: Base table or view not found: 1146 + 1146 => TableNotFoundException::class, ]; try { parent::__construct($config); @@ -687,7 +691,8 @@ public function proccessBindCallback($matches) if (isset($matches[6]) && ( strpos($matches[6], "'") !== false || strpos($matches[6], ':') !== false || - strpos($matches[6], '?') !== false) + strpos($matches[6], '?') !== false + ) ) { $bindName = ':_mage_bind_var_' . (++$this->_bindIncrement); $this->_bindParams[$bindName] = $this->_unQuote($matches[6]); @@ -1822,7 +1827,7 @@ public function modifyColumnByDdl($tableName, $columnName, $definition, $flushDa * Retrieve column data type by data from describe table * * @param array $column - * @return string + * @return string|null * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _getColumnTypeByDdl($column) @@ -1862,6 +1867,7 @@ protected function _getColumnTypeByDdl($column) case 'numeric': return Table::TYPE_DECIMAL; } + return null; } /** @@ -4020,6 +4026,7 @@ public function getAutoIncrementField($tableName, $schemaName = null) * Required to listen all DDL changes done by 3-rd party modules with old Install/UpgradeSchema scripts. * * @return SchemaListener + * @since 102.0.0 */ public function getSchemaListener() { @@ -4031,6 +4038,8 @@ public function getSchemaListener() /** * Closes the connection. + * + * @since 102.0.4 */ public function closeConnection() { diff --git a/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php b/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php new file mode 100644 index 0000000000000..7fded1fa01e91 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\DB\Adapter; + +/** + * Database base table or view not found exception + */ +class TableNotFoundException extends \Zend_Db_Adapter_Exception +{ +} From b4c7ed2f9696dd70c5bb331f929123e3698551e6 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 8 Apr 2020 13:04:27 +0300 Subject: [PATCH 710/734] Fix Delete folder functionality --- lib/web/mage/adminhtml/browser.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index d6502af9ab74b..06a77cb46064b 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -418,7 +418,10 @@ define([ }).done($.proxy(function () { self.tree.jstree('refresh', self.activeNode.id); self.reload(); - $(window).trigger('fileDeleted.mediabrowser'); + $(window).trigger('fileDeleted.mediabrowser', { + ids: self.activeNode.id + }); + }, this)); }, From 1d9317d2ae415ed90b9fc587183016df32b57b05 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 8 Apr 2020 13:15:52 +0300 Subject: [PATCH 711/734] MC-32547: Error when adding product as button link in Pagebuilder --- .../base/web/js/form/element/ui-select.js | 2 + .../Magento/Ui/base/js/form/ui-select.test.js | 51 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index 5667ce5d71d81..b488a4b2f8c16 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -1160,6 +1160,7 @@ define([ if (this.isSearchKeyCached(searchKey)) { cachedSearchResult = this.getCachedSearchResults(searchKey); + this.cacheOptions.plain = cachedSearchResult.options; this.options(cachedSearchResult.options); this.afterLoadOptions(searchKey, cachedSearchResult.lastPage, cachedSearchResult.total); @@ -1274,6 +1275,7 @@ define([ }); this.total = response.total; + this.cacheOptions.plain = existingOptions; this.options(existingOptions); }, diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js index ac6e230e7ed1c..f46ff6b30abbe 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js @@ -624,6 +624,24 @@ define([ expect(obj.options).toHaveBeenCalledWith([]); expect(obj.processRequest).toHaveBeenCalledWith(searchKey, 1); }); + it('Should update cacheOptions if response was cached', function () { + var searchKey = 'cake', + searchResult = 'piece a cake'; + + obj.deviation = 30; + obj.cachedSearchResults = { + cake: { + options: [searchResult], + lastPage: 1, + total: 1 + } + }; + + spyOn(obj, 'options'); + obj.loadOptions(searchKey); + expect(obj.options).toHaveBeenCalledWith([searchResult]); + expect(obj.cacheOptions.plain).toContain(searchResult); + }); }); describe('"isSearchKeyCached" method', function () { it('Should return false if searchKey has already been cached and total covers > 1 page', function () { @@ -672,7 +690,7 @@ define([ }); }); describe('"processRequest" method', function () { - it('Should store options successfully fetched from ajax request', function () { + it('Should store options and update cache successfully after fetched from ajax request', function () { var ajaxRequest, successfulAjaxResponse = { options: { @@ -686,6 +704,24 @@ define([ } }; + // place a number of options to cache prior fetch + obj.cacheOptions.plain = [{ + '2053': { + value: '2057', + label: 'testProductName5a8ddfd933b5c', + 'is_active': 1, + path: 'testSku5a8ddfd933b5c', + optgroup: false + }, + '2054': { + value: '2058', + label: 'testProductName5a8ddfd933b5c', + 'is_active': 1, + path: 'testSku5a8ddfd933b5c', + optgroup: false + } + }]; + $.ajax = jasmine.createSpy().and.callFake(function (request) { ajaxRequest = request.success.bind(obj); }); @@ -693,7 +729,18 @@ define([ expect(obj.processRequest()).toBeUndefined(); ajaxRequest(successfulAjaxResponse); - expect(JSON.stringify(obj.options())).toEqual(JSON.stringify([successfulAjaxResponse.options['2053']])); + + expect( + JSON.stringify(obj.options()) + ).toEqual( + JSON.stringify([successfulAjaxResponse.options['2053']]) + ); + + expect( + JSON.stringify(obj.cacheOptions.plain) + ).toEqual( + JSON.stringify([successfulAjaxResponse.options['2053']]) + ); }); }); }); From c2515ccb21757ddf8c5b0294d39669eb49b056b4 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 8 Apr 2020 14:41:14 +0300 Subject: [PATCH 712/734] MC-33117: PayPal payflow pro cancel fraud transaction on paypal no reference to Magento --- .../Paypal/Model/Payflow/Transparent.php | 38 ++++++++++++++++++- app/code/Magento/Paypal/i18n/en_US.csv | 1 + .../Adminhtml/Order/ReviewPayment.php | 12 +++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index 44a9b6f73c80e..87cb0df7b0d7f 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -61,7 +61,7 @@ class Transparent extends Payflowpro implements TransparentInterface * * @var bool */ - protected $_canFetchTransactionInfo = false; + protected $_canFetchTransactionInfo = true; /** * @var ResponseValidator @@ -355,11 +355,11 @@ public function capture(InfoInterface $payment, $amount) * * @param InfoInterface $payment * @return bool - * @throws InvalidTransitionException * @throws LocalizedException */ public function acceptPayment(InfoInterface $payment) { + $this->validatePaymentTransaction($payment); if ($this->getConfigPaymentAction() === MethodInterface::ACTION_AUTHORIZE_CAPTURE) { $invoices = iterator_to_array($payment->getOrder()->getInvoiceCollection()); $invoice = count($invoices) ? reset($invoices) : null; @@ -387,6 +387,20 @@ public function denyPayment(InfoInterface $payment) return true; } + /** + * @inheritDoc + */ + public function fetchTransactionInfo(InfoInterface $payment, $transactionId) + { + $result = parent::fetchTransactionInfo($payment, $transactionId); + $this->_canFetchTransactionInfo = false; + if ($payment->getIsTransactionApproved()) { + $this->acceptPayment($payment); + } + + return $result; + } + /** * Marks payment as fraudulent. * @@ -444,4 +458,24 @@ private function getZeroAmountAuthorizationId(InfoInterface $payment): string { return (string)$payment->getAdditionalInformation(self::PNREF); } + + /** + * Validates payment transaction status on PayPal. + * + * @param InfoInterface $payment + * @throws LocalizedException + */ + private function validatePaymentTransaction(InfoInterface $payment): void + { + if ($payment->canFetchTransactionInfo()) { + $transactionId = $payment->getLastTransId(); + parent::fetchTransactionInfo($payment, $transactionId); + $this->_canFetchTransactionInfo = false; + if ($payment->getIsTransactionDenied()) { + throw new LocalizedException( + __('Payment can\'t be accepted since transaction was rejected by merchant.') + ); + } + } + } } diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index 54dd611d49073..912b99e2e6427 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -739,3 +739,4 @@ User,User "Elektronisches Lastschriftverfahren - German ELV","Elektronisches Lastschriftverfahren - German ELV" "Please enter at least 0 and at most 65535","Please enter at least 0 and at most 65535" "Order is suspended as an account verification transaction is suspected to be fraudulent.","Order is suspended as an account verification transaction is suspected to be fraudulent." +"Payment can't be accepted since transaction was rejected by merchant.","Payment can't be accepted since transaction was rejected by merchant." diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php index 93c8305ec2396..57cd21ffa9eb1 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php @@ -8,7 +8,6 @@ namespace Magento\Sales\Controller\Adminhtml\Order; -use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; /** @@ -63,17 +62,20 @@ public function execute() } $this->orderRepository->save($order); $this->messageManager->addSuccessMessage($message); - $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getEntityId()]); - } else { - $resultRedirect->setPath('sales/*/'); - return $resultRedirect; } // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); $this->messageManager->addErrorMessage(__('We can\'t update the payment right now.')); $this->logger->critical($e); + } + + if ($order) { + $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getEntityId()]); + } else { $resultRedirect->setPath('sales/*/'); } + return $resultRedirect; } } From fcd1b5808625364e09dc6aea2d9eb8c0ee209d25 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 8 Apr 2020 16:10:07 +0300 Subject: [PATCH 713/734] MC-33138: Extend using Magento Recommendations failed --- .../Quote/Model/ShippingMethodManagement.php | 73 ++++++++++-------- .../Model/ShippingMethodManagementTest.php | 74 +++++++++++++------ 2 files changed, 95 insertions(+), 52 deletions(-) diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index 73a2a43b2581f..d9fa37c0185a9 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -3,20 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Quote\Model; +use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Api\Data\EstimateAddressInterface; +use Magento\Quote\Api\Data\ShippingMethodInterface; use Magento\Quote\Api\ShipmentEstimationInterface; +use Magento\Quote\Model\Cart\ShippingMethodConverter; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Address\Rate; +use Magento\Quote\Model\Quote\TotalsCollector; use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; /** @@ -33,21 +40,21 @@ class ShippingMethodManagement implements /** * Quote repository. * - * @var \Magento\Quote\Api\CartRepositoryInterface + * @var CartRepositoryInterface */ protected $quoteRepository; /** * Shipping method converter * - * @var \Magento\Quote\Model\Cart\ShippingMethodConverter + * @var ShippingMethodConverter */ protected $converter; /** * Customer Address repository * - * @var \Magento\Customer\Api\AddressRepositoryInterface + * @var AddressRepositoryInterface */ protected $addressRepository; @@ -57,7 +64,7 @@ class ShippingMethodManagement implements protected $totalsCollector; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor $dataProcessor + * @var DataObjectProcessor $dataProcessor */ private $dataProcessor; @@ -79,19 +86,19 @@ class ShippingMethodManagement implements /** * Constructor * - * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository + * @param CartRepositoryInterface $quoteRepository * @param Cart\ShippingMethodConverter $converter - * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param AddressRepositoryInterface $addressRepository * @param Quote\TotalsCollector $totalsCollector * @param AddressInterfaceFactory|null $addressFactory * @param QuoteAddressResource|null $quoteAddressResource * @param CustomerSession|null $customerSession */ public function __construct( - \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, + CartRepositoryInterface $quoteRepository, Cart\ShippingMethodConverter $converter, - \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, - \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, + AddressRepositoryInterface $addressRepository, + TotalsCollector $totalsCollector, AddressInterfaceFactory $addressFactory = null, QuoteAddressResource $quoteAddressResource = null, CustomerSession $customerSession = null @@ -112,10 +119,10 @@ public function __construct( */ public function get($cartId) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); - /** @var \Magento\Quote\Model\Quote\Address $shippingAddress */ + /** @var Address $shippingAddress */ $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { throw new StateException(__('The shipping address is missing. Set the address and try again.')); @@ -127,7 +134,7 @@ public function get($cartId) } $shippingAddress->collectShippingRates(); - /** @var \Magento\Quote\Model\Quote\Address\Rate $shippingRate */ + /** @var Rate $shippingRate */ $shippingRate = $shippingAddress->getShippingRateByCode($shippingMethod); if (!$shippingRate) { return null; @@ -142,7 +149,7 @@ public function getList($cartId) { $output = []; - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products @@ -169,7 +176,7 @@ public function getList($cartId) */ public function set($cartId, $carrierCode, $methodCode) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); try { $this->apply($cartId, $carrierCode, $methodCode); @@ -199,7 +206,7 @@ public function set($cartId, $carrierCode, $methodCode) */ public function apply($cartId, $carrierCode, $methodCode) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); if (0 == $quote->getItemsCount()) { throw new InputException( @@ -223,9 +230,9 @@ public function apply($cartId, $carrierCode, $methodCode) /** * @inheritDoc */ - public function estimateByAddress($cartId, \Magento\Quote\Api\Data\EstimateAddressInterface $address) + public function estimateByAddress($cartId, EstimateAddressInterface $address) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products @@ -241,7 +248,7 @@ public function estimateByAddress($cartId, \Magento\Quote\Api\Data\EstimateAddre */ public function estimateByExtendedAddress($cartId, AddressInterface $address) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products @@ -256,7 +263,7 @@ public function estimateByExtendedAddress($cartId, AddressInterface $address) */ public function estimateByAddressId($cartId, $addressId) { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products @@ -276,13 +283,13 @@ public function estimateByAddressId($cartId, $addressId) * @param string $postcode * @param int $regionId * @param string $region - * @param \Magento\Framework\Api\ExtensibleDataInterface|null $address - * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] An array of shipping methods. + * @param ExtensibleDataInterface|null $address + * @return ShippingMethodInterface[] An array of shipping methods. * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @deprecated 100.2.0 */ protected function getEstimatedRates( - \Magento\Quote\Model\Quote $quote, + Quote $quote, $country, $postcode, $regionId, @@ -301,9 +308,9 @@ protected function getEstimatedRates( /** * Get list of available shipping methods * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Framework\Api\ExtensibleDataInterface $address - * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] + * @param Quote $quote + * @param ExtensibleDataInterface $address + * @return ShippingMethodInterface[] */ private function getShippingMethods(Quote $quote, $address) { @@ -334,27 +341,31 @@ private function getShippingMethods(Quote $quote, $address) /** * Get transform address interface into Array * - * @param \Magento\Framework\Api\ExtensibleDataInterface $address + * @param ExtensibleDataInterface $address * @return array */ private function extractAddressData($address) { $className = \Magento\Customer\Api\Data\AddressInterface::class; - if ($address instanceof \Magento\Quote\Api\Data\AddressInterface) { - $className = \Magento\Quote\Api\Data\AddressInterface::class; + if ($address instanceof AddressInterface) { + $className = AddressInterface::class; } elseif ($address instanceof EstimateAddressInterface) { $className = EstimateAddressInterface::class; } - return $this->getDataObjectProcessor()->buildOutputDataArray( + + $addressData = $this->getDataObjectProcessor()->buildOutputDataArray( $address, $className ); + unset($addressData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); + + return $addressData; } /** * Gets the data object processor * - * @return \Magento\Framework\Reflection\DataObjectProcessor + * @return DataObjectProcessor * @deprecated 100.2.0 */ private function getDataObjectProcessor() diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php index 34d7707d31666..c5b0b264bb79c 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php @@ -1,14 +1,19 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Quote\Test\Unit\Model; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Quote\Api\Data\ShippingMethodInterfaceFactory; use Magento\Quote\Model\Cart\ShippingMethodConverter; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; @@ -18,12 +23,13 @@ use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Quote\Model\ShippingMethodManagement; use Magento\Store\Model\Store; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase +class ShippingMethodManagementTest extends TestCase { /** * @var ShippingMethodManagement @@ -31,12 +37,12 @@ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $shippingMethodMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $methodDataFactoryMock; @@ -46,7 +52,7 @@ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase protected $converter; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ protected $objectManager; @@ -66,17 +72,17 @@ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase private $shippingAddress; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor|MockObject + * @var DataObjectProcessor|MockObject */ private $dataProcessor; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory|MockObject + * @var AddressInterfaceFactory|MockObject */ private $addressFactory; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface|MockObject + * @var AddressRepositoryInterface|MockObject */ private $addressRepository; @@ -95,19 +101,22 @@ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase */ private $quoteAddressResource; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->quoteRepository = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); - $this->addressRepository = $this->createMock(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->quoteRepository = $this->createMock(CartRepositoryInterface::class); + $this->addressRepository = $this->createMock(AddressRepositoryInterface::class); - $className = \Magento\Quote\Api\Data\ShippingMethodInterfaceFactory::class; + $className = ShippingMethodInterfaceFactory::class; $this->methodDataFactoryMock = $this->createPartialMock($className, ['create']); - $className = \Magento\Customer\Api\Data\AddressInterfaceFactory::class; + $className = AddressInterfaceFactory::class; $this->addressFactory = $this->createPartialMock($className, ['create']); - $className = \Magento\Framework\Reflection\DataObjectProcessor::class; + $className = DataObjectProcessor::class; $this->dataProcessor = $this->createMock($className); $this->quoteAddressResource = $this->createMock(QuoteAddressResource::class); @@ -196,6 +205,9 @@ public function testGetMethodWhenShippingAddressIsNotSet() $this->assertNull($this->model->get($cartId)); } + /** + * Test to returns selected shipping method for a specified quote + */ public function testGetMethod() { $cartId = 666; @@ -213,14 +225,14 @@ public function testGetMethod() ->method('getShippingMethod')->will($this->returnValue('one_two')); $this->shippingAddress->expects($this->once())->method('collectShippingRates')->willReturnSelf(); - $shippingRateMock = $this->createMock(\Magento\Quote\Model\Quote\Address\Rate::class); + $shippingRateMock = $this->createMock(Rate::class); $this->shippingAddress->expects($this->once()) ->method('getShippingRateByCode') ->with('one_two') ->willReturn($shippingRateMock); - $this->shippingMethodMock = $this->createMock(\Magento\Quote\Api\Data\ShippingMethodInterface::class); + $this->shippingMethodMock = $this->createMock(ShippingMethodInterface::class); $this->converter->expects($this->once()) ->method('modelToDataObject') ->with($shippingRateMock, $currencyCode) @@ -228,6 +240,9 @@ public function testGetMethod() $this->model->get($cartId); } + /** + * Test to returns selected shipping method for a specified quote if method isn't set + */ public function testGetMethodIfMethodIsNotSet() { $cartId = 666; @@ -245,6 +260,9 @@ public function testGetMethodIfMethodIsNotSet() $this->assertNull($this->model->get($cartId)); } + /** + * Test to get lists applicable shipping methods for a specified quote + */ public function testGetListForVirtualCart() { $cartId = 834; @@ -256,6 +274,9 @@ public function testGetListForVirtualCart() $this->assertEquals([], $this->model->getList($cartId)); } + /** + * Test to get lists applicable shipping methods for a specified quote + */ public function testGetListForEmptyCart() { $cartId = 834; @@ -289,6 +310,9 @@ public function testGetListWhenShippingAddressIsNotSet() $this->model->getList($cartId); } + /** + * Test to get lists applicable shipping methods for a specified quote + */ public function testGetList() { $cartId = 834; @@ -302,7 +326,7 @@ public function testGetList() ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(345)); $this->shippingAddress->expects($this->once())->method('collectShippingRates'); - $shippingRateMock = $this->createMock(\Magento\Quote\Model\Quote\Address\Rate::class); + $shippingRateMock = $this->createMock(Rate::class); $this->shippingAddress->expects($this->once()) ->method('getGroupedAllShippingRates') ->will($this->returnValue([[$shippingRateMock]])); @@ -441,6 +465,9 @@ public function testSetMethodWithoutAddress() $this->model->set($cartId, $carrierCode, $methodCode); } + /** + * Test to sets the carrier and shipping methods codes for a specified cart + */ public function testSetMethod() { $cartId = 12; @@ -472,6 +499,9 @@ public function testEstimateByExtendedAddress() { $cartId = 1; + $addressExtAttr = [ + 'discounts' => 100 + ]; $addressData = [ 'region' => 'California', 'region_id' => 23, @@ -509,7 +539,9 @@ public function testEstimateByExtendedAddress() $this->dataProcessor->expects(static::any()) ->method('buildOutputDataArray') - ->willReturn($addressData); + ->willReturn($addressData + [ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY => $addressExtAttr]); + + $this->shippingAddress->expects($this->once())->method('addData')->with($addressData); $this->shippingAddress->expects(static::once()) ->method('setCollectShippingRates') @@ -561,9 +593,9 @@ public function testEstimateByAddressId() $currencyCode = 'UAH'; /** - * @var \Magento\Customer\Api\Data\AddressInterface|MockObject $address + * @var AddressInterface|MockObject $address */ - $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) + $address = $this->getMockBuilder(AddressInterface::class) ->disableOriginalConstructor() ->getMock(); From eb809fd3fae3413cc014a58b38ab6368973828c3 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Wed, 8 Apr 2020 12:43:32 -0500 Subject: [PATCH 714/734] MQE-2046: remove deprecated entities usages in MFTF tests --- .../Backend/Test/Mftf/Section/AdminMessagesSection.xml | 6 +++--- .../Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml | 2 +- .../StorefrontAddSimpleProductToCartActionGroup.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index dbd5348d7d696..fce74f1f419e6 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -9,12 +9,12 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> - <element name="success" type="text" selector="#messages div.message-success"/> + <element name="success" type="text" selector=".message-success"/> <element name="nthSuccess" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> - <element name="error" type="text" selector="#messages div.message-error"/> + <element name="error" type="text" selector=".message.message-error.error"/> <element name="notice" type="text" selector=".message.message-notice.notice"/> <element name="messageByType" type="text" selector="#messages div.message-{{messageType}}" parameterized="true" /> - <element name="warning" type="text" selector="#messages div.message-warning"/> + <element name="warning" type="text" selector=".message-warning"/> <element name="accessDenied" type="text" selector=".access-denied-page"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml index 81e3b8c99d9d2..b4f7571429151 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AddSimpleProductToCartActionGroup"> <annotations> - <description>Navigates to the Storefront Product page. Then adds the Product to the Cart. Validates that the Success Message is present and correct.</description> + <description>Only works on Storefront Product Page - Add Simple Product to Cart</description> </annotations> <arguments> <argument name="product" defaultValue="product"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml index a17a6b4353783..d6add9a5d5c00 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml @@ -11,12 +11,12 @@ <!-- Add Product to Cart from the category page and check message --> <actionGroup name="StorefrontAddSimpleProductToCartActionGroup"> <annotations> - <description>Adds the provided Product to the Storefront Shopping Cart from a Storefront Category page. Validates that the provided Success Message is present and correct.</description> + <description>Only works on Storefront Category Page - Add Simple Product to Cart</description> </annotations> <arguments> <argument name="product" type="entity"/> </arguments> - + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> From f2b6435d4bcd1fa06366d6b07327ddbfe05a1a8d Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Wed, 8 Apr 2020 16:21:50 -0500 Subject: [PATCH 715/734] MQE-2046: remove deprecated entities usages in MFTF tests --- .../Test/Mftf/Section/AdminMessagesSection.xml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index fce74f1f419e6..c1d5599f9e1c2 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -9,12 +9,20 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> - <element name="success" type="text" selector=".message-success"/> + <!-- Keep both Set1 and Set2 elements --> + <!-- Neither Set1 nor Set2 works for all Magento blocks --> + <!-- Set1 --> + <element name="success" type="text" selector="#messages div.message-success"/> <element name="nthSuccess" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> - <element name="error" type="text" selector=".message.message-error.error"/> + <element name="error" type="text" selector="#messages div.message-error"/> <element name="notice" type="text" selector=".message.message-notice.notice"/> <element name="messageByType" type="text" selector="#messages div.message-{{messageType}}" parameterized="true" /> - <element name="warning" type="text" selector=".message-warning"/> + <element name="warning" type="text" selector="#messages div.message-warning"/> <element name="accessDenied" type="text" selector=".access-denied-page"/> + <!-- Set2 --> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + <element name="warningMessage" type="text" selector=".message-warning"/> + <element name="noticeMessage" type="text" selector=".message-notice"/> </section> </sections> From c5837830a4802b8822160ebe4fdbff0a8179090b Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Thu, 9 Apr 2020 11:27:38 +0300 Subject: [PATCH 716/734] MC-31544: Email Template New Pickup Order or add New Template order items are missing in Mail --- .../Order/Email/Sender/AbstractSenderTest.php | 74 +++++++++++-------- .../Email/Sender/CreditmemoSenderTest.php | 25 ++++--- .../Order/Email/Sender/InvoiceSenderTest.php | 25 ++++--- .../Order/Email/Sender/OrderSenderTest.php | 20 +++-- .../Order/Email/Sender/ShipmentSenderTest.php | 25 ++++--- .../Order/Shipment/Sender/EmailSenderTest.php | 33 +++++---- 6 files changed, 123 insertions(+), 79 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php index 2864f846f5a1d..39c85b955d9b5 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php @@ -5,6 +5,22 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Framework\App\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\Manager; +use Magento\Payment\Helper\Data; +use Magento\Payment\Model\Info; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Address\Renderer; +use Magento\Sales\Model\Order\Email\Container\Template; +use Magento\Sales\Model\Order\Email\Sender; +use Magento\Sales\Model\Order\Email\SenderBuilderFactory; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + /** * Class AbstractSenderTest * @@ -13,87 +29,87 @@ abstract class AbstractSenderTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Sales\Model\Order\Email\Sender|\PHPUnit_Framework_MockObject_MockObject + * @var Sender|MockObject */ protected $senderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $senderBuilderFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $templateContainerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $identityContainerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $storeMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $orderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $paymentHelper; /** - * @var \Magento\Sales\Model\Order\Address\Renderer|\PHPUnit_Framework_MockObject_MockObject + * @var Renderer|MockObject */ protected $addressRenderer; /** * Global configuration storage mock. * - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ protected $globalConfig; /** - * @var \Magento\Sales\Model\Order\Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ protected $addressMock; /** - * @var \Magento\Framework\Event\Manager | \PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $eventManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $loggerMock; public function stepMockSetup() { $this->senderMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Sender::class, + Sender::class, ['send', 'sendCopyTo'] ); $this->senderBuilderFactoryMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\SenderBuilderFactory::class, + SenderBuilderFactory::class, ['create'] ); $this->templateContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\Template::class, + Template::class, ['setTemplateVars'] ); - $this->storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getStoreId', '__wakeup']); + $this->storeMock = $this->createPartialMock(Store::class, ['getStoreId', '__wakeup']); $this->orderMock = $this->createPartialMock( - \Magento\Sales\Model\Order::class, + Order::class, [ 'getId', 'getStore', 'getBillingAddress', 'getPayment', '__wakeup', 'getCustomerIsGuest', 'getCustomerName', @@ -105,23 +121,23 @@ public function stepMockSetup() $this->orderMock->expects($this->any()) ->method('getStore') ->will($this->returnValue($this->storeMock)); - $paymentInfoMock = $this->createMock(\Magento\Payment\Model\Info::class); + $paymentInfoMock = $this->createMock(Info::class); $this->orderMock->expects($this->any()) ->method('getPayment') ->will($this->returnValue($paymentInfoMock)); - $this->addressRenderer = $this->createMock(\Magento\Sales\Model\Order\Address\Renderer::class); - $this->addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\Manager::class); + $this->addressRenderer = $this->createMock(Renderer::class); + $this->addressMock = $this->createMock(Address::class); + $this->eventManagerMock = $this->createMock(Manager::class); - $this->paymentHelper = $this->createPartialMock(\Magento\Payment\Helper\Data::class, ['getInfoBlockHtml']); + $this->paymentHelper = $this->createPartialMock(Data::class, ['getInfoBlockHtml']); $this->paymentHelper->expects($this->any()) ->method('getInfoBlockHtml') ->will($this->returnValue('payment')); - $this->globalConfig = $this->createPartialMock(\Magento\Framework\App\Config::class, ['getValue']); + $this->globalConfig = $this->createPartialMock(Config::class, ['getValue']); - $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); } /** @@ -168,14 +184,14 @@ public function stepIdentityContainerInit($identityMockClassName) } /** - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $sendExpects - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $sendCopyToExpects + * @param InvokedCount $sendExpects + * @param InvokedCount $sendCopyToExpects */ protected function stepSend( - \PHPUnit\Framework\MockObject\Matcher\InvokedCount $sendExpects, - \PHPUnit\Framework\MockObject\Matcher\InvokedCount $sendCopyToExpects + InvokedCount $sendExpects, + InvokedCount $sendCopyToExpects ) { - $senderMock = $this->createPartialMock(\Magento\Sales\Model\Order\Email\Sender::class, ['send', 'sendCopyTo']); + $senderMock = $this->createPartialMock(Sender::class, ['send', 'sendCopyTo']); $senderMock->expects($sendExpects) ->method('send'); $senderMock->expects($sendCopyToExpects) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php index 8162f89c3e281..6bcabea7df202 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php @@ -5,7 +5,14 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\ResourceModel\Order\Creditmemo as CreditmemoResource; +use Magento\Sales\Model\Order\Email\Container\CreditmemoIdentity; use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** * Test for Magento\Sales\Model\Order\Email\Sender\CreditmemoSender class. @@ -17,17 +24,17 @@ class CreditmemoSenderTest extends AbstractSenderTest private const ORDER_ID = 1; /** - * @var \Magento\Sales\Model\Order\Email\Sender\CreditmemoSender + * @var CreditmemoSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Creditmemo|\PHPUnit_Framework_MockObject_MockObject + * @var Creditmemo|MockObject */ protected $creditmemoMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $creditmemoResourceMock; @@ -36,12 +43,12 @@ protected function setUp() $this->stepMockSetup(); $this->creditmemoResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Creditmemo::class, + CreditmemoResource::class, ['saveAttribute'] ); $this->creditmemoMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Creditmemo::class, + Creditmemo::class, [ 'getStore', 'getId', @@ -65,7 +72,7 @@ protected function setUp() ->willReturn(self::ORDER_ID); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\CreditmemoIdentity::class, + CreditmemoIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -113,7 +120,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly(2)) ->method('format') @@ -251,7 +258,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $frontendStatusLabel = 'Complete'; $isNotVirtual = false; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $this->orderMock->expects($this->any()) ->method('getCustomerName') @@ -278,7 +285,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php index ef196ef229c2a..206db7b9c7e0b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php @@ -5,7 +5,14 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\InvoiceIdentity; use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\ResourceModel\Order\Invoice as InvoiceResource; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** * Test for Magento\Sales\Model\Order\Email\Sender\InvoiceSender class. @@ -17,17 +24,17 @@ class InvoiceSenderTest extends AbstractSenderTest private const ORDER_ID = 1; /** - * @var \Magento\Sales\Model\Order\Email\Sender\InvoiceSender + * @var InvoiceSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Invoice|\PHPUnit_Framework_MockObject_MockObject + * @var Invoice|MockObject */ protected $invoiceMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $invoiceResourceMock; @@ -36,12 +43,12 @@ protected function setUp() $this->stepMockSetup(); $this->invoiceResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Invoice::class, + InvoiceResource::class, ['saveAttribute'] ); $this->invoiceMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Invoice::class, + Invoice::class, [ 'getStore', 'getId', @@ -66,7 +73,7 @@ protected function setUp() ->willReturn(self::ORDER_ID); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\InvoiceIdentity::class, + InvoiceIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -114,7 +121,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -252,7 +259,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $billingAddress = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $customerName = 'Test Customer'; $frontendStatusLabel = 'Complete'; $isNotVirtual = false; @@ -266,7 +273,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php index 9590602187f1f..7d5cb7028ddc3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php @@ -5,19 +5,25 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\OrderIdentity; use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use Magento\Sales\Model\ResourceModel\Order; +use PHPUnit\Framework\MockObject\MockObject; class OrderSenderTest extends AbstractSenderTest { private const ORDER_ID = 1; /** - * @var \Magento\Sales\Model\Order\Email\Sender\OrderSender + * @var OrderSender */ protected $sender; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $orderResourceMock; @@ -26,12 +32,12 @@ protected function setUp() $this->stepMockSetup(); $this->orderResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order::class, + Order::class, ['saveAttribute'] ); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\OrderIdentity::class, + OrderIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -91,7 +97,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->method('getCopyMethod') ->willReturn('copy'); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -243,7 +249,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $address = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $createdAtFormatted='Oct 14, 2019, 4:11:58 PM'; $customerName = 'test customer'; $frontendStatusLabel = 'Complete'; @@ -266,7 +272,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->method('getCopyMethod') ->willReturn('copy'); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index 64dd9285ddf88..7dfda5f9e0b41 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -5,7 +5,14 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\ShipmentIdentity; use Magento\Sales\Model\Order\Email\Sender\ShipmentSender; +use Magento\Sales\Model\Order\Shipment; +use Magento\Sales\Model\ResourceModel\Order\Shipment as ShipmentResource; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class @@ -20,17 +27,17 @@ class ShipmentSenderTest extends AbstractSenderTest private const ORDER_ID = 1; /** - * @var \Magento\Sales\Model\Order\Email\Sender\ShipmentSender + * @var ShipmentSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject + * @var Shipment|MockObject */ protected $shipmentMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $shipmentResourceMock; @@ -39,12 +46,12 @@ protected function setUp() $this->stepMockSetup(); $this->shipmentResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Shipment::class, + ShipmentResource::class, ['saveAttribute'] ); $this->shipmentMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Shipment::class, + Shipment::class, [ 'getStore', 'getId', @@ -69,7 +76,7 @@ protected function setUp() ->willReturn(self::ORDER_ID); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class, + ShipmentIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -117,7 +124,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -255,7 +262,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $address = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $customerName = 'Test Customer'; $frontendStatusLabel = 'Complete'; $isNotVirtual = false; @@ -269,7 +276,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php index 9c080d422b0d1..4bca8e6a4b730 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php @@ -21,6 +21,7 @@ use Magento\Sales\Model\Order\Shipment\Sender\EmailSender; use Magento\Sales\Model\ResourceModel\Order\Shipment; use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -42,82 +43,82 @@ class EmailSenderTest extends TestCase private $subject; /** - * @var Order|\PHPUnit_Framework_MockObject_MockObject + * @var Order|MockObject */ private $orderMock; /** - * @var Store|\PHPUnit_Framework_MockObject_MockObject + * @var Store|MockObject */ private $storeMock; /** - * @var Sender|\PHPUnit_Framework_MockObject_MockObject + * @var Sender|MockObject */ private $senderMock; /** - * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ private $loggerMock; /** - * @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentInterface|MockObject */ private $shipmentMock; /** - * @var ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentCommentCreationInterface|MockObject */ private $commentMock; /** - * @var Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ private $addressMock; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $globalConfigMock; /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ private $eventManagerMock; /** - * @var Info|\PHPUnit_Framework_MockObject_MockObject + * @var Info|MockObject */ private $paymentInfoMock; /** - * @var Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ private $paymentHelperMock; /** - * @var Shipment|\PHPUnit_Framework_MockObject_MockObject + * @var Shipment|MockObject */ private $shipmentResourceMock; /** - * @var Renderer|\PHPUnit_Framework_MockObject_MockObject + * @var Renderer|MockObject */ private $addressRendererMock; /** - * @var Template|\PHPUnit_Framework_MockObject_MockObject + * @var Template|MockObject */ private $templateContainerMock; /** - * @var ShipmentIdentity|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentIdentity|MockObject */ private $identityContainerMock; /** - * @var SenderBuilderFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SenderBuilderFactory|MockObject */ private $senderBuilderFactoryMock; From 2a824b03b01a000fd3874ca8a97854b0e55f0aa5 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 9 Apr 2020 11:32:41 +0300 Subject: [PATCH 717/734] Use Action Group to click on minicart --- .../Mftf/Test/CurrencyChangingBundleProductInCartTest.xml | 3 +-- .../CaptchaWithDisabledGuestCheckoutTest.xml | 4 ++-- .../Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml | 2 +- .../Test/AdminConfigureProductImagePlaceholderTest.xml | 4 ++-- ...ProductWithRegularPriceInStockWithCustomOptionsTest.xml | 2 +- ...teCatalogPriceRuleEntityFromConfigurableProductTest.xml | 3 +-- ...inDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml | 2 +- ...ErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml | 7 +++---- .../ShoppingCartAndMiniShoppingCartPerCustomerTest.xml | 3 +-- ...StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml | 2 +- ...StorefrontAddBundleDynamicProductToShoppingCartTest.xml | 3 +-- .../StorefrontAddConfigurableProductToShoppingCartTest.xml | 3 +-- .../StorefrontAddDownloadableProductToShoppingCartTest.xml | 3 +-- .../Test/StorefrontAddGroupedProductToShoppingCartTest.xml | 3 +-- ...AddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml | 3 +-- ...orefrontDeleteBundleProductFromMiniShoppingCartTest.xml | 3 +-- ...ntDeleteDownloadableProductFromMiniShoppingCartTest.xml | 3 +-- ...leteSimpleAndVirtualProductFromMiniShoppingCartTest.xml | 3 +-- ...orefrontDeleteSimpleProductFromMiniShoppingCartTest.xml | 3 +-- ...ctNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml | 2 +- .../Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml | 2 +- .../Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml | 2 +- .../Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- .../Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 +- 24 files changed, 28 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index 0ae7ce836a4a9..b65cf573cc4b3 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -77,8 +77,7 @@ <argument name="product" value="$$simpleProduct2$$"/> <argument name="currency" value="USD - US Dollar"/> </actionGroup> - <click stepKey="openMiniCart" selector="{{StorefrontMinicartSection.showCart}}"/> - <waitForPageLoad stepKey="waitForMiniCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <see stepKey="seeCartSubtotal" userInput="$12,300.00"/> </test> </tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml index c3b43d7788433..40f1c73b81786 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml @@ -38,7 +38,7 @@ <waitForPageLoad stepKey="waitForAddToCart"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> @@ -50,7 +50,7 @@ <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart2"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart2"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index 4aa96c91eb299..c04ee46037123 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -83,7 +83,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.price}}" stepKey="seeProductPriceInMiniCart"/> <seeElement selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="seeCheckOutButtonInMiniCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml index a4414901517b9..cc42c0f4ae821 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml @@ -114,7 +114,7 @@ <seeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="seeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart1"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded1"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart1"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productNoImages.name$$)}}" userInput="src" stepKey="getThumbnailPlaceholderImageSrc"/> <assertContains stepKey="checkThumbnailPlaceholderImage"> <actualResult type="variable">$getThumbnailPlaceholderImageSrc</actualResult> @@ -132,7 +132,7 @@ <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="dontSeeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart2"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded2"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart2"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart2"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productWithImages.name$$)}}" userInput="src" stepKey="getThumbnailImageSrc"/> <assertNotContains stepKey="checkThumbnailImage"> <actualResult type="variable">$getThumbnailImageSrc</actualResult> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index c924c94a9aba8..c23b17d8f157d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -155,7 +155,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeYouAddedSimpleprod4ToYourShoppingCartSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.storefront_new_cartprice}}" stepKey="seeProductPriceInMiniCart"/> </test> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml index 5fb33c9482709..36611a2a86afe 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml @@ -135,8 +135,7 @@ <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added $$createConfigProduct1.name$ to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> <see selector="{{StorefrontMinicartSection.productPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigProduct1.price$$" stepKey="seeCorrectProductPrice1"/> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml index fc37fc893f871..fa2fc59ab5cde 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml @@ -80,7 +80,7 @@ <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> <argument name="productName" value="$$createProduct1.name$$"/> </actionGroup> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> <!-- Assert that the rule isn't present on the Checkout page --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index fd6656b1d1b28..288b02f39bc6d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -41,17 +41,16 @@ <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> <!-- open the minicart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart1"/> <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editProductFromMiniCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart2"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart2"/> <click selector="{{StorefrontMinicartSection.deleteMiniCartItem}}" stepKey="deleteMiniCartItem"/> <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitFortheConfirmationModal"/> <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> <waitForPageLoad stepKey="waitForDeleteToFinish"/> <dontSeeElement selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="dontSeeDeleteProductFromCheckoutCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <waitForPageLoad stepKey="WaitForPageLoad3"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index 741fde91f851e..aad28b9107559 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -168,8 +168,7 @@ <!-- Assert product in mini cart --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> <argument name="productPrice" value="$$createSimpleProduct.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml index f7ae9d29ada55..fd8bfe1c31f25 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml @@ -45,7 +45,7 @@ </assertEquals> <!--Open minicart and change Qty--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.quantity}}" stepKey="waitForElementQty"/> <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="deleteFiled"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" stepKey="changeQty"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml index 5e5278a256194..ca2c236753507 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml @@ -111,8 +111,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$50.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index b8e70d7492539..0d6f9d07b7aba 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -157,8 +157,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createConfigProduct.name$$"/> <argument name="productPrice" value="$$createConfigChildProduct2.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index e098c15c0eb6a..228da8a87dea5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -77,8 +77,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml index 0fc4cee5e0582..92ec1f619e030 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml @@ -101,8 +101,7 @@ </actionGroup> <!-- Assert product1 details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simple3.name$$"/> <argument name="productPrice" value="$300.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml index 14788250a9bca..fd80af59109c5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml @@ -112,8 +112,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$60.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml index fe320fb276c8a..369cb53d8d710 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml @@ -65,8 +65,7 @@ <waitForPageLoad stepKey="waitForMiniCartPanelToAppear"/> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index 3c234602df17a..202bbb76c81b2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -49,8 +49,7 @@ <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml index 643c6d8c14dd7..0530a1c6142cd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml @@ -47,8 +47,7 @@ </actionGroup> <!-- Assert Simple and Virtual products in mini cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProductInMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml index a7bb3d927f620..2cfc473ec4039 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml @@ -36,8 +36,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index e6e5c4f1a9299..4d8e9ead40016 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -69,7 +69,7 @@ <waitForPageLoad stepKey="waitForStoreView"/> <!--Check product name in Minicart--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <grabTextFrom selector="{{StorefrontMinicartSection.productName}}" stepKey="grabProductNameMinicart"/> <assertContains stepKey="assertProductNameMinicart"> <actualResult type="const">$grabProductNameMinicart</actualResult> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 542c0ed6586e9..a39b0b03dc55e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -68,7 +68,7 @@ </actionGroup> <!--Proceed to shipment--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickToOpenCard"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickToOpenCard"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="clickToProceedToCheckout"/> <waitForPageLoad stepKey="waitForTheFormIsOpened"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml index c2ac4dde21c35..37e11dda29a54 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -60,7 +60,7 @@ <waitForPageLoad stepKey="waitForMiniCart"/> <!-- Edit Item in Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/> <!-- Check if Product Configuration is still selected --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 1411f7b292757..93ff0275d9e4b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -36,7 +36,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <fillField selector="{{CheckoutShippingGuestInfoSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 09197963434eb..189e90e1ab668 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -66,7 +66,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverOverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductToAdd"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <!-- fill out customer information --> From 3edb1f8d841be19db6cc9e00108df75f001f66d5 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 9 Apr 2020 12:07:05 +0300 Subject: [PATCH 718/734] fixed own mistake --- .../Mftf/Test/CurrencyChangingBundleProductInCartTest.xml | 2 +- .../CaptchaWithDisabledGuestCheckoutTest.xml | 4 ++-- .../Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml | 2 +- .../Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml | 4 ++-- ...eProductWithRegularPriceInStockWithCustomOptionsTest.xml | 2 +- ...eteCatalogPriceRuleEntityFromConfigurableProductTest.xml | 2 +- ...minDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml | 2 +- ...oErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml | 6 +++--- .../Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml | 2 +- .../StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml | 2 +- .../StorefrontAddBundleDynamicProductToShoppingCartTest.xml | 2 +- .../StorefrontAddConfigurableProductToShoppingCartTest.xml | 2 +- .../StorefrontAddDownloadableProductToShoppingCartTest.xml | 2 +- .../Test/StorefrontAddGroupedProductToShoppingCartTest.xml | 2 +- ...tAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml | 2 +- ...torefrontDeleteBundleProductFromMiniShoppingCartTest.xml | 2 +- ...ontDeleteDownloadableProductFromMiniShoppingCartTest.xml | 2 +- ...eleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml | 2 +- ...torefrontDeleteSimpleProductFromMiniShoppingCartTest.xml | 2 +- ...uctNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml | 2 +- .../Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml | 2 +- .../Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml | 2 +- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- .../Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 +- 24 files changed, 28 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index b65cf573cc4b3..ada91d068efcf 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -77,7 +77,7 @@ <argument name="product" value="$$simpleProduct2$$"/> <argument name="currency" value="USD - US Dollar"/> </actionGroup> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <see stepKey="seeCartSubtotal" userInput="$12,300.00"/> </test> </tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml index 40f1c73b81786..bfea4e99996c3 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml @@ -38,7 +38,7 @@ <waitForPageLoad stepKey="waitForAddToCart"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> @@ -50,7 +50,7 @@ <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitForPageLoad2"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart2"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index c04ee46037123..52da8c70a3bc8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -83,7 +83,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.price}}" stepKey="seeProductPriceInMiniCart"/> <seeElement selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="seeCheckOutButtonInMiniCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml index cc42c0f4ae821..85378ecfb76a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml @@ -114,7 +114,7 @@ <seeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="seeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart1"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded1"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart1"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productNoImages.name$$)}}" userInput="src" stepKey="getThumbnailPlaceholderImageSrc"/> <assertContains stepKey="checkThumbnailPlaceholderImage"> <actualResult type="variable">$getThumbnailPlaceholderImageSrc</actualResult> @@ -132,7 +132,7 @@ <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="dontSeeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart2"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded2"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart2"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productWithImages.name$$)}}" userInput="src" stepKey="getThumbnailImageSrc"/> <assertNotContains stepKey="checkThumbnailImage"> <actualResult type="variable">$getThumbnailImageSrc</actualResult> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index c23b17d8f157d..855a2b1d9b0cc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -155,7 +155,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeYouAddedSimpleprod4ToYourShoppingCartSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.storefront_new_cartprice}}" stepKey="seeProductPriceInMiniCart"/> </test> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml index 36611a2a86afe..6b34fd1e67e9b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml @@ -135,7 +135,7 @@ <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added $$createConfigProduct1.name$ to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> <see selector="{{StorefrontMinicartSection.productPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigProduct1.price$$" stepKey="seeCorrectProductPrice1"/> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml index fa2fc59ab5cde..49a9d5e8ae51b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml @@ -80,7 +80,7 @@ <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> <argument name="productName" value="$$createProduct1.name$$"/> </actionGroup> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> <!-- Assert that the rule isn't present on the Checkout page --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index 288b02f39bc6d..ceaf72fff83bb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -41,16 +41,16 @@ <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> <!-- open the minicart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart1"/> <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editProductFromMiniCart"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart2"/> <click selector="{{StorefrontMinicartSection.deleteMiniCartItem}}" stepKey="deleteMiniCartItem"/> <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitFortheConfirmationModal"/> <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> <waitForPageLoad stepKey="waitForDeleteToFinish"/> <dontSeeElement selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="dontSeeDeleteProductFromCheckoutCart"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index aad28b9107559..1ed6912f2ca46 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -168,7 +168,7 @@ <!-- Assert product in mini cart --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> <argument name="productPrice" value="$$createSimpleProduct.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml index fd8bfe1c31f25..1f9948f80f391 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml @@ -45,7 +45,7 @@ </assertEquals> <!--Open minicart and change Qty--> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.quantity}}" stepKey="waitForElementQty"/> <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="deleteFiled"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" stepKey="changeQty"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml index ca2c236753507..e82f3c0588835 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml @@ -111,7 +111,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$50.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index 0d6f9d07b7aba..21e785de6cab3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -157,7 +157,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createConfigProduct.name$$"/> <argument name="productPrice" value="$$createConfigChildProduct2.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index 228da8a87dea5..bbc0a29000a77 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -77,7 +77,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml index 92ec1f619e030..3e2f32a4ab055 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml @@ -101,7 +101,7 @@ </actionGroup> <!-- Assert product1 details in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simple3.name$$"/> <argument name="productPrice" value="$300.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml index fd80af59109c5..265f9a7cbbc98 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml @@ -112,7 +112,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$60.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml index 369cb53d8d710..d116d0049c9df 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml @@ -65,7 +65,7 @@ <waitForPageLoad stepKey="waitForMiniCartPanelToAppear"/> <!-- Assert Product in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index 202bbb76c81b2..8a52fa7740b95 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -49,7 +49,7 @@ <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!-- Assert product details in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml index 0530a1c6142cd..21966875519dc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml @@ -47,7 +47,7 @@ </actionGroup> <!-- Assert Simple and Virtual products in mini cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProductInMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml index 2cfc473ec4039..effd376ab4bfb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml @@ -36,7 +36,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 4d8e9ead40016..f666226233b6d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -69,7 +69,7 @@ <waitForPageLoad stepKey="waitForStoreView"/> <!--Check product name in Minicart--> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <grabTextFrom selector="{{StorefrontMinicartSection.productName}}" stepKey="grabProductNameMinicart"/> <assertContains stepKey="assertProductNameMinicart"> <actualResult type="const">$grabProductNameMinicart</actualResult> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index a39b0b03dc55e..43d20e0d676ea 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -68,7 +68,7 @@ </actionGroup> <!--Proceed to shipment--> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickToOpenCard"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickToOpenCard"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="clickToProceedToCheckout"/> <waitForPageLoad stepKey="waitForTheFormIsOpened"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml index 37e11dda29a54..10d8aeb875742 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -60,7 +60,7 @@ <waitForPageLoad stepKey="waitForMiniCart"/> <!-- Edit Item in Cart --> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/> <!-- Check if Product Configuration is still selected --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 93ff0275d9e4b..cdc81c66b833d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -36,7 +36,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <fillField selector="{{CheckoutShippingGuestInfoSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 189e90e1ab668..36f8df7b271d3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -66,7 +66,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverOverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductToAdd"/> - <actionGroup name="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <!-- fill out customer information --> From 88cb58901068236c9a57e557d5471744391617c2 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Thu, 9 Apr 2020 15:37:35 +0300 Subject: [PATCH 719/734] add AdminClickFirstRowOrderGridActionGroup --- .../Test/AdminDashboardWithChartsChart.xml | 7 +++---- ...ProductWithZeroPriceToShoppingCartTest.xml | 4 ++-- ...ctCustomOptionsDifferentStoreViewsTest.xml | 4 ++-- ...thCustomOptionsWithLongValuesTitleTest.xml | 4 ++-- ...tedProductToConfigurableOutOfStockTest.xml | 3 +-- ...efrontApplyPromoCodeDuringCheckoutTest.xml | 4 ++-- .../StorefrontGuestCheckoutTest.xml | 3 +-- ...SubtotalOrdersWithProcessingStatusTest.xml | 4 ++-- .../ProductsQtyReturnAfterOrderCancelTest.xml | 3 +-- .../Test/AdminCreatingShippingLabelTest.xml | 2 +- ...AdminClickFirstRowOrderGridActionGroup.xml | 19 +++++++++++++++++++ ...ectnessInvoicedItemInBundleProductTest.xml | 3 +-- .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 3 +-- ...editMemoTotalAfterShippingDiscountTest.xml | 3 +-- 14 files changed, 39 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index 147f2fa1dfaed..55b1648d1f23f 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -83,8 +83,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForSearchingOrder"/> <!-- Create invoice --> <comment userInput="Create invoice" stepKey="createInvoice"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForInvoicePageToLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> @@ -95,8 +95,7 @@ <comment userInput="Create Shipment for the order" stepKey="createShipmentForOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage2"/> <waitForPageLoad time="30" stepKey="waitForOrderListPageLoading"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="openOrderPageForShip"/> - <waitForPageLoad stepKey="waitForOrderDetailsPage"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="openOrderPageForShip"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> <waitForPageLoad stepKey="waitForShipmentPagePage"/> <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 1a40489387197..5c75441f85b9c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -70,8 +70,8 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForAdminOrderPageLoad"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="$0.00" stepKey="checkSubtotal"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index 95af6e299662b..cfa82ab18e9a4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -199,8 +199,8 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="openOrdersGrid"> <argument name="orderId" value="{$grabOrderNumber}"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad10"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <!-- Checking the correctness of displayed custom options for user parameters on Order --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml index 3c1dc2bc844cf..f539940f1cdc1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml @@ -104,8 +104,8 @@ <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <!-- Checking the correctness of displayed custom options for user parameters on Order --> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index b291fb46ae71c..84da79f64f700 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -124,8 +124,7 @@ <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml index e4eb53a1f1925..5b48c014ef713 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml @@ -83,8 +83,8 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForAdminOrderPageLoad"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <scrollTo selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="scrollToOrderTotalSection"/> <see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="$$createProduct.price$$" stepKey="checkTotal"/> </test> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml index 5c06f9ed55067..9c2c18242c3b3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml @@ -64,8 +64,7 @@ <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 542c0ed6586e9..75e6f44c8f776 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -93,8 +93,8 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> + + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <!--Verify that Created order is in Processing status--> <see selector="{{AdminShipmentOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeShipmentOrderStatus"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index 355c4fa049f01..a01bcb786795a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -70,8 +70,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="1" stepKey="ChangeQtyToInvoice"/> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index 3850bfabc0c34..bd84dce153001 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -109,7 +109,7 @@ <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchOrder"> <argument name="keyword" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <!--Create Invoice--> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> <!--Create shipping label--> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml new file mode 100644 index 0000000000000..ffbbee5d0c276 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClickFirstRowOrderGridActionGroup"> + <annotations> + <description>Click on first row of Order Grid.</description> + </annotations> + + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickFirstOrderRow"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 69e3ade9231d0..3b4e3b79c1142 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -81,8 +81,7 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <actionGroup ref="GoToInvoiceIntoOrderActionGroup" stepKey="goToInvoiceIntoOrderPage"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="5" stepKey="ChangeQtyToInvoice"/> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQuantity"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 1411f7b292757..386f736525ebb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -68,8 +68,7 @@ <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 09197963434eb..1f35a42b5a657 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -103,8 +103,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> <!-- Create invoice --> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> From 2027d8ac986621f537df9c8377ff8facd7cb885c Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Thu, 9 Apr 2020 10:02:45 -0500 Subject: [PATCH 720/734] MQE-2046: remove deprecated entities usages in MFTF tests --- .../Backend/Test/Mftf/Section/AdminMessagesSection.xml | 7 +++---- .../Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index c1d5599f9e1c2..7983554051f89 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -9,9 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> - <!-- Keep both Set1 and Set2 elements --> - <!-- Neither Set1 nor Set2 works for all Magento blocks --> - <!-- Set1 --> + <!-- Keep both sets of elements --> + <!-- Set One - more specific, works for most of message blocks --> <element name="success" type="text" selector="#messages div.message-success"/> <element name="nthSuccess" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> <element name="error" type="text" selector="#messages div.message-error"/> @@ -19,7 +18,7 @@ <element name="messageByType" type="text" selector="#messages div.message-{{messageType}}" parameterized="true" /> <element name="warning" type="text" selector="#messages div.message-warning"/> <element name="accessDenied" type="text" selector=".access-denied-page"/> - <!-- Set2 --> + <!-- Set Two - more generic, needed for message blocks in Product Import, Staging Update, etc --> <element name="successMessage" type="text" selector=".message-success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> <element name="warningMessage" type="text" selector=".message-warning"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index 0a94bf8a52aaf..111ba187c6f26 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -30,6 +30,6 @@ <argument name="importFile" value="importSpecChars.csv"/> </actionGroup> <see selector="{{AdminImportHeaderSection.messageNote}}" userInput='File must be saved in UTF-8 encoding for proper import' stepKey="seeNoteMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> </test> </tests> From baebe46dee141a95bf67695ee1bbf4b30af49018 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Thu, 9 Apr 2020 16:47:42 -0500 Subject: [PATCH 721/734] MC-33086: [Improvement] Apply Event To Category observer --- app/code/Magento/Catalog/Block/Product/ListProduct.php | 9 ++++++++- app/code/Magento/Catalog/Model/Product.php | 9 +++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index 59f5cc1b53b26..76fcdfbf232e5 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -136,7 +136,14 @@ public function getLayer() */ public function getLoadedProductCollection() { - return $this->_getProductCollection(); + $collection = $this->_getProductCollection(); + + $categoryId = $this->getLayer()->getCurrentCategory()->getId(); + foreach ($collection as $product) { + $product->setData('category_id', $categoryId); + } + + return $collection; } /** diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index a9907c1661bd8..fd59130db4bdf 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -725,9 +725,14 @@ public function getIdBySku($sku) */ public function getCategoryId() { + if ($this->hasData('category_id')) { + return $this->getData('category_id'); + } $category = $this->_registry->registry('current_category'); - if ($category && in_array($category->getId(), $this->getCategoryIds())) { - return $category->getId(); + $categoryId = $category ? $category->getId() : null; + if ($categoryId && in_array($categoryId, $this->getCategoryIds())) { + $this->setData('category_id', $categoryId); + return $categoryId; } return false; } From d4b108a26b3b0bdf56ba9cde55cb142f419c088d Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 10 Apr 2020 09:18:21 +0300 Subject: [PATCH 722/734] MC-33278: [Magento Cloud] - Magento\Framework\App\DeploymentConfig\Reader::load function is taking too much time --- .../Magento/Developer/Model/Logger/Handler/DebugTest.php | 2 +- lib/internal/Magento/Framework/App/DeploymentConfig.php | 4 +++- lib/internal/Magento/Framework/Locale/Resolver.php | 4 ++-- lib/internal/Magento/Framework/Module/ModuleList.php | 7 ++----- .../Magento/Framework/Module/Test/Unit/ModuleListTest.php | 5 ----- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index fec71206accd8..30e70bd8f10d6 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -139,7 +139,7 @@ public function testDebugInDeveloperMode() { $message = 'test message'; $this->reinitDebugHandler(State::MODE_DEVELOPER); - + $this->deploymentConfig->resetData(); $this->removeDebugLog(); $this->logger->debug($message); $this->assertFileExists($this->getDebuggerLogPath()); diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index ddd7faa80b906..6aeec2c2d0192 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -15,6 +15,7 @@ * Application deployment configuration * * @api + * @since 100.0.2 */ class DeploymentConfig { @@ -148,7 +149,7 @@ public function isDbAvailable() */ private function load() { - if (null === $this->data) { + if (empty($this->data)) { $this->data = $this->reader->load(); if ($this->overrideData) { $this->data = array_replace($this->data, $this->overrideData); @@ -183,6 +184,7 @@ private function flattenParams(array $params, $path = null, array &$flattenResul $newPath = $key; } if (isset($flattenResult[$newPath])) { + //phpcs:ignore Magento2.Exceptions.DirectThrow throw new RuntimeException(new Phrase("Key collision '%1' is already defined.", [$newPath])); } $flattenResult[$newPath] = $param; diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index d058bfd41ab1a..55ef2a4e9a30c 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -15,7 +15,7 @@ class Resolver implements ResolverInterface { /** - * Default locale + * Resolver default locale */ const DEFAULT_LOCALE = 'en_US'; @@ -79,7 +79,7 @@ public function __construct( $this->scopeConfig = $scopeConfig; $this->defaultLocalePath = $defaultLocalePath; $this->scopeType = $scopeType; - $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class); + $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class); $this->setLocale($locale); } diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php index 5a60a1c102b05..b3cf433bbaf45 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList.php +++ b/lib/internal/Magento/Framework/Module/ModuleList.php @@ -140,11 +140,8 @@ public function isModuleInfoAvailable() */ private function loadConfigData() { - if (null === $this->configData) { - $this->config->resetData(); - if (null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) { - $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES); - } + if (null === $this->configData && null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) { + $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES); } } } diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php index 3142bbbc6771a..363f90501dd0d 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php @@ -47,7 +47,6 @@ protected function setUp() public function testGetAll() { - $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(); $this->setLoadConfigExpectation(); $expected = ['foo' => self::$allFixture['foo']]; @@ -65,7 +64,6 @@ public function testGetAllNoData() public function testGetOne() { - $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(); $this->setLoadConfigExpectation(); $this->assertSame(['key' => 'value'], $this->model->getOne('foo')); @@ -74,7 +72,6 @@ public function testGetOne() public function testGetNames() { - $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(false); $this->setLoadConfigExpectation(); $this->assertSame(['foo'], $this->model->getNames()); @@ -83,7 +80,6 @@ public function testGetNames() public function testHas() { - $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(false); $this->setLoadConfigExpectation(); $this->assertTrue($this->model->has('foo')); @@ -92,7 +88,6 @@ public function testHas() public function testIsModuleInfoAvailable() { - $this->config->expects($this->once())->method('resetData'); $this->setLoadConfigExpectation(true); $this->assertTrue($this->model->isModuleInfoAvailable()); } From aa8ed066962411571cf605f5b80efec9da26ddf3 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 10 Apr 2020 11:03:03 +0300 Subject: [PATCH 723/734] MC-33278: [Magento Cloud] - Magento\Framework\App\DeploymentConfig\Reader::load function is taking too much time --- .../App/Test/Unit/DeploymentConfigTest.php | 12 +++++++----- .../Framework/Module/Test/Unit/ModuleListTest.php | 13 +++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php index 81a4f842bdf1d..f93153f3cd75c 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php @@ -6,10 +6,12 @@ namespace Magento\Framework\App\Test\Unit; -use \Magento\Framework\App\DeploymentConfig; -use \Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DeploymentConfigTest extends \PHPUnit\Framework\TestCase +class DeploymentConfigTest extends TestCase { /** * @var array @@ -63,7 +65,7 @@ class DeploymentConfigTest extends \PHPUnit\Framework\TestCase protected $_deploymentConfigMerged; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $reader; @@ -124,7 +126,7 @@ public function testNotAvailable(): void */ public function testNotAvailableThenAvailable(): void { - $this->reader->expects($this->once())->method('load')->willReturn([]); + $this->reader->expects($this->once())->method('load')->willReturn(['Test']); $object = new DeploymentConfig($this->reader); $this->assertFalse($object->isAvailable()); $this->assertFalse($object->isAvailable()); diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php index 363f90501dd0d..f8b02d177e097 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php @@ -5,9 +5,14 @@ */ namespace Magento\Framework\Module\Test\Unit; -use \Magento\Framework\Module\ModuleList; +use Magento\Framework\Module\ModuleList; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ModuleListTest extends \PHPUnit\Framework\TestCase +/** + * Test for module list + */ +class ModuleListTest extends TestCase { /** * Fixture for all modules' meta-information @@ -24,12 +29,12 @@ class ModuleListTest extends \PHPUnit\Framework\TestCase private static $enabledFixture = ['foo' => 1, 'bar' => 0]; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $config; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $loader; From 0621f4f5179b5a42c81c8ba7eac779d1b1efb0f3 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 10 Apr 2020 12:27:51 +0300 Subject: [PATCH 724/734] MC-32414: API call bug on add to cart as guest --- .../Quote/Model/Quote/Item/Compare.php | 13 +- .../Unit/Model/Quote/Item/CompareTest.php | 197 ++++++++---------- 2 files changed, 94 insertions(+), 116 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/Compare.php b/app/code/Magento/Quote/Model/Quote/Item/Compare.php index 76ba324518dc1..abe8b0d966050 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Compare.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Compare.php @@ -71,17 +71,14 @@ public function compare(Item $target, Item $compared) if ($target->getProductId() != $compared->getProductId()) { return false; } - $targetOptions = $this->getOptions($target); - $comparedOptions = $this->getOptions($compared); - if (array_diff_key($targetOptions, $comparedOptions) != array_diff_key($comparedOptions, $targetOptions) - ) { + $targetOptionByCode = $target->getOptionsByCode(); + $comparedOptionsByCode = $compared->getOptionsByCode(); + if (!$target->compareOptions($targetOptionByCode, $comparedOptionsByCode)) { return false; } - foreach ($targetOptions as $name => $value) { - if ($comparedOptions[$name] != $value) { - return false; - } + if (!$target->compareOptions($comparedOptionsByCode, $targetOptionByCode)) { + return false; } return true; } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php index c3e69237fc2bb..e5e267713f963 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php @@ -3,57 +3,65 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Quote\Test\Unit\Model\Quote\Item; +use Magento\Framework\Serialize\JsonValidator; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Item; +use Magento\Quote\Model\Quote\Item\Compare; +use Magento\Quote\Model\Quote\Item\Option; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class CompareTest + * Tests the class that is used to compare Quote Item Options */ -class CompareTest extends \PHPUnit\Framework\TestCase +class CompareTest extends TestCase { /** - * @var \Magento\Quote\Model\Quote\Item\Compare + * @var Compare */ private $helper; /** - * @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject + * @var Item|MockObject */ private $itemMock; /** - * @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject + * @var Item|MockObject */ private $comparedMock; /** - * @var \Magento\Quote\Model\Quote\Item\Option|\PHPUnit_Framework_MockObject_MockObject + * @var Option|MockObject */ private $optionMock; /** - * @var \Magento\Framework\Serialize\JsonValidator|\PHPUnit_Framework_MockObject_MockObject + * @var JsonValidator|MockObject */ private $jsonValidatorMock; /** - * test setUp + * @inheritdoc */ protected function setUp() { $this->itemMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Item::class, - ['__wakeup', 'getProductId', 'getOptions'] + Item::class, + ['__wakeup', 'getProductId', 'getOptions', 'getOptionsByCode'] ); $this->comparedMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Item::class, - ['__wakeup', 'getProductId', 'getOptions'] + Item::class, + ['__wakeup', 'getProductId', 'getOptions', 'getOptionsByCode'] ); $this->optionMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Item\Option::class, + Option::class, ['__wakeup', 'getCode', 'getValue'] ); - $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + $serializer = $this->getMockBuilder(Json::class) ->setMethods(['unserialize']) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -65,13 +73,13 @@ function ($value) { } ); - $this->jsonValidatorMock = $this->getMockBuilder(\Magento\Framework\Serialize\JsonValidator::class) + $this->jsonValidatorMock = $this->getMockBuilder(JsonValidator::class) ->disableOriginalConstructor() ->getMock(); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $this->helper = $objectManagerHelper->getObject( - \Magento\Quote\Model\Quote\Item\Compare::class, + Compare::class, [ 'serializer' => $serializer, 'jsonValidator' => $this->jsonValidatorMock @@ -82,17 +90,17 @@ function ($value) { /** * @param string $code * @param mixed $value - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ protected function getOptionMock($code, $value) { $optionMock = clone $this->optionMock; - $optionMock->expects($this->once()) + $optionMock->expects($this->any()) ->method('getCode') - ->will($this->returnValue($code)); - $optionMock->expects($this->once()) + ->willReturn($code); + $optionMock->expects($this->any()) ->method('getValue') - ->will($this->returnValue($value)); + ->willReturn($value); return $optionMock; } @@ -103,10 +111,10 @@ public function testCompareDifferentProduct() { $this->itemMock->expects($this->once()) ->method('getProductId') - ->will($this->returnValue(1)); + ->willReturn(1); $this->itemMock->expects($this->once()) ->method('getProductId') - ->will($this->returnValue(2)); + ->willReturn(2); $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); } @@ -116,36 +124,47 @@ public function testCompareDifferentProduct() */ public function testCompareProductWithDifferentOptions() { + // Identical Product Ids $this->itemMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); + ->willReturn(1); $this->comparedMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); + ->willReturn(1); + // Identical Option Keys $this->itemMock->expects($this->any()) ->method('getOptions') - ->will( - $this->returnValue( - [ - $this->getOptionMock('option-1', 1), - $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', json_encode(['value' => 'value-1', 'qty' => 2])) - ] - ) - ); + ->willReturn([$this->getOptionMock('identical', 'value')]); $this->comparedMock->expects($this->any()) ->method('getOptions') - ->will($this->returnValue( + ->willReturn([$this->getOptionMock('identical', 'value')]); + + // Different Option Values + $this->itemMock->expects($this->once()) + ->method('getOptionsByCode') + ->willReturn( [ - $this->getOptionMock('option-4', 1), - $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', json_encode([ - 'value' => 'value-1', - 'qty' => 2, - ])), + 'info_buyRequest' => $this->getOptionMock('info_buyRequest', ['value-1']), + 'option' => $this->getOptionMock('option', 1), + 'simple_product' => $this->getOptionMock('simple_product', 3), + 'product_qty_2' => $this->getOptionMock('product_qty_2', 10), + 'attributes' => $this->getOptionMock('attributes', 93), ] - )); + ); + + $this->comparedMock->expects($this->once()) + ->method('getOptionsByCode') + ->willReturn( + [ + 'info_buyRequest' => $this->getOptionMock('info_buyRequest', ['value-2']), + 'option' => $this->getOptionMock('option', 1), + 'simple_product' => $this->getOptionMock('simple_product', 3), + 'product_qty_2' => $this->getOptionMock('product_qty_2', 10), + 'attributes' => $this->getOptionMock('attributes', 94), + ] + ); + $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); } @@ -156,24 +175,24 @@ public function testCompareItemWithComparedWithoutOption() { $this->itemMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); + ->willReturn(1); $this->comparedMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); - $this->itemMock->expects($this->any()) - ->method('getOptions') - ->will( - $this->returnValue( - [ - $this->getOptionMock('option-1', 1), - $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', json_encode(['value' => 'value-1', 'qty' => 2])), - ] - ) + ->willReturn(1); + $this->itemMock->expects($this->once()) + ->method('getOptionsByCode') + ->willReturn( + [ + 'info_buyRequest' => $this->getOptionMock('info_buyRequest', ['value-1']), + 'option' => $this->getOptionMock('option', 1), + 'simple_product' => $this->getOptionMock('simple_product', 3), + 'product_qty_2' => $this->getOptionMock('product_qty_2', 10), + 'attributes' => $this->getOptionMock('attributes', 93), + ] ); $this->comparedMock->expects($this->any()) - ->method('getOptions') - ->will($this->returnValue([])); + ->method('getOptionsByCode') + ->willReturn([]); $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); } @@ -184,62 +203,24 @@ public function testCompareItemWithoutOptionWithCompared() { $this->itemMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); + ->willReturn(1); $this->comparedMock->expects($this->any()) ->method('getProductId') - ->will($this->returnValue(1)); - $this->comparedMock->expects($this->any()) - ->method('getOptions') - ->will($this->returnValue( - [ - $this->getOptionMock('option-1', 1), - $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock( - 'option-3', - json_encode(['value' => 'value-1', 'qty' => 2]) - ), - ] - )); - $this->itemMock->expects($this->any()) - ->method('getOptions') - ->will($this->returnValue([])); - $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); - } - - /** - * Verify that compare ignores empty options. - */ - public function testCompareWithEmptyValues() - { - $itemOptionValue = '{"non-empty-option":"test","empty_option":""}'; - $comparedOptionValue = '{"non-empty-option":"test"}'; - - $this->jsonValidatorMock->expects($this->any()) - ->method('isValid') - ->willReturn(true); - - $this->itemMock->expects($this->any()) - ->method('getProductId') - ->will($this->returnValue(1)); - $this->comparedMock->expects($this->any()) - ->method('getProductId') - ->will($this->returnValue(1)); - - $this->itemMock->expects($this->once()) - ->method('getOptions') - ->willReturn( - [ - $this->getOptionMock('option-1', $itemOptionValue) - ] - ); + ->willReturn(1); $this->comparedMock->expects($this->once()) - ->method('getOptions') + ->method('getOptionsByCode') ->willReturn( [ - $this->getOptionMock('option-1', $comparedOptionValue) + 'info_buyRequest' => $this->getOptionMock('info_buyRequest', ['value-2']), + 'option' => $this->getOptionMock('option', 1), + 'simple_product' => $this->getOptionMock('simple_product', 3), + 'product_qty_2' => $this->getOptionMock('product_qty_2', 10), + 'attributes' => $this->getOptionMock('attributes', 94), ] ); - - $this->assertTrue($this->helper->compare($this->itemMock, $this->comparedMock)); + $this->itemMock->expects($this->any()) + ->method('getOptionsByCode') + ->willReturn([]); + $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); } } From d18adc4aca8bdb91ab8f3f65bbb501377f355376 Mon Sep 17 00:00:00 2001 From: Michail Slabko <mslabko@magento.com> Date: Fri, 10 Apr 2020 12:15:45 -0500 Subject: [PATCH 725/734] MC-33086: [Improvement] Apply Event To Category observer - fix tests --- .../testsuite/Magento/Catalog/Block/Product/ListTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListTest.php index 6c735e52f8c1a..25e26bc66ab51 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListTest.php @@ -92,8 +92,10 @@ public function testGetAdditionalHtml() public function testSetCollection() { - $this->_block->setCollection('test'); - $this->assertEquals('test', $this->_block->getLoadedProductCollection()); + $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Framework\Data\Collection::class); + $this->_block->setCollection($collection); + $this->assertEquals($collection, $this->_block->getLoadedProductCollection()); } public function testGetPriceBlockTemplate() From 8ac3a67b1c44802387264c8902b3987de0e82f1b Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Fri, 10 Apr 2020 22:30:20 +0300 Subject: [PATCH 726/734] Renamed ActionGroup --- .../Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml | 4 ++-- ...refrontAddBundleProductWithZeroPriceToShoppingCartTest.xml | 2 +- ...ontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml | 2 +- ...urchaseProductWithCustomOptionsWithLongValuesTitleTest.xml | 2 +- .../Test/AssociatedProductToConfigurableOutOfStockTest.xml | 2 +- .../Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml | 2 +- .../StorefrontGuestCheckoutTest.xml | 2 +- .../Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml | 2 +- .../Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml | 2 +- .../Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml | 2 +- ...onGroup.xml => AdminOrderGridClickFirstRowActionGroup.xml} | 2 +- .../Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml | 2 +- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- .../Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) rename app/code/Magento/Sales/Test/Mftf/ActionGroup/{AdminClickFirstRowOrderGridActionGroup.xml => AdminOrderGridClickFirstRowActionGroup.xml} (90%) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index 55b1648d1f23f..972947656cd3d 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -84,7 +84,7 @@ <!-- Create invoice --> <comment userInput="Create invoice" stepKey="createInvoice"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForInvoicePageToLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> @@ -95,7 +95,7 @@ <comment userInput="Create Shipment for the order" stepKey="createShipmentForOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage2"/> <waitForPageLoad time="30" stepKey="waitForOrderListPageLoading"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="openOrderPageForShip"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="openOrderPageForShip"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> <waitForPageLoad stepKey="waitForShipmentPagePage"/> <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 5c75441f85b9c..b3c542af7bbc9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -71,7 +71,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="$0.00" stepKey="checkSubtotal"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index cfa82ab18e9a4..767e0c88b7af2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -200,7 +200,7 @@ <argument name="orderId" value="{$grabOrderNumber}"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <!-- Checking the correctness of displayed custom options for user parameters on Order --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml index f539940f1cdc1..fbecc15a59b1f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml @@ -105,7 +105,7 @@ <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <!-- Checking the correctness of displayed custom options for user parameters on Order --> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index 84da79f64f700..95d3943d04b80 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -124,7 +124,7 @@ <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml index 5b48c014ef713..3648ac361ccc9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml @@ -84,7 +84,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <scrollTo selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="scrollToOrderTotalSection"/> <see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="$$createProduct.price$$" stepKey="checkTotal"/> </test> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml index 9c2c18242c3b3..0d69306a4b1ba 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml @@ -64,7 +64,7 @@ <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 75e6f44c8f776..0dd4d0be6c808 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -94,7 +94,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <!--Verify that Created order is in Processing status--> <see selector="{{AdminShipmentOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeShipmentOrderStatus"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index a01bcb786795a..a8856288b422a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -70,7 +70,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="1" stepKey="ChangeQtyToInvoice"/> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index bd84dce153001..1c61bd290f005 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -109,7 +109,7 @@ <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchOrder"> <argument name="keyword" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <!--Create Invoice--> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> <!--Create shipping label--> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridClickFirstRowActionGroup.xml similarity index 90% rename from app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml rename to app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridClickFirstRowActionGroup.xml index ffbbee5d0c276..92b67af7d7f5a 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickFirstRowOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridClickFirstRowActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminClickFirstRowOrderGridActionGroup"> + <actionGroup name="AdminOrderGridClickFirstRowActionGroup"> <annotations> <description>Click on first row of Order Grid.</description> </annotations> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 3b4e3b79c1142..b8612f7f795fb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -81,7 +81,7 @@ <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <actionGroup ref="GoToInvoiceIntoOrderActionGroup" stepKey="goToInvoiceIntoOrderPage"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="5" stepKey="ChangeQtyToInvoice"/> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQuantity"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 386f736525ebb..1796a46a2af26 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -68,7 +68,7 @@ <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 1f35a42b5a657..ca35d7f8c8a90 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -103,7 +103,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> <!-- Create invoice --> - <actionGroup ref="AdminClickFirstRowOrderGridActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageToLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> From df6d0c5233b02fcb0a39cd3d82413ae9b1e717d4 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Fri, 10 Apr 2020 19:12:08 -0500 Subject: [PATCH 727/734] MQE-2069: [MTF-to-MFTF] Create Newsletter Template Entity #761 --- .../AdminIntegrationOpenExistingEntityActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml index 8d9af21c63ef6..5631da6213bfe 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminIntegrationOpenExistingEntityActionGroup.xml @@ -8,7 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminIntegrationOpenExistingEntityActionGroup"> <click stepKey="clickEditIcon" selector="{{AdminIntegrationsGridSection.edit}}"/> </actionGroup> From 6fe59941502035b484c34eadad6938c63c036bc6 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 10 Apr 2020 21:11:38 -0500 Subject: [PATCH 728/734] MQE-2046: remove deprecated entities usages in MFTF tests --- ...torefrontSortBundleProductsByPriceTest.xml | 12 +- ...AssertProductOnCategoryPageActionGroup.xml | 20 -- ...leteProductAttributeByLabelActionGroup.xml | 29 -- ...ProductAbsentOnCategoryPageActionGroup.xml | 25 -- ...roductWithTierPriceForGeneralGroupTest.xml | 4 +- ...eInStockVisibleInCategoryAndSearchTest.xml | 4 +- ...rontRecentlyComparedAtWebsiteLevelTest.xml | 6 +- .../CatalogPriceRuleActionGroup.xml | 2 +- ...eRuleByUIWithConditionIsSKUActionGroup.xml | 2 +- ...eProductWithAssignedSimpleProductsTest.xml | 271 ------------------ ...eForConfigurableProductWithOptionsTest.xml | 224 --------------- ...refrontSelectOptionDropDownActionGroup.xml | 22 -- ...CartAndMiniShoppingCartPerCustomerTest.xml | 6 +- ...pesOfCustomOptionToTheShoppingCartTest.xml | 5 +- ...hangedWhenSavingProductWithSameSkuTest.xml | 4 +- ...onfigurableProductBasedOnParentSkuTest.xml | 4 +- ...ctWithCreatingCategoryAndAttributeTest.xml | 4 +- ...ctWithTwoOptionsAssignedToCategoryTest.xml | 4 +- ...woOptionsWithoutAssignedToCategoryTest.xml | 4 +- ...efrontVisibilityOfDuplicateProductTest.xml | 8 +- ...ductQuickSearchUsingElasticSearch6Test.xml | 16 +- ...oreFrontMyAccountWithMultishipmentTest.xml | 117 -------- .../AdminReportsOrderedGroupedBySkuTest.xml | 4 +- ...tImageColorWhenFilterByColorFilterTest.xml | 4 +- ...SwatchAttributesDisplayInWidgetCMSTest.xml | 4 +- ...rlKeyForStoreViewAndMovingCategoryTest.xml | 87 ------ 26 files changed, 50 insertions(+), 842 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml delete mode 100644 app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml delete mode 100644 app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml delete mode 100644 app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml delete mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml index 26321f269e6d2..1c7cb39d7746f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml @@ -120,8 +120,8 @@ </actionGroup> <!-- Assert first bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstBundleProduct"> - <argument name="product" value="$$createFirstBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstBundleProduct"> + <argument name="productName" value="$$createFirstBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForFirstBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createFirstBundleProduct.id$$)}}"/> @@ -133,8 +133,8 @@ </actionGroup> <!-- Assert second bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondBundleProduct"> - <argument name="product" value="$$createSecondBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondBundleProduct"> + <argument name="productName" value="$$createSecondBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForSecondBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createSecondBundleProduct.id$$)}}"/> @@ -146,8 +146,8 @@ </actionGroup> <!-- Assert third bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertThirdBundleProduct"> - <argument name="product" value="$$createThirdBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertThirdBundleProduct"> + <argument name="productName" value="$$createThirdBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForThirdBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createThirdBundleProduct.id$$)}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml deleted file mode 100644 index 4225ed3a7fcc8..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml +++ /dev/null @@ -1,20 +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="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup" deprecated="some"> - <annotations> - <description>DEPRECATED. Use AssertStorefrontProductIsPresentOnCategoryPageActionGroup. - EXTENDS:StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice', 'moveMouseOverProduct', 'AssertAddToCart'</description> - </annotations> - <remove keyForRemoval="AssertProductPrice"/> - <remove keyForRemoval="moveMouseOverProduct"/> - <remove keyForRemoval="AssertAddToCart"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml deleted file mode 100644 index 15c3d55fb9382..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml +++ /dev/null @@ -1,29 +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="DeleteProductAttributeByLabelActionGroup"> - <annotations> - <description>DEPRECATED. Please use AdminDeleteProductAttributeByLabelActionGroup instead. Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="ProductAttribute"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.default_label}}" stepKey="setAttributeCode"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml deleted file mode 100644 index 0effec5329cc0..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml +++ /dev/null @@ -1,25 +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="StorefrontAssertProductAbsentOnCategoryPageActionGroup"> - <annotations> - <description>DEPRECATED. Please use AssertStorefrontProductAbsentOnCategoryPageActionGroup instead. - Navigate to category page and verify product is absent.</description> - </annotations> - <arguments> - <argument name="category" defaultValue="_defaultCategory"/> - <argument name="product" defaultValue="SimpleProduct"/> - </arguments> - <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <dontSee selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{product.name}}" stepKey="assertProductIsNotPresent"/> - <dontSee selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{product.price}}" stepKey="assertProductIsNotPricePresent"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 9305e9ed3fd49..5076ab2515332 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -96,8 +96,8 @@ <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> <argument name="category" value="$categoryEntity$"/> </actionGroup> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> - <argument name="product" value="virtualProductGeneralGroup"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="productName" value="{{virtualProductGeneralGroup.name}}"/> </actionGroup> <!--Verify customer see updated virtual product with tier price on product storefront page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index e0c5d83b4b014..ab5d23f0f875e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -108,8 +108,8 @@ <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> <argument name="category" value="$categoryEntity$"/> </actionGroup> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> - <argument name="product" value="updateVirtualProductTierPriceInStock"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="productName" value="{{updateVirtualProductTierPriceInStock.name}}"/> </actionGroup> <!--Verify customer see updated virtual product with tier price on product storefront page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml index f675e66f42aea..51150a4ecffb3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml @@ -35,7 +35,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product widget --> <actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget"> <argument name="widget" value="RecentlyComparedProductsWidget"/> @@ -51,7 +51,7 @@ <argument name="widget" value="RecentlyComparedProductsWidget"/> </actionGroup> <!-- Logout Admin --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Reset Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website--> <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/> <!-- Delete Products and Category --> @@ -66,7 +66,7 @@ <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessage"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterAddedProductToCart"/> <!--Add to compare Simple Product and Simple Product 2--> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct1ToCompare" > diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index d20798aef4838..85961a28fac53 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -11,7 +11,7 @@ <!-- action group to create a new catalog price rule giving a catalogRule entity --> <actionGroup name="NewCatalogPriceRuleByUIActionGroup"> <annotations> - <description>DEPRECATED. Please use set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup + <description>Consider using set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup Goes to the Catalog Price Rule grid. Clicks on Add. Fills in the provided Catalog Rule details.</description> </annotations> <arguments> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml index 217a7519defa7..bd8d1d21bf694 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup" extends="NewCatalogPriceRuleByUIActionGroup"> <annotations> - <description>DEPRECATED. Please use set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleAddSkuConditionActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup + <description>Consider using set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleAddSkuConditionActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided SKU.</description> </annotations> <arguments> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml deleted file mode 100644 index 4cc9449557520..0000000000000 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ /dev/null @@ -1,271 +0,0 @@ -<?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="AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest" deprecated="some"> - <annotations> - <features value="CatalogRuleConfigurable"/> - <stories value="Apply catalog price rule"/> - <title value="DEPRECATED. Apply catalog rule for configurable product with assigned simple products"/> - <description value="DEPRECATED. Admin should be able to apply catalog rule for configurable product with assigned simple products"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-14063"/> - <group value="catalogRuleConfigurable"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test instead</issueId> - </skip> - </annotations> - <before> - <!-- Create category for first configurable product --> - <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/> - - <!-- Create first configurable product with two options --> - <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct"> - <requiredEntity createDataKey="firstSimpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getFirstConfigAttributeSecondOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </getData> - - <!-- Create two child products for first configurable product --> - <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createFirstConfigSecondChildProduct"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createFirstConfigProductOption"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddSecondChild"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigSecondChildProduct"/> - </createData> - - <!-- Add customizable options to first product --> - <updateData createDataKey="createFirstConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateFirstProductWithOption"/> - - <!-- Create category for second configurable product --> - <createData entity="SimpleSubCategory" stepKey="secondSimpleCategory"/> - - <!-- Create second configurable product with two options --> - <createData entity="ApiConfigurableProduct" stepKey="createSecondConfigProduct"> - <requiredEntity createDataKey="secondSimpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createSecondConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createSecondConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createSecondConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addSecondProductToAttributeSet"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSecondConfigAttributeFirstOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSecondConfigAttributeSecondOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </getData> - - <!-- Create two child products for second configurable product --> - <createData entity="ApiSimpleOne" stepKey="createSecondConfigFirstChildProduct"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createSecondConfigSecondChildProduct"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createSecondConfigProductOption"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddFirstChild"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddSecondChild"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigSecondChildProduct"/> - </createData> - - <!-- Add customizable options to second product --> - <updateData createDataKey="createSecondConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateSecondProductWithOption"/> - - <!--Create customer group --> - <createData entity="CustomCustomerGroup" stepKey="customerGroup"/> - - <!-- Create Customer --> - <createData entity="SimpleUsCustomerWithNewCustomerGroup" stepKey="createCustomer"> - <requiredEntity createDataKey="customerGroup" /> - </createData> - - <!-- Login as Admin --> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> - </before> - <after> - <!-- Customer log out --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - - <!-- Delete created data --> - <deleteData createDataKey="createFirstConfigProduct" stepKey="deleteFirstConfigProduct"/> - <deleteData createDataKey="createFirstConfigFirstChildProduct" stepKey="deleteFirstConfigFirstChildProduct"/> - <deleteData createDataKey="createFirstConfigSecondChildProduct" stepKey="deleteFirstConfigSecondChildProduct"/> - <deleteData createDataKey="createFirstConfigProductAttribute" stepKey="deleteFirstConfigProductAttribute"/> - <deleteData createDataKey="firstSimpleCategory" stepKey="deleteFirstSimpleCategory"/> - - <deleteData createDataKey="createSecondConfigProduct" stepKey="deleteSecondConfigProduct"/> - <deleteData createDataKey="createSecondConfigFirstChildProduct" stepKey="deleteSecondConfigFirstChildProduct"/> - <deleteData createDataKey="createSecondConfigSecondChildProduct" stepKey="deleteSecondConfigSecondChildProduct"/> - <deleteData createDataKey="createSecondConfigProductAttribute" stepKey="deleteSecondConfigProductAttribute"/> - <deleteData createDataKey="secondSimpleCategory" stepKey="deleteSimpleCategory"/> - - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/> - - <!-- Delete created price rules --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForFirstOption"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <!-- Admin log out --> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - <!-- Create price rule --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createPriceRule"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - <actionGroup ref="CatalogSelectCustomerGroupActionGroup" stepKey="addCustomerGroup"> - <argument name="customerGroupName" value="$$customerGroup.code$$"/> - </actionGroup> - - <!-- Save price rule --> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessage"/> - - <!-- Run full reindex and clear caches --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Login to storefront from customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInFromCustomer"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!-- Assert first product in category --> - <amOnPage url="$$firstSimpleCategory.name$$.html" stepKey="goToFirstCategoryPageStorefront"/> - <waitForPageLoad stepKey="waitForFirstCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory"> - <argument name="productName" value="$$createFirstConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert second product in category --> - <amOnPage url="$$secondSimpleCategory.name$$.html" stepKey="goToSecondCategoryPageStorefront"/> - <waitForPageLoad stepKey="waitForSecondCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkSecondProductPriceInCategory"> - <argument name="productName" value="$$createSecondConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert first product in storefront product page --> - <amOnPage url="$$createFirstConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnFirstProductPage"/> - <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkFirstProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createFirstConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Add first product with selected options to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addFirstProductWithSelectedOptionToCart1"> - <argument name="product" value="$$createFirstConfigProduct$$"/> - <argument name="option" value="$$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons1.title}}"/> - </actionGroup> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabForthProductUpdatedPrice1"/> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addFirstProductWithSelectedOptionToCart2"> - <argument name="product" value="$$createFirstConfigProduct$$"/> - <argument name="option" value="$$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons3.title}}"/> - </actionGroup> - - <!-- Assert second product in storefront product page --> - <amOnPage url="$$createSecondConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnSecondProductPage"/> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkSecondProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createSecondConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Add second product with selected options to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addSecondProductWithSelectedOptionToCart1"> - <argument name="product" value="$$createSecondConfigProduct$$"/> - <argument name="option" value="$$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons1.title}}"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addSecondProductWithSelectedOptionToCart2"> - <argument name="product" value="$$createSecondConfigProduct$$"/> - <argument name="option" value="$$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons3.title}}"/> - </actionGroup> - - <!--Assert products prices in the cart --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> - <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> - <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForFirstProductOption"/> - <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForSecondProductOption"/> - <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForFirstProductOption"/> - <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForSecondProductOption"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml deleted file mode 100644 index 436fc7714ce2d..0000000000000 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ /dev/null @@ -1,224 +0,0 @@ -<?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="AdminApplyCatalogRuleForConfigurableProductWithOptionsTest" deprecated="some"> - <annotations> - <features value="CatalogRuleConfigurable"/> - <stories value="Apply catalog price rule"/> - <title value="DEPRECATED. Apply catalog price rule for configurable product with options"/> - <description value="DEPRECATED. Admin should be able to apply the catalog rule for configurable product with options"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-14062"/> - <group value="catalogRuleConfigurable"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminApplyCatalogRuleForConfigurableProductWithOptions2Test instead</issueId> - </skip> - </annotations> - <before> - <!-- Create category --> - <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - - <!-- Create configurable product with three options --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="simpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeThirdOption"> - <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> - <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeThirdOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create three child products --> - <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createConfigSecondChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeSecondOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createConfigThirdChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeThirdOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getConfigAttributeSecondOption"/> - <requiredEntity createDataKey="getConfigAttributeThirdOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigSecondChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddThirdChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigThirdChildProduct"/> - </createData> - - <!-- Login as Admin --> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> - </before> - <after> - <!-- Delete created data --> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteFirstSimpleProduct"/> - <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteSecondSimpleProduct"/> - <deleteData createDataKey="createConfigThirdChildProduct" stepKey="deleteThirdSimpleProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> - - <!-- Delete created price rules --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForFirstOption"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForSecondOption"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForThirdOption"> - <argument name="name" value="{{CatalogRuleWithoutDiscount.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <!-- Log out --> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - - <!-- Reindex invalidated indices after product attribute has been created/deleted --> - <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - </after> - - <!-- Create price rule for first configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createFirstPriceRule"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForFirstPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createFirstCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyFirstPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForFirstPriceRule"/> - - <!-- Create price rule for second configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createSecondPriceRule"> - <argument name="catalogRule" value="_defaultCatalogRule"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForSecondPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createSecondCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplySecondPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForSecondPriceRule"/> - - <!-- Create price rule for third configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createThirdPriceRule"> - <argument name="catalogRule" value="CatalogRuleWithoutDiscount"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForThirdPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createThirdCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyThirdPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForThirdPriceRule"/> - - <!-- Run full reindex and clear caches --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Assert product in storefront product page --> - <amOnPage url="$$createConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="assertUpdatedProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert product options price in storefront product page --> - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToFirstProductOption"> - <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToSecondProductOption"> - <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="$110.70"/> - </actionGroup> - - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToThirdProductOption"> - <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="{{ApiConfigurableProduct.price}}"/> - </actionGroup> - - <!-- Add product with selected option to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedFirstOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedSecondOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedThirdOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <!--Assert product price in the cart --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> - <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> - <see userInput="{{CatalogRuleToFixed.discount_amount}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForFirstProductOption"/> - <see userInput="$110.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForSecondProductOption"/> - <see userInput="{{ApiConfigurableProduct.price}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForThirdProductOption"/> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml deleted file mode 100644 index fa169373c1096..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontSelectOptionDropDownActionGroup"> - <annotations> - <description>DEPRECATED. Please use StorefrontProductPageSelectDropDownOptionValueActionGroup instead. Selects the provided Product Option Value under the provided Product Option Title on a Storefront Product page.</description> - </annotations> - <arguments> - <argument name="optionTitle" defaultValue="ProductOptionDropDown"/> - <argument name="option" defaultValue="ProductOptionValueDropdown2.title"/> - </arguments> - - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect(optionTitle.title)}}" userInput="{{option}}" stepKey="fillOptionDropDown"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index 741fde91f851e..8f7ff6a751b96 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -75,9 +75,9 @@ <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> <!-- Fill the custom options values --> - <actionGroup ref="StorefrontSelectOptionDropDownActionGroup" stepKey="selectFirstOption"> - <argument name="optionTitle" value="ProductOptionValueDropdown"/> - <argument name="option" value="ProductOptionValueWithSkuDropdown1.title"/> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOption"> + <argument name="attributeLabel" value="{{ProductOptionValueDropdown.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueWithSkuDropdown1.title}}"/> </actionGroup> <fillField selector="{{StorefrontProductInfoMainSection.productOptionFieldInput(ProductOptionField.title)}}" userInput="OptionField" stepKey="fillProductOptionInputField"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml index 8081abcff307b..e8a72b6e88109 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml @@ -48,7 +48,10 @@ <actionGroup ref="StorefrontAttachOptionFileActionGroup" stepKey="selectAndAttachFile"/> <!--Select Option From DropDown option --> - <actionGroup ref="StorefrontSelectOptionDropDownActionGroup" stepKey="selectDropDownOption"/> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOption"> + <argument name="attributeLabel" value="{{ProductOptionDropDown.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueDropdown2.title}}"/> + </actionGroup> <scrollTo selector="{{StorefrontProductInfoMainSection.customOptionLabel(ProductOptionMultiSelect.title)}}" stepKey="scrollToMultiSelect"/> <!-- Select CheckBox From CheckBox option --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml index d58e7cfab1350..6e26d73f3a36f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml @@ -27,8 +27,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Log out --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml index 78cbca2d4c099..e5456429373e1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml @@ -27,8 +27,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Log out --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml index 3913139c9b7e6..32117fdfe4366 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml @@ -36,8 +36,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 868690e19f6ba..bceabb27e3ae7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -39,8 +39,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index 9335f49c9bc2e..9bb5b5073215b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -35,8 +35,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 6126338461fdd..180172ac287d8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -36,12 +36,12 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <!--Delete product attributes--> <comment userInput="Delete product attributes" stepKey="deleteCommentAttributes"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridFirst"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductSecondAttribute"> - <argument name="ProductAttribute" value="productAttributeColor"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="{{deleteProductSecondAttribute.default_label}}"> + <argument name="productAttributeLabel" value="productAttributeColor"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridSecond"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml index 3cb06014234dc..c3b76d4ff1c9e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -46,15 +46,15 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchSimpleProduct"> <argument name="phrase" value="AAA"/> </actionGroup> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductOnCatalogSearchPage"> - <argument name="product" value="$createFirstProduct$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstProductOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertSecondProductIsMissingOnCatalogSearchPage"> <argument name="productName" value="$createSecondProduct.name$"/> </actionGroup> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickNextPageCatalogSearchPager"/> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductOnCatalogSearchPage"> - <argument name="product" value="$createSecondProduct$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondProductOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnCatalogSearchPage"> <argument name="productName" value="$createFirstProduct.name$"/> @@ -64,11 +64,11 @@ <dontSeeInCurrentUrl stepKey="assertRedirectedToFirstPage" url="?p=2"/> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> - <argument name="product" value="$createFirstProduct$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> </actionGroup> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> - <argument name="product" value="$createSecondProduct$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml deleted file mode 100644 index fd15b344da600..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?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="StorefrontMyAccountWithMultishipmentTest" deprecated="some"> - <annotations> - <features value="Multishipping"/> - <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> - <title value="DEPRECATED. Verify Shipping price for Storefront after multiple address checkout"/> - <description value="Verify that shipping price on My account matches with shipping method prices after multiple addresses checkout (Order view page)"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19303"/> - <group value="multishipping"/> - <skip> - <issueId value="DEPRECATED">Please use StorefrontOrderWithMultishippingTest instead</issueId> - </skip> - </annotations> - - <before> - <createData entity="SimpleSubCategory" stepKey="category"/> - <createData entity="SimpleProduct" stepKey="product1"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="SimpleProduct" stepKey="product2"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="Simple_US_Customer_Two_Addresses" stepKey="customer"/> - <!-- Set configurations --> - <magentoCLI command="config:set {{EnableMultiShippingCheckoutMultiple.path}} {{EnableMultiShippingCheckoutMultiple.value}}" stepKey="allowShippingToMultipleAddresses"/> - <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> - <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> - <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </before> - <after> - <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> - <magentoCLI command="config:set {{DisableMultiShippingCheckoutMultiple.path}} {{DisableMultiShippingCheckoutMultiple.value}}" stepKey="withdrawShippingToMultipleAddresses"/> - <deleteData createDataKey="category" stepKey="deleteCategory"/> - <deleteData createDataKey="product1" stepKey="deleteProduct1"/> - <deleteData createDataKey="product2" stepKey="deleteProduct2"/> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <magentoCLI command="config:set {{DisableFreeShippingMethod.path}} {{DisableFreeShippingMethod.value}}" stepKey="disableFreeShipping"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearAllFilters"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> - </after> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct1ToCart"> - <argument name="product" value="$$product1$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct2ToCart"> - <argument name="product" value="$$product2$$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> - <actionGroup ref="CheckingWithMultipleAddressesActionGroup" stepKey="checkoutWithMultipleAddresses"/> - <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> - <actionGroup ref="SelectMultiShippingInfoActionGroup" stepKey="checkoutWithMultipleShipping"/> - <!--Select Check / Money order Payment method--> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <actionGroup ref="SelectBillingInfoActionGroup" stepKey="checkoutWithPaymentMethod"/> - <waitForPageLoad stepKey="waitForReviewOrderPageLoad"/> - <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"> - <argument name="totalNameForFirstOrder" value="Shipping & Handling"/> - <argument name="totalPositionForFirstOrder" value="1"/> - <argument name="totalNameForSecondOrder" value="Shipping & Handling"/> - <argument name="totalPositionForSecondOrder" value="2"/> - </actionGroup> - <waitForPageLoad stepKey="waitForPlaceOrderPageLoad"/> - <actionGroup ref="StorefrontPlaceOrderForMultipleAddressesActionGroup" stepKey="placeOrder"> - <argument name="firstOrderPosition" value="1"/> - <argument name="secondOrderPosition" value="2"/> - </actionGroup> - <waitForPageLoad stepKey="waitForOrderPageLoad"/> - <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder"/> - <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForFirstOrder"> - <argument name="dataHref" value="$dataHrefForFirstOrderPlaceOrder"/> - </actionGroup> - <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder2"/> - <waitForPageLoad stepKey="waitForOrderPageLoad2"/> - <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForSecondOrder"> - <argument name="dataHref" value="$dataHrefForSecondOrderPlaceOrder"/> - </actionGroup> - <waitForPageLoad stepKey="waitForAdminPageToLoad"/> - <!-- Go to Stores > Configuration > Sales > Orders --> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage"/> - <waitForPageLoad stepKey="waitForOrderPageLoad3"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> - <!--Assert order in orders grid --> - <!-- Go to order page --> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openFirstOrderPage"> - <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> - </actionGroup> - <!-- Check status --> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeFirstOrderPendingStatus"/> - <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForFirstOrder"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage2"/> - <waitForPageLoad stepKey="waitForOrderPageLoad4"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters2"/> - <!-- Go to order page --> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openSecondOrderPage"> - <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> - </actionGroup> - <!-- Check status --> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeSecondOrderPendingStatus"/> - <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForSecondOrder"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="gotToHomePage"/> - </test> -</tests> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml index 6d0b335ac7094..650097214a0c4 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml @@ -28,8 +28,8 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProduct"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttributeSet"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttributeSet"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index 47ca8ce44bec3..03c09c70f3654 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -30,8 +30,8 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> - <argument name="ProductAttribute" value="visualSwatchAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> + <argument name="productAttributeLabel" value="{{visualSwatchAttribute.default_label}}"/> </actionGroup> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index a1d346ffa2744..2e58aba5963ec 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -36,8 +36,8 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> - <argument name="ProductAttribute" value="visualSwatchAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> + <argument name="productAttributeLabel" value="{{visualSwatchAttribute.default_label}}"/> </actionGroup> <!--delete root category--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml deleted file mode 100644 index 6a1bcb38bdb31..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?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="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest" deprecated="some"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Update url rewrites"/> - <title value="DEPRECATED. Check url rewrites in catalog categories after changing url key"/> - <description value="DEPRECATED. Check url rewrites in catalog categories after changing url key for store view and moving category"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-5352"/> - <group value="url_rewrite"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="DEPRECATED">Use AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test instead</issueId> - </skip> - </annotations> - <before> - <!-- Create two sub-categories in default category with simple products --> - <createData entity="_defaultCategory" stepKey="createFirstCategory"/> - <createData entity="_defaultProduct" stepKey="createFirstSimpleProduct"> - <requiredEntity createDataKey="createFirstCategory"/> - </createData> - <createData entity="_defaultCategory" stepKey="createSecondCategory"/> - <createData entity="_defaultProduct" stepKey="createSecondSimpleProduct"> - <requiredEntity createDataKey="createSecondCategory"/> - </createData> - - <!-- Log in to backend --> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - - <!--Create additional Store View in Main Website Store --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> - - <!-- Reindex and flush the cache --> - <magentoCLI command="indexer:reindex" stepKey="runReindex"/> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> - <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> - <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> - <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - </after> - - <!-- On the categories editing page change store view to created additional view --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="customStore.name"/> - <argument name="CatName" value="$$createFirstCategory.name$$"/> - </actionGroup> - - <!-- Change url key for category for first category; save --> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeUrlKey"> - <argument name="value" value="{{SimpleRootSubCategory.url_key}}"/> - </actionGroup> - - <!-- Change store view to "All store views" for first category --> - <actionGroup ref="SwitchCategoryToAllStoreViewActionGroup" stepKey="switchToAllStoreViewProduct"> - <argument name="CatName" value="$$createFirstCategory.name$$"/> - </actionGroup> - - <!-- Move first category inside second category --> - <actionGroup ref="MoveCategoryActionGroup" stepKey="moveFirstCategoryToSecondCategory"> - <argument name="childCategory" value="$$createFirstCategory.name$$"/> - <argument name="parentCategory" value="$$createSecondCategory.name$$"/> - </actionGroup> - - <!-- Switch default store view on store view created below for first category --> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> - <argument name="storeView" value="customStore"/> - </actionGroup> - - <!-- Assert category url with custom store view --> - <amOnPage url="{{StorefrontHomePage.url}}$$createSecondCategory.name$$/{{SimpleRootSubCategory.url_key}}.html" stepKey="amOnCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <see userInput="$$createFirstSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeProductInCategory"/> - </test> -</tests> From 7e5b5b150c5bb81d6f79aa77db68d14870704bab Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 10 Apr 2020 21:18:42 -0500 Subject: [PATCH 729/734] MQE-2046: remove deprecated entities usages in MFTF tests --- .../Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 180172ac287d8..801dfdb8540e8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -40,8 +40,8 @@ <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridFirst"/> - <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="{{deleteProductSecondAttribute.default_label}}"> - <argument name="productAttributeLabel" value="productAttributeColor"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductSecondAttribute"> + <argument name="productAttributeLabel" value="{{productAttributeColor.default_label}}"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridSecond"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> From 29e876ccc0625bdcb4d249ca613962cfd94771cb Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 13 Apr 2020 09:22:16 +0300 Subject: [PATCH 730/734] MC-33110: Paypal Express Checkout - payment loading infinitely in checkout --- .../Paypal/Model/SmartButtonConfig.php | 21 +++++++++- .../Test/Unit/Model/SmartButtonConfigTest.php | 13 ++++-- .../Unit/Model/_files/expected_config.php | 42 +++++++++++-------- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Paypal/Model/SmartButtonConfig.php b/app/code/Magento/Paypal/Model/SmartButtonConfig.php index 59e4db6d84201..921dc4679b089 100644 --- a/app/code/Magento/Paypal/Model/SmartButtonConfig.php +++ b/app/code/Magento/Paypal/Model/SmartButtonConfig.php @@ -13,7 +13,7 @@ use Magento\Store\Model\ScopeInterface; /** - * Smart button config + * Smart button configuration. */ class SmartButtonConfig { @@ -96,7 +96,14 @@ public function getConfig(string $page): array private function getDisallowedFunding(): array { $disallowedFunding = $this->config->getValue('disable_funding_options'); - return $disallowedFunding ? explode(',', $disallowedFunding) : []; + $result = $disallowedFunding ? explode(',', $disallowedFunding) : []; + + // PayPal Guest Checkout Credit Card Icons only available when Guest Checkout option is enabled + if ($this->isPaypalGuestCheckoutAllowed() === false && !in_array('CARD', $result)) { + array_push($result, 'CARD'); + } + + return $result; } /** @@ -168,4 +175,14 @@ private function updateStyles(array $styles, string $page): array return $styles; } + + /** + * Returns if is allowed PayPal Guest Checkout. + * + * @return bool + */ + private function isPaypalGuestCheckoutAllowed(): bool + { + return $this->config->getValue('solution_type') === Config::EC_SOLUTION_TYPE_SOLE; + } } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php index 5aa3dee0874b2..7446b9d7a74e3 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php @@ -13,9 +13,6 @@ use Magento\Framework\Locale\ResolverInterface; use Magento\Paypal\Model\ConfigFactory; -/** - * Class SmartButtonConfigTest - */ class SmartButtonConfigTest extends \PHPUnit\Framework\TestCase { /** @@ -65,6 +62,7 @@ protected function setUp() * * @param string $page * @param string $locale + * @param bool $isCustomize * @param string $disallowedFundings * @param string $layout * @param string $size @@ -73,6 +71,7 @@ protected function setUp() * @param string $color * @param string $installmentPeriodLabel * @param string $installmentPeriodLocale + * @param string $isPaypalGuestCheckoutEnabled * @param array $expected * @dataProvider getConfigDataProvider * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -89,13 +88,19 @@ public function testGetConfig( string $color, string $installmentPeriodLabel, string $installmentPeriodLocale, + string $isPaypalGuestCheckoutEnabled, array $expected = [] ) { - $this->localeResolverMock->expects($this->any())->method('getLocale')->willReturn($locale); + $this->localeResolverMock->method('getLocale')->willReturn($locale); $this->configMock->method('getValue')->will( $this->returnValueMap( [ ['merchant_id', null, 'merchant'], + [ + 'solution_type', + null, + $isPaypalGuestCheckoutEnabled ? Config::EC_SOLUTION_TYPE_SOLE : Config::EC_SOLUTION_TYPE_MARK + ], ['sandbox_flag', null, true], ['disable_funding_options', null, $disallowedFundings], ["{$page}_page_button_customize", null, $isCustomize], diff --git a/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php b/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php index 7256984ab5226..f17b9f9fb4979 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php @@ -18,6 +18,7 @@ 'blue', 'my_label', 'mx', + true, [ 'merchantId' => 'merchant', 'environment' => 'sandbox', @@ -48,6 +49,7 @@ 'blue', 'my_label', 'br', + true, [ 'merchantId' => 'merchant', 'environment' => 'sandbox', @@ -78,6 +80,7 @@ 'blue', 'my_label', 'br', + true, [ 'merchantId' => 'merchant', 'environment' => 'sandbox', @@ -95,11 +98,11 @@ 'isGuestCheckoutAllowed' => true ] ], - 'mini_cart' => [ + 'product' => [ 'cart', 'en', false, - null, + 'CREDIT', 'horizontal', 'small', 'pillow', @@ -107,28 +110,29 @@ 'blue', 'my_label', 'br', + true, [ 'merchantId' => 'merchant', 'environment' => 'sandbox', 'locale' => 'en', - 'allowedFunding' => ['CREDIT', 'ELV'], - 'disallowedFunding' => [], + 'allowedFunding' => ['ELV'], + 'disallowedFunding' => ['CREDIT'], 'styles' => [ 'layout' => 'vertical', 'size' => 'responsive', 'color' => 'gold', 'shape' => 'rect', - 'label' => 'paypal' + 'label' => 'paypal', ], 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ], - 'product' => [ + 'checkout_with_paypal_guest_checkout_disabled' => [ 'cart', - 'en', - false, - 'CREDIT', + 'en_BR', + true, + null, 'horizontal', 'small', 'pillow', @@ -136,21 +140,23 @@ 'blue', 'my_label', 'br', + false, [ 'merchantId' => 'merchant', 'environment' => 'sandbox', - 'locale' => 'en', - 'allowedFunding' => ['ELV'], - 'disallowedFunding' => ['CREDIT'], + 'locale' => 'en_BR', + 'allowedFunding' => ['CREDIT', 'ELV'], + 'disallowedFunding' => ['CARD'], 'styles' => [ - 'layout' => 'vertical', - 'size' => 'responsive', - 'color' => 'gold', - 'shape' => 'rect', - 'label' => 'paypal', + 'layout' => 'horizontal', + 'size' => 'small', + 'color' => 'blue', + 'shape' => 'pillow', + 'label' => 'installment', + 'installmentperiod' => 0 ], 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] - ] + ], ]; From 663c21b4d75c758af69cd3d9fe62a3f9be52dba8 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 13 Apr 2020 12:23:46 +0300 Subject: [PATCH 731/734] MC-33110: Paypal Express Checkout - payment loading infinitely in checkout --- .../Test/Unit/Model/SmartButtonConfigTest.php | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php index 7446b9d7a74e3..4002c78bb0ecc 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/SmartButtonConfigTest.php @@ -12,8 +12,13 @@ use Magento\Paypal\Model\SmartButtonConfig; use Magento\Framework\Locale\ResolverInterface; use Magento\Paypal\Model\ConfigFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class SmartButtonConfigTest extends \PHPUnit\Framework\TestCase +/** + * Test for smart button config + */ +class SmartButtonConfigTest extends TestCase { /** * @var \Magento\Paypal\Model\SmartButtonConfig @@ -21,15 +26,18 @@ class SmartButtonConfigTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $localeResolverMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $configMock; + /** + * @inheritdoc + */ protected function setUp() { $this->localeResolverMock = $this->getMockForAbstractClass(ResolverInterface::class); @@ -37,12 +45,12 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - /** @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject $scopeConfigMock */ + /** @var ScopeConfigInterface|MockObject $scopeConfigMock */ $scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); $scopeConfigMock->method('isSetFlag') ->willReturn(true); - /** @var \PHPUnit_Framework_MockObject_MockObject $configFactoryMock */ + /** @var MockObject $configFactoryMock */ $configFactoryMock = $this->getMockBuilder(ConfigFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) @@ -122,6 +130,8 @@ public function testGetConfig( } /** + * Get config data provider + * * @return array */ public function getConfigDataProvider() @@ -130,6 +140,8 @@ public function getConfigDataProvider() } /** + * Get default styles + * * @return array */ private function getDefaultStyles() @@ -138,6 +150,8 @@ private function getDefaultStyles() } /** + * Get allowed fundings + * * @return array */ private function getAllowedFundings() From 48b65332a658f40e57407f187f38bd4268a0d537 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Mon, 13 Apr 2020 11:45:00 -0500 Subject: [PATCH 732/734] MQE-2066: [MTF-to-MFTF] Update Created Integration Entity #751 --- .../Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml | 4 ++-- .../AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml | 4 ++-- .../Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml | 4 ++-- .../Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml | 4 ++-- .../Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml | 4 ++-- .../Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml index 41efc72c35fb4..50aa83bc79eed 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Navigate To Integrations Page --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> @@ -43,7 +43,7 @@ <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="searchForIntegration"> <argument name="name" value="{{defaultIntegrationData.name}} Updated"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- TEST BODY --> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml index 403e886ea8209..4be4301146a04 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminUpdateIntegrationEntityWithIncorrectPasswordTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Navigate To Integrations Page --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> @@ -36,7 +36,7 @@ <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- TEST BODY --> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml index 16bd135163307..d3ebe3cbc30de 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingCreateNewsletterTemplateTest.xml @@ -21,7 +21,7 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <!-- Delete Created Newsletter Template --> @@ -31,7 +31,7 @@ </actionGroup> <actionGroup ref="AdminMarketingOpenNewsletterTemplateFromGridActionGroup" stepKey="openTemplate"/> <actionGroup ref="AdminMarketingDeleteNewsletterTemplateActionGroup" stepKey="deleteTemplate"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--TEST BODY --> <!-- Navigate To MARKETING > Newsletter Template --> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml index edd20737eb0a7..94f0e0a0487d6 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapEntityTest.xml @@ -19,13 +19,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteCreatedSitemap"> <argument name="filename" value="sitemap.xml"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--TEST BODY --> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml index 530e813ae440a..ae51db561e27c 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapFailFolderSaveTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--TEST BODY --> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml index 656502e09ad63..2300e05adce1d 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingCreateSitemapPathErrorTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--TEST BODY --> From a80b84d4eb4ee0c53aa0e717377c80f921caf7c8 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Mon, 13 Apr 2020 13:25:38 -0500 Subject: [PATCH 733/734] Update ObserverTest.php Fixed PHPUnit_Framework_MockObject_MockObject usages --- .../Sitemap/Test/Unit/Model/ObserverTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php index 8ab8cbfb6122b..7d2bdcc93b686 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php @@ -28,37 +28,37 @@ class ObserverTest extends \PHPUnit\Framework\TestCase private $observer; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ private $scopeConfigMock; /** - * @var \Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory|\PHPUnit\Framework\MockObject\MockObject */ private $collectionFactoryMock; /** - * @var \Magento\Sitemap\Model\ResourceModel\Sitemap\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sitemap\Model\ResourceModel\Sitemap\Collection|\PHPUnit\Framework\MockObject\MockObject */ private $sitemapCollectionMock; /** - * @var \Magento\Sitemap\Model\Sitemap|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sitemap\Model\Sitemap|\PHPUnit\Framework\MockObject\MockObject */ private $sitemapMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $objectManagerMock; /** - * @var Emulation|\PHPUnit_Framework_MockObject_MockObject + * @var Emulation|\PHPUnit\Framework\MockObject\MockObject */ private $appEmulationMock; /** - * @var EmailNotification|\PHPUnit_Framework_MockObject_MockObject + * @var EmailNotification|\PHPUnit\Framework\MockObject\MockObject */ private $emailNotificationMock; From 2234210fdab5ca3ed52ecce378af0652730f34d9 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 14 Apr 2020 11:05:30 +0300 Subject: [PATCH 734/734] MC-33151: Performance issue caused by checking if DB tables exist on every call --- .../Unit/App/Config/Source/RuntimeConfigSourceTest.php | 10 ++++++---- .../Store/Test/Unit/Model/ResourceModel/StoreTest.php | 10 ++++++---- .../Test/Unit/Model/ResourceModel/WebsiteTest.php | 10 ++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php b/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php index 104c1bbba2614..cf1c4c62787a7 100644 --- a/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php +++ b/app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php @@ -11,15 +11,17 @@ use Magento\Framework\DB\Adapter\TableNotFoundException; use Magento\Framework\DB\Select; use Magento\Store\App\Config\Source\RuntimeConfigSource; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RuntimeConfigSourceTest extends \PHPUnit\Framework\TestCase +class RuntimeConfigSourceTest extends TestCase { /** - * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + * @var DeploymentConfig|MockObject */ private $deploymentConfig; @@ -29,12 +31,12 @@ class RuntimeConfigSourceTest extends \PHPUnit\Framework\TestCase private $configSource; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $connection; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $resourceConnection; diff --git a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php index b4e6c5af7c9f9..731850ba58dcb 100644 --- a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/StoreTest.php @@ -11,22 +11,24 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\ResourceModel\Store; use Magento\Framework\DB\Adapter\AdapterInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class StoreTest extends \PHPUnit\Framework\TestCase +class StoreTest extends TestCase { /** @var Store */ protected $model; /** - * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection|MockObject */ protected $resourceMock; - /** @var Select | \PHPUnit_Framework_MockObject_MockObject */ + /** @var Select | MockObject */ protected $select; /** - * @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface|MockObject */ protected $connectionMock; diff --git a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php index 3238023cd6e09..6e8049e9e37e5 100644 --- a/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/ResourceModel/WebsiteTest.php @@ -10,22 +10,24 @@ use Magento\Framework\DB\Select; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\ResourceModel\Website; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; -class WebsiteTest extends \PHPUnit\Framework\TestCase +class WebsiteTest extends TestCase { /** @var Website */ protected $model; /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ResourceConnection|MockObject */ protected $resourceMock; - /** @var Select | \PHPUnit_Framework_MockObject_MockObject */ + /** @var Select | MockObject */ protected $select; /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\DB\Adapter\AdapterInterface|MockObject */ protected $connectionMock;